|
| 1 | +/* |
| 2 | + * yosys -- Yosys Open SYnthesis Suite |
| 3 | + * |
| 4 | + * Copyright (C) 2024 Martin Povišer <[email protected]> |
| 5 | + * |
| 6 | + * Permission to use, copy, modify, and/or distribute this software for any |
| 7 | + * purpose with or without fee is hereby granted, provided that the above |
| 8 | + * copyright notice and this permission notice appear in all copies. |
| 9 | + * |
| 10 | + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| 11 | + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| 12 | + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| 13 | + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| 14 | + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| 15 | + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| 16 | + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| 17 | + * |
| 18 | + */ |
| 19 | +#include "kernel/yosys.h" |
| 20 | +#include "kernel/celltypes.h" |
| 21 | +#include "backends/rtlil/rtlil_backend.h" |
| 22 | + |
| 23 | +USING_YOSYS_NAMESPACE |
| 24 | +PRIVATE_NAMESPACE_BEGIN |
| 25 | + |
| 26 | +std::optional<std::string> format(std::string fmt, const dict<IdString, Const> ¶meters) |
| 27 | +{ |
| 28 | + std::stringstream result; |
| 29 | + |
| 30 | + auto it = fmt.begin(); |
| 31 | + while (it != fmt.end()) { |
| 32 | + if (*it == '{') { |
| 33 | + it++; |
| 34 | + auto beg = it; |
| 35 | + while (it != fmt.end() && *it != '}') it++; |
| 36 | + if (it == fmt.end()) { |
| 37 | + log("Unclosed curly brackets in format string '%s'\n", fmt.c_str()); |
| 38 | + return {}; |
| 39 | + } |
| 40 | + |
| 41 | + auto id = RTLIL::escape_id(std::string(beg, it)); |
| 42 | + if (!parameters.count(id)) { |
| 43 | + log("Parameter %s referenced in format string '%s' not found\n", log_id(id), fmt.c_str()); |
| 44 | + return {}; |
| 45 | + } |
| 46 | + |
| 47 | + RTLIL_BACKEND::dump_const(result, parameters.at(id)); |
| 48 | + } else { |
| 49 | + result << *it; |
| 50 | + } |
| 51 | + it++; |
| 52 | + } |
| 53 | + |
| 54 | + return {result.str()}; |
| 55 | +} |
| 56 | + |
| 57 | +struct WrapcellPass : Pass { |
| 58 | + WrapcellPass() : Pass("wrapcell", "wrap individual cells into new modules") {} |
| 59 | + |
| 60 | + void help() override |
| 61 | + { |
| 62 | + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| |
| 63 | + log("\n"); |
| 64 | + log(" wrapcell -name <format> [selection]\n"); |
| 65 | + log("\n"); |
| 66 | + log("This command wraps the selected cells individually into modules. The name for\n"); |
| 67 | + log("each wrapper module is derived from the template <format> by substituting\n"); |
| 68 | + log("parameter values as specified in curly brackets. If the named module already\n"); |
| 69 | + log("exists, it is reused.\n"); |
| 70 | + log("\n"); |
| 71 | + log(" -setattr <attribute-name>\n"); |
| 72 | + log(" set the given boolean attribute on each created wrapper module\n"); |
| 73 | + log("\n"); |
| 74 | + log(" -formatattr <attribute-name> <format>\n"); |
| 75 | + log(" set a string attribute on the created wrapper module by substituting\n"); |
| 76 | + log(" parameter values into <format>\n"); |
| 77 | + log("\n"); |
| 78 | + log("Currently this command only supports wrapping internal cell types.\n"); |
| 79 | + log("\n"); |
| 80 | + } |
| 81 | + |
| 82 | + void execute(std::vector<std::string> args, Design *d) override |
| 83 | + { |
| 84 | + log_header(d, "Executing WRAPCELL pass. (wrap selected cells)\n"); |
| 85 | + |
| 86 | + struct AttrRule { |
| 87 | + IdString name; |
| 88 | + std::string value_fmt; |
| 89 | + |
| 90 | + AttrRule(IdString name, std::string value_fmt) |
| 91 | + : name(name), value_fmt(value_fmt) {} |
| 92 | + }; |
| 93 | + std::vector<AttrRule> attributes; |
| 94 | + std::string name_fmt; |
| 95 | + |
| 96 | + size_t argidx; |
| 97 | + for (argidx = 1; argidx < args.size(); argidx++) { |
| 98 | + if (args[argidx] == "-setattr" && argidx+1 < args.size()) { |
| 99 | + attributes.emplace_back(RTLIL::escape_id(args[++argidx]), ""); |
| 100 | + } else if (args[argidx] == "-formatattr" && argidx+2 < args.size()) { |
| 101 | + IdString id = RTLIL::escape_id(args[++argidx]); |
| 102 | + attributes.emplace_back(id, args[++argidx]); |
| 103 | + } else if (args[argidx] == "-name" && argidx+1 < args.size()) { |
| 104 | + name_fmt = args[++argidx]; |
| 105 | + } else { |
| 106 | + break; |
| 107 | + } |
| 108 | + } |
| 109 | + extra_args(args, argidx, d); |
| 110 | + |
| 111 | + if (name_fmt.empty()) |
| 112 | + log_cmd_error("Argument -name required"); |
| 113 | + |
| 114 | + CellTypes ct; |
| 115 | + ct.setup(); |
| 116 | + |
| 117 | + for (auto module : d->selected_modules()) { |
| 118 | + for (auto cell : module->selected_cells()) { |
| 119 | + std::optional<std::string> unescaped_name = format(name_fmt, cell->parameters); |
| 120 | + if (!unescaped_name) |
| 121 | + log_error("Formatting error when processing cell '%s' in module '%s'\n", |
| 122 | + log_id(cell), log_id(module)); |
| 123 | + |
| 124 | + IdString name = RTLIL::escape_id(unescaped_name.value()); |
| 125 | + |
| 126 | + if (d->module(name)) { |
| 127 | + cell->type = name; |
| 128 | + cell->parameters.clear(); |
| 129 | + continue; |
| 130 | + } |
| 131 | + |
| 132 | + if (!ct.cell_known(cell->type)) |
| 133 | + log_error("Non-internal cell type '%s' on cell '%s' in module '%s' unsupported\n", |
| 134 | + log_id(cell->type), log_id(cell), log_id(module)); |
| 135 | + |
| 136 | + Module *subm = d->addModule(name); |
| 137 | + Cell *subcell = subm->addCell("$1", cell->type); |
| 138 | + for (auto conn : cell->connections()) { |
| 139 | + Wire *w = subm->addWire(conn.first, conn.second.size()); |
| 140 | + if (ct.cell_output(cell->type, w->name)) |
| 141 | + w->port_output = true; |
| 142 | + else |
| 143 | + w->port_input = true; |
| 144 | + subcell->setPort(conn.first, w); |
| 145 | + } |
| 146 | + subcell->parameters = cell->parameters; |
| 147 | + subm->fixup_ports(); |
| 148 | + |
| 149 | + for (auto rule : attributes) { |
| 150 | + if (rule.value_fmt.empty()) { |
| 151 | + subm->set_bool_attribute(rule.name); |
| 152 | + } else { |
| 153 | + std::optional<std::string> value = format(rule.value_fmt, cell->parameters); |
| 154 | + |
| 155 | + if (!value) |
| 156 | + log_error("Formatting error when processing cell '%s' in module '%s'\n", |
| 157 | + log_id(cell), log_id(module)); |
| 158 | + |
| 159 | + subm->set_string_attribute(rule.name, value.value()); |
| 160 | + } |
| 161 | + } |
| 162 | + |
| 163 | + cell->type = name; |
| 164 | + cell->parameters.clear(); |
| 165 | + } |
| 166 | + } |
| 167 | + } |
| 168 | +} WrapcellPass; |
| 169 | + |
| 170 | +PRIVATE_NAMESPACE_END |
0 commit comments