From b0aab4e3046f1a598314e2563fa84b3a8ddc7fa5 Mon Sep 17 00:00:00 2001
From: Clifford Wolf <clifford@clifford.at>
Date: Tue, 9 Aug 2016 19:56:55 +0200
Subject: [PATCH] Added "attrmap" command

---
 passes/techmap/Makefile.inc |   1 +
 passes/techmap/attrmap.cc   | 250 ++++++++++++++++++++++++++++++++++++
 passes/techmap/attrmvcp.cc  |   2 +
 3 files changed, 253 insertions(+)
 create mode 100644 passes/techmap/attrmap.cc

diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc
index c07d69fb2..b5024fa96 100644
--- a/passes/techmap/Makefile.inc
+++ b/passes/techmap/Makefile.inc
@@ -30,6 +30,7 @@ OBJS += passes/techmap/shregmap.o
 OBJS += passes/techmap/deminout.o
 OBJS += passes/techmap/insbuf.o
 OBJS += passes/techmap/attrmvcp.o
+OBJS += passes/techmap/attrmap.o
 endif
 
 GENFILES += passes/techmap/techmap.inc
diff --git a/passes/techmap/attrmap.cc b/passes/techmap/attrmap.cc
new file mode 100644
index 000000000..648d1bc5a
--- /dev/null
+++ b/passes/techmap/attrmap.cc
@@ -0,0 +1,250 @@
+/*
+ *  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
+
+Const make_value(string &value)
+{
+	if (GetSize(value) >= 2 && value.front() == '"' && value.back() == '"')
+		return Const(value.substr(1, GetSize(value)-2));
+
+	SigSpec sig;
+	SigSpec::parse(sig, nullptr, value);
+	return sig.as_const();
+}
+
+bool match_name(string &name, IdString &id, bool ignore_case=false)
+{
+	string str1 = RTLIL::escape_id(name);
+	string str2 = id.str();
+
+	if (ignore_case)
+		return !strcasecmp(str1.c_str(), str2.c_str());
+
+	return str1 == str2;
+}
+
+bool match_value(string &value, Const &val, bool ignore_case=false)
+{
+	if (ignore_case && ((val.flags & RTLIL::CONST_FLAG_STRING) != 0) && GetSize(value) && value.front() == '"' && value.back() == '"') {
+		string str1 = value.substr(1, GetSize(value)-2);
+		string str2 = val.decode_string();
+		return !strcasecmp(str1.c_str(), str2.c_str());
+	}
+
+	return make_value(value) == val;
+}
+
+struct AttrmapAction {
+	virtual ~AttrmapAction() { }
+	virtual bool apply(IdString &id, Const &val) = 0;
+};
+
+struct AttrmapTocase : AttrmapAction {
+	string name;
+	virtual bool apply(IdString &id, Const&) {
+		if (match_name(name, id, true))
+			id = RTLIL::escape_id(name);
+		return true;
+	}
+};
+
+struct AttrmapRename : AttrmapAction {
+	string old_name, new_name;
+	virtual bool apply(IdString &id, Const&) {
+		if (match_name(old_name, id))
+			id = RTLIL::escape_id(new_name);
+		return true;
+	}
+};
+
+struct AttrmapMap : AttrmapAction {
+	bool imap;
+	string old_name, new_name;
+	string old_value, new_value;
+	virtual bool apply(IdString &id, Const &val) {
+		if (match_name(old_name, id) && match_value(old_value, val, true)) {
+			id = RTLIL::escape_id(new_name);
+			val = make_value(new_value);
+		}
+		return true;
+	}
+};
+
+struct AttrmapRemove : AttrmapAction {
+	string name, value;
+	virtual bool apply(IdString &id, Const &val) {
+		return !(match_name(name, id) && match_value(value, val));
+	}
+};
+
+void attrmap_apply(string objname, vector<std::unique_ptr<AttrmapAction>> &actions, dict<RTLIL::IdString, RTLIL::Const> &attributes)
+{
+	dict<RTLIL::IdString, RTLIL::Const> new_attributes;
+
+	for (auto attr : attributes)
+	{
+		auto new_attr = attr;
+		for (auto &action : actions)
+			if (!action->apply(new_attr.first, new_attr.second))
+				goto delete_this_attr;
+
+		if (new_attr != attr)
+			log("Changed attribute on %s: %s=%s -> %s=%s\n", objname.c_str(),
+					log_id(attr.first), log_const(attr.second), log_id(new_attr.first), log_const(new_attr.second));
+
+		new_attributes[new_attr.first] = new_attr.second;
+
+		if (0)
+	delete_this_attr:
+			log("Removed attribute on %s: %s=%s\n", objname.c_str(), log_id(attr.first), log_const(attr.second));
+	}
+
+	attributes.swap(new_attributes);
+}
+
+struct AttrmapPass : public Pass {
+	AttrmapPass() : Pass("attrmap", "renaming attributes") { }
+	virtual void help()
+	{
+		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+		log("\n");
+		log("    attrmap [options] [selection]\n");
+		log("\n");
+		log("This command renames attributes and/or mapps key/value pairs to\n");
+		log("other key/value pairs.\n");
+		log("\n");
+		log("    -tocase <name>\n");
+		log("        Match attribute names case-insensitively and set it to the specified\n");
+		log("        name.\n");
+		log("\n");
+		log("    -rename <old_name> <new_name>\n");
+		log("        Rename attributes as specified\n");
+		log("\n");
+		log("    -map <old_name>=<old_value> <new_name>=<new_value>\n");
+		log("        Map key/value pairs as indicated.\n");
+		log("\n");
+		log("    -imap <old_name>=<old_value> <new_name>=<new_value>\n");
+		log("        Like -map, but use case-insensitive match for <old_value> when\n");
+		log("        it is a string value.\n");
+		log("\n");
+		log("    -remove <name>=<value>\n");
+		log("        Remove attributes matching this pattern.\n");
+		log("\n");
+		log("    -modattr\n");
+		log("        Operate on module attributes instead of attributes on wires and cells.\n");
+		log("\n");
+		log("For example, mapping Xilinx-style \"keep\" attributes to Yosys-style:\n");
+		log("\n");
+		log("    attrmap -tocase keep -imap keep=\"true\" keep=1 \\\n");
+		log("            -imap keep=\"false\" keep=0 -remove keep=0\n");
+		log("\n");
+	}
+	virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+	{
+		log_header(design, "Executing ATTRMAP pass (move or copy attributes).\n");
+
+		bool modattr_mode = false;
+		vector<std::unique_ptr<AttrmapAction>> actions;
+
+		size_t argidx;
+		for (argidx = 1; argidx < args.size(); argidx++)
+		{
+			std::string arg = args[argidx];
+			if (arg == "-tocase" && argidx+1 < args.size()) {
+				auto action = new AttrmapTocase;
+				action->name = args[++argidx];
+				actions.push_back(std::move(std::unique_ptr<AttrmapAction>(action)));
+				continue;
+			}
+			if (arg == "-rename" && argidx+2 < args.size()) {
+				auto action = new AttrmapRename;
+				action->old_name = args[++argidx];
+				action->new_name = args[++argidx];
+				actions.push_back(std::move(std::unique_ptr<AttrmapAction>(action)));
+				continue;
+			}
+			if ((arg == "-map" || arg == "-imap") && argidx+2 < args.size()) {
+				string arg1 = args[++argidx];
+				string arg2 = args[++argidx];
+				string val1, val2;
+				size_t p = arg1.find("=");
+				if (p != string::npos) {
+					val1 = arg1.substr(p+1);
+					arg1 = arg1.substr(0, p);
+				}
+				p = arg2.find("=");
+				if (p != string::npos) {
+					val2 = arg2.substr(p+1);
+					arg2 = arg2.substr(0, p);
+				}
+				auto action = new AttrmapMap;
+				action->imap = (arg == "-map");
+				action->old_name = arg1;
+				action->new_name = arg2;
+				action->old_value = val1;
+				action->new_value = val2;
+				actions.push_back(std::move(std::unique_ptr<AttrmapAction>(action)));
+				continue;
+			}
+			if (arg == "-remove" && argidx+1 < args.size()) {
+				string arg1 = args[++argidx], val1;
+				size_t p = arg1.find("=");
+				if (p != string::npos) {
+					val1 = arg1.substr(p+1);
+					arg1 = arg1.substr(0, p);
+				}
+				auto action = new AttrmapRemove;
+				action->name = arg1;
+				action->value = val1;
+				actions.push_back(std::move(std::unique_ptr<AttrmapAction>(action)));
+				continue;
+			}
+			if (arg == "-modattr") {
+				modattr_mode = true;
+				continue;
+			}
+			break;
+		}
+		extra_args(args, argidx, design);
+
+		if (modattr_mode)
+		{
+			for (auto module : design->selected_whole_modules())
+				attrmap_apply(stringf("%s", log_id(module)), actions, module->attributes);
+		}
+		else
+		{
+			for (auto module : design->selected_modules())
+			{
+				for (auto wire : module->selected_wires())
+					attrmap_apply(stringf("%s.%s", log_id(module), log_id(wire)), actions, wire->attributes);
+
+				for (auto cell : module->selected_cells())
+					attrmap_apply(stringf("%s.%s", log_id(module), log_id(cell)), actions, cell->attributes);
+			}
+		}
+	}
+} AttrmapPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/attrmvcp.cc b/passes/techmap/attrmvcp.cc
index 1af6077f5..50eaf61df 100644
--- a/passes/techmap/attrmvcp.cc
+++ b/passes/techmap/attrmvcp.cc
@@ -119,6 +119,8 @@ struct AttrmvcpPass : public Pass {
 					for (auto bit : sigmap(wire))
 						if (net2cells.count(bit))
 							for (auto cell : net2cells.at(bit)) {
+								log("Moving attribute %s=%s from %s.%s to %s.%s.\n", log_id(attr.first), log_const(attr.second),
+										log_id(module), log_id(wire), log_id(module), log_id(cell));
 								cell->attributes[attr.first] = attr.second;
 								did_something = true;
 							}