WIP integration of output-ffs into DSP cells

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 738e428..0bb4f1f 100644
--- a/dsp_ff-plugin/dsp_ff.cc
+++ b/dsp_ff-plugin/dsp_ff.cc
@@ -45,39 +45,42 @@
 
     // ..........................................
 
+    /// Describes a flip-flop type that can be integrated with a DSP cell
     struct FlopType {
         RTLIL::IdString name;
 
-        struct {
-            RTLIL::IdString clk;
-            RTLIL::IdString rst;
-            RTLIL::IdString ena;
-            RTLIL::IdString d;
-            RTLIL::IdString q;
-        } ports;
+        /// A dict of port names indexed by their functions (like "clk", "rst")
+        dict<RTLIL::IdString, RTLIL::IdString> ports;
 
         struct {
-            std::vector<RTLIL::IdString>           matching;
-            dict<RTLIL::IdString, RTLIL::Const>    required;
-            dict<RTLIL::IdString, RTLIL::Const>    set;
+            /// A list of parameters that must match for all flip-flops
+            std::vector<RTLIL::IdString> matching;
+            /// A dict of parameter values that must match for a flip-flop
+            dict<RTLIL::IdString, RTLIL::Const> required;
+            /// A dict of parameters to be set in the DSP cell after integration
+            dict<RTLIL::IdString, RTLIL::Const> set;
+            /// A dict of parameters to be mapped to the DSP cell after integration
             dict<RTLIL::IdString, RTLIL::IdString> map;
         } params;
     };
 
+    /// Describes a DSP cell port that has built-in register (flip-flops)
     struct DspPortType {
         RTLIL::IdString name;
 
-        struct {
-            RTLIL::IdString clk;
-            RTLIL::IdString rst;
-            RTLIL::IdString ena;
-        } assoc;
+        /// A dict of associated cell ports indexed by their function (like "clk, "rst")
+        /// along with the default value to connect when unused.
+        dict<RTLIL::IdString, std::pair<RTLIL::IdString, RTLIL::State>> assoc;
 
         struct {
+            /// A dict of parameters to be set in the cell after integration
             dict<RTLIL::IdString, RTLIL::Const>    set;
+            /// A dict of parameters to be mapped to the cell after integration
             dict<RTLIL::IdString, RTLIL::IdString> map;
         } params;
 
+        /// A list of ports to be connected to specific constants after flip-flop
+        /// integration.
         dict<RTLIL::IdString, RTLIL::Const> connect;
     };
 
@@ -88,6 +91,38 @@
 
     // ..........................................
 
