First working version of the pass

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 0bb4f1f..952c0dd 100644
--- a/dsp_ff-plugin/dsp_ff.cc
+++ b/dsp_ff-plugin/dsp_ff.cc
@@ -41,6 +41,22 @@
                     (port == ref.port) &&
                     (bit  == ref.bit);
         }
+
+        std::string as_string () const {
+            if (cell != nullptr) {
+                return stringf("%s.%s[%d]",
+                    RTLIL::unescape_id(cell->name).c_str(),
+                    RTLIL::unescape_id(port).c_str(),
+                    bit
+                );
+            }
+            else {
+                return stringf("%s[%d]",
+                    RTLIL::unescape_id(port).c_str(),
+                    bit
+                );
+            }
+        }
     };
 
     // ..........................................
@@ -84,11 +100,20 @@
         dict<RTLIL::IdString, RTLIL::Const> connect;
     };
 
+    /// Describes a DSP cell type
     struct DspType {
         RTLIL::IdString name;
+
+        /// A list of data ports with registers
         std::vector<DspPortType> ports;
     };
 
+    /// Describes a changes made to a DSP cell
+    struct DspChanges {
+        pool<RTLIL::IdString>  params; // Modified params
+        pool<RTLIL::IdString>  conns;  // Altered connections (ports)
+    };
+
     // ..........................................
 
     struct FlopData {
@@ -505,6 +530,8 @@
 //    dict<RTLIL::SigBit, RTLIL::SigBit> m_NetMap;
     /// Cells to be removed (per module!)
     pool<RTLIL::Cell*> m_CellsToRemove;
+    /// DSP cells that got changed
+    dict<RTLIL::Cell*, DspChanges> m_DspChanges;
 
     /// DSP types
     dict<RTLIL::IdString, DspType>    m_DspTypes;
@@ -583,17 +610,7 @@
                         continue;
                     }
 
-                    if (cell->input(portRule.name)) {
-                        processInputPort(cell, portRule);
-                    }
-                    else if (cell->output(portRule.name)) {
-                        processOutputPort(cell, portRule);
-                    }
-                    else {
-                        log(" The port '%s.%s' is neither input nor output!\n",
-                            cell->type.c_str(), portRule.name.c_str());
-                        continue;
-                    }
+                    processPort(cell, portRule);
                 }
             }
 
@@ -655,12 +672,13 @@
         return isOk;
     }
 
