Merge remote-tracking branch 'origin/xaig_dff' into eddie/exp
diff --git a/passes/memory/memory_bram.cc b/passes/memory/memory_bram.cc
index aa8f941..cd8c9c5 100644
--- a/passes/memory/memory_bram.cc
+++ b/passes/memory/memory_bram.cc
@@ -134,6 +134,8 @@
 		dict<string, int> min_limits, max_limits;
 		bool or_next_if_better, make_transp, make_outreg;
 		char shuffle_enable;
+		IdString attr;
+		Const value;
 	};
 
 	dict<IdString, vector<bram_t>> brams;
@@ -327,6 +329,13 @@
 				continue;
 			}
 
+			if (GetSize(tokens) >= 2 && tokens[0] == "attribute") {
+				data.attr = RTLIL::escape_id(tokens[1]);
+				if (GetSize(tokens) > 2)
+					data.value = tokens[2];
+				continue;
+			}
+
 			syntax_error();
 		}
 	}
@@ -813,6 +822,23 @@
 			return false;
 		}
 
+		if (!match.attr.empty()) {
+			auto it = cell->attributes.find(match.attr);
+			if (it == cell->attributes.end()) {
+				if (!match.value.empty())
+					log("    Rule for bram type %s rejected: requirement 'attribute %s=\"%s\"' not met.\n",
+							log_id(match.name), log_id(match.attr), match.value.decode_string().c_str());
+					return false;
+			}
+			else {
+				if (it->second != match.value) {
+					log("    Rule for bram type %s rejected: requirement 'attribute %s=\"%s\"' not met.\n",
+							log_id(match.name), log_id(match.attr), match.value.decode_string().c_str());
+					return false;
+				}
+			}
+		}
+
 		if (mode == 1)
 			return true;
 	}
@@ -1100,6 +1126,24 @@
 				goto next_match_rule;
 			}
 
+			if (!match.attr.empty()) {
+				auto it = cell->attributes.find(match.attr);
+				if (it == cell->attributes.end()) {
+					if (!match.value.empty()) {
+						log("    Rule for bram type %s rejected: requirement 'attribute %s=\"%s\"' not met.\n",
+								log_id(match.name), log_id(match.attr), match.value.decode_string().c_str());
+						goto next_match_rule;
+					}
+				}
+				else {
+					if (it->second != match.value) {
+						log("    Rule for bram type %s rejected: requirement 'attribute %s=\"%s\"' not met.\n",
+								log_id(match.name), log_id(match.attr), match.value.decode_string().c_str());
+						goto next_match_rule;
+					}
+				}
+			}
+
 			log("    Rule #%d for bram type %s (variant %d) accepted.\n", i+1, log_id(bram.name), bram.variant);
 
 			if (or_next_if_better || !best_rule_cache.empty())
@@ -1225,6 +1269,11 @@
 		log("    dcells  .......  number of cells in 'data-direction'\n");
 		log("    cells  ........  total number of cells (acells*dcells*dups)\n");
 		log("\n");
+		log("A match containing the condition 'attribute' followed by a name and optional\n");
+		log("value requires that the memory contains the given attribute name and value\n");
+		log("(if specified) or that the attribute is not present or the value is empty (if\n");
+		log("value is not specified\n).");
+		log("\n");
 		log("The interface for the created bram instances is derived from the bram\n");
 		log("description. Use 'techmap' to convert the created bram instances into\n");
 		log("instances of the actual bram cells of your target architecture.\n");
diff --git a/passes/memory/memory_collect.cc b/passes/memory/memory_collect.cc
index 9dcb3f0..6acbce6 100644
--- a/passes/memory/memory_collect.cc
+++ b/passes/memory/memory_collect.cc
@@ -218,10 +218,6 @@
 	mem->setPort("\\RD_DATA", sig_rd_data);
 	mem->setPort("\\RD_EN", sig_rd_en);
 
-	// Copy attributes from RTLIL memory to $mem
-	for (auto attr : memory->attributes)
-		mem->attributes[attr.first] = attr.second;
-
 	for (auto c : memcells)
 		module->remove(c);
 
diff --git a/passes/pmgen/Makefile.inc b/passes/pmgen/Makefile.inc
index 145d2eb..a4d9d42 100644
--- a/passes/pmgen/Makefile.inc
+++ b/passes/pmgen/Makefile.inc
@@ -45,3 +45,9 @@
 OBJS += passes/pmgen/xilinx_srl.o
 passes/pmgen/xilinx_srl.o: passes/pmgen/xilinx_srl_pm.h
 $(eval $(call add_extra_objs,passes/pmgen/xilinx_srl_pm.h))
