Switched to a connection map instead of looking for cell pin connectivity each time it is needed.

Signed-off-by: Maciej Kurc <mkurc@antmicro.com>
diff --git a/dsp-ff-plugin/dsp_ff.cc b/dsp-ff-plugin/dsp_ff.cc
index 752fe9a..cab0e8e 100644
--- a/dsp-ff-plugin/dsp_ff.cc
+++ b/dsp-ff-plugin/dsp_ff.cc
@@ -45,6 +45,79 @@
 
     // ..........................................
 
+    /// Connection map
+    struct ConnMap {
+
+        /// Maps source SigBit to all sinks it drives CellPin.
+        dict<RTLIL::SigBit, std::vector<CellPin>> sinks;
+        /// Maps source SigBit to its driver CellPin
+        dict<RTLIL::SigBit, CellPin> drivers;
+
+        /// Builds the map
+        void build(RTLIL::Module *module, const SigMap &sigmap)
+        {
+            clear();
+
+            // Scan cell ports
+            for (auto *cell : module->cells()) {
+                for (const auto &it : cell->connections_) {
+                    const auto &port = it.first;
+                    const auto &sigbits = it.second.bits();
+                    for (size_t i = 0; i < sigbits.size(); ++i) {
+                        auto sigbit = sigmap(sigbits[i]);
+
+                        // This is an input port (sink))
+                        if (cell->input(port)) {
+                            auto &vec = sinks[sigbit];
+                            vec.push_back(CellPin(cell, port, i));
+                        }
+                        // This is a source
+                        if (cell->output(port)) {
+                            drivers.insert(std::make_pair(sigbit, CellPin(cell, port, i)));
+                        }
+                    }
+                }
+            }
+
+            // Scan top-level ports
+            for (auto &it : module->wires_) {
+                auto *wire = it.second;
+
+                if (!wire->port_input && !wire->port_output) {
+                    continue;
+                }
+
+                RTLIL::SigSpec sigspec(wire, wire->start_offset, wire->width);
+                const auto &sigbits = sigspec.bits();
+                for (size_t i = 0; i < sigbits.size(); ++i) {
+                    auto sigbit = sigbits[i];
+                    if (!sigbit.wire) {
+                        continue;
+                    }
+
+                    // Output port (sink)
+                    if (sigbit.wire->port_output) {
+                        auto &vec = sinks[sigmap(sigbit)];
+                        vec.push_back(CellPin(nullptr, sigbit.wire->name, i));
+                    }
+                    // Input port (source)
+                    if (sigbit.wire->port_input) {
+                        drivers.insert(std::make_pair(sigbit, CellPin(nullptr, sigbit.wire->name, i)));
+                    }
+                }
+            }
+        }
+
+        /// Clears the map
+        void clear()
+        {
+            sinks.clear();
+            drivers.clear();
+        };
+    };
+
+    // ..........................................
+
     /// Describes a flip-flop type that can be integrated with a DSP cell
     struct FlopType {
         RTLIL::IdString name;
@@ -633,8 +706,9 @@
 
     /// Temporary SigBit to SigBit helper map.
     SigMap m_SigMap;
-    //    /// Net map
-    //    dict<RTLIL::SigBit, RTLIL::SigBit> m_NetMap;
+    /// Module connection map
+    ConnMap m_ConnMap;
+
     /// Cells to be removed (per module!)
     pool<RTLIL::Cell *> m_CellsToRemove;
     /// DSP cells that got changed
@@ -765,8 +839,9 @@
             m_SigMap.clear();
             m_SigMap.set(module);
 
-            //            // Build the net map
-            //            buildNetMap(module);
+            // Build the connection map
+            m_ConnMap.clear();
+            m_ConnMap.build(module, m_SigMap);
 
             // Look for DSP cells
             for (auto cell : module->cells()) {
@@ -792,14 +867,11 @@
 
         // Clear maps
         m_SigMap.clear();
+        m_ConnMap.clear();
     }
 
     // ..........................................
 
-    //    void buildNetMap (RTLIL::Module* a_Module) {
-    //        // TODO:
-    //    }
-
     bool checkFlop(RTLIL::Cell *a_Cell)
     {
         const auto &flopType = m_FlopTypes.at(a_Cell->type);
@@ -976,7 +1048,7 @@
 
             flops[port.name] = std::vector<RTLIL::Cell *>(sigbits.size(), nullptr);
             for (size_t i = 0; i < sigbits.size(); ++i) {
-                auto sigbit = sigbits[i];
+                auto sigbit = m_SigMap(sigbits[i]);
 
                 log_debug("  %2zu. ", i);
 
@@ -997,27 +1069,32 @@
                 // Get sinks(s), discard the port completely if more than one sink
                 // is found.
                 if (a_Cell->output(port.name)) {
-                    others = getSinks(CellPin(a_Cell, port.name, i));
-                    if (others.size() > 1) {
-                        log_debug("multiple sinks\n");
-                        flopsOk = false;
-                        continue;
-                    }
-                }
-                // Get driver. Discard if the driver drives something else too
-                // TODO: This is slow - we are first looking for a driver and then
-                // for all its sinks.
-                else if (a_Cell->input(port.name)) {
-                    auto driver = getDriver(CellPin(a_Cell, port.name, i));
-                    if (driver.cell != nullptr) {
-                        auto sinks = getSinks(driver);
-                        if (sinks.size() > 1) {
-                            log_debug("multiple sinks\n");
-                            flopsOk = false;
-                            continue;
+                    if (m_ConnMap.sinks.count(sigbit)) {
+                        for (const auto &sink : m_ConnMap.sinks.at(sigbit)) {
+                            if (sink.cell != nullptr && m_CellsToRemove.count(sink.cell)) {
+                                continue;
+                            }
+                            others.insert(sink);
                         }
                     }
-                    others.insert(driver);
+
+                }
+                // Get driver. Discard if the driver drives something else too
+                else if (a_Cell->input(port.name)) {
+                    if (m_ConnMap.drivers.count(sigbit)) {
+                        auto driver = m_ConnMap.drivers.at(sigbit);
+
+                        if (m_ConnMap.sinks.count(sigbit)) {
+                            auto sinks = m_ConnMap.sinks.at(sigbit);
+                            if (sinks.size() > 1) {
+                                log_debug("multiple sinks (%zu)\n", others.size());
+                                flopsOk = false;
+                                continue;
+                            }
+                        }
+
+                        others.insert(driver);
+                    }
                 }
 
                 // No others - unconnected
@@ -1026,6 +1103,12 @@
                     continue;
                 }
 
+                if (others.size() > 1) {
+                    log_debug("multiple sinks (%zu)\n", others.size());
+                    flopsOk = false;
+                    continue;
+                }
+
                 // Get the sink, check if this is a flip-flop
                 auto &other = *others.begin();
                 auto *flop = other.cell;
@@ -1285,157 +1368,6 @@
         return data;
     }
 
-    /// Retrieves a list of sinks driven by the given cell pin.
-    /// TODO: This is slow, need to make a lookup for that.
-    pool<CellPin> getSinks(const CellPin &a_Driver)
-    {
-
-        auto module = a_Driver.cell->module;
-        pool<CellPin> sinks;
-
-        // The driver has to be an output pin
-        log_assert(a_Driver.cell->output(a_Driver.port));
-
-        // Get the driver sigbit
-        auto driverSigspec = a_Driver.cell->getPort(a_Driver.port);
-        auto driverSigbit = m_SigMap(driverSigspec.bits().at(a_Driver.bit));
-
-        // Look for connected sinks
-        for (auto cell : module->cells()) {
-
-            if (m_CellsToRemove.count(cell)) {
-                continue;
-            }
-
-            for (auto conn : cell->connections()) {
-                auto port = conn.first;
-                auto sigspec = conn.second;
-
-                // Consider only sinks (inputs)
-                if (!cell->input(port)) {
-                    continue;
-                }
-
-                // Check all sigbits
-                auto sigbits = sigspec.bits();
-                for (size_t bit = 0; bit < sigbits.size(); ++bit) {
-
-                    auto sigbit = sigbits[bit];
-                    if (!sigbit.wire) {
-                        continue;
-                    }
-
-                    // Got a sink pin of another cell
-                    sigbit = m_SigMap(sigbit);
-                    if (sigbit == driverSigbit) {
-                        sinks.insert(CellPin(cell, port, bit));
-                    }
-                }
-            }
-        }
-
-        // Look for connected top-level output ports
-        for (auto conn : module->connections()) {
-            auto dst = conn.first;
-            auto src = conn.second;
-
-            auto sigbits = dst.bits();
-            for (size_t bit = 0; bit < sigbits.size(); ++bit) {
-
-                auto sigbit = sigbits[bit];
-                if (!sigbit.wire) {
-                    continue;
-                }
-
-                if (!sigbit.wire->port_output) {
-                    continue;
-                }
-
-                sigbit = m_SigMap(sigbit);
-                if (sigbit == driverSigbit) {
-                    sinks.insert(CellPin(nullptr, sigbit.wire->name, bit));
-                }
-            }
-        }
-
-        return sinks;
-    }
-
-    /// Finds a driver for the given cell pin
-    /// TODO: This is slow, need to make a lookup for that.
-    CellPin getDriver(const CellPin &a_Sink)
-    {
-        auto module = a_Sink.cell->module;
-
-        // The sink has to be an input pin
-        log_assert(a_Sink.cell->input(a_Sink.port));
-
-        // Get the sink sigbit
-        auto sinkSigspec = a_Sink.cell->getPort(a_Sink.port);
-        auto sinkSigbit = m_SigMap(sinkSigspec.bits().at(a_Sink.bit));
-
-        // Look for connected top-level input ports
-        for (auto conn : module->connections()) {
-            auto dst = conn.first;
-            auto src = conn.second;
-
-            auto sigbits = dst.bits();
-            for (size_t bit = 0; bit < sigbits.size(); ++bit) {
-
-                auto sigbit = sigbits[bit];
-                if (!sigbit.wire) {
-                    continue;
-                }
-
-                if (!sigbit.wire->port_input) {
-                    continue;
-                }
-
-                sigbit = m_SigMap(sigbit);
-                if (sigbit == sinkSigbit) {
-                    CellPin(nullptr, sigbit.wire->name, bit);
-                }
-            }
-        }
-
-        // Look for the driver among cells
-        for (auto cell : module->cells()) {
-
-            if (m_CellsToRemove.count(cell)) {
-                continue;
-            }
-
-            for (auto conn : cell->connections()) {
-                auto port = conn.first;
-                auto sigspec = conn.second;
-
-                // Consider only outputs
-                if (!cell->output(port)) {
-                    continue;
-                }
-
-                // Check all sigbits
-                auto sigbits = sigspec.bits();
-                for (size_t bit = 0; bit < sigbits.size(); ++bit) {
-
-                    auto sigbit = sigbits[bit];
-                    if (!sigbit.wire) {
-                        continue;
-                    }
-
-                    // Got a driver pin of another cell
-                    sigbit = m_SigMap(sigbit);
-                    if (sigbit == sinkSigbit) {
-                        return CellPin(cell, port, bit);
-                    }
-                }
-            }
-        }
-
-        // No driver found. FIXME: Implement a cleaner way of indicating that
-        return CellPin(nullptr, RTLIL::IdString(), -1);
-    }
-
 } DspFF;
 
 PRIVATE_NAMESPACE_END