-/*    
+    
     bool checkFlopDataAgainstDspPort (const FlopData& a_FlopData,
                                       RTLIL::Cell* a_Cell,
                                       const DspPortType& a_PortRule)
     {
         const auto& flopType = m_FlopTypes.at(a_FlopData.type);
+        const auto& changes  = m_DspChanges[a_Cell];
         bool isOk = true;
 
         log_debug("  Checking connected flip-flop settings against the DSP port... ");
@@ -668,7 +686,7 @@
         // Check control signal connections
         for (const auto& it : a_PortRule.assoc) {
             const auto& key  = it.first;
-            const auto& port = it.second;
+            const auto& port = it.second.first;
 
             SigBit conn(RTLIL::Sx);
             if (!port.empty() && a_Cell->hasPort(port)) {
@@ -688,42 +706,53 @@
             }
         }
 
+        auto checkParam = [&](const RTLIL::IdString& name,
+                              const RTLIL::Const& curr,
+                              const RTLIL::Const& next)
+        {
+            if (curr != next && changes.params.count(name)) {
+                log_debug("\n   the param '%s' mismatch ('%s' instead of '%s')",
+                    name.c_str(), curr.decode_string().c_str(),
+                    next.decode_string().c_str());
+                isOk = false;
+                return false;
+            }
+            return true;
+        };
+
+        // Check parameters to be mapped (by the port rule)
+        for (const auto& it : a_PortRule.params.map) {
+            if (a_Cell->hasParam(it.first) && a_FlopData.params.dsp.count(it.second)) {
+                const auto curr = a_Cell->getParam(it.first);
+                const auto flop = a_FlopData.params.dsp.at(it.second);
+                checkParam(it.first, curr, flop);
+            }
+        }
+
         // Check parameters to be set (by the port rule)
         for (const auto& it : a_PortRule.params.set) {
             if (a_Cell->hasParam(it.first)) {
                 const auto curr = a_Cell->getParam(it.first);
-                if (curr != it.second) {
-                    log_debug("\n   the param '%s' mismatch ('%s' instead of '%s')",
-                        it.first.c_str(), curr.decode_string().c_str(),
-                        it.second.decode_string().c_str());
-                    isOk = false;
-                }
-            }
-        }
-
-        // Check parameters to be mapped (by the port rule)
-        for (const auto& it : a_PortRule.params.map) {
-            if (a_Cell->hasParam(it.first) && a_FlopData.params.count(it.second)) {
-                const auto curr = a_Cell->getParam(it.first);
-                const auto flop = a_FlopData.params.at(it.second);
-                if (curr != flop) {
-                    log_debug("\n   the param '%s' mismatch ('%s' instead of '%s')",
-                        it.first.c_str(), curr.decode_string().c_str(),
-                        flop.decode_string().c_str());
-                    isOk = false;
-                }
+                checkParam(it.first, curr, it.second);
             }
         }
         
-//        // Check parameters to be set (by the flip-flop type)
-//        for (const auto& it : flopType.params.set) {
-//            const auto curr = a_Cell->getParam(it.first);
-//            if (curr == it.second) {
-//                log_debug("\n   the param '%s' is already set to '%s'",
-//                    it.first.c_str(), it.second.decode_string().c_str());
-//                isOk = false;
-//            }
-//        }
+        // Check parameters to be mapped (by the flip-flop rule)
+        for (const auto& it : flopType.params.map) {
+            if (a_Cell->hasParam(it.first) && a_FlopData.params.dsp.count(it.second)) {
+                const auto curr = a_Cell->getParam(it.first);
+                const auto flop = a_FlopData.params.dsp.at(it.second);
+                checkParam(it.first, curr, flop);
+            }
+        }
+
+        // Check parameters to be set (by the flip-flop rule)
+        for (const auto& it : flopType.params.set) {
+            if (a_Cell->hasParam(it.first)) {
+                const auto curr = a_Cell->getParam(it.first);
+                checkParam(it.first, curr, it.second);
+            }
+        }
         
         if (isOk) {
             log_debug("Ok\n");
@@ -732,7 +761,6 @@
         }
         return isOk;
     }
-*/
 
     static std::string sigBitName (const RTLIL::SigBit& a_SigBit) {
         if (a_SigBit.is_wire()) {
@@ -754,16 +782,7 @@
 
     // ..........................................
 
-    void processInputPort (RTLIL::Cell* a_Cell, const DspPortType& a_PortRule) {
-
-        log_debug(" Attempting flip-flop integration for %s.%s of %s\n",
-            a_Cell->type.c_str(), a_PortRule.name.c_str(), a_Cell->name.c_str());
-
-        // TODO
-        log_debug("  TODO: An input port\n");
-    }
-
-    void processOutputPort (RTLIL::Cell* a_Cell, const DspPortType& a_PortRule) {
+    void processPort (RTLIL::Cell* a_Cell, const DspPortType& a_PortRule) {
 
         log_debug(" Attempting flip-flop integration for %s.%s of %s\n",
             a_Cell->type.c_str(), a_PortRule.name.c_str(), a_Cell->name.c_str());
@@ -790,22 +809,42 @@
                 continue;
             }
 
+            log_assert(a_Cell->output(a_PortRule.name) || a_Cell->input(a_PortRule.name));
+
+            pool<CellPin> others;
+
             // Get sinks(s), discard the port completely if more than one sink
             // is found.
-            auto sinks = getSinks(CellPin(a_Cell, a_PortRule.name, i));
-            if (sinks.size() > 1) {
-                log_debug("  multiple sinks found, cannot integrate.\n");
-                return;
+            if (a_Cell->output(a_PortRule.name)) {
+                others = getSinks(CellPin(a_Cell, a_PortRule.name, i));
+                if (others.size() > 1) {
+                    log_debug("  multiple sinks found, cannot integrate.\n");
+                    return;
+                }
+            }
+            // 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(a_PortRule.name)) {
+                auto driver = getDriver(CellPin(a_Cell, a_PortRule.name, i));
+                if (driver.cell != nullptr) {
+                    auto sinks = getSinks(driver);
+                    if (sinks.size() > 1) {
+                        log_debug("  multiple sinks found, cannot integrate.\n");
+                        return;
+                    }
+                }
+                others.insert(driver);
             }
 
-            // No sinks - output unconnected
-            if (sinks.empty()) {
+            // No others - unconnected
+            if (others.empty()) {
                 continue;
             }
 
             // Get the sink, check if this is a flip-flop
-            auto& sink = *sinks.begin();
-            auto* flop = sink.cell;
+            auto& other = *others.begin();
+            auto* flop  = other.cell;
             if (flop == nullptr || !m_FlopTypes.count(flop->type)) {
                 continue;
             }
@@ -815,10 +854,17 @@
                 continue;
             }
 
-            // Check if the connection goes to the data input port
+            // Check if the connection goes to the data input/output port
             const auto& flopType = m_FlopTypes.at(flop->type);
-            if (flopType.ports.at(RTLIL::escape_id("d")) != sink.port) {
-                continue;
+            if (a_Cell->output(a_PortRule.name)) {
+                if (flopType.ports.at(RTLIL::escape_id("d")) != other.port) {
+                    continue;
+                }
+            }
+            else if (a_Cell->input(a_PortRule.name)) {
+                if (flopType.ports.at(RTLIL::escape_id("q")) != other.port) {
+                    continue;
+                }
             }
 
             // Skip if the flip-flop is going to be removed
@@ -862,11 +908,10 @@
 
         // Validate the flip flop data agains the DSP cell
         const auto& flopData = groups.begin()->first;
-        const auto& flopType = m_FlopTypes.at(flopData.type);
-//        if (!checkFlopDataAgainstDspPort(flopData, a_Cell, a_PortRule)) {
-//            log_debug("  flip-flop vs. DSP check failed\n");
-//            return;
-//        }
+        if (!checkFlopDataAgainstDspPort(flopData, a_Cell, a_PortRule)) {
+            log_debug("  flip-flop vs. DSP check failed\n");
+            return;
+        }
 
         // Debug log
         log(" %s %s.%s\n", a_Cell->type.c_str(), a_Cell->name.c_str(), a_PortRule.name.c_str());
@@ -882,6 +927,7 @@
         }
 
         // Reconnect data signals, mark the flip-flop for removal
+        const auto& flopType = m_FlopTypes.at(flopData.type);
         for (size_t i=0; i<flops.size(); ++i) {
 
             auto* flop = flops[i].first;
@@ -889,7 +935,14 @@
                 continue;
             }
 
-            const auto& port = flopType.ports.at(RTLIL::escape_id("q"));
+            RTLIL::IdString port;
+            if (a_Cell->output(a_PortRule.name)) {
+                port = flopType.ports.at(RTLIL::escape_id("q"));
+            }
+            else if (a_Cell->input(a_PortRule.name)) {
+                port = flopType.ports.at(RTLIL::escape_id("d"));
+            }
+            
             if (!flop->hasPort(port)) {
                 log_error("  cell '%s' does not have port '%s'!\n",
                     flop->type.c_str(), port.c_str());
@@ -920,6 +973,7 @@
             log_debug("  connecting %s.%s to %s\n", a_Cell->type.c_str(),
                 port.c_str(), sigBitName(conn).c_str());
             a_Cell->setPort(port, conn);
+            m_DspChanges[a_Cell].conns.insert(port);
         }
 
         // Connect control signals according to DSP port rule
@@ -927,6 +981,7 @@
             log_debug("  connecting %s.%s to %s\n", a_Cell->type.c_str(),
                 it.first.c_str(), it.second.as_string().c_str());
             a_Cell->setPort(it.first, it.second);
+            m_DspChanges[a_Cell].conns.insert(it.first);
         }
 
         // Map parameters (port rule)
@@ -935,6 +990,7 @@
                 const auto& param = flopData.params.dsp.at(it.second);
                 log_debug("  setting param '%s' to '%s'\n", it.first.c_str(), param.decode_string().c_str());
                 a_Cell->setParam(it.first, param);
+                m_DspChanges[a_Cell].params.insert(it.first);
             }
         }
 
