|
| 1 | +#include "kernel/celltypes.h" |
| 2 | +#include "kernel/register.h" |
| 3 | +#include "kernel/rtlil.h" |
| 4 | +#include "kernel/sigtools.h" |
| 5 | +#include "kernel/consteval.h" |
| 6 | +#include "kernel/utils.h" |
| 7 | + |
| 8 | +#include <algorithm> |
| 9 | + |
| 10 | +USING_YOSYS_NAMESPACE |
| 11 | +YOSYS_NAMESPACE_BEGIN |
| 12 | + |
| 13 | +// return module's inputs in canonical order |
| 14 | +SigSpec module_inputs(Module *m) |
| 15 | +{ |
| 16 | + SigSpec ret; |
| 17 | + for (auto port : m->ports) { |
| 18 | + Wire *w = m->wire(port); |
| 19 | + if (!w->port_input) |
| 20 | + continue; |
| 21 | + if (w->width != 1) |
| 22 | + log_error("Unsupported wide port (%s) of non-unit width found in module %s.\n", |
| 23 | + log_id(w), log_id(m)); |
| 24 | + ret.append(w); |
| 25 | + } |
| 26 | + return ret; |
| 27 | +} |
| 28 | + |
| 29 | +// return module's outputs in canonical order |
| 30 | +SigSpec module_outputs(Module *m) |
| 31 | +{ |
| 32 | + SigSpec ret; |
| 33 | + for (auto port : m->ports) { |
| 34 | + Wire *w = m->wire(port); |
| 35 | + if (!w->port_output) |
| 36 | + continue; |
| 37 | + if (w->width != 1) |
| 38 | + log_error("Unsupported wide port (%s) of non-unit width found in module %s.\n", |
| 39 | + log_id(w), log_id(m)); |
| 40 | + ret.append(w); |
| 41 | + } |
| 42 | + return ret; |
| 43 | +} |
| 44 | + |
| 45 | +// Permute the inputs of a single-output k-LUT according to varmap |
| 46 | +uint64_t permute_lut(uint64_t lut, const std::vector<int> &varmap) |
| 47 | +{ |
| 48 | + int k = varmap.size(); |
| 49 | + uint64_t ret = 0; |
| 50 | + // Index j iterates over all bits in lut. |
| 51 | + // When (j & 1 << n) is true, |
| 52 | + // (lut & 1 << j) represents an output value where input var n is set. |
| 53 | + // We use this fact to permute the LUT such that |
| 54 | + // every variable n is remapped to varmap[n]. |
| 55 | + for (int j = 0; j < 1 << k; j++) { |
| 56 | + int m = 0; |
| 57 | + for (int l = 0; l < k; l++) |
| 58 | + if (j & 1 << l) |
| 59 | + m |= 1 << varmap[l]; |
| 60 | + if (lut & 1 << m) |
| 61 | + ret |= 1 << j; |
| 62 | + } |
| 63 | + return ret; |
| 64 | +} |
| 65 | + |
| 66 | +// Find the LUT with the minimum integer representation |
| 67 | +// such that it is a permutation of the given lut. |
| 68 | +// The resulting LUT becomes the "fingerprint" of the "permutation class". |
| 69 | +// This function checks all possible input permutations. |
| 70 | +uint64_t p_class(int k, uint64_t lut) |
| 71 | +{ |
| 72 | + std::vector<int> map; |
| 73 | + for (int j = 0; j < k; j++) |
| 74 | + map.push_back(j); |
| 75 | + |
| 76 | + uint64_t repr = ~(uint64_t) 0; |
| 77 | + std::vector<int> repr_vars; |
| 78 | + while (true) { |
| 79 | + uint64_t perm = permute_lut(lut, map); |
| 80 | + if (perm <= repr) { |
| 81 | + repr = perm; |
| 82 | + repr_vars = map; |
| 83 | + } |
| 84 | + if (!std::next_permutation(map.begin(), map.end())) |
| 85 | + break; |
| 86 | + } |
| 87 | + return repr; |
| 88 | +} |
| 89 | + |
| 90 | +// Represent module m as N single-output k-LUTs |
| 91 | +// where k is the number of module inputs, |
| 92 | +// and N is the number of module outputs. |
| 93 | +bool derive_module_luts(Module *m, std::vector<uint64_t> &luts) |
| 94 | +{ |
| 95 | + CellTypes ff_types; |
| 96 | + ff_types.setup_stdcells_mem(); |
| 97 | + for (auto cell : m->cells()) { |
| 98 | + if (ff_types.cell_known(cell->type)) { |
| 99 | + log("Ignoring module '%s' which isn't purely combinational.\n", log_id(m)); |
| 100 | + return false; |
| 101 | + } |
| 102 | + } |
| 103 | + |
| 104 | + SigSpec inputs = module_inputs(m); |
| 105 | + SigSpec outputs = module_outputs(m); |
| 106 | + int ninputs = inputs.size(), noutputs = outputs.size(); |
| 107 | + |
| 108 | + if (ninputs > 6) { |
| 109 | + log_warning("Skipping module %s with more than 6 inputs bits.\n", log_id(m)); |
| 110 | + return false; |
| 111 | + } |
| 112 | + |
| 113 | + luts.clear(); |
| 114 | + luts.resize(noutputs); |
| 115 | + |
| 116 | + ConstEval ceval(m); |
| 117 | + for (int i = 0; i < 1 << ninputs; i++) { |
| 118 | + ceval.clear(); |
| 119 | + for (int j = 0; j < ninputs; j++) |
| 120 | + ceval.set(inputs[j], (i & (1 << j)) ? State::S1 : State::S0); |
| 121 | + for (int j = 0; j < noutputs; j++) { |
| 122 | + SigSpec bit = outputs[j]; |
| 123 | + |
| 124 | + if (!ceval.eval(bit)) { |
| 125 | + log("Failed to evaluate output '%s' in module '%s'.\n", |
| 126 | + log_signal(outputs[j]), log_id(m)); |
| 127 | + return false; |
| 128 | + } |
| 129 | + |
| 130 | + log_assert(ceval.eval(bit)); |
| 131 | + |
| 132 | + if (bit[0] == State::S1) |
| 133 | + luts[j] |= 1 << i; |
| 134 | + } |
| 135 | + } |
| 136 | + |
| 137 | + return true; |
| 138 | +} |
| 139 | + |
| 140 | +struct CellmatchPass : Pass { |
| 141 | + CellmatchPass() : Pass("cellmatch", "match cells to their targets in cell library") {} |
| 142 | + void help() override |
| 143 | + { |
| 144 | + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| |
| 145 | + log("\n"); |
| 146 | + log(" cellmatch -lib <design> [module selection]\n"); |
| 147 | + log("\n"); |
| 148 | + log("This pass identifies functionally equivalent counterparts between each of the\n"); |
| 149 | + log("selected modules and a module from the secondary design <design>. For every such\n"); |
| 150 | + log("correspondence found, a techmap rule is generated for mapping instances of the\n"); |
| 151 | + log("former to instances of the latter. This techmap rule is saved in yet another\n"); |
| 152 | + log("design called '$cellmatch', which is created if non-existent.\n"); |
| 153 | + log("\n"); |
| 154 | + log("This pass restricts itself to combinational modules. Modules are functionally\n"); |
| 155 | + log("equivalent as long as their truth tables are identical upto a permutation of\n"); |
| 156 | + log("inputs and outputs. The supported number of inputs is limited to 6.\n"); |
| 157 | + log("\n"); |
| 158 | + } |
| 159 | + void execute(std::vector<std::string> args, RTLIL::Design *d) override |
| 160 | + { |
| 161 | + log_header(d, "Executing CELLMATCH pass. (match cells)\n"); |
| 162 | + |
| 163 | + size_t argidx; |
| 164 | + bool lut_attrs = false; |
| 165 | + Design *lib = NULL; |
| 166 | + for (argidx = 1; argidx < args.size(); argidx++) { |
| 167 | + if (args[argidx] == "-lut_attrs") { |
| 168 | + // an undocumented debugging option |
| 169 | + lut_attrs = true; |
| 170 | + } else if (args[argidx] == "-lib" && argidx + 1 < args.size()) { |
| 171 | + if (!saved_designs.count(args[++argidx])) |
| 172 | + log_cmd_error("No design '%s' found!\n", args[argidx].c_str()); |
| 173 | + lib = saved_designs.at(args[argidx]); |
| 174 | + } else { |
| 175 | + break; |
| 176 | + } |
| 177 | + } |
| 178 | + extra_args(args, argidx, d); |
| 179 | + |
| 180 | + if (!lib && !lut_attrs) |
| 181 | + log_cmd_error("Missing required -lib option.\n"); |
| 182 | + |
| 183 | + struct Target { |
| 184 | + Module *module; |
| 185 | + std::vector<uint64_t> luts; |
| 186 | + }; |
| 187 | + |
| 188 | + dict<pool<uint64_t>, std::vector<Target>> targets; |
| 189 | + |
| 190 | + if (lib) |
| 191 | + for (auto m : lib->modules()) { |
| 192 | + pool<uint64_t> p_classes; |
| 193 | + |
| 194 | + // produce a fingerprint in p_classes |
| 195 | + int ninputs = module_inputs(m).size(); |
| 196 | + std::vector<uint64_t> luts; |
| 197 | + if (!derive_module_luts(m, luts)) |
| 198 | + continue; |
| 199 | + for (auto lut : luts) |
| 200 | + p_classes.insert(p_class(ninputs, lut)); |
| 201 | + |
| 202 | + log_debug("Registered %s\n", log_id(m)); |
| 203 | + |
| 204 | + // save as a viable target |
| 205 | + targets[p_classes].push_back(Target{m, luts}); |
| 206 | + } |
| 207 | + |
| 208 | + auto r = saved_designs.emplace("$cellmatch", nullptr); |
| 209 | + if (r.second) |
| 210 | + r.first->second = new Design; |
| 211 | + Design *map_design = r.first->second; |
| 212 | + |
| 213 | + for (auto m : d->selected_whole_modules_warn()) { |
| 214 | + std::vector<uint64_t> luts; |
| 215 | + if (!derive_module_luts(m, luts)) |
| 216 | + continue; |
| 217 | + |
| 218 | + SigSpec inputs = module_inputs(m); |
| 219 | + SigSpec outputs = module_outputs(m); |
| 220 | + |
| 221 | + if (lut_attrs) { |
| 222 | + int no = 0; |
| 223 | + for (auto bit : outputs) { |
| 224 | + log_assert(bit.is_wire()); |
| 225 | + bit.wire->attributes[ID(p_class)] = p_class(inputs.size(), luts[no]); |
| 226 | + bit.wire->attributes[ID(lut)] = luts[no++]; |
| 227 | + } |
| 228 | + } |
| 229 | + |
| 230 | + // fingerprint |
| 231 | + pool<uint64_t> p_classes; |
| 232 | + for (auto lut : luts) |
| 233 | + p_classes.insert(p_class(inputs.size(), lut)); |
| 234 | + |
| 235 | + for (auto target : targets[p_classes]) { |
| 236 | + log_debug("Candidate %s for matching to %s\n", log_id(target.module), log_id(m)); |
| 237 | + |
| 238 | + SigSpec target_inputs = module_inputs(target.module); |
| 239 | + SigSpec target_outputs = module_outputs(target.module); |
| 240 | + |
| 241 | + if (target_inputs.size() != inputs.size()) |
| 242 | + continue; |
| 243 | + |
| 244 | + if (target_outputs.size() != outputs.size()) |
| 245 | + continue; |
| 246 | + |
| 247 | + std::vector<int> input_map; |
| 248 | + for (int i = 0; i < inputs.size(); i++) |
| 249 | + input_map.push_back(i); |
| 250 | + |
| 251 | + bool found_match = false; |
| 252 | + // For each input_map |
| 253 | + while (!found_match) { |
| 254 | + std::vector<int> output_map; |
| 255 | + for (int i = 0; i < outputs.size(); i++) |
| 256 | + output_map.push_back(i); |
| 257 | + |
| 258 | + // For each output_map |
| 259 | + while (!found_match) { |
| 260 | + int out_no = 0; |
| 261 | + bool match = true; |
| 262 | + for (auto lut : luts) { |
| 263 | + if (permute_lut(target.luts[output_map[out_no++]], input_map) != lut) { |
| 264 | + match = false; |
| 265 | + break; |
| 266 | + } |
| 267 | + } |
| 268 | + |
| 269 | + if (match) { |
| 270 | + log("Module %s matches %s\n", log_id(m), log_id(target.module)); |
| 271 | + // Add target.module to map_design ("$cellmatch") |
| 272 | + // as a techmap rule to match m and replace it with target.module |
| 273 | + Module *map = map_design->addModule(stringf("\\_60_%s_%s", log_id(m), log_id(target.module))); |
| 274 | + Cell *cell = map->addCell(ID::_TECHMAP_REPLACE_, target.module->name); |
| 275 | + |
| 276 | + map->attributes[ID(techmap_celltype)] = m->name.str(); |
| 277 | + |
| 278 | + for (int i = 0; i < outputs.size(); i++) { |
| 279 | + log_assert(outputs[i].is_wire()); |
| 280 | + Wire *w = map->addWire(outputs[i].wire->name, 1); |
| 281 | + w->port_id = outputs[i].wire->port_id; |
| 282 | + w->port_output = true; |
| 283 | + log_assert(target_outputs[output_map[i]].is_wire()); |
| 284 | + cell->setPort(target_outputs[output_map[i]].wire->name, w); |
| 285 | + } |
| 286 | + |
| 287 | + for (int i = 0; i < inputs.size(); i++) { |
| 288 | + log_assert(inputs[i].is_wire()); |
| 289 | + Wire *w = map->addWire(inputs[i].wire->name, 1); |
| 290 | + w->port_id = inputs[i].wire->port_id; |
| 291 | + w->port_input = true; |
| 292 | + log_assert(target_inputs[input_map[i]].is_wire()); |
| 293 | + cell->setPort(target_inputs[input_map[i]].wire->name, w); |
| 294 | + } |
| 295 | + |
| 296 | + map->fixup_ports(); |
| 297 | + found_match = true; |
| 298 | + } |
| 299 | + |
| 300 | + if (!std::next_permutation(output_map.begin(), output_map.end())) |
| 301 | + break; |
| 302 | + } |
| 303 | + |
| 304 | + if (!std::next_permutation(input_map.begin(), input_map.end())) |
| 305 | + break; |
| 306 | + } |
| 307 | + } |
| 308 | + } |
| 309 | + } |
| 310 | +} CellmatchPass; |
| 311 | + |
| 312 | +YOSYS_NAMESPACE_END |
0 commit comments