/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf * * 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 string_compare_nocase(const string &str1, const string &str2) { if (str1.size() != str2.size()) return false; for (size_t i = 0; i < str1.size(); i++) { char ch1 = str1[i], ch2 = str2[i]; if ('a' <= ch1 && ch1 <= 'z') ch1 -= 'a' - 'A'; if ('a' <= ch2 && ch2 <= 'z') ch2 -= 'a' - 'A'; if (ch1 != ch2) return false; } return true; } 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 string_compare_nocase(str1, str2); 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 string_compare_nocase(str1, str2); } return make_value(value) == val; } struct AttrmapAction { virtual ~AttrmapAction() { } virtual bool apply(IdString &id, Const &val) = 0; }; struct AttrmapTocase : AttrmapAction { string name; bool apply(IdString &id, Const&) YS_OVERRIDE { if (match_name(name, id, true)) id = RTLIL::escape_id(name); return true; } }; struct AttrmapRename : AttrmapAction { string old_name, new_name; bool apply(IdString &id, Const&) YS_OVERRIDE { 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; bool apply(IdString &id, Const &val) YS_OVERRIDE { 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; bool apply(IdString &id, Const &val) YS_OVERRIDE { return !(match_name(name, id) && match_value(value, val)); } }; void attrmap_apply(string objname, vector> &actions, dict &attributes) { dict 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") { } void help() YS_OVERRIDE { // |---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 \n"); log(" Match attribute names case-insensitively and set it to the specified\n"); log(" name.\n"); log("\n"); log(" -rename \n"); log(" Rename attributes as specified\n"); log("\n"); log(" -map = =\n"); log(" Map key/value pairs as indicated.\n"); log("\n"); log(" -imap = =\n"); log(" Like -map, but use case-insensitive match for when\n"); log(" it is a string value.\n"); log("\n"); log(" -remove =\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"); } void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing ATTRMAP pass (move or copy attributes).\n"); bool modattr_mode = false; vector> 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::unique_ptr(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::unique_ptr(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::unique_ptr(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::unique_ptr(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