Added recursion support to techmap
[yosys.git] / passes / techmap / techmap.cc
1 /*
2 * yosys -- Yosys Open SYnthesis Suite
3 *
4 * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
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
20 #include "kernel/register.h"
21 #include "kernel/sigtools.h"
22 #include "kernel/log.h"
23 #include <stdlib.h>
24 #include <assert.h>
25 #include <stdio.h>
26 #include <string.h>
27
28 #include "passes/techmap/stdcells.inc"
29
30 // see simplemap.cc
31 extern void simplemap_get_mappers(std::map<std::string, void(*)(RTLIL::Module*, RTLIL::Cell*)> &mappers);
32
33 static void apply_prefix(std::string prefix, std::string &id)
34 {
35 if (id[0] == '\\')
36 id = prefix + "." + id.substr(1);
37 else
38 id = "$techmap" + prefix + "." + id;
39 }
40
41 static void apply_prefix(std::string prefix, RTLIL::SigSpec &sig, RTLIL::Module *module)
42 {
43 for (size_t i = 0; i < sig.chunks.size(); i++) {
44 if (sig.chunks[i].wire == NULL)
45 continue;
46 std::string wire_name = sig.chunks[i].wire->name;
47 apply_prefix(prefix, wire_name);
48 assert(module->wires.count(wire_name) > 0);
49 sig.chunks[i].wire = module->wires[wire_name];
50 }
51 }
52
53 struct TechmapWorker
54 {
55 std::map<std::string, void(*)(RTLIL::Module*, RTLIL::Cell*)> simplemap_mappers;
56 std::map<std::pair<RTLIL::IdString, std::map<RTLIL::IdString, RTLIL::Const>>, RTLIL::Module*> techmap_cache;
57 std::map<RTLIL::Module*, bool> techmap_do_cache;
58
59 struct TechmapWireData {
60 RTLIL::Wire *wire;
61 RTLIL::SigSpec value;
62 };
63
64 typedef std::map<std::string, std::vector<TechmapWireData>> TechmapWires;
65
66 TechmapWires techmap_find_special_wires(RTLIL::Module *module)
67 {
68 TechmapWires result;
69
70 if (module == NULL)
71 return result;
72
73 for (auto &it : module->wires) {
74 const char *p = it.first.c_str();
75 if (*p == '$')
76 continue;
77
78 const char *q = strrchr(p+1, '.');
79 p = q ? q : p+1;
80
81 if (!strncmp(p, "_TECHMAP_", 9)) {
82 TechmapWireData record;
83 record.wire = it.second;
84 record.value = it.second;
85 result[p].push_back(record);
86 it.second->attributes["\\keep"] = RTLIL::Const(1);
87 it.second->attributes["\\_techmap_special_"] = RTLIL::Const(1);
88 }
89 }
90
91 if (!result.empty()) {
92 SigMap sigmap(module);
93 for (auto &it1 : result)
94 for (auto &it2 : it1.second)
95 sigmap.apply(it2.value);
96 }
97
98 return result;
99 }
100
101 void techmap_module_worker(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Cell *cell, RTLIL::Module *tpl, bool flatten_mode)
102 {
103 log("Mapping `%s.%s' using `%s'.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(tpl->name));
104
105 if (tpl->memories.size() != 0)
106 log_error("Technology map yielded memories -> this is not supported.\n");
107
108 if (tpl->processes.size() != 0)
109 log_error("Technology map yielded processes -> this is not supported.\n");
110
111 std::map<RTLIL::IdString, RTLIL::IdString> positional_ports;
112
113 for (auto &it : tpl->wires) {
114 if (it.second->port_id > 0)
115 positional_ports[stringf("$%d", it.second->port_id)] = it.first;
116 RTLIL::Wire *w = new RTLIL::Wire(*it.second);
117 apply_prefix(cell->name, w->name);
118 w->port_input = false;
119 w->port_output = false;
120 w->port_id = 0;
121 if (it.second->get_bool_attribute("\\_techmap_special_"))
122 w->attributes.clear();
123 module->wires[w->name] = w;
124 design->select(module, w);
125 }
126
127 SigMap port_signal_map;
128
129 for (auto &it : cell->connections) {
130 RTLIL::IdString portname = it.first;
131 if (positional_ports.count(portname) > 0)
132 portname = positional_ports.at(portname);
133 if (tpl->wires.count(portname) == 0 || tpl->wires.at(portname)->port_id == 0) {
134 if (portname.substr(0, 1) == "$")
135 log_error("Can't map port `%s' of cell `%s' to template `%s'!\n", portname.c_str(), cell->name.c_str(), tpl->name.c_str());
136 continue;
137 }
138 RTLIL::Wire *w = tpl->wires.at(portname);
139 RTLIL::SigSig c;
140 if (w->port_output) {
141 c.first = it.second;
142 c.second = RTLIL::SigSpec(w);
143 apply_prefix(cell->name, c.second, module);
144 } else {
145 c.first = RTLIL::SigSpec(w);
146 c.second = it.second;
147 apply_prefix(cell->name, c.first, module);
148 }
149 if (c.second.width > c.first.width)
150 c.second.remove(c.first.width, c.second.width - c.first.width);
151 if (c.second.width < c.first.width)
152 c.second.append(RTLIL::SigSpec(RTLIL::State::S0, c.first.width - c.second.width));
153 assert(c.first.width == c.second.width);
154 #if 0
155 // more conservative approach:
156 // connect internal and external wires
157 module->connections.push_back(c);
158 #else
159 // approach that yields nicer outputs:
160 // replace internal wires that are connected to external wires
161 if (w->port_output)
162 port_signal_map.add(c.second, c.first);
163 else
164 port_signal_map.add(c.first, c.second);
165 #endif
166 }
167
168 for (auto &it : tpl->cells) {
169 RTLIL::Cell *c = new RTLIL::Cell(*it.second);
170 if (!flatten_mode && c->type.substr(0, 2) == "\\$")
171 c->type = c->type.substr(1);
172 apply_prefix(cell->name, c->name);
173 for (auto &it2 : c->connections) {
174 apply_prefix(cell->name, it2.second, module);
175 port_signal_map.apply(it2.second);
176 }
177 module->cells[c->name] = c;
178 design->select(module, c);
179 }
180
181 for (auto &it : tpl->connections) {
182 RTLIL::SigSig c = it;
183 apply_prefix(cell->name, c.first, module);
184 apply_prefix(cell->name, c.second, module);
185 port_signal_map.apply(c.first);
186 port_signal_map.apply(c.second);
187 module->connections.push_back(c);
188 }
189
190 module->cells.erase(cell->name);
191 delete cell;
192 }
193
194 bool techmap_module(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Design *map, std::set<RTLIL::Cell*> &handled_cells,
195 const std::map<RTLIL::IdString, std::set<RTLIL::IdString>> &celltypeMap, bool flatten_mode)
196 {
197 if (!design->selected(module))
198 return false;
199
200 bool log_continue = false;
201 bool did_something = false;
202 std::vector<std::string> cell_names;
203
204 for (auto &cell_it : module->cells)
205 cell_names.push_back(cell_it.first);
206
207 for (auto &cell_name : cell_names)
208 {
209 if (module->cells.count(cell_name) == 0)
210 continue;
211
212 RTLIL::Cell *cell = module->cells[cell_name];
213
214 if (!design->selected(module, cell) || handled_cells.count(cell) > 0)
215 continue;
216
217 if (celltypeMap.count(cell->type) == 0)
218 continue;
219
220 for (auto &tpl_name : celltypeMap.at(cell->type))
221 {
222 std::string derived_name = tpl_name;
223 RTLIL::Module *tpl = map->modules[tpl_name];
224 std::map<RTLIL::IdString, RTLIL::Const> parameters = cell->parameters;
225
226 if (!flatten_mode)
227 {
228 if (tpl->get_bool_attribute("\\techmap_simplemap")) {
229 log("Mapping %s.%s (%s) with simplemap.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type));
230 if (simplemap_mappers.count(cell->type) == 0)
231 log_error("No simplemap mapper for cell type %s found!\n", RTLIL::id2cstr(cell->type));
232 simplemap_mappers.at(cell->type)(module, cell);
233 module->cells.erase(cell->name);
234 delete cell;
235 cell = NULL;
236 did_something = true;
237 break;
238 }
239
240 for (auto conn : cell->connections) {
241 if (conn.first.substr(0, 1) == "$")
242 continue;
243 if (tpl->wires.count(conn.first) > 0 && tpl->wires.at(conn.first)->port_id > 0)
244 continue;
245 if (!conn.second.is_fully_const() || parameters.count(conn.first) > 0 || tpl->avail_parameters.count(conn.first) == 0)
246 goto next_tpl;
247 parameters[conn.first] = conn.second.as_const();
248 }
249
250 if (0) {
251 next_tpl:
252 continue;
253 }
254
255 if (tpl->avail_parameters.count("\\_TECHMAP_CELLTYPE_") != 0)
256 parameters["\\_TECHMAP_CELLTYPE_"] = RTLIL::unescape_id(cell->type);
257 }
258
259 std::pair<RTLIL::IdString, std::map<RTLIL::IdString, RTLIL::Const>> key(tpl_name, parameters);
260 if (techmap_cache.count(key) > 0) {
261 tpl = techmap_cache[key];
262 } else {
263 if (cell->parameters.size() != 0) {
264 derived_name = tpl->derive(map, parameters);
265 tpl = map->modules[derived_name];
266 log_continue = true;
267 }
268 techmap_cache[key] = tpl;
269 }
270
271 if (flatten_mode)
272 techmap_do_cache[tpl] = true;
273
274 if (techmap_do_cache.count(tpl) == 0)
275 {
276 bool keep_running = true;
277 techmap_do_cache[tpl] = true;
278
279 while (keep_running)
280 {
281 TechmapWires twd = techmap_find_special_wires(tpl);
282 keep_running = false;
283
284 for (auto &it : twd["_TECHMAP_FAIL_"]) {
285 RTLIL::SigSpec value = it.value;
286 if (value.is_fully_const() && value.as_bool()) {
287 log("Not using module `%s' from techmap as it contains a %s marker wire with non-zero value %s.\n",
288 derived_name.c_str(), RTLIL::id2cstr(it.wire->name), log_signal(value));
289 techmap_do_cache[tpl] = false;
290 }
291 }
292
293 if (!techmap_do_cache[tpl])
294 break;
295
296 for (auto &it : twd)
297 {
298 if (it.first.substr(0, 12) != "_TECHMAP_DO_" || it.second.empty())
299 continue;
300
301 auto &data = it.second.front();
302
303 if (!data.value.is_fully_const())
304 log_error("Techmap yielded config wire %s with non-const value %s.\n", RTLIL::id2cstr(data.wire->name), log_signal(data.value));
305
306 tpl->wires.erase(data.wire->name);
307 const char *p = data.wire->name.c_str();
308 const char *q = strrchr(p+1, '.');
309 q = q ? q : p+1;
310
311 assert(!strncmp(q, "_TECHMAP_DO_", 12));
312 std::string new_name = data.wire->name.substr(0, q-p) + "_TECHMAP_DONE_" + data.wire->name.substr(q-p+12);
313 while (tpl->wires.count(new_name))
314 new_name += "_";
315 data.wire->name = new_name;
316 tpl->add(data.wire);
317
318 std::string cmd_string = data.value.as_const().decode_string();
319
320 RTLIL::Selection tpl_mod_sel(false);
321 tpl_mod_sel.select(tpl);
322 map->selection_stack.push_back(tpl_mod_sel);
323 Pass::call(map, cmd_string);
324 map->selection_stack.pop_back();
325
326 keep_running = true;
327 break;
328 }
329 }
330
331 TechmapWires twd = techmap_find_special_wires(tpl);
332 for (auto &it : twd) {
333 if (it.first != "_TECHMAP_FAIL_" && it.first.substr(0, 12) != "_TECHMAP_DO_" && it.first.substr(0, 14) != "_TECHMAP_DONE_")
334 log_error("Techmap yielded unknown config wire %s.\n", it.first.c_str());
335 if (techmap_do_cache[tpl])
336 for (auto &it2 : it.second)
337 if (!it2.value.is_fully_const())
338 log_error("Techmap yielded config wire %s with non-const value %s.\n", RTLIL::id2cstr(it2.wire->name), log_signal(it2.value));
339 }
340 }
341
342 if (techmap_do_cache.at(tpl) == false)
343 continue;
344
345 if (log_continue) {
346 log_header("Continuing TECHMAP pass.\n");
347 log_continue = false;
348 }
349
350 techmap_module_worker(design, module, cell, tpl, flatten_mode);
351 did_something = true;
352 cell = NULL;
353 break;
354 }
355
356 handled_cells.insert(cell);
357 }
358
359 if (log_continue) {
360 log_header("Continuing TECHMAP pass.\n");
361 log_continue = false;
362 }
363
364 return did_something;
365 }
366 };
367
368 struct TechmapPass : public Pass {
369 TechmapPass() : Pass("techmap", "generic technology mapper") { }
370 virtual void help()
371 {
372 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
373 log("\n");
374 log(" techmap [-map filename] [selection]\n");
375 log("\n");
376 log("This pass implements a very simple technology mapper that replaces cells in\n");
377 log("the design with implementations given in form of a verilog or ilang source\n");
378 log("file.\n");
379 log("\n");
380 log(" -map filename\n");
381 log(" the library of cell implementations to be used.\n");
382 log(" without this parameter a builtin library is used that\n");
383 log(" transforms the internal RTL cells to the internal gate\n");
384 log(" library.\n");
385 log("\n");
386 log(" -share_map filename\n");
387 log(" like -map, but look for the file in the share directory (where the\n");
388 log(" yosys data files are). this is mainly used internally when techmap\n");
389 log(" is called from other commands.\n");
390 log("\n");
391 log(" -D <define>, -I <incdir>\n");
392 log(" this options are passed as-is to the verilog frontend for loading the\n");
393 log(" map file. Note that the verilog frontend is also called with the\n");
394 log(" '-ignore_redef' option set.\n");
395 log("\n");
396 log("When a module in the map file has the 'techmap_celltype' attribute set, it will\n");
397 log("match cells with a type that match the text value of this attribute. Otherwise\n");
398 log("the module name will be used to match the cell.\n");
399 log("\n");
400 log("When a module in the map file has the 'techmap_simplemap' attribute set, techmap\n");
401 log("will use 'simplemap' (see 'help simplemap') to map cells matching the module.\n");
402 log("\n");
403 log("All wires in the modules from the map file matching the pattern _TECHMAP_*\n");
404 log("or *._TECHMAP_* are special wires that are used to pass instructions from\n");
405 log("the mapping module to the techmap command. At the moment the following special\n");
406 log("wires are supported:\n");
407 log("\n");
408 log(" _TECHMAP_FAIL_\n");
409 log(" When this wire is set to a non-zero constant value, techmap will not\n");
410 log(" use this module and instead try the next module with a matching\n");
411 log(" 'techmap_celltype' attribute.\n");
412 log("\n");
413 log(" When such a wire exists but does not have a constant value after all\n");
414 log(" _TECHMAP_DO_* commands have been executed, an error is generated.\n");
415 log("\n");
416 log(" _TECHMAP_DO_*\n");
417 log(" This wires are evaluated in alphabetical order. The constant text value\n");
418 log(" of this wire is a yosys command (or sequence of commands) that is run\n");
419 log(" by techmap on the module. A common use case is to run 'proc' on modules\n");
420 log(" that are written using always-statements.\n");
421 log("\n");
422 log(" When such a wire has a non-constant value at the time it is to be\n");
423 log(" evaluated, an error is produced. That means it is possible for such a\n");
424 log(" wire to start out as non-constant and evaluate to a constant value\n");
425 log(" during processing of other _TECHMAP_DO_* commands.\n");
426 log("\n");
427 log("In addition to this special wires, techmap also supports special parameters in\n");
428 log("modules in the map file:\n");
429 log("\n");
430 log(" _TECHMAP_CELLTYPE_\n");
431 log(" When a parameter with this name exists, it will be set to the type name\n");
432 log(" of the cell that matches the module.\n");
433 log("\n");
434 log("When a module in the map file has a parameter where the according cell in the\n");
435 log("design has a port, the module from the map file is only used if the port in\n");
436 log("the design is connected to a constant value. The parameter is then set to the\n");
437 log("constant value.\n");
438 log("\n");
439 log("See 'help extract' for a pass that does the opposite thing.\n");
440 log("\n");
441 log("See 'help flatten' for a pass that does flatten the design (which is\n");
442 log("esentially techmap but using the design itself as map library).\n");
443 log("\n");
444 }
445 virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
446 {
447 log_header("Executing TECHMAP pass (map to technology primitives).\n");
448 log_push();
449
450 std::vector<std::string> map_files;
451 std::string verilog_frontend = "verilog -ignore_redef";
452
453 size_t argidx;
454 for (argidx = 1; argidx < args.size(); argidx++) {
455 if (args[argidx] == "-map" && argidx+1 < args.size()) {
456 map_files.push_back(args[++argidx]);
457 continue;
458 }
459 if (args[argidx] == "-share_map" && argidx+1 < args.size()) {
460 map_files.push_back(get_share_file_name(args[++argidx]));
461 continue;
462 }
463 if (args[argidx] == "-D" && argidx+1 < args.size()) {
464 verilog_frontend += " -D " + args[++argidx];
465 continue;
466 }
467 if (args[argidx] == "-I" && argidx+1 < args.size()) {
468 verilog_frontend += " -I " + args[++argidx];
469 continue;
470 }
471 break;
472 }
473 extra_args(args, argidx, design);
474
475 TechmapWorker worker;
476 simplemap_get_mappers(worker.simplemap_mappers);
477
478 RTLIL::Design *map = new RTLIL::Design;
479 if (map_files.empty()) {
480 FILE *f = fmemopen(stdcells_code, strlen(stdcells_code), "rt");
481 Frontend::frontend_call(map, f, "<stdcells.v>", verilog_frontend);
482 fclose(f);
483 } else
484 for (auto &fn : map_files) {
485 FILE *f = fopen(fn.c_str(), "rt");
486 if (f == NULL)
487 log_cmd_error("Can't open map file `%s'\n", fn.c_str());
488 Frontend::frontend_call(map, f, fn, (fn.size() > 3 && fn.substr(fn.size()-3) == ".il") ? "ilang" : verilog_frontend);
489 fclose(f);
490 }
491
492 std::map<RTLIL::IdString, RTLIL::Module*> modules_new;
493 for (auto &it : map->modules) {
494 if (it.first.substr(0, 2) == "\\$")
495 it.second->name = it.first.substr(1);
496 modules_new[it.second->name] = it.second;
497 }
498 map->modules.swap(modules_new);
499
500 std::map<RTLIL::IdString, std::set<RTLIL::IdString>> celltypeMap;
501 for (auto &it : map->modules) {
502 if (it.second->attributes.count("\\techmap_celltype") && !it.second->attributes.at("\\techmap_celltype").bits.empty()) {
503 char *p = strdup(it.second->attributes.at("\\techmap_celltype").decode_string().c_str());
504 for (char *q = strtok(p, " \t\r\n"); q; q = strtok(NULL, " \t\r\n"))
505 celltypeMap[RTLIL::escape_id(q)].insert(it.first);
506 free(p);
507 } else
508 celltypeMap[it.first].insert(it.first);
509 }
510
511 bool did_something = true;
512 std::set<RTLIL::Cell*> handled_cells;
513 while (did_something) {
514 did_something = false;
515 for (auto &mod_it : design->modules)
516 if (worker.techmap_module(design, mod_it.second, map, handled_cells, celltypeMap, false))
517 did_something = true;
518 if (did_something)
519 design->check();
520 }
521
522 log("No more expansions possible.\n");
523 delete map;
524
525 log_pop();
526 }
527 } TechmapPass;
528
529 struct FlattenPass : public Pass {
530 FlattenPass() : Pass("flatten", "flatten design") { }
531 virtual void help()
532 {
533 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
534 log("\n");
535 log(" flatten [selection]\n");
536 log("\n");
537 log("This pass flattens the design by replacing cells by their implementation. This\n");
538 log("pass is very simmilar to the 'techmap' pass. The only difference is that this\n");
539 log("pass is using the current design as mapping library.\n");
540 log("\n");
541 }
542 virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
543 {
544 log_header("Executing FLATTEN pass (flatten design).\n");
545 log_push();
546
547 extra_args(args, 1, design);
548
549 TechmapWorker worker;
550
551 std::map<RTLIL::IdString, std::set<RTLIL::IdString>> celltypeMap;
552 for (auto &it : design->modules)
553 celltypeMap[it.first].insert(it.first);
554
555 RTLIL::Module *top_mod = NULL;
556 if (design->full_selection())
557 for (auto &mod_it : design->modules)
558 if (mod_it.second->get_bool_attribute("\\top"))
559 top_mod = mod_it.second;
560
561 bool did_something = true;
562 std::set<RTLIL::Cell*> handled_cells;
563 while (did_something) {
564 did_something = false;
565 if (top_mod != NULL) {
566 if (worker.techmap_module(design, top_mod, design, handled_cells, celltypeMap, true))
567 did_something = true;
568 } else {
569 for (auto &mod_it : design->modules)
570 if (worker.techmap_module(design, mod_it.second, design, handled_cells, celltypeMap, true))
571 did_something = true;
572 }
573 }
574
575 log("No more expansions possible.\n");
576
577 if (top_mod != NULL) {
578 std::map<RTLIL::IdString, RTLIL::Module*> new_modules;
579 for (auto &mod_it : design->modules)
580 if (mod_it.second == top_mod) {
581 new_modules[mod_it.first] = mod_it.second;
582 } else {
583 log("Deleting now unused module %s.\n", RTLIL::id2cstr(mod_it.first));
584 delete mod_it.second;
585 }
586 design->modules.swap(new_modules);
587 }
588
589 log_pop();
590 }
591 } FlattenPass;
592