+    struct FlopData {
+        RTLIL::IdString type;
+        dict<RTLIL::IdString, RTLIL::SigBit> conns;
+        struct {
+            dict<RTLIL::IdString, RTLIL::Const> flop;
+            dict<RTLIL::IdString, RTLIL::Const> dsp;
+        } params;
+
+        FlopData (const RTLIL::IdString& _type) : type(_type) {};
+
+        FlopData (const FlopData& ref) = default;
+        FlopData (FlopData&& ref) = default;
+
+        unsigned int hash () const {
+            unsigned int h = 0;
+            h = mkhash_add(h, type.hash());
+            h = mkhash_add(h, conns.hash());
+            h = mkhash_add(h, params.flop.hash());
+            h = mkhash_add(h, params.dsp.hash());
+            return h;
+        }
+
+        bool operator == (const FlopData& ref) const {
+            return  (type == ref.type) &&
+                    (conns == ref.conns) &&
+                    (params.flop == ref.params.flop) &&
+                    (params.dsp == ref.params.dsp);
+        }
+    };
+
+    // ..........................................
+
     void load_rules(const std::string& a_FileName) {
 
         // Parses a vector of strings like "<name>=<value>" starting from the
@@ -181,6 +216,9 @@
                 auto& ports = dspTypes.back().ports;
                 ports.resize(ports.size() + 1);
                 ports.back().name = RTLIL::escape_id(fields[1]);
+                ports.back().assoc.insert(std::make_pair(RTLIL::escape_id("clk"), std::make_pair(RTLIL::IdString(), RTLIL::Sx)));
+                ports.back().assoc.insert(std::make_pair(RTLIL::escape_id("rst"), std::make_pair(RTLIL::IdString(), RTLIL::Sx)));
+                ports.back().assoc.insert(std::make_pair(RTLIL::escape_id("ena"), std::make_pair(RTLIL::IdString(), RTLIL::Sx)));
             }
             else if (fields[0] == "endport") {
                 if (fields.size() != 1) {
@@ -204,6 +242,11 @@
 
                 flopTypes.resize(flopTypes.size() + 1);
                 flopTypes.back().name = RTLIL::escape_id(fields[1]);
+                flopTypes.back().ports.insert(std::make_pair(RTLIL::escape_id("clk"), RTLIL::IdString()));
+                flopTypes.back().ports.insert(std::make_pair(RTLIL::escape_id("rst"), RTLIL::IdString()));
+                flopTypes.back().ports.insert(std::make_pair(RTLIL::escape_id("ena"), RTLIL::IdString()));
+                flopTypes.back().ports.insert(std::make_pair(RTLIL::escape_id("d"),   RTLIL::IdString()));
+                flopTypes.back().ports.insert(std::make_pair(RTLIL::escape_id("q"),   RTLIL::IdString()));
             }
             else if (fields[0] == "endff") {
                 if (fields.size() != 1) {
@@ -227,10 +270,10 @@
                 // Associated clock
                 if (tok.back() == "port") {
                     auto& ports = dspTypes.back().ports;
-                    ports.back().assoc.clk = RTLIL::escape_id(fields[1]);
+                    ports.back().assoc[RTLIL::escape_id("clk")] = std::make_pair(RTLIL::escape_id(fields[1]), RTLIL::S0);
                 }
                 else if (tok.back() == "ff") {
-                    flopTypes.back().ports.clk = RTLIL::escape_id(fields[1]);
+                    flopTypes.back().ports[RTLIL::escape_id("clk")] = RTLIL::escape_id(fields[1]);
                 }
             }
             else if (fields[0] == "rst") {
@@ -244,10 +287,10 @@
                 // Associated reset
                 if (tok.back() == "port") {
                     auto& ports = dspTypes.back().ports;
-                    ports.back().assoc.rst = RTLIL::escape_id(fields[1]);
+                    ports.back().assoc[RTLIL::escape_id("rst")] = std::make_pair(RTLIL::escape_id(fields[1]), RTLIL::S0);
                 }
                 else if (tok.back() == "ff") {
-                    flopTypes.back().ports.rst = RTLIL::escape_id(fields[1]);
+                    flopTypes.back().ports[RTLIL::escape_id("rst")] = RTLIL::escape_id(fields[1]);
                 }
             }
             else if (fields[0] == "ena") {
@@ -261,10 +304,10 @@
                 // Associated enable
                 if (tok.back() == "port") {
                     auto& ports = dspTypes.back().ports;
-                    ports.back().assoc.ena = RTLIL::escape_id(fields[1]);
+                    ports.back().assoc[RTLIL::escape_id("ena")] = std::make_pair(RTLIL::escape_id(fields[1]), RTLIL::S0);
                 }
                 else if (tok.back() == "ff") {
-                    flopTypes.back().ports.ena = RTLIL::escape_id(fields[1]);
+                    flopTypes.back().ports[RTLIL::escape_id("ena")] = RTLIL::escape_id(fields[1]);
                 }
             }
 
@@ -276,7 +319,7 @@
                     log_error(" unexpected keyword '%s'\n", fields[0].c_str());
                 }
 
-                flopTypes.back().ports.d = RTLIL::escape_id(fields[1]);
+                flopTypes.back().ports[RTLIL::escape_id("d")] = RTLIL::escape_id(fields[1]);
             }
             else if (fields[0] == "q") {
                 if (fields.size() != 2) {
@@ -286,7 +329,7 @@
                     log_error(" unexpected keyword '%s'\n", fields[0].c_str());
                 }
 
-                flopTypes.back().ports.q = RTLIL::escape_id(fields[1]);
+                flopTypes.back().ports[RTLIL::escape_id("q")] = RTLIL::escape_id(fields[1]);
             }
 
             // Parameters to set