+
+# --------------------------------------
+
+OBJS += passes/pmgen/xilinx_dff.o
+passes/pmgen/xilinx_dff.o: passes/pmgen/xilinx_dff_pm.h
+$(eval $(call add_extra_objs,passes/pmgen/xilinx_dff_pm.h))
diff --git a/passes/pmgen/xilinx_dff.cc b/passes/pmgen/xilinx_dff.cc
new file mode 100644
index 0000000..bf0c735
--- /dev/null
+++ b/passes/pmgen/xilinx_dff.cc
@@ -0,0 +1,62 @@
+/*
+ *  yosys -- Yosys Open SYnthesis Suite
+ *
+ *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at>
+ *                2019  Eddie Hung    <eddie@fpgeh.com>
+ *
+ *  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
+
+#include "passes/pmgen/xilinx_dff_pm.h"
+
+struct XilinxDffPass : public Pass {
+	XilinxDffPass() : Pass("xilinx_dff", "Xilinx: TODO") { }
+	void help() YS_OVERRIDE
+	{
+		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+		log("\n");
+		log("    xilinx_dff [options] [selection]\n");
+		log("\n");
+		log("TODO\n");
+		log("\n");
+	}
+	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+	{
+		log_header(design, "Executing XILINX_DFF pass (TODO).\n");
+
+		size_t argidx;
+		for (argidx = 1; argidx < args.size(); argidx++)
+		{
+			// if (args[argidx] == "-singleton") {
+			// 	singleton_mode = true;
+			// 	continue;
+			// }
+			break;
+		}
+		extra_args(args, argidx, design);
+
+		for (auto module : design->selected_modules()) {
+			xilinx_dff_pm pm(module, module->selected_cells());
+			pm.run_xilinx_dff();
+		}
+	}
+} XilinxDffPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/pmgen/xilinx_dff.pmg b/passes/pmgen/xilinx_dff.pmg
new file mode 100644
index 0000000..4de25c3
--- /dev/null
+++ b/passes/pmgen/xilinx_dff.pmg
@@ -0,0 +1,26 @@
+pattern xilinx_dff
+
+match fd
+	select fd->type.in(\FDRE)
+	select port(fd, \R).is_fully_zero()
+	select port(fd, \CE).is_fully_ones()
+endmatch
+
+match lut
+	select lut->type.in(\LUT2)
+	index <SigSpec> port(lut, \O) === port(fd, \D)
+endmatch
+
+code
+	if (lut->type == \LUT2) {
+		if (param(lut, \INIT) == Const::from_string("0010")) {
+			fd->setPort(\D, port(lut, \I0));
+			fd->setPort(\R, port(lut, \I1));
+		}
+		else if (param(lut, \INIT) == Const::from_string("0100")) {
+			fd->setPort(\R, port(lut, \I0));
+			fd->setPort(\D, port(lut, \I1));
+		}
+	}
+	else log_abort();
+endcode
diff --git a/techlibs/xilinx/synth_xilinx.cc b/techlibs/xilinx/synth_xilinx.cc
index 554c42d..df7514f 100644
--- a/techlibs/xilinx/synth_xilinx.cc
+++ b/techlibs/xilinx/synth_xilinx.cc
@@ -559,6 +559,7 @@
 			else
 				techmap_args += " -map " + ff_map_file;
 			run("techmap " + techmap_args);
+			run("xilinx_dff");
 		}
 
 		if (check_label("finalize")) {
diff --git a/techlibs/xilinx/xc7_xcu_brams.txt b/techlibs/xilinx/xc7_xcu_brams.txt
index f116111..7d3d997 100644
--- a/techlibs/xilinx/xc7_xcu_brams.txt
+++ b/techlibs/xilinx/xc7_xcu_brams.txt
@@ -81,10 +81,18 @@
 endmatch
 
 match $__XILINX_RAMB18_SDP
-  min bits 4096
+  min bits 1024
   min efficiency 5
   shuffle_enable B
   make_transp
+  attribute ram_style
+  or_next_if_better
+endmatch
+
+match $__XILINX_RAMB18_SDP
+  shuffle_enable B
+  make_transp
+  attribute ram_style block
   or_next_if_better
 endmatch
 
@@ -97,7 +105,7 @@
 endmatch
 
 match $__XILINX_RAMB18_TDP
-  min bits 4096
+  min bits 1024
   min efficiency 5
   shuffle_enable B
   make_transp