Add dfflegalize pass.
authorMarcelina Kościelnicka <mwk@0x04.net>
Tue, 23 Jun 2020 12:36:34 +0000 (14:36 +0200)
committerMarcelina Kościelnicka <mwk@0x04.net>
Tue, 30 Jun 2020 23:57:15 +0000 (01:57 +0200)
CHANGELOG
passes/techmap/Makefile.inc
passes/techmap/dfflegalize.cc [new file with mode: 0644]

index 638ce558bfbd2da83ea027426d3d717829d96651..12fc885507313fd98de65acce18f5e3d6454312c 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -67,6 +67,8 @@ Yosys 0.9 .. Yosys 0.9-dev
     - Added "select -unset"
     - Use YosysHQ/abc instead of upstream berkeley-abc/abc
     - Added $divfloor and $modfloor cells
+    - Added $adffe, $dffsre, $sdff, $sdffe, $sdffce, $adlatch cells
+    - Added "dfflegalize" pass
 
 Yosys 0.8 .. Yosys 0.9
 ----------------------
index a54b4913d6e6ba36fc2616fc84908a6391f9efaa..e3b3d8dea11a2492629f6d361fc71672a45fa135 100644 (file)
@@ -41,6 +41,7 @@ OBJS += passes/techmap/insbuf.o
 OBJS += passes/techmap/attrmvcp.o
 OBJS += passes/techmap/attrmap.o
 OBJS += passes/techmap/zinit.o
+OBJS += passes/techmap/dfflegalize.o
 OBJS += passes/techmap/dff2dffs.o
 OBJS += passes/techmap/flowmap.o
 OBJS += passes/techmap/extractinv.o
