xilinx: Add xilinx_dffopt pass (#1557)
authorMarcin Kościelnicki <mwk@0x04.net>
Wed, 18 Dec 2019 12:42:26 +0000 (13:42 +0100)
committerMarcin Kościelnicki <mwk@0x04.net>
Wed, 18 Dec 2019 12:43:43 +0000 (13:43 +0100)
CHANGELOG
passes/equiv/equiv_opt.cc
techlibs/xilinx/Makefile.inc
techlibs/xilinx/cells_sim.v
techlibs/xilinx/cells_xtra.py
techlibs/xilinx/cells_xtra.v
techlibs/xilinx/synth_xilinx.cc
techlibs/xilinx/xilinx_dffopt.cc [new file with mode: 0644]
tests/arch/xilinx/fsm.ys
tests/arch/xilinx/xilinx_dffopt.ys [new file with mode: 0644]
tests/arch/xilinx/xilinx_dffopt_blacklist.txt [new file with mode: 0644]

index cb2b7bf0c02cd6856a43071ee6411c137cb19181..9b87af8f04d790d7074c3b28da52752dff5cd7fa 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -55,6 +55,7 @@ Yosys 0.9 .. Yosys 0.9-dev
     - Added "check -mapped"
     - Added checking of SystemVerilog always block types (always_comb,
       always_latch and always_ff)
+    - Added "xilinx_dffopt" pass
 
 Yosys 0.8 .. Yosys 0.9
 ----------------------
index c7e6d71a62e2a69a9794cc6f3056e2403fbd0dd0..7c6c2e685306a0b2eb095aa207b928da48ed9262 100644 (file)
@@ -44,6 +44,10 @@ struct EquivOptPass:public ScriptPass
                log("        expand the modules in this file before proving equivalence. this is\n");
                log("        useful for handling architecture-specific primitives.\n");
                log("\n");
+               log("    -blacklist <file>\n");
+               log("        Do not match cells or signals that match the names in the file\n");
+               log("        (passed to equiv_make).\n");
+               log("\n");
                log("    -assert\n");
                log("        produce an error if the circuits are not equivalent.\n");
                log("\n");
@@ -61,13 +65,14 @@ struct EquivOptPass:public ScriptPass
                log("\n");
        }
 
-       std::string command, techmap_opts;
+       std::string command, techmap_opts, make_opts;
        bool assert, undef, multiclock, async2sync;
 
        void clear_flags() YS_OVERRIDE
        {
                command = "";
                techmap_opts = "";
+               make_opts = "";
                assert = false;
                undef = false;
                multiclock = false;
@@ -93,6 +98,10 @@ struct EquivOptPass:public ScriptPass
                                techmap_opts += " -map " + args[++argidx];
                                continue;
                        }