@@ -404,9 +447,9 @@
             log(" ports:\n");
             for (const auto& port : dsp.ports) {
                 log("  %s.%s\n", dsp.name.c_str(), port.name.c_str());
-                log("   clk: %s\n", !port.assoc.clk.empty() ? port.assoc.clk.c_str() : "<none>");
-                log("   rst: %s\n", !port.assoc.rst.empty() ? port.assoc.rst.c_str() : "<none>");
-                log("   ena: %s\n", !port.assoc.ena.empty() ? port.assoc.ena.c_str() : "<none>");
+                for (const auto& it : port.assoc) {
+                    log("   %.3s: %s\n", it.first.c_str(), !it.second.first.empty() ? it.second.first.c_str() : "<none>");
+                }
 
                 if (!port.params.set.empty()) {
                     log("   set params:\n");
@@ -434,11 +477,10 @@
         for (const auto& it : m_FlopTypes) {
             const auto& ff = it.second;
             log(" %s\n", ff.name.c_str());
-            log("  clk: %s\n", !ff.ports.clk.empty() ? ff.ports.clk.c_str() : "<none>");
-            log("  rst: %s\n", !ff.ports.rst.empty() ? ff.ports.rst.c_str() : "<none>");
-            log("  ena: %s\n", !ff.ports.ena.empty() ? ff.ports.ena.c_str() : "<none>");
-            log("  d  : %s\n", !ff.ports.d.empty()   ? ff.ports.d.c_str()   : "<none>");
-            log("  q  : %s\n", !ff.ports.q.empty()   ? ff.ports.q.c_str()   : "<none>");
+            
+            for (const auto& it : ff.ports) {
+                log("  %.3s: %s\n", it.first.c_str(), !it.second.empty() ? it.second.c_str() : "<none>");
+            }
 
             if (!ff.params.set.empty()) {
                 log("  set params:\n");
@@ -459,8 +501,10 @@
 
     /// Temporary SigBit to SigBit helper map.
     SigMap m_SigMap;
-    /// Net map
-    dict<RTLIL::SigBit, RTLIL::SigBit> m_NetMap;
+//    /// Net map
+//    dict<RTLIL::SigBit, RTLIL::SigBit> m_NetMap;
+    /// Cells to be removed (per module!)
+    pool<RTLIL::Cell*> m_CellsToRemove;
 
     /// DSP types
     dict<RTLIL::IdString, DspType>    m_DspTypes;
@@ -481,7 +525,7 @@
         log("\n");
     }
 
-    void execute(std::vector<std::string> a_Args, RTLIL::Design *a_Design) override
+    void execute (std::vector<std::string> a_Args, RTLIL::Design *a_Design) override
     {
         log_header(a_Design, "Executing DSP_FF pass.\n");
 
@@ -517,13 +561,464 @@
             m_SigMap.clear();
             m_SigMap.set(module);
 
+//            // Build the net map
+//            buildNetMap(module);
+
+            // Look for DSP cells
+            for (auto cell : module->cells()) {
+
+                // Not a DSP
+                if (!m_DspTypes.count(cell->type)) {
+                    continue;
+                }
+
+                // Check ports
+                auto& rule = m_DspTypes.at(cell->type);
+                for (auto& portRule : rule.ports) {
+
+                    // Sanity check
+                    if (!cell->hasPort(portRule.name)) {
+                        log(" The DSP cell '%s' does not have a port named '%s'!\n",
+                            cell->type.c_str(), portRule.name.c_str());
+                        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;
+                    }
+                }
+            }
+
+            // Remove cells
+            for (const auto& cell : m_CellsToRemove) {
+                module->remove(cell);
+            }
+            m_CellsToRemove.clear();
         }
 
         // Clear maps
         m_SigMap.clear();
     }
 
-/*
+    // ..........................................
+
+//    void buildNetMap (RTLIL::Module* a_Module) {
+//        // TODO:
+//    }
+
+    bool checkDspPort (RTLIL::Cell* a_Cell, const DspPortType& a_PortRule) {
+        bool isOk = true;
+
+        // The cell parameters must not be set
+        for (const auto& it : a_PortRule.params.set) {
+            const auto curr = a_Cell->getParam(it.first);
+            if (curr == it.second) {
+                log_debug("  the param '%s' is already set to '%s'\n",
+                    it.first.c_str(), it.second.decode_string().c_str());
+                isOk = false;
+            }
+        }
+
+        return isOk;
+    }
+
+    bool checkFlop (RTLIL::Cell* a_Cell) {
+        const auto& flopType = m_FlopTypes.at(a_Cell->type);
+        bool isOk = true;
+
+        log_debug("  Checking connected flip-flop '%s' of type '%s'... ",
+            a_Cell->name.c_str(), a_Cell->type.c_str());
+
+        // Check if required parameters are set as they should be
+        for (const auto& it : flopType.params.required) {
+            const auto curr = a_Cell->getParam(it.first);
+            if (curr != it.second) {
+                log_debug("\n   param '%s' mismatch ('%s' instead of '%s')",
+                    it.first.c_str(), curr.decode_string().c_str(), it.second.decode_string().c_str());
+                isOk = false;
+            }
+        }
+
+        if (isOk) {
+            log_debug("Ok\n");
+        } else {
+            log_debug("\n");
+        }
+        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);
+        bool isOk = true;
+
+        log_debug("  Checking connected flip-flop settings against the DSP port... ");
+
+        // Check control signal connections
+        for (const auto& it : a_PortRule.assoc) {
+            const auto& key  = it.first;
+            const auto& port = it.second;
+
+            SigBit conn(RTLIL::Sx);
+            if (!port.empty() && a_Cell->hasPort(port)) {
+                auto sigspec = a_Cell->getPort(port);
+                auto sigbits = sigspec.bits();
+                log_assert(sigbits.size() <= 1);
+                if (!sigbits.empty()) {
+                    conn = m_SigMap(sigbits[0]);
+                }
+            }
+
+            if (conn.is_wire() || (!conn.is_wire() && conn.data != RTLIL::Sx)) {
+                if (conn != a_FlopData.conns.at(key)) {
+                    log_debug("\n   connection to port '%s' mismatch", port.c_str());
+                    isOk = false;
+                }
+            }
+        }
+
+        // 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;
+                }
+            }
+        }
+        
+//        // 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;
+//            }
+//        }
+        
+        if (isOk) {
+            log_debug("Ok\n");
+        } else {
+            log_debug("\n");
+        }
+        return isOk;
+    }
+*/
+
+    static std::string sigBitName (const RTLIL::SigBit& a_SigBit) {
+        if (a_SigBit.is_wire()) {
+            RTLIL::Wire* w = a_SigBit.wire;
+            return RTLIL::unescape_id(w->name);
+        } else {
+            switch (a_SigBit.data)
+            {
+            case RTLIL::State::S0: return "1'b0";
+            case RTLIL::State::S1: return "1'b1";
+            case RTLIL::State::Sx: return "1'bx";
+            case RTLIL::State::Sz: return "1'bz";
+            case RTLIL::State::Sa: return "-";
+            case RTLIL::State::Sm: return "m";
+            }
+            return "?";
+        }
+    }
+
+    // ..........................................
+
+    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) {
+
+        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());
+
+        // Check if the port can be used for FF integration
+        if (!checkDspPort(a_Cell, a_PortRule)) {
+            log_debug("  port check failed\n");
+            return;
+        }
+
+        // Get port connections
+        auto sigspec = a_Cell->getPort(a_PortRule.name);
+        auto sigbits = sigspec.bits();
+
+        // Collect flip-flops, identify their group count
+        dict<FlopData, int> groups;
+
+        std::vector<std::pair<RTLIL::Cell*, int>> flops
+            (sigbits.size(), std::make_pair(nullptr, -1));
+
+        for (size_t i=0; i<sigbits.size(); ++i) {
+            auto sigbit = sigbits[i];
+            if (!sigbit.wire) {
+                continue;
+            }
+
+            // 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;
+            }
+
+            // No sinks - output unconnected
+            if (sinks.empty()) {
+                continue;
+            }
+
+            // Get the sink, check if this is a flip-flop
+            auto& sink = *sinks.begin();
+            auto* flop = sink.cell;
+            if (flop == nullptr || !m_FlopTypes.count(flop->type)) {
+                continue;
+            }
+
+            // Must not have the "keep" attribute
+            if (flop->has_keep_attr()) {
+                continue;
+            }
+
+            // Check if the connection goes to the data input port
+            const auto& flopType = m_FlopTypes.at(flop->type);
+            if (flopType.ports.at(RTLIL::escape_id("d")) != sink.port) {
+                continue;
+            }
+
+            // Skip if the flip-flop is going to be removed
+            if (m_CellsToRemove.count(flop)) {
+                continue;
+            }
+
+            // Check the flip-flop
+            if (!checkFlop(flop)) {
+                continue;
+            }
+
+            // Get parameters to be mapped to the DSP according to the port
+            // rule.
+            dict<RTLIL::IdString, RTLIL::Const> mappedParams;
+            for (const auto& it : a_PortRule.params.map) {
+                if (flop->hasParam(it.second)) {
+                    const auto& value = flop->getParam(it.second);
+                    mappedParams.insert(std::make_pair(it.first, value));
+                }
+            }
+
+            // Store the flop and its data
+            auto res = groups.insert(
+                std::make_pair(getFlopData(flop, mappedParams),groups.size())
+            );
+            flops[i] = std::make_pair(flop, res.first->second);
+        }
+
+        // No matching flip-flop groups
+        if (groups.empty()) {
+            log_debug("  no matching flip-flops found\n");
+            return;
+        }
+
+        // Do not allow more than a single group
+        if (groups.size() != 1) {
+            log_debug("  %zu flip-flop groups, only a single one allowed\n", groups.size());
+            return;
+        }
+
+        // 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;
+//        }
+
+        // Debug log
+        log(" %s %s.%s\n", a_Cell->type.c_str(), a_Cell->name.c_str(), a_PortRule.name.c_str());
+        for (size_t i=0; i<flops.size(); ++i) {
+            if (flops[i].first != nullptr) {
+                log_debug("  %2zu. (%d) %s %s\n", i,
+                    flops[i].second,
+                    flops[i].first->type.c_str(), flops[i].first->name.c_str());
+            }
+            else {
+                log_debug("  %2zu. None\n", i);
+            }
+        }
+
+        // Reconnect data signals, mark the flip-flop for removal
+        for (size_t i=0; i<flops.size(); ++i) {
+
+            auto* flop = flops[i].first;
+            if (flop == nullptr) {
+                continue;
+            }
+
+            const auto& port = flopType.ports.at(RTLIL::escape_id("q"));
+            if (!flop->hasPort(port)) {
+                log_error("  cell '%s' does not have port '%s'!\n",
+                    flop->type.c_str(), port.c_str());
+            }
+
+            sigbits[i] = SigBit(RTLIL::Sx);
+            auto sigspec = flop->getPort(port);
+            log_assert(sigspec.bits().size() <= 1);
+            if (sigspec.bits().size() == 1) {
+                sigbits[i] = sigspec.bits()[0];
+            }
+
+            m_CellsToRemove.insert(flop);
+        }
+        a_Cell->setPort(a_PortRule.name, RTLIL::SigSpec(sigbits));
+
+        // Reconnect (map) control signals. Connect the default value if
+        // a particular signal is not present in the flip-flop.
+        for (const auto& it : a_PortRule.assoc) {
+            const auto& key  = it.first;
+            const auto& port = it.second.first;
+ 
+            auto conn = RTLIL::SigBit(it.second.second);
+            if (flopData.conns.count(key)) {
+                conn = flopData.conns.at(key);
+            }
+
+            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);
+        }
+
+        // Connect control signals according to DSP port rule
+        for (const auto& it : a_PortRule.connect) {
+            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);
+        }
+
+        // Map parameters (port rule)
+        for (const auto& it : a_PortRule.params.map) {            
+            if (flopData.params.dsp.count(it.second)) {
+                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);
+            }
+        }
+
+        // Map parameters (flip-flop rule)
+        for (const auto& it : flopType.params.map) {            
+            if (flopData.params.dsp.count(it.second)) {
+                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);
+            }
+        }
+
+        // Set parameters (port rule)
+        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);
+        }
+
+        // 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);
+        }
+    }
+
+    // ..........................................
+
+    /// Collects flip-flop connectivity data and parameters which defines the
+    /// group it belongs to.
+    FlopData getFlopData (RTLIL::Cell* a_Cell,
+                          const dict<RTLIL::IdString, RTLIL::Const>& a_ExtraParams)
+    {
+        FlopData data (a_Cell->type);
+
+        log_assert(m_FlopTypes.count(a_Cell->type) != 0);
+        const auto& flopType = m_FlopTypes.at(a_Cell->type);
+
+        // Gather connections to control ports
+        for (const auto& it : flopType.ports) {
+
+            // Skip "D" and "Q" as they connection will always differ.
+            if (it.first == RTLIL::escape_id("d") ||
+                it.first == RTLIL::escape_id("q"))
+            {
+                continue;
+            }
+
+            if (!it.second.empty() && a_Cell->hasPort(it.second)) {
+                auto sigspec = a_Cell->getPort(it.second);
+                auto sigbits = sigspec.bits();
+                log_assert(sigbits.size() <= 1);
+                if (!sigbits.empty()) {
+                    data.conns[it.first] = m_SigMap(sigbits[0]);
+                }
+            }
+        }
+
+        // Gather flip-flop parameters that need to match
+        for (const auto& it : flopType.params.matching) {
+            log_assert(a_Cell->hasParam(it));
+            data.params.flop.insert(std::make_pair(it, a_Cell->getParam(it)));
+        }
+
+        // Gather flip-flop parameters to be mapped to the DSP as well
+        for (const auto& it : flopType.params.map) {
+            log_assert(a_Cell->hasParam(it.second));
+            data.params.flop.insert(std::make_pair(it.second, a_Cell->getParam(it.second)));
+        }
+
+        // Gather DSP parameters and their values to be set to too
+        for (const auto& it : flopType.params.set) {
+            data.params.dsp.insert(it);
+        }
+
+        // Append extra DSP parameters
+        for (const auto& it : a_ExtraParams) {
+            data.params.dsp.insert(it);
+        }
+
+        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;
@@ -593,7 +1088,6 @@
 
         return sinks;
     }
-*/
 
 } DspFF;