diff --git a/passes/techmap/dfflegalize.cc b/passes/techmap/dfflegalize.cc
new file mode 100644 (file)
index 0000000..013f2d9
--- /dev/null
@@ -0,0 +1,1356 @@
+/*
+ *  yosys -- Yosys Open SYnthesis Suite
+ *
+ *  Copyright (C) 2020  Marcelina Kościelnicka <mwk@0x04.net>
+ *
+ *  Permission to use, copy, modify, and/or distribute this software for any
+ *  purpose with or without fee is hereby granted, provided that the above
+ *  copyright notice and this permission notice appear in all copies.
+ *
+ *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+enum FfType {
+       FF_DFF,
+       FF_DFFE,
+       FF_ADFF0,
+       FF_ADFF1,
+       FF_ADFFE0,
+       FF_ADFFE1,
+       FF_DFFSR,
+       FF_DFFSRE,
+       FF_SDFF0,
+       FF_SDFF1,
+       FF_SDFFE0,
+       FF_SDFFE1,
+       FF_SDFFCE0,
+       FF_SDFFCE1,
+       FF_SR,
+       FF_DLATCH,
+       FF_ADLATCH0,
+       FF_ADLATCH1,
+       FF_DLATCHSR,
+       NUM_FFTYPES,
+};
+
+enum FfNeg {
+       NEG_R = 0x1,
+       NEG_S = 0x2,
+       NEG_E = 0x4,
+       NEG_C = 0x8,
+       NUM_NEG = 0x10,
+};
+
+enum FfInit {
+       INIT_X = 0x1,
+       INIT_0 = 0x2,
+       INIT_1 = 0x4,
+};
+
+struct DffLegalizePass : public Pass {
+       DffLegalizePass() : Pass("dfflegalize", "convert FFs to types supported by the target") { }
+       void help() override
+       {
+               //   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+               log("\n");
+               log("    dfflegalize [options] [selection]\n");
+               log("\n");
+               log("Converts FFs to types supported by the target.\n");
+               log("\n");
+               log("    -cell <cell_type_pattern> <init_values>\n");
+               log("        specifies a supported group of FF cells.  <cell_type_pattern>\n");
+               log("        is a yosys internal fine cell name, where ? characters can be\n");
+               log("        as a wildcard matching any character.  <init_values> specifies\n");
+               log("        which initialization values these FF cells can support, and can\n");
+               log("        be one of:\n");
+               log("\n");
+               log("        - x (no init value supported)\n");
+               log("        - 0\n");
+               log("        - 1\n");
+               log("        - r (init value has to match reset value, only for some FF types)\n");
+               log("        - 01 (both 0 and 1 supported).\n");
+               log("\n");
+               log("    -mince <num>\n");
+               log("        specifies a minimum number of FFs that should be using any given\n");
+               log("        clock enable signal.  If a clock enable signal doesn't meet this\n");
+               log("        threshold, it is unmapped into soft logic.\n");
+               log("\n");
+               log("    -minsrst <num>\n");
+               log("        specifies a minimum number of FFs that should be using any given\n");
+               log("        sync set/reset signal.  If a sync set/reset signal doesn't meet this\n");
+               log("        threshold, it is unmapped into soft logic.\n");
+               log("\n");
+               log("The following cells are supported by this pass (ie. will be ingested,\n");
+               log("and can be specified as allowed targets):\n");
+               log("\n");
+               log("- $_DFF_[NP]_\n");
+               log("- $_DFFE_[NP][NP]_\n");
+               log("- $_DFF_[NP][NP][01]_\n");
+               log("- $_DFFE_[NP][NP][01][NP]_\n");
+               log("- $_DFFSR_[NP][NP][NP]_\n");
+               log("- $_DFFSRE_[NP][NP][NP][NP]_\n");
+               log("- $_SDFF_[NP][NP][01]_\n");
+               log("- $_SDFFE_[NP][NP][01][NP]_\n");
+               log("- $_SDFFCE_[NP][NP][01][NP]_\n");
+               log("- $_SR_[NP][NP]_\n");
+               log("- $_DLATCH_[NP]_\n");
+               log("- $_DLATCH_[NP][NP][01]_\n");
+               log("- $_DLATCHSR_[NP][NP][NP]_\n");
+               log("\n");
+               log("The following transformations are performed by this pass:");
+               log("");
+               log("- upconversion from a less capable cell to a more capable cell, if the less");
+               log("  capable cell is not supported (eg. dff -> dffe, or adff -> dffsr)");
+               log("");
+               log("- unmapping FFs with clock enable (due to unsupported cell type or -mince)");
+               log("");
+               log("- unmapping FFs with sync reset (due to unsupported cell type or -minsrst)");
+               log("");
+               log("- adding inverters on the control pins (due to unsupported polarity)");
+               log("");
+               log("- adding inverters on the D and Q pins and inverting the init/reset values\n");
+               log("  (due to unsupported init or reset value)");
+               log("");
+               log("- converting sr into adlatch (by tying D to 1 and using E as set input)");
+               log("");
+               log("- emulating unsupported dffsr cell by adff + adff + sr + mux");
+               log("");
+               log("- emulating unsupported dlatchsr cell by adlatch + adlatch + sr + mux");
+               log("");
+               log("- emulating adff when the (reset, init) value combination is unsupported by\n");
+               log("  dff + adff + dlatch + mux");
+               log("");
+               log("- emulating adlatch when the (reset, init) value combination is unsupported by\n");
+               log("- dlatch + adlatch + dlatch + mux");
+               log("");
+               log("If the pass is unable to realize a given cell type (eg. adff when only plain dff");
+               log("is available), an error is raised.");
+       }
+
+       // Table of all supported cell types.
+       // First index in the array is one of the FF_* values, second 
+       // index is the set of negative-polarity inputs (OR of NEG_*
+       // values), and the value is the set of supported init values
+       // (OR of INIT_* values).
+       int supported_cells_neg[NUM_FFTYPES][NUM_NEG];
+       // Aggregated table ignoring signal polarity.
+       int supported_cells[NUM_FFTYPES];
+       // Aggregated for all *dff* cells.
+       int supported_dff;
+       // Aggregated for all dffsr* cells.
+       int supported_dffsr;
+       // Aggregated for all adff* cells.
+       int supported_adff0;
+       int supported_adff1;
+       // Aggregated for all sdff* cells.
+       int supported_sdff0;
+       int supported_sdff1;
+       // Aggregated for all ways to obtain a SR latch.
+       int supported_sr;
+       // Aggregated for all *dlatch* cells.
+       int supported_dlatch;
+
+       int mince;
+       int minsrst;
+
+       dict<SigBit, int> ce_used;
+       dict<SigBit, int> srst_used;
+
+       SigMap sigmap;
+       dict<SigBit, std::pair<State,SigBit>> initbits;
+
+       int flip_initmask(int mask) {
+               int res = mask & INIT_X;
+               if (mask & INIT_0)
+                       res |= INIT_1;
+               if (mask & INIT_1)
+                       res |= INIT_0;
+               return res;
+       }
+
+       void handle_ff(Cell *cell) {
+               std::string type_str = cell->type.str();
+
+               FfType ff_type;
+               int ff_neg = 0;
+               SigSpec sig_d;
+               SigSpec sig_q;
+               SigSpec sig_c;
+               SigSpec sig_e;
+               SigSpec sig_r;
+               SigSpec sig_s;
+               bool has_srst = false;
+
+               if (cell->hasPort(ID::D))
+                       sig_d = cell->getPort(ID::D);
+               if (cell->hasPort(ID::Q))
+                       sig_q = cell->getPort(ID::Q);
+               if (cell->hasPort(ID::C))
+                       sig_c = cell->getPort(ID::C);
+               if (cell->hasPort(ID::E))
+                       sig_e = cell->getPort(ID::E);
+               if (cell->hasPort(ID::R))
+                       sig_r = cell->getPort(ID::R);
+               if (cell->hasPort(ID::S))
+                       sig_s = cell->getPort(ID::S);
+               
+               if (type_str.substr(0, 5) == "$_SR_") {
+                       ff_type = FF_SR;
+                       if (type_str[5] == 'N')
+                               ff_neg |= NEG_S;
+                       if (type_str[6] == 'N')
+                               ff_neg |= NEG_R;
+               } else if (type_str.substr(0, 6) == "$_DFF_" && type_str.size() == 8) {
+                       ff_type = FF_DFF;
+                       if (type_str[6] == 'N')
+                               ff_neg |= NEG_C;
+               } else if (type_str.substr(0, 7) == "$_DFFE_" && type_str.size() == 10) {
+                       ff_type = FF_DFFE;
+                       if (type_str[7] == 'N')
+                               ff_neg |= NEG_C;
+                       if (type_str[8] == 'N')
+                               ff_neg |= NEG_E;
+               } else if (type_str.substr(0, 6) == "$_DFF_" && type_str.size() == 10) {
+                       ff_type = type_str[8] == '1' ? FF_ADFF1 : FF_ADFF0;
+                       if (type_str[6] == 'N')
+                               ff_neg |= NEG_C;
+                       if (type_str[7] == 'N')
+                               ff_neg |= NEG_R;
+               } else if (type_str.substr(0, 7) == "$_DFFE_" && type_str.size() == 12) {
+                       ff_type = type_str[9] == '1' ? FF_ADFFE1 : FF_ADFFE0;
+                       if (type_str[7] == 'N')
+                               ff_neg |= NEG_C;
+                       if (type_str[8] == 'N')
+                               ff_neg |= NEG_R;
+                       if (type_str[10] == 'N')
+                               ff_neg |= NEG_E;
+               } else if (type_str.substr(0, 8) == "$_DFFSR_" && type_str.size() == 12) {
+                       ff_type = FF_DFFSR;
+                       if (type_str[8] == 'N')
+                               ff_neg |= NEG_C;
+                       if (type_str[9] == 'N')
+                               ff_neg |= NEG_S;
+                       if (type_str[10] == 'N')
+                               ff_neg |= NEG_R;
+               } else if (type_str.substr(0, 9) == "$_DFFSRE_" && type_str.size() == 14) {
+                       ff_type = FF_DFFSRE;
+                       if (type_str[9] == 'N')
+                               ff_neg |= NEG_C;
+                       if (type_str[10] == 'N')
+                               ff_neg |= NEG_S;
+                       if (type_str[11] == 'N')
+                               ff_neg |= NEG_R;
+                       if (type_str[12] == 'N')
+                               ff_neg |= NEG_E;
+               } else if (type_str.substr(0, 7) == "$_SDFF_" && type_str.size() == 11) {
+                       ff_type = type_str[9] == '1' ? FF_SDFF1 : FF_SDFF0;
+                       if (type_str[7] == 'N')
+                               ff_neg |= NEG_C;
+                       if (type_str[8] == 'N')
+                               ff_neg |= NEG_R;
+                       has_srst = true;
+               } else if (type_str.substr(0, 8) == "$_SDFFE_" && type_str.size() == 13) {
+                       ff_type = type_str[10] == '1' ? FF_SDFFE1 : FF_SDFFE0;
+                       if (type_str[8] == 'N')
+                               ff_neg |= NEG_C;
+                       if (type_str[9] == 'N')
+                               ff_neg |= NEG_R;
+                       if (type_str[11] == 'N')
+                               ff_neg |= NEG_E;
+                       has_srst = true;
+               } else if (type_str.substr(0, 9) == "$_SDFFCE_" && type_str.size() == 14) {
+                       ff_type = type_str[11] == '1' ? FF_SDFFCE1 : FF_SDFFCE0;
+                       if (type_str[9] == 'N')
+                               ff_neg |= NEG_C;
+                       if (type_str[10] == 'N')
+                               ff_neg |= NEG_R;
+                       if (type_str[12] == 'N')
+                               ff_neg |= NEG_E;
+                       has_srst = true;
+               } else if (type_str.substr(0, 9) == "$_DLATCH_" && type_str.size() == 11) {
+                       ff_type = FF_DLATCH;
+                       if (type_str[9] == 'N')
+                               ff_neg |= NEG_E;
+               } else if (type_str.substr(0, 9) == "$_DLATCH_" && type_str.size() == 13) {
+                       ff_type = type_str[11] == '1' ? FF_ADLATCH1 : FF_ADLATCH0;
+                       if (type_str[9] == 'N')
+                               ff_neg |= NEG_E;
+                       if (type_str[10] == 'N')
+                               ff_neg |= NEG_R;
+               } else if (type_str.substr(0, 11) == "$_DLATCHSR_" && type_str.size() == 15) {
+                       ff_type = FF_DLATCHSR;
+                       if (type_str[11] == 'N')
+                               ff_neg |= NEG_E;
+                       if (type_str[12] == 'N')
+                               ff_neg |= NEG_S;
+                       if (type_str[13] == 'N')
+                               ff_neg |= NEG_R;
+               } else {
+                       log_warning("Ignoring unknown ff type %s [%s.%s].\n", log_id(cell->type), log_id(cell->module->name), log_id(cell->name));
+                       return;
+               }
+
+               State initval = State::Sx;
+               SigBit initbit;
+               if (GetSize(sig_q) > 0 && initbits.count(sigmap(sig_q[0]))) {
+                       const auto &d = initbits.at(sigmap(sig_q[0]));
+                       initval = d.first;
+                       initbit = d.second;
+               }
+               
+               FfInit initmask = INIT_X;
+               if (initval == State::S0)
+                       initmask = INIT_0;
+               else if (initval == State::S1)
+                       initmask = INIT_1;
+               const char *reason;
+
+               bool kill_ce = mince && GetSize(sig_c) && GetSize(sig_e) && sig_e[0].wire && ce_used[sig_e[0]] < mince;
+               bool kill_srst = minsrst && has_srst && sig_r[0].wire && srst_used[sig_r[0]] < minsrst;
+
+               while (!(supported_cells[ff_type] & initmask) || kill_ce || kill_srst) {
+                       // Well, cell is not directly supported.  Decide how to deal with it.
+
+                       if (ff_type == FF_DFF || ff_type == FF_DFFE) {
+                               if (kill_ce) {
+                                       ff_type = FF_DFF;
+                                       goto unmap_enable;
+                               }
+                               if (!(supported_dff & initmask)) {
+                                       // This init value is not supported at all...
+                                       if (supported_dff & flip_initmask(initmask)) {
+                                               // The other one is, though.  Negate D, Q, and init.
+flip_dqi:
+                                               if (initval == State::S0) {
+                                                       initval = State::S1;
+                                                       initmask = INIT_1;
+                                               } else if (initval == State::S1) {
+                                                       initval = State::S0;
+                                                       initmask = INIT_0;
+                                               }
+                                               if (ff_type != FF_SR)
+                                                       sig_d = cell->module->NotGate(NEW_ID, sig_d[0]);
+                                               SigBit new_q = SigSpec(cell->module->addWire(NEW_ID))[0];
+                                               cell->module->addNotGate(NEW_ID, new_q, sig_q[0]);
+                                               if (initbit.wire) {
+                                                       initbit.wire->attributes.at(ID::init)[initbit.offset] = State::Sx;
+                                                       initbit = new_q;
+                                                       new_q.wire->attributes[ID::init] = initval;
+                                                       initbits[new_q] = std::make_pair(initval, new_q);
+                                               }
+                                               sig_q = new_q;
+                                               continue;
+                                       }
+                                       if (!supported_dff)
+                                               reason = "dffs are not supported";
+                                       else
+                                               reason = "initialized dffs are not supported";
+                                       goto error;
+                               }
+
+                               // Some DFF is supported with this init val.  Just pick a type.
+                               if (ff_type == FF_DFF) {
+                                       // Try adding a set or reset pin.
+                                       for (auto new_type: {FF_ADFF0, FF_ADFF1, FF_SDFF0, FF_SDFF1})
+                                               if (supported_cells[new_type] & initmask) {
+                                                       ff_type = new_type;
+                                                       sig_r = State::S0;
+                                                       goto cell_ok;
+                                               }
+                                       // Try adding both.
+                                       if (supported_cells[FF_DFFSR] & initmask) {
+                                               ff_type = FF_DFFSR;
+                                               sig_r = State::S0;
+                                               sig_s = State::S0;
+                                               break;
+                                       }
+                                       // Nope.  Will need to add enable and go the DFFE route.
+                                       sig_e = State::S1;
+                                       if (supported_cells[FF_DFFE] & initmask) {
+                                               ff_type = FF_DFFE;
+                                               break;
+                                       }
+                               }
+                               // Try adding a set or reset pin.
+                               for (auto new_type: {FF_SDFFE0, FF_SDFFE1, FF_SDFFCE0, FF_SDFFCE1, FF_ADFFE0, FF_ADFFE1})
+                                       if (supported_cells[new_type] & initmask) {
+                                               ff_type = new_type;
+                                               sig_r = State::S0;
+                                               goto cell_ok;
+                                       }
+                               // Try adding both.
+                               if (supported_cells[FF_DFFSRE] & initmask) {
+                                       ff_type = FF_DFFSRE;
+                                       sig_r = State::S0;
+                                       sig_s = State::S0;
+                                       break;
+                               }
+
+                               // Seems that no DFFs with enable are supported.
+                               // The enable input needs to be unmapped.
+                               // This should not be reached if we started from plain DFF.
+                               log_assert(ff_type == FF_DFFE);
+                               ff_type = FF_DFF;
+unmap_enable:
+                               if (ff_neg & NEG_E)
+                                       sig_d = cell->module->MuxGate(NEW_ID, sig_d[0], sig_q[0], sig_e[0]);
+                               else
+                                       sig_d = cell->module->MuxGate(NEW_ID, sig_q[0], sig_d[0], sig_e[0]);
+                               ff_neg &= ~NEG_E;
+                               sig_e = SigSpec();
+                               kill_ce = false;
+                               // Now try again as plain DFF.
+                               continue;
+                       } else if (ff_type == FF_ADFF0 || ff_type == FF_ADFF1 || ff_type == FF_ADFFE0 || ff_type == FF_ADFFE1) {
+                               bool has_set = ff_type == FF_ADFF1 || ff_type == FF_ADFFE1;
+                               bool has_en = ff_type == FF_ADFFE0 || ff_type == FF_ADFFE1;
+                               if (kill_ce) {
+                                       ff_type = has_set ? FF_ADFF1 : FF_ADFF0;
+                                       goto unmap_enable;
+                               }
+                               if (!has_en && (supported_cells[has_set ? FF_ADFFE1 : FF_ADFFE0] & initmask)) {
+                                       // Just add enable.
+                                       sig_e = State::S1;
+                                       ff_type = has_set ? FF_ADFFE1 : FF_ADFFE0;
+                                       break;
+                               }
+                               if (supported_dffsr & initmask) {
+                                       // Throw in a set/reset, retry in DFFSR/DFFSRE branch.
+                                       if (has_set) {
+                                               sig_s = sig_r;
+                                               sig_r = State::S0;
+                                               if (ff_neg & NEG_R) {
+                                                       ff_neg &= ~NEG_R;
+                                                       ff_neg |= NEG_S;
+                                               }
+                                       } else {
+                                               sig_s = State::S0;
+                                       }
+                                       if (has_en)
+                                               ff_type = FF_DFFSRE;
+                                       else
+                                               ff_type = FF_DFFSR;
+                                       continue;
+                               }
+                               if (has_en && (supported_cells[has_set ? FF_ADFF1 : FF_ADFF0] & initmask)) {
+                                       // Unmap enable.
+                                       ff_type = has_set ? FF_ADFF1 : FF_ADFF0;
+                                       goto unmap_enable;
+                               }
+                               log_assert(!((has_set ? supported_adff1 : supported_adff0) & initmask));
+                               // Alright, so this particular combination of initval and
+                               // resetval is not natively supported.  First, try flipping
+                               // them both to see whether this helps.
+                               int flipmask = flip_initmask(initmask);
+                               if ((has_set ? supported_adff0 : supported_adff1) & flipmask) {
+                                       // Checks out, do it.
+                                       ff_type = has_en ? (has_set ? FF_ADFFE0 : FF_ADFFE1) : (has_set ? FF_ADFF0 : FF_ADFF1);
+                                       goto flip_dqi;
+                               }
+
+                               if (!supported_adff0 && !supported_adff1) {
+                                       reason = "dffs with async set or reset are not supported";
+                                       goto error;
+                               }
+                               if (!(supported_dff & ~INIT_X)) {
+                                       reason = "initialized dffs are not supported";
+                                       goto error;
+                               }
+                               // If we got here, initialized dff is supported, but not this
+                               // particular reset+init combination (nor its negation).
+                               // The only hope left is breaking down to adff + dff + dlatch + mux.
+                               if (!(supported_dlatch & ~INIT_X)) {
+                                       reason = "unsupported initial value and async reset value combination";
+                                       goto error;
+                               }
+
+                               // If we have to unmap enable anyway, do it before breakdown.
+                               if (has_en && !supported_cells[FF_ADFFE0] && !supported_cells[FF_ADFFE1]) {
+                                       ff_type = has_set ? FF_ADFF1 : FF_ADFF0;
+                                       goto unmap_enable;
+                               }
+
+                               log_warning("Emulating mismatched async reset and init with several FFs and a mux for %s.%s\n", log_id(cell->module->name), log_id(cell->name));
+                               if (initbit.wire)
+                                       initbit.wire->attributes.at(ID::init)[initbit.offset] = State::Sx;
+                               Wire *adff_q = cell->module->addWire(NEW_ID);
+                               Wire *dff_q = cell->module->addWire(NEW_ID);
+                               Wire *sel_q = cell->module->addWire(NEW_ID);
+                               dff_q->attributes[ID::init] = initval;
+                               initbits[SigBit(dff_q, 0)] = std::make_pair(initval, SigBit(dff_q, 0));
+                               sel_q->attributes[ID::init] = State::S0;
+                               initbits[SigBit(sel_q, 0)] = std::make_pair(State::S0, SigBit(sel_q, 0));
+                               Cell *cell_dff;
+                               Cell *cell_adff;
+                               Cell *cell_sel;
+                               if (!has_en) {
+                                       cell_dff = cell->module->addDffGate(NEW_ID, sig_c, sig_d, dff_q, !(ff_neg & NEG_C));
+                                       cell_adff = cell->module->addAdffGate(NEW_ID, sig_c, sig_r, sig_d, adff_q, has_set, !(ff_neg & NEG_C), !(ff_neg & NEG_R));
+                               } else {
+                                       cell_dff = cell->module->addDffeGate(NEW_ID, sig_c, sig_e, sig_d, dff_q, !(ff_neg & NEG_C), !(ff_neg & NEG_E));
+                                       cell_adff = cell->module->addAdffeGate(NEW_ID, sig_c, sig_e, sig_r, sig_d, adff_q, has_set, !(ff_neg & NEG_C), !(ff_neg & NEG_E), !(ff_neg & NEG_R));
+                               }
+                               cell_sel = cell->module->addDlatchGate(NEW_ID, sig_r, State::S1, sel_q, !(ff_neg & NEG_R));
+                               cell->module->addMuxGate(NEW_ID, dff_q, adff_q, sel_q, sig_q);
+
+                               // Bye, cell.
+                               cell->module->remove(cell);
+                               handle_ff(cell_dff);
+                               handle_ff(cell_adff);
+                               handle_ff(cell_sel);
+                               return;
+                       } else if (ff_type == FF_DFFSR || ff_type == FF_DFFSRE) {
+                               if (kill_ce) {
+                                       ff_type = FF_DFFSR;
+                                       goto unmap_enable;
+                               }
+                               // First, see if mapping/unmapping enable will help.
+                               if (supported_cells[FF_DFFSRE] & initmask) {
+                                       ff_type = FF_DFFSRE;
+                                       sig_e = State::S1;
+                                       break;
+                               }
+                               if (supported_cells[FF_DFFSR] & initmask) {
+                                       ff_type = FF_DFFSR;
+                                       goto unmap_enable;
+                               }
+                               if (supported_dffsr & flip_initmask(initmask)) {
+flip_dqisr:;
+                                       log_warning("Flipping D/Q/init and inseerting set/reset fixup to handle init value on %s.%s [%s]\n", log_id(cell->module->name), log_id(cell->name), log_id(cell->type));
+                                       SigSpec new_r;
+                                       bool neg_r = (ff_neg & NEG_R);
+                                       bool neg_s = (ff_neg & NEG_S);
+                                       if (!(ff_neg & NEG_S)) {
+                                               if (!(ff_neg & NEG_R))
+                                                       new_r = cell->module->AndnotGate(NEW_ID, sig_s, sig_r);
+                                               else
+                                                       new_r = cell->module->AndGate(NEW_ID, sig_s, sig_r);
+                                       } else {
+                                               if (!(ff_neg & NEG_R))
+                                                       new_r = cell->module->OrGate(NEW_ID, sig_s, sig_r);
+                                               else
+                                                       new_r = cell->module->OrnotGate(NEW_ID, sig_s, sig_r);
+                                       }
+                                       ff_neg &= ~(NEG_R | NEG_S);
+                                       if (neg_r)
+                                               ff_neg |= NEG_S;
+                                       if (neg_s)
+                                               ff_neg |= NEG_R;
+                                       sig_s = sig_r;
+                                       sig_r = new_r;
+                                       goto flip_dqi;
+                               }
+                               // No native DFFSR.  However, if we can conjure
+                               // a SR latch and ADFF, it can still be emulated.
+                               int flipmask = flip_initmask(initmask);
+                               bool init0 = true;
+                               bool init1 = true;
+                               State initsel = State::Sx;
+                               if (((supported_adff0 & initmask) || (supported_adff1 & flipmask)) && ((supported_adff1 & initmask) || (supported_adff0 & flipmask)) && supported_sr) {
+                                       // OK
+                               } else if (((supported_adff0 & initmask) || (supported_adff1 & flipmask)) && (supported_sr & INIT_0)) {
+                                       init1 = false;
+                                       initsel = State::S0;
+                               } else if (((supported_adff1 & initmask) || (supported_adff0 & flipmask)) && (supported_sr & INIT_1)) {
+                                       init0 = false;
+                                       initsel = State::S1;
+                               } else if (((supported_adff0 & initmask) || (supported_adff1 & flipmask)) && (supported_sr & INIT_1)) {
+                                       init1 = false;
+                                       initsel = State::S0;
+                               } else if (((supported_adff1 & initmask) || (supported_adff0 & flipmask)) && (supported_sr & INIT_0)) {
+                                       init0 = false;
+                                       initsel = State::S1;
+                               } else {
+                                       if (!supported_dffsr)
+                                               reason = "dffs with async set and reset are not supported";
+                                       else
+                                               reason = "initialized dffs with async set and reset are not supported";
+                                       goto error;
+                               }
+
+                               // If we have to unmap enable anyway, do it before breakdown.
+                               if (ff_type == FF_DFFSRE && !supported_cells[FF_ADFFE0] && !supported_cells[FF_ADFFE1]) {
+                                       ff_type = FF_DFFSR;
+                                       goto unmap_enable;
+                               }
+
+                               log_warning("Emulating async set + reset with several FFs and a mux for %s.%s\n", log_id(cell->module->name), log_id(cell->name));
+                               if (initbit.wire)
+                                       initbit.wire->attributes.at(ID::init)[initbit.offset] = State::Sx;
+                               Wire *adff0_q = cell->module->addWire(NEW_ID);
+                               Wire *adff1_q = cell->module->addWire(NEW_ID);
+                               Wire *sel_q = cell->module->addWire(NEW_ID);
+                               if (init0) {
+                                       adff0_q->attributes[ID::init] = initval;
+                                       initbits[SigBit(adff0_q, 0)] = std::make_pair(initval, SigBit(adff0_q, 0));
+                               }
+                               if (init1) {
+                                       adff1_q->attributes[ID::init] = initval;
+                                       initbits[SigBit(adff1_q, 0)] = std::make_pair(initval, SigBit(adff1_q, 0));
+                               }
+                               sel_q->attributes[ID::init] = initsel;
+                               initbits[SigBit(sel_q, 0)] = std::make_pair(initsel, SigBit(sel_q, 0));
+                               Cell *cell_adff0;
+                               Cell *cell_adff1;
+                               Cell *cell_sel;
+                               if (ff_type == FF_DFFSR) {
+                                       cell_adff0 = cell->module->addAdffGate(NEW_ID, sig_c, sig_r, sig_d, adff0_q, false, !(ff_neg & NEG_C), !(ff_neg & NEG_R));
+                                       cell_adff1 = cell->module->addAdffGate(NEW_ID, sig_c, sig_s, sig_d, adff1_q, true, !(ff_neg & NEG_C), !(ff_neg & NEG_S));
+                               } else {
+                                       cell_adff0 = cell->module->addAdffeGate(NEW_ID, sig_c, sig_e, sig_r, sig_d, adff0_q, false, !(ff_neg & NEG_C), !(ff_neg & NEG_E), !(ff_neg & NEG_R));
+                                       cell_adff1 = cell->module->addAdffeGate(NEW_ID, sig_c, sig_e, sig_s, sig_d, adff1_q, true, !(ff_neg & NEG_C), !(ff_neg & NEG_E), !(ff_neg & NEG_S));
+                               }
+                               cell_sel = cell->module->addSrGate(NEW_ID, sig_s, sig_r, sel_q, !(ff_neg & NEG_S), !(ff_neg & NEG_R));
+                               cell->module->addMuxGate(NEW_ID, adff0_q, adff1_q, sel_q, sig_q);
+
+                               // Bye, cell.
+                               cell->module->remove(cell);
+                               handle_ff(cell_adff0);
+                               handle_ff(cell_adff1);
+                               handle_ff(cell_sel);
+                               return;
+                       } else if (ff_type == FF_SR) {
+                               if (supported_cells[FF_ADLATCH0] & initmask || supported_cells[FF_ADLATCH1] & flip_initmask(initmask)) {
+                                       // Convert to ADLATCH0.  May get converted to ADLATCH1.
+                                       ff_type = FF_ADLATCH0;
+                                       sig_e = sig_s;
+                                       sig_d = State::S1;
+                                       if (ff_neg & NEG_S) {
+                                               ff_neg &= ~NEG_S;
+                                               ff_neg |= NEG_E;
+                                       }
+                                       continue;
+                               } else if (supported_cells[FF_DLATCHSR] & initmask) {
+                                       // Upgrade to DLATCHSR.
+                                       ff_type = FF_DLATCHSR;
+                                       sig_e = State::S0;
+                                       sig_d = State::Sx;
+                                       break;
+                               } else if (supported_dffsr & initmask) {
+                                       // Upgrade to DFFSR.  May get further upgraded to DFFSRE.
+                                       ff_type = FF_DFFSR;
+                                       sig_c = State::S0;
+                                       sig_d = State::Sx;
+                                       continue;
+                               } else if (supported_sr & flip_initmask(initmask)) {
+                                       goto flip_dqisr;
+                               } else {
+                                       if (!supported_sr)
+                                               reason = "sr latches are not supported";
+                                       else
+                                               reason = "initialized sr latches are not supported";
+                                       goto error;
+                               }
+                       } else if (ff_type == FF_DLATCH) {
+                               if (!(supported_dlatch & initmask)) {
+                                       // This init value is not supported at all...
+                                       if (supported_dlatch & flip_initmask(initmask))
+                                               goto flip_dqi;
+                                       if (!supported_dlatch)
+                                               reason = "dlatch are not supported";
+                                       else
+                                               reason = "initialized dlatch are not supported";
+                                       goto error;
+                               }
+
+                               // Some DLATCH is supported with this init val.  Just pick a type.
+                               if (supported_cells[FF_ADLATCH0] & initmask) {
+                                       ff_type = FF_ADLATCH0;
+                                       sig_r = State::S0;
+                                       break;
+                               }
+                               if (supported_cells[FF_ADLATCH1] & initmask) {
+                                       ff_type = FF_ADLATCH1;
+                                       sig_r = State::S0;
+                                       break;
+                               }
+                               if (supported_cells[FF_DLATCHSR] & initmask) {
+                                       ff_type = FF_DLATCHSR;
+                                       sig_r = State::S0;
+                                       sig_s = State::S0;
+                                       break;
+                               }
+                               log_assert(0);
+                       } else if (ff_type == FF_ADLATCH0 || ff_type == FF_ADLATCH1) {
+                               if (supported_cells[FF_DLATCHSR] & initmask) {
+                                       if (ff_type == FF_ADLATCH1) {
+                                               sig_s = sig_r;
+                                               sig_r = State::S0;
+                                               if (ff_neg & NEG_R) {
+                                                       ff_neg &= ~NEG_R;
+                                                       ff_neg |= NEG_S;
+                                               }
+                                       } else {
+                                               sig_s = State::S0;
+                                       }
+                                       ff_type = FF_DLATCHSR;
+                                       break;
+                               }
+                               FfType flip_type = ff_type == FF_ADLATCH0 ? FF_ADLATCH1 : FF_ADLATCH0;
+                               if ((supported_cells[flip_type] | supported_cells[FF_DLATCHSR]) & flip_initmask(initmask)) {
+                                       ff_type = flip_type;
+                                       goto flip_dqi;
+                               }
+
+                               if (!supported_cells[FF_ADLATCH0] && !supported_cells[FF_ADLATCH1] && !supported_cells[FF_DLATCHSR]) {
+                                       reason = "dlatch with async set or reset are not supported";
+                                       goto error;
+                               }
+                               if (!(supported_dlatch & ~INIT_X)) {
+                                       reason = "initialized dlatch are not supported";
+                                       goto error;
+                               }
+
+                               if (!(supported_dlatch & ~INIT_X)) {
+                                       reason = "initialized dlatch are not supported";
+                                       goto error;
+                               }
+                               // If we got here, initialized dlatch is supported, but not this
+                               // particular reset+init combination (nor its negation).
+                               // The only hope left is breaking down to adff + dff + dlatch + mux.
+
+                               log_warning("Emulating mismatched async reset and init with several latches and a mux for %s.%s\n", log_id(cell->module->name), log_id(cell->name));
+                               if (initbit.wire)
+                                       initbit.wire->attributes.at(ID::init)[initbit.offset] = State::Sx;
+                               Wire *adlatch_q = cell->module->addWire(NEW_ID);
+                               Wire *dlatch_q = cell->module->addWire(NEW_ID);
+                               Wire *sel_q = cell->module->addWire(NEW_ID);
+                               dlatch_q->attributes[ID::init] = initval;
+                               initbits[SigBit(dlatch_q, 0)] = std::make_pair(initval, SigBit(dlatch_q, 0));
+                               sel_q->attributes[ID::init] = State::S0;
+                               initbits[SigBit(sel_q, 0)] = std::make_pair(State::S0, SigBit(sel_q, 0));
+                               Cell *cell_dlatch;
+                               Cell *cell_adlatch;
+                               Cell *cell_sel;
+                               cell_dlatch = cell->module->addDlatchGate(NEW_ID, sig_e, sig_d, dlatch_q, !(ff_neg & NEG_E));
+                               cell_adlatch = cell->module->addAdlatchGate(NEW_ID, sig_e, sig_r, sig_d, adlatch_q, ff_type == FF_ADLATCH1, !(ff_neg & NEG_E), !(ff_neg & NEG_R));
+                               cell_sel = cell->module->addDlatchGate(NEW_ID, sig_r, State::S1, sel_q, !(ff_neg & NEG_R));
+                               cell->module->addMuxGate(NEW_ID, dlatch_q, adlatch_q, sel_q, sig_q);
+
+                               // Bye, cell.
+                               cell->module->remove(cell);
+                               handle_ff(cell_dlatch);
+                               handle_ff(cell_adlatch);
+                               handle_ff(cell_sel);
+                               return;
+                       } else if (ff_type == FF_DLATCHSR) {
+                               if (supported_cells[FF_DLATCHSR] & flip_initmask(initmask)) {
+                                       goto flip_dqisr;
+                               }
+                               // No native DFFSR.  However, if we can conjure
+                               // a SR latch and ADFF, it can still be emulated.
+                               int flipmask = flip_initmask(initmask);
+                               bool init0 = true;
+                               bool init1 = true;
+                               State initsel = State::Sx;
+                               if (((supported_cells[FF_ADLATCH0] & initmask) || (supported_cells[FF_ADLATCH1] & flipmask)) && ((supported_cells[FF_ADLATCH1] & initmask) || (supported_cells[FF_ADLATCH0] & flipmask)) && supported_sr) {
+                                       // OK
+                               } else if (((supported_cells[FF_ADLATCH0] & initmask) || (supported_cells[FF_ADLATCH1] & flipmask)) && (supported_sr & INIT_0)) {
+                                       init1 = false;
+                                       initsel = State::S0;
+                               } else if (((supported_cells[FF_ADLATCH1] & initmask) || (supported_cells[FF_ADLATCH0] & flipmask)) && (supported_sr & INIT_1)) {
+                                       init0 = false;
+                                       initsel = State::S1;
+                               } else if (((supported_cells[FF_ADLATCH0] & initmask) || (supported_cells[FF_ADLATCH1] & flipmask)) && (supported_sr & INIT_1)) {
+                                       init1 = false;
+                                       initsel = State::S0;
+                               } else if (((supported_cells[FF_ADLATCH1] & initmask) || (supported_cells[FF_ADLATCH0] & flipmask)) && (supported_sr & INIT_0)) {
+                                       init0 = false;
+                                       initsel = State::S1;
+                               } else {
+                                       if (!supported_cells[FF_DLATCHSR])
+                                               reason = "dlatch with async set and reset are not supported";
+                                       else
+                                               reason = "initialized dlatch with async set and reset are not supported";
+                                       goto error;
+                               }
+
+                               log_warning("Emulating async set + reset with several latches and a mux for %s.%s\n", log_id(cell->module->name), log_id(cell->name));
+                               if (initbit.wire)
+                                       initbit.wire->attributes.at(ID::init)[initbit.offset] = State::Sx;
+                               Wire *adlatch0_q = cell->module->addWire(NEW_ID);
+                               Wire *adlatch1_q = cell->module->addWire(NEW_ID);
+                               Wire *sel_q = cell->module->addWire(NEW_ID);
+                               if (init0) {
+                                       adlatch0_q->attributes[ID::init] = initval;
+                                       initbits[SigBit(adlatch0_q, 0)] = std::make_pair(initval, SigBit(adlatch0_q, 0));
+                               }
+                               if (init1) {
+                                       adlatch1_q->attributes[ID::init] = initval;
+                                       initbits[SigBit(adlatch1_q, 0)] = std::make_pair(initval, SigBit(adlatch1_q, 0));
+                               }
+                               sel_q->attributes[ID::init] = initsel;
+                               initbits[SigBit(sel_q, 0)] = std::make_pair(initsel, SigBit(sel_q, 0));
+                               Cell *cell_adlatch0;
+                               Cell *cell_adlatch1;
+                               Cell *cell_sel;
+                               cell_adlatch0 = cell->module->addAdlatchGate(NEW_ID, sig_e, sig_r, sig_d, adlatch0_q, false, !(ff_neg & NEG_E), !(ff_neg & NEG_R));
+                               cell_adlatch1 = cell->module->addAdlatchGate(NEW_ID, sig_e, sig_s, sig_d, adlatch1_q, true, !(ff_neg & NEG_E), !(ff_neg & NEG_S));
+                               cell_sel = cell->module->addSrGate(NEW_ID, sig_s, sig_r, sel_q, !(ff_neg & NEG_S), !(ff_neg & NEG_R));
+                               cell->module->addMuxGate(NEW_ID, adlatch0_q, adlatch1_q, sel_q, sig_q);
+
+                               // Bye, cell.
+                               cell->module->remove(cell);
+                               handle_ff(cell_adlatch0);
+                               handle_ff(cell_adlatch1);
+                               handle_ff(cell_sel);
+                               return;
+                       } else if (ff_type == FF_SDFF0 || ff_type == FF_SDFF1 || ff_type == FF_SDFFE0 || ff_type == FF_SDFFE1 || ff_type == FF_SDFFCE0 || ff_type == FF_SDFFCE1) {
+                               bool has_set = ff_type == FF_SDFF1 || ff_type == FF_SDFFE1 || ff_type == FF_SDFFCE1;
+                               bool has_en = ff_type == FF_SDFFE0 || ff_type == FF_SDFFE1;
+                               bool has_ce = ff_type == FF_SDFFCE0 || ff_type == FF_SDFFCE1;
+
+                               if (has_en) {
+                                       if (kill_ce || kill_srst) {
+                                               ff_type = has_set ? FF_SDFF1 : FF_SDFF0;
+                                               goto unmap_enable;
+                                       }
+                               } else if (has_ce) {
+                                       if (kill_ce || kill_srst)
+                                               goto unmap_srst;
+                               } else {
+                                       log_assert(!kill_ce);
+                                       if (kill_srst)
+                                               goto unmap_srst;
+                               }
+
+                               if (!has_ce) {
+                                       if (!has_en && (supported_cells[has_set ? FF_SDFFE1 : FF_SDFFE0] & initmask)) {
+                                               // Just add enable.
+                                               sig_e = State::S1;
+                                               ff_type = has_set ? FF_SDFFE1 : FF_SDFFE0;
+                                               break;
+                                       }
+                                       if (!has_en && (supported_cells[has_set ? FF_SDFFCE1 : FF_SDFFCE0] & initmask)) {
+                                               // Just add enable.
+                                               sig_e = State::S1;
+                                               ff_type = has_set ? FF_SDFFCE1 : FF_SDFFCE0;
+                                               break;
+                                       }
+                                       if (has_en && (supported_cells[has_set ? FF_SDFFCE1 : FF_SDFFCE0] & initmask)) {
+                                               // Convert sdffe to sdffce
+                                               if (!(ff_neg & NEG_E)) {
+                                                       if (!(ff_neg & NEG_R))
+                                                               sig_e = cell->module->OrGate(NEW_ID, sig_e, sig_r);
+                                                       else
+                                                               sig_e = cell->module->OrnotGate(NEW_ID, sig_e, sig_r);
+                                               } else {
+                                                       if (!(ff_neg & NEG_R))
+                                                               sig_e = cell->module->AndnotGate(NEW_ID, sig_e, sig_r);
+                                                       else
+                                                               sig_e = cell->module->AndGate(NEW_ID, sig_e, sig_r);
+                                               }
+                                               ff_type = has_set ? FF_SDFFCE1 : FF_SDFFCE0;
+                                               break;
+                                       }
+                                       if (has_en && (supported_cells[has_set ? FF_SDFF1 : FF_SDFF0] & initmask)) {
+                                               // Unmap enable.
+                                               ff_type = has_set ? FF_SDFF1 : FF_SDFF0;
+                                               goto unmap_enable;
+                                       }
+                                       log_assert(!((has_set ? supported_sdff1 : supported_sdff0) & initmask));
+                               } else {
+                                       if ((has_set ? supported_sdff1 : supported_sdff0) & initmask) {
+                                               // Convert sdffce to sdffe, which may be further converted to sdff.
+                                               if (!(ff_neg & NEG_R)) {
+                                                       if (!(ff_neg & NEG_E))
+                                                               sig_r = cell->module->AndGate(NEW_ID, sig_r, sig_e);
+                                                       else
+                                                               sig_r = cell->module->AndnotGate(NEW_ID, sig_r, sig_e);
+                                               } else {
+                                                       if (!(ff_neg & NEG_E))
+                                                               sig_r = cell->module->OrnotGate(NEW_ID, sig_r, sig_e);
+                                                       else
+                                                               sig_r = cell->module->OrGate(NEW_ID, sig_r, sig_e);
+                                               }
+                                               ff_type = has_set ? FF_SDFFE1 : FF_SDFFE0;
+                                               continue;
+                                       }
+                               }
+                               // Alright, so this particular combination of initval and
+                               // resetval is not natively supported.  First, try flipping
+                               // them both to see whether this helps.
+                               if ((has_set ? supported_sdff0 : supported_sdff1) & flip_initmask(initmask)) {
+                                       // Checks out, do it.
+                                       ff_type = has_ce ? (has_set ? FF_SDFFCE0 : FF_SDFFCE1) : has_en ? (has_set ? FF_SDFFE0 : FF_SDFFE1) : (has_set ? FF_SDFF0 : FF_SDFF1);
+                                       goto flip_dqi;
+                               }
+
+                               // Nope.  No way to get SDFF* of the right kind, so unmap it.
+                               // For SDFFE, the enable has to be unmapped first.
+                               if (has_en) {
+                                       ff_type = has_set ? FF_SDFF1 : FF_SDFF0;
+                                       goto unmap_enable;
+                               }
+unmap_srst:
+                               if (has_ce)
+                                       ff_type = FF_DFFE;
+                               else
+                                       ff_type = FF_DFF;
+                               if (ff_neg & NEG_R)
+                                       sig_d = cell->module->MuxGate(NEW_ID, has_set ? State::S1 : State::S0, sig_d[0], sig_r[0]);
+                               else
+                                       sig_d = cell->module->MuxGate(NEW_ID, sig_d[0], has_set ? State::S1 : State::S0, sig_r[0]);
+                               ff_neg &= ~NEG_R;
+                               sig_r = SigSpec();
+                               kill_srst = false;
+                               continue;
+                       } else {
+                               log_assert(0);
+                       }
+               }
+cell_ok:
+
+               if (!(supported_cells_neg[ff_type][ff_neg] & initmask)) {
+                       // Cell is supported, but not with those polarities.
+                       // Will need to add some inverters.
+
+                       // Find the smallest value that xored with the neg mask
+                       // results in a supported one — this results in preferentially
+                       // inverting resets before clocks, etc.
+                       int xneg;
+                       for (xneg = 0; xneg < NUM_NEG; xneg++)
+                               if (supported_cells_neg[ff_type][ff_neg ^ xneg] & initmask)
+                                       break;
+                       log_assert(xneg < NUM_NEG);
+                       if (xneg & NEG_R)
+                               sig_r = cell->module->NotGate(NEW_ID, sig_r[0]);
+                       if (xneg & NEG_S)
+                               sig_s = cell->module->NotGate(NEW_ID, sig_s[0]);
+                       if (xneg & NEG_E)
+                               sig_e = cell->module->NotGate(NEW_ID, sig_e[0]);
+                       if (xneg & NEG_C)
+                               sig_c = cell->module->NotGate(NEW_ID, sig_c[0]);
+                       ff_neg ^= xneg;
+               }
+
+               cell->unsetPort(ID::D);
+               cell->unsetPort(ID::Q);
+               cell->unsetPort(ID::C);
+               cell->unsetPort(ID::E);
+               cell->unsetPort(ID::S);
+               cell->unsetPort(ID::R);
+               switch (ff_type) {
+                       case FF_DFF:
+                               cell->type = IdString(stringf("$_DFF_%c_",
+                                               (ff_neg & NEG_C) ? 'N' : 'P'
+                                       ));
+                               cell->setPort(ID::D, sig_d);
+                               cell->setPort(ID::Q, sig_q);
+                               cell->setPort(ID::C, sig_c);
+                               break;
+                       case FF_DFFE:
+                               cell->type = IdString(stringf("$_DFFE_%c%c_",
+                                               (ff_neg & NEG_C) ? 'N' : 'P',
+                                               (ff_neg & NEG_E) ? 'N' : 'P'
+                                       ));
+                               cell->setPort(ID::D, sig_d);
+                               cell->setPort(ID::Q, sig_q);
+                               cell->setPort(ID::C, sig_c);
+                               cell->setPort(ID::E, sig_e);
+                               break;
+                       case FF_ADFF0:
+                       case FF_ADFF1:
+                               cell->type = IdString(stringf("$_DFF_%c%c%c_",
+                                               (ff_neg & NEG_C) ? 'N' : 'P',
+                                               (ff_neg & NEG_R) ? 'N' : 'P',
+                                               (ff_type == FF_ADFF1) ? '1' : '0'
+                                       ));
+                               cell->setPort(ID::D, sig_d);
+                               cell->setPort(ID::Q, sig_q);
+                               cell->setPort(ID::C, sig_c);
+                               cell->setPort(ID::R, sig_r);
+                               break;
+                       case FF_ADFFE0:
+                       case FF_ADFFE1:
+                               cell->type = IdString(stringf("$_DFFE_%c%c%c%c_",
+                                               (ff_neg & NEG_C) ? 'N' : 'P',
+                                               (ff_neg & NEG_R) ? 'N' : 'P',
+                                               (ff_type == FF_ADFFE1) ? '1' : '0',
+                                               (ff_neg & NEG_E) ? 'N' : 'P'
+                                       ));
+                               cell->setPort(ID::D, sig_d);
+                               cell->setPort(ID::Q, sig_q);
+                               cell->setPort(ID::C, sig_c);
+                               cell->setPort(ID::E, sig_e);
+                               cell->setPort(ID::R, sig_r);
+                               break;
+                       case FF_DFFSR:
+                               cell->type = IdString(stringf("$_DFFSR_%c%c%c_",
+                                               (ff_neg & NEG_C) ? 'N' : 'P',
+                                               (ff_neg & NEG_S) ? 'N' : 'P',
+                                               (ff_neg & NEG_R) ? 'N' : 'P'
+                                       ));
+                               cell->setPort(ID::D, sig_d);
+                               cell->setPort(ID::Q, sig_q);
+                               cell->setPort(ID::C, sig_c);
+                               cell->setPort(ID::S, sig_s);
+                               cell->setPort(ID::R, sig_r);
+                               break;
+                       case FF_DFFSRE:
+                               cell->type = IdString(stringf("$_DFFSRE_%c%c%c%c_",
+                                               (ff_neg & NEG_C) ? 'N' : 'P',
+                                               (ff_neg & NEG_S) ? 'N' : 'P',
+                                               (ff_neg & NEG_R) ? 'N' : 'P',
+                                               (ff_neg & NEG_E) ? 'N' : 'P'
+                                       ));
+                               cell->setPort(ID::D, sig_d);
+                               cell->setPort(ID::Q, sig_q);
+                               cell->setPort(ID::C, sig_c);
+                               cell->setPort(ID::E, sig_e);
+                               cell->setPort(ID::S, sig_s);
+                               cell->setPort(ID::R, sig_r);
+                               break;
+                       case FF_SDFF0:
+                       case FF_SDFF1:
+                               cell->type = IdString(stringf("$_SDFF_%c%c%c_",
+                                               (ff_neg & NEG_C) ? 'N' : 'P',
+                                               (ff_neg & NEG_R) ? 'N' : 'P',
+                                               (ff_type == FF_SDFF1) ? '1' : '0'
+                                       ));
+                               cell->setPort(ID::D, sig_d);
+                               cell->setPort(ID::Q, sig_q);
+                               cell->setPort(ID::C, sig_c);
+                               cell->setPort(ID::R, sig_r);
+                               break;
+                       case FF_SDFFE0:
+                       case FF_SDFFE1:
+                               cell->type = IdString(stringf("$_SDFFE_%c%c%c%c_",
+                                               (ff_neg & NEG_C) ? 'N' : 'P',
+                                               (ff_neg & NEG_R) ? 'N' : 'P',
+                                               (ff_type == FF_SDFFE1) ? '1' : '0',
+                                               (ff_neg & NEG_E) ? 'N' : 'P'
+                                       ));
+                               cell->setPort(ID::D, sig_d);
+                               cell->setPort(ID::Q, sig_q);
+                               cell->setPort(ID::C, sig_c);
+                               cell->setPort(ID::E, sig_e);
+                               cell->setPort(ID::R, sig_r);
+                               break;
+                       case FF_SDFFCE0:
+                       case FF_SDFFCE1:
+                               cell->type = IdString(stringf("$_SDFFCE_%c%c%c%c_",
+                                               (ff_neg & NEG_C) ? 'N' : 'P',
+                                               (ff_neg & NEG_R) ? 'N' : 'P',
+                                               (ff_type == FF_SDFFCE1) ? '1' : '0',
+                                               (ff_neg & NEG_E) ? 'N' : 'P'
+                                       ));
+                               cell->setPort(ID::D, sig_d);
+                               cell->setPort(ID::Q, sig_q);
+                               cell->setPort(ID::C, sig_c);
+                               cell->setPort(ID::E, sig_e);
+                               cell->setPort(ID::R, sig_r);
+                               break;
+                       case FF_DLATCH:
+                               cell->type = IdString(stringf("$_DLATCH_%c_",
+                                               (ff_neg & NEG_E) ? 'N' : 'P'
+                                       ));
+                               cell->setPort(ID::D, sig_d);
+                               cell->setPort(ID::Q, sig_q);
+                               cell->setPort(ID::E, sig_e);
+                               break;
+                       case FF_ADLATCH0:
+                       case FF_ADLATCH1:
+                               cell->type = IdString(stringf("$_DLATCH_%c%c%c_",
+                                               (ff_neg & NEG_E) ? 'N' : 'P',
+                                               (ff_neg & NEG_R) ? 'N' : 'P',
+                                               (ff_type == FF_ADLATCH1) ? '1' : '0'
+                                       ));
+                               cell->setPort(ID::D, sig_d);
+                               cell->setPort(ID::Q, sig_q);
+                               cell->setPort(ID::E, sig_e);
+                               cell->setPort(ID::R, sig_r);
+                               break;
+                       case FF_DLATCHSR:
+                               cell->type = IdString(stringf("$_DLATCHSR_%c%c%c_",
+                                               (ff_neg & NEG_E) ? 'N' : 'P',
+                                               (ff_neg & NEG_S) ? 'N' : 'P',
+                                               (ff_neg & NEG_R) ? 'N' : 'P'
+                                       ));
+                               cell->setPort(ID::D, sig_d);
+                               cell->setPort(ID::Q, sig_q);
+                               cell->setPort(ID::E, sig_e);
+                               cell->setPort(ID::S, sig_s);
+                               cell->setPort(ID::R, sig_r);
+                               break;
+                       case FF_SR:
+                               cell->type = IdString(stringf("$_SR_%c%c_",
+                                               (ff_neg & NEG_S) ? 'N' : 'P',
+                                               (ff_neg & NEG_R) ? 'N' : 'P'
+                                       ));
+                               cell->setPort(ID::Q, sig_q);
+                               cell->setPort(ID::S, sig_s);
+                               cell->setPort(ID::R, sig_r);
+                               break;
+                       default:
+                               log_assert(0);
+               }
+               return;
+
+error:
+               log_error("FF %s.%s (type %s) cannot be legalized: %s\n", log_id(cell->module->name), log_id(cell->name), log_id(cell->type), reason);
+       }
+
+       void execute(std::vector<std::string> args, RTLIL::Design *design) override
+       {
+
+               log_header(design, "Executing DFFLEGALIZE pass (convert FFs to types supported by the target).\n");
+
+               for (int i = 0; i < NUM_FFTYPES; i++) {
+                       for (int j = 0; j < NUM_NEG; j++)
+                               supported_cells_neg[i][j] = 0;
+                       supported_cells[i] = 0;
+               }
+               mince = 0;
+               minsrst = 0;
+
+               size_t argidx;
+               for (argidx = 1; argidx < args.size(); argidx++)
+               {
+                       if (args[argidx] == "-cell" && argidx + 2 < args.size()) {
+                               std::string celltype = args[++argidx];
+                               std::string inittype = args[++argidx];
+                               enum FfType ff_type[2] = {NUM_FFTYPES, NUM_FFTYPES};
+                               char pol_c = 0;
+                               char pol_e = 0;
+                               char pol_s = 0;
+                               char pol_r = 0;
+                               char srval = 0;
+                               if (celltype.substr(0, 5) == "$_SR_" && celltype.size() == 8 && celltype[7] == '_') {
+                                       ff_type[0] = FF_SR;
+                                       pol_s = celltype[5];
+                                       pol_r = celltype[6];
+                               } else if (celltype.substr(0, 6) == "$_DFF_" && celltype.size() == 8 && celltype[7] == '_') {
+                                       ff_type[0] = FF_DFF;
+                                       pol_c = celltype[6];
+                               } else if (celltype.substr(0, 7) == "$_DFFE_" && celltype.size() == 10 && celltype[9] == '_') {
+                                       ff_type[0] = FF_DFFE;
+                                       pol_c = celltype[7];
+                                       pol_e = celltype[8];
+                               } else if (celltype.substr(0, 6) == "$_DFF_" && celltype.size() == 10 && celltype[9] == '_') {
+                                       ff_type[0] = FF_ADFF0;
+                                       ff_type[1] = FF_ADFF1;
+                                       pol_c = celltype[6];
+                                       pol_r = celltype[7];
+                                       srval = celltype[8];
+                               } else if (celltype.substr(0, 7) == "$_DFFE_" && celltype.size() == 12 && celltype[11] == '_') {
+                                       ff_type[0] = FF_ADFFE0;
+                                       ff_type[1] = FF_ADFFE1;
+                                       pol_c = celltype[7];
+                                       pol_r = celltype[8];
+                                       srval = celltype[9];
+                                       pol_e = celltype[10];
+                               } else if (celltype.substr(0, 8) == "$_DFFSR_" && celltype.size() == 12 && celltype[11] == '_') {
+                                       ff_type[0] = FF_DFFSR;
+                                       pol_c = celltype[8];
+                                       pol_s = celltype[9];
+                                       pol_r = celltype[10];
+                               } else if (celltype.substr(0, 9) == "$_DFFSRE_" && celltype.size() == 14 && celltype[13] == '_') {
+                                       ff_type[0] = FF_DFFSRE;
+                                       pol_c = celltype[9];
+                                       pol_s = celltype[10];
+                                       pol_r = celltype[11];
+                                       pol_e = celltype[12];
+                               } else if (celltype.substr(0, 7) == "$_SDFF_" && celltype.size() == 11 && celltype[10] == '_') {
+                                       ff_type[0] = FF_SDFF0;
+                                       ff_type[1] = FF_SDFF1;
+                                       pol_c = celltype[7];
+                                       pol_r = celltype[8];
+                                       srval = celltype[9];
+                               } else if (celltype.substr(0, 8) == "$_SDFFE_" && celltype.size() == 13 && celltype[12] == '_') {
+                                       ff_type[0] = FF_SDFFE0;
+                                       ff_type[1] = FF_SDFFE1;
+                                       pol_c = celltype[8];
+                                       pol_r = celltype[9];
+                                       srval = celltype[10];
+                                       pol_e = celltype[11];
+                               } else if (celltype.substr(0, 9) == "$_SDFFCE_" && celltype.size() == 14 && celltype[13] == '_') {
+                                       ff_type[0] = FF_SDFFCE0;
+                                       ff_type[1] = FF_SDFFCE1;
+                                       pol_c = celltype[9];
+                                       pol_r = celltype[10];
+                                       srval = celltype[11];
+                                       pol_e = celltype[12];
+                               } else if (celltype.substr(0, 9) == "$_DLATCH_" && celltype.size() == 11 && celltype[10] == '_') {
+                                       ff_type[0] = FF_DLATCH;
+                                       pol_e = celltype[9];
+                               } else if (celltype.substr(0, 9) == "$_DLATCH_" && celltype.size() == 13 && celltype[12] == '_') {
+                                       ff_type[0] = FF_ADLATCH0;
+                                       ff_type[1] = FF_ADLATCH1;
+                                       pol_e = celltype[9];
+                                       pol_r = celltype[10];
+                                       srval = celltype[11];
+                               } else if (celltype.substr(0, 11) == "$_DLATCHSR_" && celltype.size() == 15 && celltype[14] == '_') {
+                                       ff_type[0] = FF_DLATCHSR;
+                                       pol_e = celltype[11];
+                                       pol_s = celltype[12];
+                                       pol_r = celltype[13];
+                               } else {
+unrecognized:
+                                       log_error("unrecognized cell type %s.\n", celltype.c_str());
+                               }
+                               int mask = 0;
+                               int match = 0;
+                               for (auto pair : {
+                                       std::make_pair(pol_c, NEG_C),
+                                       std::make_pair(pol_e, NEG_E),
+                                       std::make_pair(pol_s, NEG_S),
+                                       std::make_pair(pol_r, NEG_R),
+                               }) {
+                                       if (pair.first == 'N') {
+                                               mask |= pair.second;
+                                               match |= pair.second;
+                                       } else if (pair.first == 'P' || pair.first == 0) {
+                                               mask |= pair.second;
+                                       } else if (pair.first != '?') {
+                                               goto unrecognized;
+                                       }
+                               }
+                               if (srval == '0') {
+                                       ff_type[1] = NUM_FFTYPES;
+                               } else if (srval == '1') {
+                                       ff_type[0] = NUM_FFTYPES;
+                               } else if (srval != 0 && srval != '?') {
+                                       goto unrecognized;
+                               }
+                               for (int i = 0; i < 2; i++) {
+                                       if (ff_type[i] == NUM_FFTYPES)
+                                               continue;
+                                       int initmask;
+                                       if (inittype == "x") {
+                                               initmask = INIT_X;
+                                       } else if (inittype == "0") {
+                                               initmask = INIT_X | INIT_0;
+                                       } else if (inittype == "1") {
+                                               initmask = INIT_X | INIT_1;
+                                       } else if (inittype == "r") {
+                                               if (srval == 0)
+                                                       log_error("init type r not valid for cell type %s.\n", celltype.c_str());
+                                               if (i == 0)
+                                                       initmask = INIT_X | INIT_0;
+                                               else
+                                                       initmask = INIT_X | INIT_1;
+                                       } else if (inittype == "01") {
+                                               initmask = INIT_X | INIT_0 | INIT_1;
+                                       } else {
+                                               log_error("unrecognized init type %s for cell type %s.\n", inittype.c_str(), celltype.c_str());
+                                       }
+                                       for (int neg = 0; neg < NUM_NEG; neg++)
+                                               if ((neg & mask) == match)
+                                                       supported_cells_neg[ff_type[i]][neg] |= initmask;
+                                       supported_cells[ff_type[i]] |= initmask;
+                               }
+                               continue;
+                       } else if (args[argidx] == "-mince" && argidx + 1 < args.size()) {
+                               mince = atoi(args[++argidx].c_str());
+                               continue;
+                       } else if (args[argidx] == "-minsrst" && argidx + 1 < args.size()) {
+                               minsrst = atoi(args[++argidx].c_str());
+                               continue;
+                       }
+                       break;
+               }
+               extra_args(args, argidx, design);
+               supported_dffsr = supported_cells[FF_DFFSR] | supported_cells[FF_DFFSRE];
+               supported_adff0 = supported_cells[FF_ADFF0] | supported_cells[FF_ADFFE0] | supported_dffsr;
+               supported_adff1 = supported_cells[FF_ADFF1] | supported_cells[FF_ADFFE1] | supported_dffsr;
+               supported_sdff0 = supported_cells[FF_SDFF0] | supported_cells[FF_SDFFE0] | supported_cells[FF_SDFFCE0];
+               supported_sdff1 = supported_cells[FF_SDFF1] | supported_cells[FF_SDFFE1] | supported_cells[FF_SDFFCE1];
+               supported_dff = supported_cells[FF_DFF] | supported_cells[FF_DFFE] | supported_dffsr | supported_adff0 | supported_adff1 | supported_sdff0 | supported_sdff1;
+               supported_sr = supported_dffsr | supported_cells[FF_DLATCHSR] | supported_cells[FF_SR] | supported_cells[FF_ADLATCH0] | flip_initmask(supported_cells[FF_ADLATCH1]);
+               supported_dlatch = supported_cells[FF_DLATCH] | supported_cells[FF_ADLATCH0] | supported_cells[FF_ADLATCH1] | supported_cells[FF_DLATCHSR];
+
+               for (auto module : design->selected_modules())
+               {
+                       sigmap.set(module);
+                       initbits.clear();
+
+                       for (auto wire : module->selected_wires())
+                       {
+                               if (wire->attributes.count(ID::init) == 0)
+                                       continue;
+
+                               SigSpec wirebits = sigmap(wire);
+                               Const initval = wire->attributes.at(ID::init);
+
+                               for (int i = 0; i < GetSize(wirebits) && i < GetSize(initval); i++)
+                               {
+                                       SigBit bit = wirebits[i];
+                                       State val = initval[i];
+
+                                       if (val != State::S0 && val != State::S1 && bit.wire != nullptr)
+                                               continue;
+
+                                       if (initbits.count(bit)) {
+                                               if (initbits.at(bit).first != val)
+                                                       log_error("Conflicting init values for signal %s (%s = %s != %s).\n",
+                                                                       log_signal(bit), log_signal(SigBit(wire, i)),
+                                                                       log_signal(val), log_signal(initbits.at(bit).first));
+                                               continue;
+                                       }
+
+                                       initbits[bit] = std::make_pair(val,SigBit(wire,i));
+                               }
+                       }
+
+                       if (mince || minsrst) {
+                               ce_used.clear();
+                               srst_used.clear();
+
+                               for (auto cell : module->cells()) {
+                                       if (!RTLIL::builtin_ff_cell_types().count(cell->type))
+                                               continue;
+
+                                       if (cell->hasPort(ID::C) && cell->hasPort(ID::E)) {
+                                               SigSpec sig = cell->getPort(ID::E);
+                                               // Do not count const enable signals.
+                                               if (GetSize(sig) == 1 && sig[0].wire)
+                                                       ce_used[sig[0]]++;
+                                       }
+                                       if (cell->type.str().substr(0, 6) == "$_SDFF") {
+                                               SigSpec sig = cell->getPort(ID::R);
+                                               // Do not count const srst signals.
+                                               if (GetSize(sig) == 1 && sig[0].wire)
+                                                       srst_used[sig[0]]++;
+                                       }
+                               }
+                       }
+
+                       // First gather FF cells, then iterate over them later.
+                       // We may need to split an FF into several cells.
+                       std::vector<Cell *> ff_cells;
+
+                       for (auto cell : module->selected_cells())
+                       {
+                               // Early exit for non-FFs.
+                               if (!RTLIL::builtin_ff_cell_types().count(cell->type))
+                                       continue;
+
+                               ff_cells.push_back(cell);
+                       }
+
+                       for (auto cell: ff_cells)
+                               handle_ff(cell);
+               }
+
+               sigmap.clear();
+               initbits.clear();
+               ce_used.clear();
+               srst_used.clear();
+       }
+} DffLegalizePass;
+
+PRIVATE_NAMESPACE_END