+                       if (args[argidx] == "-blacklist" && argidx + 1 < args.size()) {
+                               make_opts += " -blacklist " + args[++argidx];
+                               continue;
+                       }
                        if (args[argidx] == "-assert") {
                                assert = true;
                                continue;
@@ -170,7 +179,12 @@ struct EquivOptPass:public ScriptPass
                                run("clk2fflogic", "(only with -multiclock)");
                        if (async2sync || help_mode)
                                run("async2sync", " (only with -async2sync)");
-                       run("equiv_make gold gate equiv");
+                       string opts;
+                       if (help_mode)
+                               opts = " -blacklist <filename> ...";
+                       else
+                               opts = make_opts;
+                       run("equiv_make" + opts + " gold gate equiv");
                        if (help_mode)
                                run("equiv_induct [-undef] equiv");
                        else if (undef)
index 3ebc72fe83eb625db6b6e410ac4eedad66209ed8..3f2fbcc850d5c7c0a8d962bdb73df8af4dd57f76 100644 (file)
@@ -1,5 +1,6 @@
 
 OBJS += techlibs/xilinx/synth_xilinx.o
+OBJS += techlibs/xilinx/xilinx_dffopt.o
 
 GENFILES += techlibs/xilinx/brams_init_36.vh
 GENFILES += techlibs/xilinx/brams_init_32.vh
index f9ce496ff70177ddd36f0f819dad39ef63ac1649..cf7923777ce8dd37216fd0fdade3b9a2fae49b14 100644 (file)
@@ -329,6 +329,41 @@ module FDSE (
   endcase endgenerate
 endmodule
 
+module FDRSE (
+  output reg Q,
+  (* clkbuf_sink *)
+  (* invertible_pin = "IS_C_INVERTED" *)
+  input C,
+  (* invertible_pin = "IS_CE_INVERTED" *)
+  input CE,
+  (* invertible_pin = "IS_D_INVERTED" *)
+  input D,
+  (* invertible_pin = "IS_R_INVERTED" *)
+  input R,
+  (* invertible_pin = "IS_S_INVERTED" *)
+  input S
+);
+  parameter [0:0] INIT = 1'b0;
+  parameter [0:0] IS_C_INVERTED = 1'b0;
+  parameter [0:0] IS_CE_INVERTED = 1'b0;
+  parameter [0:0] IS_D_INVERTED = 1'b0;
+  parameter [0:0] IS_R_INVERTED = 1'b0;
+  parameter [0:0] IS_S_INVERTED = 1'b0;
+  initial Q <= INIT;
+  wire c = C ^ IS_C_INVERTED;
+  wire ce = CE ^ IS_CE_INVERTED;
+  wire d = D ^ IS_D_INVERTED;
+  wire r = R ^ IS_R_INVERTED;
+  wire s = S ^ IS_S_INVERTED;
+  always @(posedge c)
+    if (r)
+      Q <= 0;
+    else if (s)
+      Q <= 1;
+    else if (ce)
+      Q <= d;
+endmodule
+
 module FDCE (
   (* abc9_arrival=303 *)
   output reg Q,
index e4c580b9d8b417981f6484f1e48d3a6fa1cb44cc..6d5adf1aa43ddc142993e0607235e9aa99f4cf07 100644 (file)
@@ -66,7 +66,7 @@ CELLS = [
     # CLB -- registers/latches.
     # Virtex 1/2/4/5, Spartan 3.
     Cell('FDCPE', port_attrs={'C': ['clkbuf_sink']}),
-    Cell('FDRSE', port_attrs={'C': ['clkbuf_sink']}),
+    Cell('FDRSE', port_attrs={'C': ['clkbuf_sink']}),
     Cell('LDCPE', port_attrs={'C': ['clkbuf_sink']}),
     # Virtex 6, Spartan 6, Series 7, Ultrascale.
     # Cell('FDCE'),
index 8ac596459c1f64581a469b454a7daaff01a3917f..66b7c583f8fce2d43ab940d9f47c7d505516c147 100644 (file)
@@ -17,27 +17,6 @@ module FDCPE (...);
     input PRE;
 endmodule
 
-module FDRSE (...);
-    parameter [0:0] INIT = 1'b0;
-    parameter [0:0] IS_C_INVERTED = 1'b0;
-    parameter [0:0] IS_CE_INVERTED = 1'b0;
-    parameter [0:0] IS_D_INVERTED = 1'b0;
-    parameter [0:0] IS_R_INVERTED = 1'b0;
-    parameter [0:0] IS_S_INVERTED = 1'b0;
-    output Q;
-    (* clkbuf_sink *)
-    (* invertible_pin = "IS_C_INVERTED" *)
-    input C;
-    (* invertible_pin = "IS_CE_INVERTED" *)
-    input CE;
-    (* invertible_pin = "IS_D_INVERTED" *)
-    input D;
-    (* invertible_pin = "IS_R_INVERTED" *)
-    input R;
-    (* invertible_pin = "IS_S_INVERTED" *)
-    input S;
-endmodule
-
 module LDCPE (...);
     parameter [0:0] INIT = 1'b0;
     parameter [0:0] IS_CLR_INVERTED = 1'b0;
index a061c8dc0dc6465888f5a7b1a2232f2e797912fc..971089b289a385230718a3ff02de4d14560a78a0 100644 (file)
@@ -570,6 +570,7 @@ struct SynthXilinxPass : public ScriptPass
                        else
                                techmap_args += " -map " + ff_map_file;
                        run("techmap " + techmap_args);
+                       run("xilinx_dffopt");
                        run("clean");
                }
 
diff --git a/techlibs/xilinx/xilinx_dffopt.cc b/techlibs/xilinx/xilinx_dffopt.cc
new file mode 100644 (file)
index 0000000..1256a08
--- /dev/null
@@ -0,0 +1,351 @@
+/*
+ *  yosys -- Yosys Open SYnthesis Suite
+ *
+ *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at>
+ *
+ *  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
+
+typedef std::pair<Const, std::vector<SigBit>> LutData;
+
+// Compute a LUT implementing (select ^ select_inv) ? alt_data : data.  Returns true if successful.
+bool merge_lut(LutData &result, const LutData &data, const LutData select, bool select_inv, SigBit alt_data, int max_lut_size) {
+       // First, gather input signals.
+       result.second = data.second;
+       int idx_alt = -1;
+       if (alt_data.wire) {
+               // Check if we already have it.
+               for (int i = 0; i < GetSize(result.second); i++)
+                       if (result.second[i] == alt_data)
+                               idx_alt = i;
+               // If not, add it.
+               if (idx_alt == -1) {
+                       idx_alt = GetSize(result.second);
+                       result.second.push_back(alt_data);
+               }
+       }
+       std::vector<int> idx_sel;
+       for (auto bit : select.second) {
+               int idx = -1;
+               for (int i = 0; i < GetSize(result.second); i++)
+                       if (result.second[i] == bit)
+                               idx = i;
+               if (idx == -1) {
+                       idx = GetSize(result.second);
+                       result.second.push_back(bit);
+               }
+               idx_sel.push_back(idx);
+       }
+
+       // If LUT would be too large, bail.
+       if (GetSize(result.second) > max_lut_size)
+               return false;
+
+       // Okay, we're doing it — compute the LUT mask.
+       result.first = Const(0, 1 << GetSize(result.second));
+       for (int i = 0; i < GetSize(result.first); i++) {
+               int sel_lut_idx = 0;
+               for (int j = 0; j < GetSize(select.second); j++)
+                       if (i & 1 << idx_sel[j])
+                               sel_lut_idx |= 1 << j;
+               bool select_val = (select.first.bits[sel_lut_idx] == State::S1);
+               bool new_bit;
+               if (select_val ^ select_inv) {
+                       // Use alt_data.
+                       if (alt_data.wire)
+                               new_bit = (i & 1 << idx_alt) != 0;
+                       else
+                               new_bit = alt_data.data == State::S1;
+               } else {
+                       // Use original LUT.
+                       int lut_idx = i & ((1 << GetSize(data.second)) - 1);
+                       new_bit = data.first.bits[lut_idx] == State::S1;
+               }
+               result.first.bits[i] = new_bit ? State::S1 : State::S0;
+       }
+       return true;
+}
+
+struct XilinxDffOptPass : public Pass {
+       XilinxDffOptPass() : Pass("xilinx_dffopt", "Xilinx: optimize FF control signal usage") { }
+       void help() YS_OVERRIDE
+       {
+               //   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+               log("\n");
+               log("    xilinx_dffopt [options] [selection]\n");
+               log("\n");
+               log("Converts hardware clock enable and set/reset signals on FFs to emulation\n");
+               log("using LUTs, if doing so would improve area.  Operates on post-techmap Xilinx\n");
+               log("cells (LUT*, FD*).\n");
+               log("\n");
+               log("    -lut4\n");
+               log("        Assume a LUT4-based device (instead of a LUT6-based device).\n");
+               log("\n");
+       }
+       void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+       {
+               log_header(design, "Executing XILINX_DFFOPT pass (optimize FF control signal usage).\n");
+
+               size_t argidx;
+               int max_lut_size = 6;
+               for (argidx = 1; argidx < args.size(); argidx++)
+               {
+                       if (args[argidx] == "-lut4") {
+                               max_lut_size = 4;
+                               continue;
+                       }
+                       break;
+               }
+               extra_args(args, argidx, design);
+
+               for (auto module : design->selected_modules())
+               {
+                       log("Optimizing FFs in %s.\n", log_id(module));
+
+                       SigMap sigmap(module);
+                       dict<SigBit, pair<LutData, Cell *>> bit_to_lut;
+                       dict<SigBit, int> bit_uses;
+
+                       // Gather LUTs.
+                       for (auto cell : module->selected_cells())
+                       {
+                               for (auto port : cell->connections())
+                                       for (auto bit : port.second)
+                                               bit_uses[sigmap(bit)]++;
+                               if (cell->get_bool_attribute(ID::keep))
+                                       continue;
+                               if (cell->type == ID(INV)) {
+                                       SigBit sigout = sigmap(cell->getPort(ID(O)));
+                                       SigBit sigin = sigmap(cell->getPort(ID(I)));
+                                       bit_to_lut[sigout] = make_pair(LutData(Const(1, 2), {sigin}), cell);
+                               } else if (cell->type.in(ID(LUT1), ID(LUT2), ID(LUT3), ID(LUT4), ID(LUT5), ID(LUT6))) {
+                                       SigBit sigout = sigmap(cell->getPort(ID(O)));
+                                       const Const &init = cell->getParam(ID(INIT));
+                                       std::vector<SigBit> sigin;
+                                       sigin.push_back(sigmap(cell->getPort(ID(I0))));
+                                       if (cell->type == ID(LUT1))
+                                               goto lut_sigin_done;
+                                       sigin.push_back(sigmap(cell->getPort(ID(I1))));
+                                       if (cell->type == ID(LUT2))
+                                               goto lut_sigin_done;
+                                       sigin.push_back(sigmap(cell->getPort(ID(I2))));
+                                       if (cell->type == ID(LUT3))
+                                               goto lut_sigin_done;
+                                       sigin.push_back(sigmap(cell->getPort(ID(I3))));
+                                       if (cell->type == ID(LUT4))
+                                               goto lut_sigin_done;
+                                       sigin.push_back(sigmap(cell->getPort(ID(I4))));
+                                       if (cell->type == ID(LUT5))
+                                               goto lut_sigin_done;
+                                       sigin.push_back(sigmap(cell->getPort(ID(I5))));
+lut_sigin_done:
+                                       bit_to_lut[sigout] = make_pair(LutData(init, sigin), cell);
+                               }
+                       }
+                       for (auto wire : module->wires())
+                               if (wire->port_output || wire->port_input)
+                                       for (int i = 0; i < GetSize(wire); i++)
+                                               bit_uses[sigmap(SigBit(wire, i))]++;
+
+                       // Iterate through FFs.
+                       for (auto cell : module->selected_cells())
+                       {
+                               bool has_s = false, has_r = false;
+                               if (cell->type.in(ID(FDCE), ID(FDPE), ID(FDCPE), ID(FDCE_1), ID(FDPE_1), ID(FDCPE_1))) {
+                                       // Async reset.
+                               } else if (cell->type.in(ID(FDRE), ID(FDRE_1))) {
+                                       has_r = true;
+                               } else if (cell->type.in(ID(FDSE), ID(FDSE_1))) {
+                                       has_s = true;
+                               } else if (cell->type.in(ID(FDRSE), ID(FDRSE_1))) {
+                                       has_r = true;
+                                       has_s = true;
+                               } else {
+                                       // Not a FF.
+                                       continue;
+                               }
+                               if (cell->get_bool_attribute(ID::keep))
+                                       continue;
+
+                               // Don't bother if D has more than one use.
+                               SigBit sig_D = sigmap(cell->getPort(ID(D)));
+                               if (bit_uses[sig_D] > 2)
+                                       continue;
+
+                               // Find the D LUT.
+                               auto it_D = bit_to_lut.find(sig_D);
+                               if (it_D == bit_to_lut.end())
+                                       continue;
+                               LutData lut_d = it_D->second.first;
+                               Cell *cell_d = it_D->second.second;
+                               if (cell->hasParam(ID(IS_D_INVERTED)) && cell->getParam(ID(IS_D_INVERTED)).as_bool()) {
+                                       // Flip all bits in the LUT.
+                                       for (int i = 0; i < GetSize(lut_d.first); i++)
+                                               lut_d.first.bits[i] = (lut_d.first.bits[i] == State::S1) ? State::S0 : State::S1;
+                               }
+
+                               LutData lut_d_post_ce;
+                               LutData lut_d_post_s;
+                               LutData lut_d_post_r;
+                               bool worthy_post_ce = false;
+                               bool worthy_post_s = false;
+                               bool worthy_post_r = false;
+
+                               // First, unmap CE.
+                               SigBit sig_Q = sigmap(cell->getPort(ID(Q)));
+                               SigBit sig_CE = sigmap(cell->getPort(ID(CE)));
+                               LutData lut_ce = LutData(Const(2, 2), {sig_CE});
+                               auto it_CE = bit_to_lut.find(sig_CE);
+                               if (it_CE != bit_to_lut.end())
+                                       lut_ce = it_CE->second.first;
+                               if (sig_CE.wire) {
+                                       // Merge CE LUT and D LUT into one.  If it cannot be done, nothing to do about this FF.
+                                       if (!merge_lut(lut_d_post_ce, lut_d, lut_ce, true, sig_Q, max_lut_size))
+                                               continue;
+
+                                       // If this gets rid of a CE LUT, it's worth it.  If not, it still may be worth it, if we can remove set/reset as well.
+                                       if (it_CE != bit_to_lut.end())
+                                               worthy_post_ce = true;
+                               } else if (sig_CE.data != State::S1) {
+                                       // Strange.  Should not happen in a reasonable flow, so bail.
+                                       continue;
+                               } else {
+                                       lut_d_post_ce = lut_d;
+                               }
+
+                               // Second, unmap S, if any.
+                               lut_d_post_s = lut_d_post_ce;
+                               if (has_s) {
+                                       SigBit sig_S = sigmap(cell->getPort(ID(S)));
+                                       LutData lut_s = LutData(Const(2, 2), {sig_S});
+                                       bool inv_s = cell->hasParam(ID(IS_S_INVERTED)) && cell->getParam(ID(IS_S_INVERTED)).as_bool();
+                                       auto it_S = bit_to_lut.find(sig_S);
+                                       if (it_S != bit_to_lut.end())
+                                               lut_s = it_S->second.first;
+                                       if (sig_S.wire) {
+                                               // Merge S LUT and D LUT into one.  If it cannot be done, try to at least merge CE.
+                                               if (!merge_lut(lut_d_post_s, lut_d_post_ce, lut_s, inv_s, SigBit(State::S1), max_lut_size))
+                                                       goto unmap;
+                                               // If this gets rid of an S LUT, it's worth it.
+                                               if (it_S != bit_to_lut.end())
+                                                       worthy_post_s = true;
+                                       } else if (sig_S.data != (inv_s ? State::S1 : State::S0)) {
+                                               // Strange.  Should not happen in a reasonable flow, so bail.
+                                               continue;
+                                       }
+                               }
+
+                               // Third, unmap R, if any.
+                               lut_d_post_r = lut_d_post_s;
+                               if (has_r) {
+                                       SigBit sig_R = sigmap(cell->getPort(ID(R)));
+                                       LutData lut_r = LutData(Const(2, 2), {sig_R});
+                                       bool inv_r = cell->hasParam(ID(IS_R_INVERTED)) && cell->getParam(ID(IS_R_INVERTED)).as_bool();
+                                       auto it_R = bit_to_lut.find(sig_R);
+                                       if (it_R != bit_to_lut.end())
+                                               lut_r = it_R->second.first;
+                                       if (sig_R.wire) {
+                                               // Merge R LUT and D LUT into one.  If it cannot be done, try to at least merge CE/S.
+                                               if (!merge_lut(lut_d_post_r, lut_d_post_s, lut_r, inv_r, SigBit(State::S0), max_lut_size))
+                                                       goto unmap;
+                                               // If this gets rid of an S LUT, it's worth it.
+                                               if (it_R != bit_to_lut.end())
+                                                       worthy_post_r = true;
+                                       } else if (sig_R.data != (inv_r ? State::S1 : State::S0)) {
+                                               // Strange.  Should not happen in a reasonable flow, so bail.
+                                               continue;
+                                       }
+                               }
+
+unmap:
+                               LutData final_lut;
+                               if (worthy_post_r) {
+                                       final_lut = lut_d_post_r;
+                                       log("  Merging R LUT for %s/%s (%d -> %d)\n", log_id(cell), log_id(sig_Q.wire), GetSize(lut_d.second), GetSize(final_lut.second));
+                               } else if (worthy_post_s) {
+                                       final_lut = lut_d_post_s;
+                                       log("  Merging S LUT for %s/%s (%d -> %d)\n", log_id(cell), log_id(sig_Q.wire), GetSize(lut_d.second), GetSize(final_lut.second));
+                               } else if (worthy_post_ce) {
+                                       final_lut = lut_d_post_ce;
+                                       log("  Merging CE LUT for %s/%s (%d -> %d)\n", log_id(cell), log_id(sig_Q.wire), GetSize(lut_d.second), GetSize(final_lut.second));
+                               } else {
+                                       // Nothing to do here.
+                                       continue;
+                               }
+
+                               // Okay, we're doing it.  Unmap ports.
+                               if (worthy_post_r) {
+                                       cell->unsetParam(ID(IS_R_INVERTED));
+                                       cell->setPort(ID(R), Const(0, 1));
+                               }
+                               if (has_s && (worthy_post_r || worthy_post_s)) {
+                                       cell->unsetParam(ID(IS_S_INVERTED));
+                                       cell->setPort(ID(S), Const(0, 1));
+                               }
+                               cell->setPort(ID(CE), Const(1, 1));
+                               cell->unsetParam(ID(IS_D_INVERTED));
+
+                               // Create the new LUT.
+                               Cell *lut_cell = 0;
+                               switch (GetSize(final_lut.second)) {
+                                       case 1:
+                                               lut_cell = module->addCell(NEW_ID, ID(LUT1));
+                                               break;
+                                       case 2:
+                                               lut_cell = module->addCell(NEW_ID, ID(LUT2));
+                                               break;
+                                       case 3:
+                                               lut_cell = module->addCell(NEW_ID, ID(LUT3));
+                                               break;
+                                       case 4:
+                                               lut_cell = module->addCell(NEW_ID, ID(LUT4));
+                                               break;
+                                       case 5:
+                                               lut_cell = module->addCell(NEW_ID, ID(LUT5));
+                                               break;
+                                       case 6:
+                                               lut_cell = module->addCell(NEW_ID, ID(LUT6));
+                                               break;
+                                       default:
+                                               log_assert(!"unknown lut size");
+                               }
+                               lut_cell->attributes = cell_d->attributes;
+                               Wire *lut_out = module->addWire(NEW_ID);
+                               lut_cell->setParam(ID(INIT), final_lut.first);
+                               cell->setPort(ID(D), lut_out);
+                               lut_cell->setPort(ID(O), lut_out);
+                               lut_cell->setPort(ID(I0), final_lut.second[0]);
+                               if (GetSize(final_lut.second) >= 2)
+                                       lut_cell->setPort(ID(I1), final_lut.second[1]);
+                               if (GetSize(final_lut.second) >= 3)
+                                       lut_cell->setPort(ID(I2), final_lut.second[2]);
+                               if (GetSize(final_lut.second) >= 4)
+                                       lut_cell->setPort(ID(I3), final_lut.second[3]);
+                               if (GetSize(final_lut.second) >= 5)
+                                       lut_cell->setPort(ID(I4), final_lut.second[4]);
+                               if (GetSize(final_lut.second) >= 6)
+                                       lut_cell->setPort(ID(I5), final_lut.second[5]);
+                       }
+               }
+       }
+} XilinxDffOptPass;
+
+PRIVATE_NAMESPACE_END
+
index 4545cf6d71431ecd0b9c1bb2af2f7e4bd44b890a..f03400fe76ad8f5ebef794468148d573a9f09e95 100644 (file)
@@ -14,6 +14,6 @@ select -assert-count 1 t:BUFG
 select -assert-count 4 t:FDRE
 select -assert-count 1 t:FDSE
 select -assert-count 1 t:LUT2
-select -assert-count 2 t:LUT3
-select -assert-count 4 t:LUT5
-select -assert-none t:BUFG t:FDRE t:FDSE t:LUT2 t:LUT3 t:LUT5 %% t:* %D
+select -assert-count 3 t:LUT5
+select -assert-count 1 t:LUT6
+select -assert-none t:BUFG t:FDRE t:FDSE t:LUT2 t:LUT5 t:LUT6 %% t:* %D
diff --git a/tests/arch/xilinx/xilinx_dffopt.ys b/tests/arch/xilinx/xilinx_dffopt.ys
new file mode 100644 (file)
index 0000000..dc036ac
--- /dev/null
@@ -0,0 +1,216 @@
+read_verilog << EOT
+
+// FDRE, mergeable CE and R.
+
+module t0 (...);
+input wire clk;
+input wire [7:0] i;
+output wire [7:0] o;
+
+wire [7:0] tmp ;
+
+LUT2 #(.INIT(4'h6)) lut0 (.I0(i[0]), .I1(i[1]), .O(tmp[0]));
+LUT2 #(.INIT(4'h6)) lut1 (.I0(i[1]), .I1(i[2]), .O(tmp[1]));
+LUT2 #(.INIT(4'h6)) lut2 (.I0(i[3]), .I1(i[4]), .O(tmp[2]));
+
+FDRE ff (.D(tmp[0]), .CE(tmp[1]), .R(tmp[2]), .Q(o[0]));
+
+endmodule
+
+EOT
+
+design -save t0
+
+equiv_opt -blacklist xilinx_dffopt_blacklist.txt -assert -map +/xilinx/cells_sim.v xilinx_dffopt
+design -load postopt
+clean
+
+select -assert-count 1 t:FDRE
+select -assert-count 1 t:LUT6
+select -assert-count 3 t:LUT2
+select -assert-none t:FDRE t:LUT6 t:LUT2 %% t:* %D
+
+design -load t0
+
+equiv_opt -blacklist xilinx_dffopt_blacklist.txt -assert -map +/xilinx/cells_sim.v xilinx_dffopt -lut4
+design -load postopt
+clean
+
+select -assert-count 1 t:FDRE
+select -assert-count 1 t:LUT4
+select -assert-count 3 t:LUT2
+select -assert-none t:FDRE t:LUT4 t:LUT2 %% t:* %D
+
+design -reset
+
+
+read_verilog << EOT
+
+// FDSE, mergeable CE and S, inversions.
+
+module t0 (...);
+input wire clk;
+input wire [7:0] i;
+output wire [7:0] o;
+
+wire [7:0] tmp ;
+
+LUT2 #(.INIT(4'h6)) lut0 (.I0(i[0]), .I1(i[1]), .O(tmp[0]));
+LUT2 #(.INIT(4'h6)) lut1 (.I0(i[1]), .I1(i[2]), .O(tmp[1]));
+LUT2 #(.INIT(4'h6)) lut2 (.I0(i[3]), .I1(i[4]), .O(tmp[2]));
+
+FDSE #(.IS_D_INVERTED(1'b1), .IS_S_INVERTED(1'b1)) ff (.D(tmp[0]), .CE(tmp[1]), .S(tmp[2]), .Q(o[0]));
+
+endmodule
+
+EOT
+
+design -save t0
+
+equiv_opt -blacklist xilinx_dffopt_blacklist.txt -assert -map +/xilinx/cells_sim.v xilinx_dffopt
+design -load postopt
+clean
+
+select -assert-count 1 t:FDSE
+select -assert-count 1 t:LUT6
+select -assert-count 3 t:LUT2
+select -assert-none t:FDSE t:LUT6 t:LUT2 %% t:* %D
+
+design -load t0
+
+equiv_opt -blacklist xilinx_dffopt_blacklist.txt -assert -map +/xilinx/cells_sim.v xilinx_dffopt -lut4
+design -load postopt
+clean
+
+select -assert-count 1 t:FDSE
+select -assert-count 1 t:LUT4
+select -assert-count 3 t:LUT2
+select -assert-none t:FDSE t:LUT4 t:LUT2 %% t:* %D
+
+design -reset
+
+
+read_verilog << EOT
+
+// FDCE, mergeable CE.
+
+module t0 (...);
+input wire clk;
+input wire [7:0] i;
+output wire [7:0] o;
+
+wire [7:0] tmp ;
+
+LUT2 #(.INIT(4'h6)) lut0 (.I0(i[0]), .I1(i[1]), .O(tmp[0]));
+LUT2 #(.INIT(4'h6)) lut1 (.I0(i[1]), .I1(i[2]), .O(tmp[1]));
+LUT2 #(.INIT(4'h6)) lut2 (.I0(i[3]), .I1(i[4]), .O(tmp[2]));
+
+FDCE ff (.D(tmp[0]), .CE(tmp[1]), .CLR(tmp[2]), .Q(o[0]));
+
+endmodule
+
+EOT
+
+design -save t0
+
+equiv_opt -async2sync -blacklist xilinx_dffopt_blacklist.txt -assert -map +/xilinx/cells_sim.v xilinx_dffopt
+design -load postopt
+clean
+
+select -assert-count 1 t:FDCE
+select -assert-count 1 t:LUT4
+select -assert-count 3 t:LUT2
+select -assert-none t:FDCE t:LUT4 t:LUT2 %% t:* %D
+
+design -reset
+
+
+read_verilog << EOT
+
+// FDSE, mergeable CE and S, but CE only not worth it.
+
+module t0 (...);
+input wire clk;
+input wire [7:0] i;
+output wire [7:0] o;
+
+wire [7:0] tmp ;
+
+LUT2 #(.INIT(4'h6)) lut0 (.I0(i[0]), .I1(i[1]), .O(tmp[0]));
+LUT2 #(.INIT(4'h6)) lut1 (.I0(i[1]), .I1(i[2]), .O(tmp[1]));
+
+FDSE ff (.D(tmp[0]), .CE(i[7]), .S(tmp[1]), .Q(o[0]));
+
+endmodule
+
+EOT
+
+design -save t0
+
+equiv_opt -blacklist xilinx_dffopt_blacklist.txt -assert -map +/xilinx/cells_sim.v xilinx_dffopt
+design -load postopt
+clean
+
+select -assert-count 1 t:FDSE
+select -assert-count 1 t:LUT5
+select -assert-count 2 t:LUT2
+select -assert-none t:FDSE t:LUT5 t:LUT2 %% t:* %D
+
+design -load t0
+
+equiv_opt -blacklist xilinx_dffopt_blacklist.txt -assert -map +/xilinx/cells_sim.v xilinx_dffopt -lut4
+design -load postopt
+clean
+
+select -assert-count 1 t:FDSE
+select -assert-count 2 t:LUT2
+select -assert-none t:FDSE t:LUT2 %% t:* %D
+
+design -reset
+
+
+read_verilog << EOT
+
+// FDRSE, mergeable CE, S, R.
+
+module t0 (...);
+input wire clk;
+input wire [7:0] i;
+output wire [7:0] o;
+
+wire [7:0] tmp ;
+
+LUT2 #(.INIT(4'h6)) lut0 (.I0(i[0]), .I1(i[1]), .O(tmp[0]));
+LUT2 #(.INIT(4'h6)) lut1 (.I0(i[1]), .I1(i[2]), .O(tmp[1]));
+LUT2 #(.INIT(4'h8)) lut2 (.I0(i[2]), .I1(i[0]), .O(tmp[2]));
+LUT2 #(.INIT(4'h6)) lut3 (.I0(i[3]), .I1(i[4]), .O(tmp[3]));
+
+FDRSE ff (.D(tmp[0]), .CE(tmp[1]), .S(tmp[2]), .R(tmp[3]), .Q(o[0]));
+
+endmodule
+
+EOT
+
+design -save t0
+
+equiv_opt -blacklist xilinx_dffopt_blacklist.txt -assert -map +/xilinx/cells_sim.v xilinx_dffopt
+design -load postopt
+clean
+
+select -assert-count 1 t:FDRSE
+select -assert-count 1 t:LUT6
+select -assert-count 4 t:LUT2
+select -assert-none t:FDRSE t:LUT6 t:LUT2 %% t:* %D
+
+design -load t0
+
+equiv_opt -blacklist xilinx_dffopt_blacklist.txt -assert -map +/xilinx/cells_sim.v xilinx_dffopt -lut4
+design -load postopt
+clean
+
+select -assert-count 1 t:FDRSE
+select -assert-count 1 t:LUT4
+select -assert-count 4 t:LUT2
+select -assert-none t:FDRSE t:LUT4 t:LUT2 %% t:* %D
+
+design -reset
diff --git a/tests/arch/xilinx/xilinx_dffopt_blacklist.txt b/tests/arch/xilinx/xilinx_dffopt_blacklist.txt
new file mode 100644 (file)
index 0000000..6a31a0c
--- /dev/null
@@ -0,0 +1,13 @@
+lut0
+lut1
+lut2
+lut3
+ff
+ff.D
+ff.R
+ff.S
+ff.CE
+ff.d
+ff.r
+ff.s
+ff.ce