@@ -944,6 +1000,7 @@
                 const auto& param = flopData.params.dsp.at(it.second);
                 log_debug("  setting param '%s' to '%s'\n", it.first.c_str(), param.decode_string().c_str());
                 a_Cell->setParam(it.first, param);
+                m_DspChanges[a_Cell].params.insert(it.first);
             }
         }
 
@@ -951,12 +1008,14 @@
         for (const auto& it : a_PortRule.params.set) {
             log_debug("  setting param '%s' to '%s'\n", it.first.c_str(), it.second.decode_string().c_str());
             a_Cell->setParam(it.first, it.second);
+            m_DspChanges[a_Cell].params.insert(it.first);
         }
 
         // Set parameters (flip-flop rule)
         for (const auto& it : flopType.params.set) {
             log_debug("  setting param '%s' to '%s'\n", it.first.c_str(), it.second.decode_string().c_str());
             a_Cell->setParam(it.first, it.second);
+            m_DspChanges[a_Cell].params.insert(it.first);
         }
     }
 
@@ -1025,13 +1084,11 @@
         pool<CellPin> sinks;
 
         // The driver has to be an output pin
-        if (!a_Driver.cell->output(a_Driver.port)) {
-            return sinks;
-        }
+        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));
+        auto driverSigbit  = m_SigMap(driverSigspec.bits().at(a_Driver.bit));
 
         // Look for connected sinks
         for (auto cell : module->cells()) {
@@ -1089,6 +1146,75 @@
         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()) {
+            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
+        return CellPin(nullptr, RTLIL::IdString(), -1);
+    }
+
 } DspFF;
 
 PRIVATE_NAMESPACE_END