| /* |
| * 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 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 { |
| bool has_value; |
| string name, value; |
| bool apply(IdString &id, Const &val) YS_OVERRIDE { |
| return !(match_name(name, id) && (!has_value || 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); |
| } |
| |
| void log_attrmap_paramap_options() |
| { |
| 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"); |
| } |
| |
| bool parse_attrmap_paramap_options(size_t &argidx, std::vector<std::string> &args, vector<std::unique_ptr<AttrmapAction>> &actions) |
| { |
| 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<AttrmapAction>(action)); |
| return true; |
| } |
| 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<AttrmapAction>(action)); |
| return true; |
| } |
| 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<AttrmapAction>(action)); |
| return true; |
| } |
| 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->has_value = (p != string::npos); |
| action->value = val1; |
| actions.push_back(std::unique_ptr<AttrmapAction>(action)); |
| return true; |
| } |
| return false; |
| } |
| |
| 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 maps key/value pairs to\n"); |
| log("other key/value pairs.\n"); |
| log("\n"); |
| log_attrmap_paramap_options(); |
| 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<std::string> args, RTLIL::Design *design) YS_OVERRIDE |
| { |
| 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++) |
| { |
| if (parse_attrmap_paramap_options(argidx, args, actions)) |
| continue; |
| if (args[argidx] == "-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); |
| |
| for (auto proc : module->processes) |
| { |
| if (!design->selected(module, proc.second)) |
| continue; |
| attrmap_apply(stringf("%s.%s", log_id(module), log_id(proc.first)), actions, proc.second->attributes); |
| |
| std::vector<RTLIL::CaseRule*> all_cases = {&proc.second->root_case}; |
| while (!all_cases.empty()) { |
| RTLIL::CaseRule *cs = all_cases.back(); |
| all_cases.pop_back(); |
| attrmap_apply(stringf("%s.%s (case)", log_id(module), log_id(proc.first)), actions, cs->attributes); |
| |
| for (auto &sw : cs->switches) { |
| attrmap_apply(stringf("%s.%s (switch)", log_id(module), log_id(proc.first)), actions, sw->attributes); |
| all_cases.insert(all_cases.end(), sw->cases.begin(), sw->cases.end()); |
| } |
| } |
| } |
| } |
| } |
| } |
| } AttrmapPass; |
| |
| struct ParamapPass : public Pass { |
| ParamapPass() : Pass("paramap", "renaming cell parameters") { } |
| void help() YS_OVERRIDE |
| { |
| // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| |
| log("\n"); |
| log(" paramap [options] [selection]\n"); |
| log("\n"); |
| log("This command renames cell parameters and/or maps key/value pairs to\n"); |
| log("other key/value pairs.\n"); |
| log("\n"); |
| log_attrmap_paramap_options(); |
| log("\n"); |
| log("For example, mapping Diamond-style ECP5 \"init\" attributes to Yosys-style:\n"); |
| log("\n"); |
| log(" paramap -tocase INIT t:LUT4\n"); |
| log("\n"); |
| } |
| void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE |
| { |
| log_header(design, "Executing PARAMAP pass (move or copy cell parameters).\n"); |
| |
| vector<std::unique_ptr<AttrmapAction>> actions; |
| |
| size_t argidx; |
| for (argidx = 1; argidx < args.size(); argidx++) |
| { |
| if (parse_attrmap_paramap_options(argidx, args, actions)) |
| continue; |
| break; |
| } |
| extra_args(args, argidx, design); |
| |
| for (auto module : design->selected_modules()) |
| for (auto cell : module->selected_cells()) |
| attrmap_apply(stringf("%s.%s", log_id(module), log_id(cell)), actions, cell->parameters); |
| } |
| } ParamapPass; |
| |
| PRIVATE_NAMESPACE_END |