--- /dev/null
+/*
+ * 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