Merge remote-tracking branch 'origin/diego/memattr' 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/techlibs/xilinx/xc7_xcu_brams.txt b/techlibs/xilinx/xc7_xcu_brams.txt
index ca71924..7d3d997 100644
--- a/techlibs/xilinx/xc7_xcu_brams.txt
+++ b/techlibs/xilinx/xc7_xcu_brams.txt
@@ -85,6 +85,14 @@
   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