Use attr/value from rules file, add use new match rule
diff --git a/passes/memory/memory_bram.cc b/passes/memory/memory_bram.cc
index 2d2897e..52e7ac7 100644
--- a/passes/memory/memory_bram.cc
+++ b/passes/memory/memory_bram.cc
@@ -134,7 +134,8 @@
 		dict<string, int> min_limits, max_limits;
 		bool or_next_if_better, make_transp, make_outreg;
 		char shuffle_enable;
-		std::string memattr;
+		IdString attr;
+		Const value;
 	};
 
 	dict<IdString, vector<bram_t>> brams;
@@ -328,8 +329,10 @@
 				continue;
 			}
 
-			if (GetSize(tokens) == 3 && tokens[0] == "attribute") {
-				data.memattr = tokens[2].c_str();
+			if (GetSize(tokens) >= 2 && tokens[0] == "attribute") {
+				data.attr = RTLIL::escape_id(tokens[1]);
+				if (GetSize(tokens) > 2)
+					data.value = tokens[2];
 				continue;
 			}
 
@@ -368,20 +371,6 @@
 	}
 };
 
-bool check_memory_attribute(Cell *cell, std::string match)
-{
-	std::string memory_attribute = cell->attributes["\\ram_style"].decode_string();
-	bool attribute_set_match = 0;
-
-	if (memory_attribute != "")
-	{
-		attribute_set_match = match.compare(memory_attribute);
-		if (!attribute_set_match)
-			return true;
-	}
-	return false;
-}
-
 bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram, const rules_t::match_t &match, dict<string, int> &match_properties, int mode)
 {
 	Module *module = cell->module;
@@ -817,8 +806,6 @@
 						it.first.c_str(), log_id(match.name));
 			if (match_properties[it.first] >= it.second)
 				continue;
-			if (check_memory_attribute(cell, match.memattr))
-				continue;
 			log("    Rule for bram type %s rejected: requirement 'min %s %d' not met.\n",
 					log_id(match.name), it.first.c_str(), it.second);
 			return false;
@@ -827,13 +814,28 @@
 			if (!match_properties.count(it.first))
 				log_error("Unknown property '%s' in match rule for bram type %s.\n",
 						it.first.c_str(), log_id(match.name));
-			if (match_properties[it.first] <= it.second)
-				continue;
 			log("    Rule for bram type %s rejected: requirement 'max %s %d' not met.\n",
 					log_id(match.name), it.first.c_str(), it.second);
 			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;
 	}
@@ -1018,7 +1020,6 @@
 	log("Processing %s.%s:\n", log_id(cell->module), log_id(cell));
 
 	bool cell_init = !SigSpec(cell->getParam("\\INIT")).is_fully_undef();
-	std::string cell_attribute = cell->attributes["\\ram_style"].decode_string();
 
 	dict<string, int> match_properties;
 	match_properties["words"]  = cell->getParam("\\SIZE").as_int();
@@ -1037,10 +1038,6 @@
 	pool<pair<IdString, int>> failed_brams;
 	dict<pair<int, int>, tuple<int, int, int>> best_rule_cache;
 
-	if (cell_attribute != "")
-		log("  Found memory attribute '%s' in object %s.\n", cell_attribute.c_str(), 
-			cell->name.c_str());
-
 	for (int i = 0; i < GetSize(rules.matches); i++)
 	{
 		auto &match = rules.matches.at(i);
@@ -1108,8 +1105,6 @@
 							it.first.c_str(), log_id(match.name));
 				if (match_properties[it.first] >= it.second)
 					continue;
-				if (check_memory_attribute(cell, match.memattr))
-					continue;
 				log("    Rule #%d for bram type %s (variant %d) rejected: requirement 'min %s %d' not met.\n",
 						i+1, log_id(bram.name), bram.variant, it.first.c_str(), it.second);
 				goto next_match_rule;
@@ -1128,6 +1123,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())
diff --git a/techlibs/xilinx/xc7_xcu_brams.txt b/techlibs/xilinx/xc7_xcu_brams.txt
index da663ce..aa20673 100644
--- a/techlibs/xilinx/xc7_xcu_brams.txt
+++ b/techlibs/xilinx/xc7_xcu_brams.txt
@@ -85,6 +85,13 @@
   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