write_verilog: write $tribuf cell as ternary.
[yosys.git] / backends / verilog / verilog_backend.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 * A simple and straightforward Verilog backend.
21 *
22 */
23
24 #include "kernel/register.h"
25 #include "kernel/celltypes.h"
26 #include "kernel/log.h"
27 #include "kernel/sigtools.h"
28 #include <string>
29 #include <sstream>
30 #include <set>
31 #include <map>
32
33 USING_YOSYS_NAMESPACE
34 PRIVATE_NAMESPACE_BEGIN
35
36 bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, defparam, decimal;
37 int auto_name_counter, auto_name_offset, auto_name_digits;
38 std::map<RTLIL::IdString, int> auto_name_map;
39 std::set<RTLIL::IdString> reg_wires, reg_ct;
40 std::string auto_prefix;
41
42 RTLIL::Module *active_module;
43 dict<RTLIL::SigBit, RTLIL::State> active_initdata;
44 SigMap active_sigmap;
45
46 void reset_auto_counter_id(RTLIL::IdString id, bool may_rename)
47 {
48 const char *str = id.c_str();
49
50 if (*str == '$' && may_rename && !norename)
51 auto_name_map[id] = auto_name_counter++;
52
53 if (str[0] != '\\' || str[1] != '_' || str[2] == 0)
54 return;
55
56 for (int i = 2; str[i] != 0; i++) {
57 if (str[i] == '_' && str[i+1] == 0)
58 continue;
59 if (str[i] < '0' || str[i] > '9')
60 return;
61 }
62
63 int num = atoi(str+2);
64 if (num >= auto_name_offset)
65 auto_name_offset = num + 1;
66 }
67
68 void reset_auto_counter(RTLIL::Module *module)
69 {
70 auto_name_map.clear();
71 auto_name_counter = 0;
72 auto_name_offset = 0;
73
74 reset_auto_counter_id(module->name, false);
75
76 for (auto it = module->wires_.begin(); it != module->wires_.end(); ++it)
77 reset_auto_counter_id(it->second->name, true);
78
79 for (auto it = module->cells_.begin(); it != module->cells_.end(); ++it) {
80 reset_auto_counter_id(it->second->name, true);
81 reset_auto_counter_id(it->second->type, false);
82 }
83
84 for (auto it = module->processes.begin(); it != module->processes.end(); ++it)
85 reset_auto_counter_id(it->second->name, false);
86
87 auto_name_digits = 1;
88 for (size_t i = 10; i < auto_name_offset + auto_name_map.size(); i = i*10)
89 auto_name_digits++;
90
91 if (verbose)
92 for (auto it = auto_name_map.begin(); it != auto_name_map.end(); ++it)
93 log(" renaming `%s' to `%s_%0*d_'.\n", it->first.c_str(), auto_prefix.c_str(), auto_name_digits, auto_name_offset + it->second);
94 }
95
96 std::string next_auto_id()
97 {
98 return stringf("%s_%0*d_", auto_prefix.c_str(), auto_name_digits, auto_name_offset + auto_name_counter++);
99 }
100
101 std::string id(RTLIL::IdString internal_id, bool may_rename = true)
102 {
103 const char *str = internal_id.c_str();
104 bool do_escape = false;
105
106 if (may_rename && auto_name_map.count(internal_id) != 0)
107 return stringf("%s_%0*d_", auto_prefix.c_str(), auto_name_digits, auto_name_offset + auto_name_map[internal_id]);
108
109 if (*str == '\\')
110 str++;
111
112 if ('0' <= *str && *str <= '9')
113 do_escape = true;
114
115 for (int i = 0; str[i]; i++)
116 {
117 if ('0' <= str[i] && str[i] <= '9')
118 continue;
119 if ('a' <= str[i] && str[i] <= 'z')
120 continue;
121 if ('A' <= str[i] && str[i] <= 'Z')
122 continue;
123 if (str[i] == '_')
124 continue;
125 do_escape = true;
126 break;
127 }
128
129 if (do_escape)
130 return "\\" + std::string(str) + " ";
131 return std::string(str);
132 }
133
134 bool is_reg_wire(RTLIL::SigSpec sig, std::string &reg_name)
135 {
136 if (!sig.is_chunk() || sig.as_chunk().wire == NULL)
137 return false;
138
139 RTLIL::SigChunk chunk = sig.as_chunk();
140
141 if (reg_wires.count(chunk.wire->name) == 0)
142 return false;
143
144 reg_name = id(chunk.wire->name);
145 if (sig.size() != chunk.wire->width) {
146 if (sig.size() == 1)
147 reg_name += stringf("[%d]", chunk.wire->start_offset + chunk.offset);
148 else if (chunk.wire->upto)
149 reg_name += stringf("[%d:%d]", (chunk.wire->width - (chunk.offset + chunk.width - 1) - 1) + chunk.wire->start_offset,
150 (chunk.wire->width - chunk.offset - 1) + chunk.wire->start_offset);
151 else
152 reg_name += stringf("[%d:%d]", chunk.wire->start_offset + chunk.offset + chunk.width - 1,
153 chunk.wire->start_offset + chunk.offset);
154 }
155
156 return true;
157 }
158
159 void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int offset = 0, bool no_decimal = false, bool set_signed = false, bool escape_comment = false)
160 {
161 if (width < 0)
162 width = data.bits.size() - offset;
163 if (nostr)
164 goto dump_hex;
165 if ((data.flags & RTLIL::CONST_FLAG_STRING) == 0 || width != (int)data.bits.size()) {
166 if (width == 32 && !no_decimal && !nodec) {
167 int32_t val = 0;
168 for (int i = offset+width-1; i >= offset; i--) {
169 log_assert(i < (int)data.bits.size());
170 if (data.bits[i] != RTLIL::S0 && data.bits[i] != RTLIL::S1)
171 goto dump_hex;
172 if (data.bits[i] == RTLIL::S1)
173 val |= 1 << (i - offset);
174 }
175 if (decimal)
176 f << stringf("%d", val);
177 else if (set_signed && val < 0)
178 f << stringf("-32'sd%u", -val);
179 else
180 f << stringf("32'%sd%u", set_signed ? "s" : "", val);
181 } else {
182 dump_hex:
183 if (nohex)
184 goto dump_bin;
185 vector<char> bin_digits, hex_digits;
186 for (int i = offset; i < offset+width; i++) {
187 log_assert(i < (int)data.bits.size());
188 switch (data.bits[i]) {
189 case RTLIL::S0: bin_digits.push_back('0'); break;
190 case RTLIL::S1: bin_digits.push_back('1'); break;
191 case RTLIL::Sx: bin_digits.push_back('x'); break;
192 case RTLIL::Sz: bin_digits.push_back('z'); break;
193 case RTLIL::Sa: bin_digits.push_back('z'); break;
194 case RTLIL::Sm: log_error("Found marker state in final netlist.");
195 }
196 }
197 if (GetSize(bin_digits) == 0)
198 goto dump_bin;
199 while (GetSize(bin_digits) % 4 != 0)
200 if (bin_digits.back() == '1')
201 bin_digits.push_back('0');
202 else
203 bin_digits.push_back(bin_digits.back());
204 for (int i = 0; i < GetSize(bin_digits); i += 4)
205 {
206 char bit_3 = bin_digits[i+3];
207 char bit_2 = bin_digits[i+2];
208 char bit_1 = bin_digits[i+1];
209 char bit_0 = bin_digits[i+0];
210 if (bit_3 == 'x' || bit_2 == 'x' || bit_1 == 'x' || bit_0 == 'x') {
211 if (bit_3 != 'x' || bit_2 != 'x' || bit_1 != 'x' || bit_0 != 'x')
212 goto dump_bin;
213 hex_digits.push_back('x');
214 continue;
215 }
216 if (bit_3 == 'z' || bit_2 == 'z' || bit_1 == 'z' || bit_0 == 'z') {
217 if (bit_3 != 'z' || bit_2 != 'z' || bit_1 != 'z' || bit_0 != 'z')
218 goto dump_bin;
219 hex_digits.push_back('z');
220 continue;
221 }
222 int val = 8*(bit_3 - '0') + 4*(bit_2 - '0') + 2*(bit_1 - '0') + (bit_0 - '0');
223 hex_digits.push_back(val < 10 ? '0' + val : 'a' + val - 10);
224 }
225 f << stringf("%d'%sh", width, set_signed ? "s" : "");
226 for (int i = GetSize(hex_digits)-1; i >= 0; i--)
227 f << hex_digits[i];
228 }
229 if (0) {
230 dump_bin:
231 f << stringf("%d'%sb", width, set_signed ? "s" : "");
232 if (width == 0)
233 f << stringf("0");
234 for (int i = offset+width-1; i >= offset; i--) {
235 log_assert(i < (int)data.bits.size());
236 switch (data.bits[i]) {
237 case RTLIL::S0: f << stringf("0"); break;
238 case RTLIL::S1: f << stringf("1"); break;
239 case RTLIL::Sx: f << stringf("x"); break;
240 case RTLIL::Sz: f << stringf("z"); break;
241 case RTLIL::Sa: f << stringf("z"); break;
242 case RTLIL::Sm: log_error("Found marker state in final netlist.");
243 }
244 }
245 }
246 } else {
247 f << stringf("\"");
248 std::string str = data.decode_string();
249 for (size_t i = 0; i < str.size(); i++) {
250 if (str[i] == '\n')
251 f << stringf("\\n");
252 else if (str[i] == '\t')
253 f << stringf("\\t");
254 else if (str[i] < 32)
255 f << stringf("\\%03o", str[i]);
256 else if (str[i] == '"')
257 f << stringf("\\\"");
258 else if (str[i] == '\\')
259 f << stringf("\\\\");
260 else if (str[i] == '/' && escape_comment && i > 0 && str[i-1] == '*')
261 f << stringf("\\/");
262 else
263 f << str[i];
264 }
265 f << stringf("\"");
266 }
267 }
268
269 void dump_reg_init(std::ostream &f, SigSpec sig)
270 {
271 Const initval;
272 bool gotinit = false;
273
274 for (auto bit : active_sigmap(sig)) {
275 if (active_initdata.count(bit)) {
276 initval.bits.push_back(active_initdata.at(bit));
277 gotinit = true;
278 } else {
279 initval.bits.push_back(State::Sx);
280 }
281 }
282
283 if (gotinit) {
284 f << " = ";
285 dump_const(f, initval);
286 }
287 }
288
289 void dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool no_decimal = false)
290 {
291 if (chunk.wire == NULL) {
292 dump_const(f, chunk.data, chunk.width, chunk.offset, no_decimal);
293 } else {
294 if (chunk.width == chunk.wire->width && chunk.offset == 0) {
295 f << stringf("%s", id(chunk.wire->name).c_str());
296 } else if (chunk.width == 1) {
297 if (chunk.wire->upto)
298 f << stringf("%s[%d]", id(chunk.wire->name).c_str(), (chunk.wire->width - chunk.offset - 1) + chunk.wire->start_offset);
299 else
300 f << stringf("%s[%d]", id(chunk.wire->name).c_str(), chunk.offset + chunk.wire->start_offset);
301 } else {
302 if (chunk.wire->upto)
303 f << stringf("%s[%d:%d]", id(chunk.wire->name).c_str(),
304 (chunk.wire->width - (chunk.offset + chunk.width - 1) - 1) + chunk.wire->start_offset,
305 (chunk.wire->width - chunk.offset - 1) + chunk.wire->start_offset);
306 else
307 f << stringf("%s[%d:%d]", id(chunk.wire->name).c_str(),
308 (chunk.offset + chunk.width - 1) + chunk.wire->start_offset,
309 chunk.offset + chunk.wire->start_offset);
310 }
311 }
312 }
313
314 void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig)
315 {
316 if (sig.is_chunk()) {
317 dump_sigchunk(f, sig.as_chunk());
318 } else {
319 f << stringf("{ ");
320 for (auto it = sig.chunks().rbegin(); it != sig.chunks().rend(); ++it) {
321 if (it != sig.chunks().rbegin())
322 f << stringf(", ");
323 dump_sigchunk(f, *it, true);
324 }
325 f << stringf(" }");
326 }
327 }
328
329 void dump_attributes(std::ostream &f, std::string indent, dict<RTLIL::IdString, RTLIL::Const> &attributes, char term = '\n', bool modattr = false)
330 {
331 if (noattr)
332 return;
333 for (auto it = attributes.begin(); it != attributes.end(); ++it) {
334 f << stringf("%s" "%s %s", indent.c_str(), attr2comment ? "/*" : "(*", id(it->first).c_str());
335 f << stringf(" = ");
336 if (modattr && (it->second == Const(0, 1) || it->second == Const(0)))
337 f << stringf(" 0 ");
338 else if (modattr && (it->second == Const(1, 1) || it->second == Const(1)))
339 f << stringf(" 1 ");
340 else
341 dump_const(f, it->second, -1, 0, false, false, attr2comment);
342 f << stringf(" %s%c", attr2comment ? "*/" : "*)", term);
343 }
344 }
345
346 void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire)
347 {
348 dump_attributes(f, indent, wire->attributes);
349 #if 0
350 if (wire->port_input && !wire->port_output)
351 f << stringf("%s" "input %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : "");
352 else if (!wire->port_input && wire->port_output)
353 f << stringf("%s" "output %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : "");
354 else if (wire->port_input && wire->port_output)
355 f << stringf("%s" "inout %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : "");
356 else
357 f << stringf("%s" "%s ", indent.c_str(), reg_wires.count(wire->name) ? "reg" : "wire");
358 if (wire->width != 1)
359 f << stringf("[%d:%d] ", wire->width - 1 + wire->start_offset, wire->start_offset);
360 f << stringf("%s;\n", id(wire->name).c_str());
361 #else
362 // do not use Verilog-2k "output reg" syntax in Verilog export
363 std::string range = "";
364 if (wire->width != 1) {
365 if (wire->upto)
366 range = stringf(" [%d:%d]", wire->start_offset, wire->width - 1 + wire->start_offset);
367 else
368 range = stringf(" [%d:%d]", wire->width - 1 + wire->start_offset, wire->start_offset);
369 }
370 if (wire->port_input && !wire->port_output)
371 f << stringf("%s" "input%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str());
372 if (!wire->port_input && wire->port_output)
373 f << stringf("%s" "output%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str());
374 if (wire->port_input && wire->port_output)
375 f << stringf("%s" "inout%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str());
376 if (reg_wires.count(wire->name)) {
377 f << stringf("%s" "reg%s %s", indent.c_str(), range.c_str(), id(wire->name).c_str());
378 if (wire->attributes.count("\\init")) {
379 f << stringf(" = ");
380 dump_const(f, wire->attributes.at("\\init"));
381 }
382 f << stringf(";\n");
383 } else if (!wire->port_input && !wire->port_output)
384 f << stringf("%s" "wire%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str());
385 #endif
386 }
387
388 void dump_memory(std::ostream &f, std::string indent, RTLIL::Memory *memory)
389 {
390 dump_attributes(f, indent, memory->attributes);
391 f << stringf("%s" "reg [%d:0] %s [%d:%d];\n", indent.c_str(), memory->width-1, id(memory->name).c_str(), memory->size+memory->start_offset-1, memory->start_offset);
392 }
393
394 void dump_cell_expr_port(std::ostream &f, RTLIL::Cell *cell, std::string port, bool gen_signed = true)
395 {
396 if (gen_signed && cell->parameters.count("\\" + port + "_SIGNED") > 0 && cell->parameters["\\" + port + "_SIGNED"].as_bool()) {
397 f << stringf("$signed(");
398 dump_sigspec(f, cell->getPort("\\" + port));
399 f << stringf(")");
400 } else
401 dump_sigspec(f, cell->getPort("\\" + port));
402 }
403
404 std::string cellname(RTLIL::Cell *cell)
405 {
406 if (!norename && cell->name[0] == '$' && reg_ct.count(cell->type) && cell->hasPort("\\Q"))
407 {
408 RTLIL::SigSpec sig = cell->getPort("\\Q");
409 if (GetSize(sig) != 1 || sig.is_fully_const())
410 goto no_special_reg_name;
411
412 RTLIL::Wire *wire = sig[0].wire;
413
414 if (wire->name[0] != '\\')
415 goto no_special_reg_name;
416
417 std::string cell_name = wire->name.str();
418
419 size_t pos = cell_name.find('[');
420 if (pos != std::string::npos)
421 cell_name = cell_name.substr(0, pos) + "_reg" + cell_name.substr(pos);
422 else
423 cell_name = cell_name + "_reg";
424
425 if (wire->width != 1)
426 cell_name += stringf("[%d]", wire->start_offset + sig[0].offset);
427
428 if (active_module && active_module->count_id(cell_name) > 0)
429 goto no_special_reg_name;
430
431 return id(cell_name);
432 }
433 else
434 {
435 no_special_reg_name:
436 return id(cell->name).c_str();
437 }
438 }
439
440 void dump_cell_expr_uniop(std::ostream &f, std::string indent, RTLIL::Cell *cell, std::string op)
441 {
442 f << stringf("%s" "assign ", indent.c_str());
443 dump_sigspec(f, cell->getPort("\\Y"));
444 f << stringf(" = %s ", op.c_str());
445 dump_attributes(f, "", cell->attributes, ' ');
446 dump_cell_expr_port(f, cell, "A", true);
447 f << stringf(";\n");
448 }
449
450 void dump_cell_expr_binop(std::ostream &f, std::string indent, RTLIL::Cell *cell, std::string op)
451 {
452 f << stringf("%s" "assign ", indent.c_str());
453 dump_sigspec(f, cell->getPort("\\Y"));
454 f << stringf(" = ");
455 dump_cell_expr_port(f, cell, "A", true);
456 f << stringf(" %s ", op.c_str());
457 dump_attributes(f, "", cell->attributes, ' ');
458 dump_cell_expr_port(f, cell, "B", true);
459 f << stringf(";\n");
460 }
461
462 bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
463 {
464 if (cell->type == "$_NOT_") {
465 f << stringf("%s" "assign ", indent.c_str());
466 dump_sigspec(f, cell->getPort("\\Y"));
467 f << stringf(" = ");
468 f << stringf("~");
469 dump_attributes(f, "", cell->attributes, ' ');
470 dump_cell_expr_port(f, cell, "A", false);
471 f << stringf(";\n");
472 return true;
473 }
474
475 if (cell->type.in("$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_", "$_ANDNOT_", "$_ORNOT_")) {
476 f << stringf("%s" "assign ", indent.c_str());
477 dump_sigspec(f, cell->getPort("\\Y"));
478 f << stringf(" = ");
479 if (cell->type.in("$_NAND_", "$_NOR_", "$_XNOR_"))
480 f << stringf("~(");
481 dump_cell_expr_port(f, cell, "A", false);
482 f << stringf(" ");
483 if (cell->type.in("$_AND_", "$_NAND_", "$_ANDNOT_"))
484 f << stringf("&");
485 if (cell->type.in("$_OR_", "$_NOR_", "$_ORNOT_"))
486 f << stringf("|");
487 if (cell->type.in("$_XOR_", "$_XNOR_"))
488 f << stringf("^");
489 dump_attributes(f, "", cell->attributes, ' ');
490 f << stringf(" ");
491 if (cell->type.in("$_ANDNOT_", "$_ORNOT_"))
492 f << stringf("~(");
493 dump_cell_expr_port(f, cell, "B", false);
494 if (cell->type.in("$_NAND_", "$_NOR_", "$_XNOR_", "$_ANDNOT_", "$_ORNOT_"))
495 f << stringf(")");
496 f << stringf(";\n");
497 return true;
498 }
499
500 if (cell->type == "$_MUX_") {
501 f << stringf("%s" "assign ", indent.c_str());
502 dump_sigspec(f, cell->getPort("\\Y"));
503 f << stringf(" = ");
504 dump_cell_expr_port(f, cell, "S", false);
505 f << stringf(" ? ");
506 dump_attributes(f, "", cell->attributes, ' ');
507 dump_cell_expr_port(f, cell, "B", false);
508 f << stringf(" : ");
509 dump_cell_expr_port(f, cell, "A", false);
510 f << stringf(";\n");
511 return true;
512 }
513
514 if (cell->type.in("$_AOI3_", "$_OAI3_")) {
515 f << stringf("%s" "assign ", indent.c_str());
516 dump_sigspec(f, cell->getPort("\\Y"));
517 f << stringf(" = ~((");
518 dump_cell_expr_port(f, cell, "A", false);
519 f << stringf(cell->type == "$_AOI3_" ? " & " : " | ");
520 dump_cell_expr_port(f, cell, "B", false);
521 f << stringf(cell->type == "$_AOI3_" ? ") |" : ") &");
522 dump_attributes(f, "", cell->attributes, ' ');
523 f << stringf(" ");
524 dump_cell_expr_port(f, cell, "C", false);
525 f << stringf(");\n");
526 return true;
527 }
528
529 if (cell->type.in("$_AOI4_", "$_OAI4_")) {
530 f << stringf("%s" "assign ", indent.c_str());
531 dump_sigspec(f, cell->getPort("\\Y"));
532 f << stringf(" = ~((");
533 dump_cell_expr_port(f, cell, "A", false);
534 f << stringf(cell->type == "$_AOI4_" ? " & " : " | ");
535 dump_cell_expr_port(f, cell, "B", false);
536 f << stringf(cell->type == "$_AOI4_" ? ") |" : ") &");
537 dump_attributes(f, "", cell->attributes, ' ');
538 f << stringf(" (");
539 dump_cell_expr_port(f, cell, "C", false);
540 f << stringf(cell->type == "$_AOI4_" ? " & " : " | ");
541 dump_cell_expr_port(f, cell, "D", false);
542 f << stringf("));\n");
543 return true;
544 }
545
546 if (cell->type.substr(0, 6) == "$_DFF_")
547 {
548 std::string reg_name = cellname(cell);
549 bool out_is_reg_wire = is_reg_wire(cell->getPort("\\Q"), reg_name);
550
551 if (!out_is_reg_wire) {
552 f << stringf("%s" "reg %s", indent.c_str(), reg_name.c_str());
553 dump_reg_init(f, cell->getPort("\\Q"));
554 f << ";\n";
555 }
556
557 dump_attributes(f, indent, cell->attributes);
558 f << stringf("%s" "always @(%sedge ", indent.c_str(), cell->type[6] == 'P' ? "pos" : "neg");
559 dump_sigspec(f, cell->getPort("\\C"));
560 if (cell->type[7] != '_') {
561 f << stringf(" or %sedge ", cell->type[7] == 'P' ? "pos" : "neg");
562 dump_sigspec(f, cell->getPort("\\R"));
563 }
564 f << stringf(")\n");
565
566 if (cell->type[7] != '_') {
567 f << stringf("%s" " if (%s", indent.c_str(), cell->type[7] == 'P' ? "" : "!");
568 dump_sigspec(f, cell->getPort("\\R"));
569 f << stringf(")\n");
570 f << stringf("%s" " %s <= %c;\n", indent.c_str(), reg_name.c_str(), cell->type[8]);
571 f << stringf("%s" " else\n", indent.c_str());
572 }
573
574 f << stringf("%s" " %s <= ", indent.c_str(), reg_name.c_str());
575 dump_cell_expr_port(f, cell, "D", false);
576 f << stringf(";\n");
577
578 if (!out_is_reg_wire) {
579 f << stringf("%s" "assign ", indent.c_str());
580 dump_sigspec(f, cell->getPort("\\Q"));
581 f << stringf(" = %s;\n", reg_name.c_str());
582 }
583
584 return true;
585 }
586
587 if (cell->type.substr(0, 8) == "$_DFFSR_")
588 {
589 char pol_c = cell->type[8], pol_s = cell->type[9], pol_r = cell->type[10];
590
591 std::string reg_name = cellname(cell);
592 bool out_is_reg_wire = is_reg_wire(cell->getPort("\\Q"), reg_name);
593
594 if (!out_is_reg_wire) {
595 f << stringf("%s" "reg %s", indent.c_str(), reg_name.c_str());
596 dump_reg_init(f, cell->getPort("\\Q"));
597 f << ";\n";
598 }
599
600 dump_attributes(f, indent, cell->attributes);
601 f << stringf("%s" "always @(%sedge ", indent.c_str(), pol_c == 'P' ? "pos" : "neg");
602 dump_sigspec(f, cell->getPort("\\C"));
603 f << stringf(" or %sedge ", pol_s == 'P' ? "pos" : "neg");
604 dump_sigspec(f, cell->getPort("\\S"));
605 f << stringf(" or %sedge ", pol_r == 'P' ? "pos" : "neg");
606 dump_sigspec(f, cell->getPort("\\R"));
607 f << stringf(")\n");
608
609 f << stringf("%s" " if (%s", indent.c_str(), pol_r == 'P' ? "" : "!");
610 dump_sigspec(f, cell->getPort("\\R"));
611 f << stringf(")\n");
612 f << stringf("%s" " %s <= 0;\n", indent.c_str(), reg_name.c_str());
613
614 f << stringf("%s" " else if (%s", indent.c_str(), pol_s == 'P' ? "" : "!");
615 dump_sigspec(f, cell->getPort("\\S"));
616 f << stringf(")\n");
617 f << stringf("%s" " %s <= 1;\n", indent.c_str(), reg_name.c_str());
618
619 f << stringf("%s" " else\n", indent.c_str());
620 f << stringf("%s" " %s <= ", indent.c_str(), reg_name.c_str());
621 dump_cell_expr_port(f, cell, "D", false);
622 f << stringf(";\n");
623
624 if (!out_is_reg_wire) {
625 f << stringf("%s" "assign ", indent.c_str());
626 dump_sigspec(f, cell->getPort("\\Q"));
627 f << stringf(" = %s;\n", reg_name.c_str());
628 }
629
630 return true;
631 }
632
633 #define HANDLE_UNIOP(_type, _operator) \
634 if (cell->type ==_type) { dump_cell_expr_uniop(f, indent, cell, _operator); return true; }
635 #define HANDLE_BINOP(_type, _operator) \
636 if (cell->type ==_type) { dump_cell_expr_binop(f, indent, cell, _operator); return true; }
637
638 HANDLE_UNIOP("$not", "~")
639 HANDLE_UNIOP("$pos", "+")
640 HANDLE_UNIOP("$neg", "-")
641
642 HANDLE_BINOP("$and", "&")
643 HANDLE_BINOP("$or", "|")
644 HANDLE_BINOP("$xor", "^")
645 HANDLE_BINOP("$xnor", "~^")
646
647 HANDLE_UNIOP("$reduce_and", "&")
648 HANDLE_UNIOP("$reduce_or", "|")
649 HANDLE_UNIOP("$reduce_xor", "^")
650 HANDLE_UNIOP("$reduce_xnor", "~^")
651 HANDLE_UNIOP("$reduce_bool", "|")
652
653 HANDLE_BINOP("$shl", "<<")
654 HANDLE_BINOP("$shr", ">>")
655 HANDLE_BINOP("$sshl", "<<<")
656 HANDLE_BINOP("$sshr", ">>>")
657
658 HANDLE_BINOP("$lt", "<")
659 HANDLE_BINOP("$le", "<=")
660 HANDLE_BINOP("$eq", "==")
661 HANDLE_BINOP("$ne", "!=")
662 HANDLE_BINOP("$eqx", "===")
663 HANDLE_BINOP("$nex", "!==")
664 HANDLE_BINOP("$ge", ">=")
665 HANDLE_BINOP("$gt", ">")
666
667 HANDLE_BINOP("$add", "+")
668 HANDLE_BINOP("$sub", "-")
669 HANDLE_BINOP("$mul", "*")
670 HANDLE_BINOP("$div", "/")
671 HANDLE_BINOP("$mod", "%")
672 HANDLE_BINOP("$pow", "**")
673
674 HANDLE_UNIOP("$logic_not", "!")
675 HANDLE_BINOP("$logic_and", "&&")
676 HANDLE_BINOP("$logic_or", "||")
677
678 #undef HANDLE_UNIOP
679 #undef HANDLE_BINOP
680
681 if (cell->type == "$shift")
682 {
683 f << stringf("%s" "assign ", indent.c_str());
684 dump_sigspec(f, cell->getPort("\\Y"));
685 f << stringf(" = ");
686 if (cell->getParam("\\B_SIGNED").as_bool())
687 {
688 f << stringf("$signed(");
689 dump_sigspec(f, cell->getPort("\\B"));
690 f << stringf(")");
691 f << stringf(" < 0 ? ");
692 dump_sigspec(f, cell->getPort("\\A"));
693 f << stringf(" << - ");
694 dump_sigspec(f, cell->getPort("\\B"));
695 f << stringf(" : ");
696 dump_sigspec(f, cell->getPort("\\A"));
697 f << stringf(" >> ");
698 dump_sigspec(f, cell->getPort("\\B"));
699 }
700 else
701 {
702 dump_sigspec(f, cell->getPort("\\A"));
703 f << stringf(" >> ");
704 dump_sigspec(f, cell->getPort("\\B"));
705 }
706 f << stringf(";\n");
707 return true;
708 }
709
710 if (cell->type == "$shiftx")
711 {
712 std::string temp_id = next_auto_id();
713 f << stringf("%s" "wire [%d:0] %s = ", indent.c_str(), GetSize(cell->getPort("\\A"))-1, temp_id.c_str());
714 dump_sigspec(f, cell->getPort("\\A"));
715 f << stringf(";\n");
716
717 f << stringf("%s" "assign ", indent.c_str());
718 dump_sigspec(f, cell->getPort("\\Y"));
719 f << stringf(" = %s[", temp_id.c_str());
720 if (cell->getParam("\\B_SIGNED").as_bool())
721 f << stringf("$signed(");
722 dump_sigspec(f, cell->getPort("\\B"));
723 if (cell->getParam("\\B_SIGNED").as_bool())
724 f << stringf(")");
725 f << stringf(" +: %d", cell->getParam("\\Y_WIDTH").as_int());
726 f << stringf("];\n");
727 return true;
728 }
729
730 if (cell->type == "$mux")
731 {
732 f << stringf("%s" "assign ", indent.c_str());
733 dump_sigspec(f, cell->getPort("\\Y"));
734 f << stringf(" = ");
735 dump_sigspec(f, cell->getPort("\\S"));
736 f << stringf(" ? ");
737 dump_attributes(f, "", cell->attributes, ' ');
738 dump_sigspec(f, cell->getPort("\\B"));
739 f << stringf(" : ");
740 dump_sigspec(f, cell->getPort("\\A"));
741 f << stringf(";\n");
742 return true;
743 }
744
745 if (cell->type == "$pmux" || cell->type == "$pmux_safe")
746 {
747 int width = cell->parameters["\\WIDTH"].as_int();
748 int s_width = cell->getPort("\\S").size();
749 std::string func_name = cellname(cell);
750
751 f << stringf("%s" "function [%d:0] %s;\n", indent.c_str(), width-1, func_name.c_str());
752 f << stringf("%s" " input [%d:0] a;\n", indent.c_str(), width-1);
753 f << stringf("%s" " input [%d:0] b;\n", indent.c_str(), s_width*width-1);
754 f << stringf("%s" " input [%d:0] s;\n", indent.c_str(), s_width-1);
755
756 dump_attributes(f, indent + " ", cell->attributes);
757 if (cell->type != "$pmux_safe" && !noattr)
758 f << stringf("%s" " (* parallel_case *)\n", indent.c_str());
759 f << stringf("%s" " casez (s)", indent.c_str());
760 if (cell->type != "$pmux_safe")
761 f << stringf(noattr ? " // synopsys parallel_case\n" : "\n");
762
763 for (int i = 0; i < s_width; i++)
764 {
765 f << stringf("%s" " %d'b", indent.c_str(), s_width);
766
767 for (int j = s_width-1; j >= 0; j--)
768 f << stringf("%c", j == i ? '1' : cell->type == "$pmux_safe" ? '0' : '?');
769
770 f << stringf(":\n");
771 f << stringf("%s" " %s = b[%d:%d];\n", indent.c_str(), func_name.c_str(), (i+1)*width-1, i*width);
772 }
773
774 f << stringf("%s" " default:\n", indent.c_str());
775 f << stringf("%s" " %s = a;\n", indent.c_str(), func_name.c_str());
776
777 f << stringf("%s" " endcase\n", indent.c_str());
778 f << stringf("%s" "endfunction\n", indent.c_str());
779
780 f << stringf("%s" "assign ", indent.c_str());
781 dump_sigspec(f, cell->getPort("\\Y"));
782 f << stringf(" = %s(", func_name.c_str());
783 dump_sigspec(f, cell->getPort("\\A"));
784 f << stringf(", ");
785 dump_sigspec(f, cell->getPort("\\B"));
786 f << stringf(", ");
787 dump_sigspec(f, cell->getPort("\\S"));
788 f << stringf(");\n");
789 return true;
790 }
791
792 if (cell->type == "$tribuf")
793 {
794 f << stringf("%s" "assign ", indent.c_str());
795 dump_sigspec(f, cell->getPort("\\Y"));
796 f << stringf(" = ");
797 dump_sigspec(f, cell->getPort("\\EN"));
798 f << stringf(" ? ");
799 dump_sigspec(f, cell->getPort("\\A"));
800 f << stringf(" : %d'bz;\n", cell->parameters.at("\\WIDTH").as_int());
801 return true;
802 }
803
804 if (cell->type == "$slice")
805 {
806 f << stringf("%s" "assign ", indent.c_str());
807 dump_sigspec(f, cell->getPort("\\Y"));
808 f << stringf(" = ");
809 dump_sigspec(f, cell->getPort("\\A"));
810 f << stringf(" >> %d;\n", cell->parameters.at("\\OFFSET").as_int());
811 return true;
812 }
813
814 if (cell->type == "$concat")
815 {
816 f << stringf("%s" "assign ", indent.c_str());
817 dump_sigspec(f, cell->getPort("\\Y"));
818 f << stringf(" = { ");
819 dump_sigspec(f, cell->getPort("\\B"));
820 f << stringf(" , ");
821 dump_sigspec(f, cell->getPort("\\A"));
822 f << stringf(" };\n");
823 return true;
824 }
825
826 if (cell->type == "$lut")
827 {
828 f << stringf("%s" "assign ", indent.c_str());
829 dump_sigspec(f, cell->getPort("\\Y"));
830 f << stringf(" = ");
831 dump_const(f, cell->parameters.at("\\LUT"));
832 f << stringf(" >> ");
833 dump_attributes(f, "", cell->attributes, ' ');
834 dump_sigspec(f, cell->getPort("\\A"));
835 f << stringf(";\n");
836 return true;
837 }
838
839 if (cell->type == "$dffsr")
840 {
841 SigSpec sig_clk = cell->getPort("\\CLK");
842 SigSpec sig_set = cell->getPort("\\SET");
843 SigSpec sig_clr = cell->getPort("\\CLR");
844 SigSpec sig_d = cell->getPort("\\D");
845 SigSpec sig_q = cell->getPort("\\Q");
846
847 int width = cell->parameters["\\WIDTH"].as_int();
848 bool pol_clk = cell->parameters["\\CLK_POLARITY"].as_bool();
849 bool pol_set = cell->parameters["\\SET_POLARITY"].as_bool();
850 bool pol_clr = cell->parameters["\\CLR_POLARITY"].as_bool();
851
852 std::string reg_name = cellname(cell);
853 bool out_is_reg_wire = is_reg_wire(sig_q, reg_name);
854
855 if (!out_is_reg_wire) {
856 f << stringf("%s" "reg [%d:0] %s", indent.c_str(), width-1, reg_name.c_str());
857 dump_reg_init(f, sig_q);
858 f << ";\n";
859 }
860
861 for (int i = 0; i < width; i++) {
862 f << stringf("%s" "always @(%sedge ", indent.c_str(), pol_clk ? "pos" : "neg");
863 dump_sigspec(f, sig_clk);
864 f << stringf(", %sedge ", pol_set ? "pos" : "neg");
865 dump_sigspec(f, sig_set);
866 f << stringf(", %sedge ", pol_clr ? "pos" : "neg");
867 dump_sigspec(f, sig_clr);
868 f << stringf(")\n");
869
870 f << stringf("%s" " if (%s", indent.c_str(), pol_clr ? "" : "!");
871 dump_sigspec(f, sig_clr);
872 f << stringf(") %s[%d] <= 1'b0;\n", reg_name.c_str(), i);
873
874 f << stringf("%s" " else if (%s", indent.c_str(), pol_set ? "" : "!");
875 dump_sigspec(f, sig_set);
876 f << stringf(") %s[%d] <= 1'b1;\n", reg_name.c_str(), i);
877
878 f << stringf("%s" " else %s[%d] <= ", indent.c_str(), reg_name.c_str(), i);
879 dump_sigspec(f, sig_d[i]);
880 f << stringf(";\n");
881 }
882
883 if (!out_is_reg_wire) {
884 f << stringf("%s" "assign ", indent.c_str());
885 dump_sigspec(f, sig_q);
886 f << stringf(" = %s;\n", reg_name.c_str());
887 }
888
889 return true;
890 }
891
892 if (cell->type == "$dff" || cell->type == "$adff" || cell->type == "$dffe")
893 {
894 RTLIL::SigSpec sig_clk, sig_arst, sig_en, val_arst;
895 bool pol_clk, pol_arst = false, pol_en = false;
896
897 sig_clk = cell->getPort("\\CLK");
898 pol_clk = cell->parameters["\\CLK_POLARITY"].as_bool();
899
900 if (cell->type == "$adff") {
901 sig_arst = cell->getPort("\\ARST");
902 pol_arst = cell->parameters["\\ARST_POLARITY"].as_bool();
903 val_arst = RTLIL::SigSpec(cell->parameters["\\ARST_VALUE"]);
904 }
905
906 if (cell->type == "$dffe") {
907 sig_en = cell->getPort("\\EN");
908 pol_en = cell->parameters["\\EN_POLARITY"].as_bool();
909 }
910
911 std::string reg_name = cellname(cell);
912 bool out_is_reg_wire = is_reg_wire(cell->getPort("\\Q"), reg_name);
913
914 if (!out_is_reg_wire) {
915 f << stringf("%s" "reg [%d:0] %s", indent.c_str(), cell->parameters["\\WIDTH"].as_int()-1, reg_name.c_str());
916 dump_reg_init(f, cell->getPort("\\Q"));
917 f << ";\n";
918 }
919
920 f << stringf("%s" "always @(%sedge ", indent.c_str(), pol_clk ? "pos" : "neg");
921 dump_sigspec(f, sig_clk);
922 if (cell->type == "$adff") {
923 f << stringf(" or %sedge ", pol_arst ? "pos" : "neg");
924 dump_sigspec(f, sig_arst);
925 }
926 f << stringf(")\n");
927
928 if (cell->type == "$adff") {
929 f << stringf("%s" " if (%s", indent.c_str(), pol_arst ? "" : "!");
930 dump_sigspec(f, sig_arst);
931 f << stringf(")\n");
932 f << stringf("%s" " %s <= ", indent.c_str(), reg_name.c_str());
933 dump_sigspec(f, val_arst);
934 f << stringf(";\n");
935 f << stringf("%s" " else\n", indent.c_str());
936 }
937
938 if (cell->type == "$dffe") {
939 f << stringf("%s" " if (%s", indent.c_str(), pol_en ? "" : "!");
940 dump_sigspec(f, sig_en);
941 f << stringf(")\n");
942 }
943
944 f << stringf("%s" " %s <= ", indent.c_str(), reg_name.c_str());
945 dump_cell_expr_port(f, cell, "D", false);
946 f << stringf(";\n");
947
948 if (!out_is_reg_wire) {
949 f << stringf("%s" "assign ", indent.c_str());
950 dump_sigspec(f, cell->getPort("\\Q"));
951 f << stringf(" = %s;\n", reg_name.c_str());
952 }
953
954 return true;
955 }
956
957 if (cell->type == "$dlatch")
958 {
959 RTLIL::SigSpec sig_en;
960 bool pol_en = false;
961
962 sig_en = cell->getPort("\\EN");
963 pol_en = cell->parameters["\\EN_POLARITY"].as_bool();
964
965 std::string reg_name = cellname(cell);
966 bool out_is_reg_wire = is_reg_wire(cell->getPort("\\Q"), reg_name);
967
968 if (!out_is_reg_wire) {
969 f << stringf("%s" "reg [%d:0] %s", indent.c_str(), cell->parameters["\\WIDTH"].as_int()-1, reg_name.c_str());
970 dump_reg_init(f, cell->getPort("\\Q"));
971 f << ";\n";
972 }
973
974 f << stringf("%s" "always @*\n", indent.c_str());
975
976 f << stringf("%s" " if (%s", indent.c_str(), pol_en ? "" : "!");
977 dump_sigspec(f, sig_en);
978 f << stringf(")\n");
979
980 f << stringf("%s" " %s = ", indent.c_str(), reg_name.c_str());
981 dump_cell_expr_port(f, cell, "D", false);
982 f << stringf(";\n");
983
984 if (!out_is_reg_wire) {
985 f << stringf("%s" "assign ", indent.c_str());
986 dump_sigspec(f, cell->getPort("\\Q"));
987 f << stringf(" = %s;\n", reg_name.c_str());
988 }
989
990 return true;
991 }
992
993 if (cell->type == "$mem")
994 {
995 RTLIL::IdString memid = cell->parameters["\\MEMID"].decode_string();
996 std::string mem_id = id(cell->parameters["\\MEMID"].decode_string());
997 int abits = cell->parameters["\\ABITS"].as_int();
998 int size = cell->parameters["\\SIZE"].as_int();
999 int offset = cell->parameters["\\OFFSET"].as_int();
1000 int width = cell->parameters["\\WIDTH"].as_int();
1001 bool use_init = !(RTLIL::SigSpec(cell->parameters["\\INIT"]).is_fully_undef());
1002
1003 // for memory block make something like:
1004 // reg [7:0] memid [3:0];
1005 // initial begin
1006 // memid[0] = ...
1007 // end
1008 f << stringf("%s" "reg [%d:%d] %s [%d:%d];\n", indent.c_str(), width-1, 0, mem_id.c_str(), size+offset-1, offset);
1009 if (use_init)
1010 {
1011 f << stringf("%s" "initial begin\n", indent.c_str());
1012 for (int i=0; i<size; i++)
1013 {
1014 f << stringf("%s" " %s[%d] = ", indent.c_str(), mem_id.c_str(), i);
1015 dump_const(f, cell->parameters["\\INIT"].extract(i*width, width));
1016 f << stringf(";\n");
1017 }
1018 f << stringf("%s" "end\n", indent.c_str());
1019 }
1020
1021 // create a map : "edge clk" -> expressions within that clock domain
1022 dict<std::string, std::vector<std::string>> clk_to_lof_body;
1023 clk_to_lof_body[""] = std::vector<std::string>();
1024 std::string clk_domain_str;
1025 // create a list of reg declarations
1026 std::vector<std::string> lof_reg_declarations;
1027
1028 int nread_ports = cell->parameters["\\RD_PORTS"].as_int();
1029 RTLIL::SigSpec sig_rd_clk, sig_rd_en, sig_rd_data, sig_rd_addr;
1030 bool use_rd_clk, rd_clk_posedge, rd_transparent;
1031 // read ports
1032 for (int i=0; i < nread_ports; i++)
1033 {
1034 sig_rd_clk = cell->getPort("\\RD_CLK").extract(i);
1035 sig_rd_en = cell->getPort("\\RD_EN").extract(i);
1036 sig_rd_data = cell->getPort("\\RD_DATA").extract(i*width, width);
1037 sig_rd_addr = cell->getPort("\\RD_ADDR").extract(i*abits, abits);
1038 use_rd_clk = cell->parameters["\\RD_CLK_ENABLE"].extract(i).as_bool();
1039 rd_clk_posedge = cell->parameters["\\RD_CLK_POLARITY"].extract(i).as_bool();
1040 rd_transparent = cell->parameters["\\RD_TRANSPARENT"].extract(i).as_bool();
1041 {
1042 std::ostringstream os;
1043 dump_sigspec(os, sig_rd_clk);
1044 clk_domain_str = stringf("%sedge %s", rd_clk_posedge ? "pos" : "neg", os.str().c_str());
1045 if( clk_to_lof_body.count(clk_domain_str) == 0 )
1046 clk_to_lof_body[clk_domain_str] = std::vector<std::string>();
1047 }
1048 if (use_rd_clk && !rd_transparent)
1049 {
1050 // for clocked read ports make something like:
1051 // reg [..] temp_id;
1052 // always @(posedge clk)
1053 // if (rd_en) temp_id <= array_reg[r_addr];
1054 // assign r_data = temp_id;
1055 std::string temp_id = next_auto_id();
1056 lof_reg_declarations.push_back( stringf("reg [%d:0] %s;\n", sig_rd_data.size() - 1, temp_id.c_str()) );
1057 {
1058 std::ostringstream os;
1059 if (sig_rd_en != RTLIL::SigBit(true))
1060 {
1061 os << stringf("if (");
1062 dump_sigspec(os, sig_rd_en);
1063 os << stringf(") ");
1064 }
1065 os << stringf("%s <= %s[", temp_id.c_str(), mem_id.c_str());
1066 dump_sigspec(os, sig_rd_addr);
1067 os << stringf("];\n");
1068 clk_to_lof_body[clk_domain_str].push_back(os.str());
1069 }
1070 {
1071 std::ostringstream os;
1072 dump_sigspec(os, sig_rd_data);
1073 std::string line = stringf("assign %s = %s;\n", os.str().c_str(), temp_id.c_str());
1074 clk_to_lof_body[""].push_back(line);
1075 }
1076 } else {
1077 if (rd_transparent) {
1078 // for rd-transparent read-ports make something like:
1079 // reg [..] temp_id;
1080 // always @(posedge clk)
1081 // temp_id <= r_addr;
1082 // assign r_data = array_reg[temp_id];
1083 std::string temp_id = next_auto_id();
1084 lof_reg_declarations.push_back( stringf("reg [%d:0] %s;\n", sig_rd_addr.size() - 1, temp_id.c_str()) );
1085 {
1086 std::ostringstream os;
1087 dump_sigspec(os, sig_rd_addr);
1088 std::string line = stringf("%s <= %s;\n", temp_id.c_str(), os.str().c_str());
1089 clk_to_lof_body[clk_domain_str].push_back(line);
1090 }
1091 {
1092 std::ostringstream os;
1093 dump_sigspec(os, sig_rd_data);
1094 std::string line = stringf("assign %s = %s[%s];\n", os.str().c_str(), mem_id.c_str(), temp_id.c_str());
1095 clk_to_lof_body[""].push_back(line);
1096 }
1097 } else {
1098 // for non-clocked read-ports make something like:
1099 // assign r_data = array_reg[r_addr];
1100 std::ostringstream os, os2;
1101 dump_sigspec(os, sig_rd_data);
1102 dump_sigspec(os2, sig_rd_addr);
1103 std::string line = stringf("assign %s = %s[%s];\n", os.str().c_str(), mem_id.c_str(), os2.str().c_str());
1104 clk_to_lof_body[""].push_back(line);
1105 }
1106 }
1107 }
1108
1109 int nwrite_ports = cell->parameters["\\WR_PORTS"].as_int();
1110 RTLIL::SigSpec sig_wr_clk, sig_wr_data, sig_wr_addr, sig_wr_en;
1111 bool wr_clk_posedge;
1112
1113 // write ports
1114 for (int i=0; i < nwrite_ports; i++)
1115 {
1116 sig_wr_clk = cell->getPort("\\WR_CLK").extract(i);
1117 sig_wr_data = cell->getPort("\\WR_DATA").extract(i*width, width);
1118 sig_wr_addr = cell->getPort("\\WR_ADDR").extract(i*abits, abits);
1119 sig_wr_en = cell->getPort("\\WR_EN").extract(i*width, width);
1120 wr_clk_posedge = cell->parameters["\\WR_CLK_POLARITY"].extract(i).as_bool();
1121 {
1122 std::ostringstream os;
1123 dump_sigspec(os, sig_wr_clk);
1124 clk_domain_str = stringf("%sedge %s", wr_clk_posedge ? "pos" : "neg", os.str().c_str());
1125 if( clk_to_lof_body.count(clk_domain_str) == 0 )
1126 clk_to_lof_body[clk_domain_str] = std::vector<std::string>();
1127 }
1128 // make something like:
1129 // always @(posedge clk)
1130 // if (wr_en_bit) memid[w_addr][??] <= w_data[??];
1131 // ...
1132 for (int i = 0; i < GetSize(sig_wr_en); i++)
1133 {
1134 int start_i = i, width = 1;
1135 SigBit wen_bit = sig_wr_en[i];
1136
1137 while (i+1 < GetSize(sig_wr_en) && active_sigmap(sig_wr_en[i+1]) == active_sigmap(wen_bit))
1138 i++, width++;
1139
1140 if (wen_bit == State::S0)
1141 continue;
1142
1143 std::ostringstream os;
1144 if (wen_bit != State::S1)
1145 {
1146 os << stringf("if (");
1147 dump_sigspec(os, wen_bit);
1148 os << stringf(") ");
1149 }
1150 os << stringf("%s[", mem_id.c_str());
1151 dump_sigspec(os, sig_wr_addr);
1152 if (width == GetSize(sig_wr_en))
1153 os << stringf("] <= ");
1154 else
1155 os << stringf("][%d:%d] <= ", i, start_i);
1156 dump_sigspec(os, sig_wr_data.extract(start_i, width));
1157 os << stringf(";\n");
1158 clk_to_lof_body[clk_domain_str].push_back(os.str());
1159 }
1160 }
1161 // Output Verilog that looks something like this:
1162 // reg [..] _3_;
1163 // always @(posedge CLK2) begin
1164 // _3_ <= memory[D1ADDR];
1165 // if (A1EN)
1166 // memory[A1ADDR] <= A1DATA;
1167 // if (A2EN)
1168 // memory[A2ADDR] <= A2DATA;
1169 // ...
1170 // end
1171 // always @(negedge CLK1) begin
1172 // if (C1EN)
1173 // memory[C1ADDR] <= C1DATA;
1174 // end
1175 // ...
1176 // assign D1DATA = _3_;
1177 // assign D2DATA <= memory[D2ADDR];
1178
1179 // the reg ... definitions
1180 for(auto &reg : lof_reg_declarations)
1181 {
1182 f << stringf("%s" "%s", indent.c_str(), reg.c_str());
1183 }
1184 // the block of expressions by clock domain
1185 for(auto &pair : clk_to_lof_body)
1186 {
1187 std::string clk_domain = pair.first;
1188 std::vector<std::string> lof_lines = pair.second;
1189 if( clk_domain != "")
1190 {
1191 f << stringf("%s" "always @(%s) begin\n", indent.c_str(), clk_domain.c_str());
1192 for(auto &line : lof_lines)
1193 f << stringf("%s%s" "%s", indent.c_str(), indent.c_str(), line.c_str());
1194 f << stringf("%s" "end\n", indent.c_str());
1195 }
1196 else
1197 {
1198 // the non-clocked assignments
1199 for(auto &line : lof_lines)
1200 f << stringf("%s" "%s", indent.c_str(), line.c_str());
1201 }
1202 }
1203
1204 return true;
1205 }
1206
1207 // FIXME: $_SR_[PN][PN]_, $_DLATCH_[PN]_, $_DLATCHSR_[PN][PN][PN]_
1208 // FIXME: $sr, $dlatch, $memrd, $memwr, $fsm
1209
1210 return false;
1211 }
1212
1213 void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell)
1214 {
1215 if (cell->type[0] == '$' && !noexpr) {
1216 if (dump_cell_expr(f, indent, cell))
1217 return;
1218 }
1219
1220 dump_attributes(f, indent, cell->attributes);
1221 f << stringf("%s" "%s", indent.c_str(), id(cell->type, false).c_str());
1222
1223 if (!defparam && cell->parameters.size() > 0) {
1224 f << stringf(" #(");
1225 for (auto it = cell->parameters.begin(); it != cell->parameters.end(); ++it) {
1226 if (it != cell->parameters.begin())
1227 f << stringf(",");
1228 f << stringf("\n%s .%s(", indent.c_str(), id(it->first).c_str());
1229 bool is_signed = (it->second.flags & RTLIL::CONST_FLAG_SIGNED) != 0;
1230 dump_const(f, it->second, -1, 0, false, is_signed);
1231 f << stringf(")");
1232 }
1233 f << stringf("\n%s" ")", indent.c_str());
1234 }
1235
1236 std::string cell_name = cellname(cell);
1237 if (cell_name != id(cell->name))
1238 f << stringf(" %s /* %s */ (", cell_name.c_str(), id(cell->name).c_str());
1239 else
1240 f << stringf(" %s (", cell_name.c_str());
1241
1242 bool first_arg = true;
1243 std::set<RTLIL::IdString> numbered_ports;
1244 for (int i = 1; true; i++) {
1245 char str[16];
1246 snprintf(str, 16, "$%d", i);
1247 for (auto it = cell->connections().begin(); it != cell->connections().end(); ++it) {
1248 if (it->first != str)
1249 continue;
1250 if (!first_arg)
1251 f << stringf(",");
1252 first_arg = false;
1253 f << stringf("\n%s ", indent.c_str());
1254 dump_sigspec(f, it->second);
1255 numbered_ports.insert(it->first);
1256 goto found_numbered_port;
1257 }
1258 break;
1259 found_numbered_port:;
1260 }
1261 for (auto it = cell->connections().begin(); it != cell->connections().end(); ++it) {
1262 if (numbered_ports.count(it->first))
1263 continue;
1264 if (!first_arg)
1265 f << stringf(",");
1266 first_arg = false;
1267 f << stringf("\n%s .%s(", indent.c_str(), id(it->first).c_str());
1268 if (it->second.size() > 0)
1269 dump_sigspec(f, it->second);
1270 f << stringf(")");
1271 }
1272 f << stringf("\n%s" ");\n", indent.c_str());
1273
1274 if (defparam && cell->parameters.size() > 0) {
1275 for (auto it = cell->parameters.begin(); it != cell->parameters.end(); ++it) {
1276 f << stringf("%sdefparam %s.%s = ", indent.c_str(), cell_name.c_str(), id(it->first).c_str());
1277 bool is_signed = (it->second.flags & RTLIL::CONST_FLAG_SIGNED) != 0;
1278 dump_const(f, it->second, -1, 0, false, is_signed);
1279 f << stringf(";\n");
1280 }
1281 }
1282
1283 }
1284
1285 void dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right)
1286 {
1287 f << stringf("%s" "assign ", indent.c_str());
1288 dump_sigspec(f, left);
1289 f << stringf(" = ");
1290 dump_sigspec(f, right);
1291 f << stringf(";\n");
1292 }
1293
1294 void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw);
1295
1296 void dump_case_body(std::ostream &f, std::string indent, RTLIL::CaseRule *cs, bool omit_trailing_begin = false)
1297 {
1298 int number_of_stmts = cs->switches.size() + cs->actions.size();
1299
1300 if (!omit_trailing_begin && number_of_stmts >= 2)
1301 f << stringf("%s" "begin\n", indent.c_str());
1302
1303 for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it) {
1304 if (it->first.size() == 0)
1305 continue;
1306 f << stringf("%s ", indent.c_str());
1307 dump_sigspec(f, it->first);
1308 f << stringf(" = ");
1309 dump_sigspec(f, it->second);
1310 f << stringf(";\n");
1311 }
1312
1313 for (auto it = cs->switches.begin(); it != cs->switches.end(); ++it)
1314 dump_proc_switch(f, indent + " ", *it);
1315
1316 if (!omit_trailing_begin && number_of_stmts == 0)
1317 f << stringf("%s /* empty */;\n", indent.c_str());
1318
1319 if (omit_trailing_begin || number_of_stmts >= 2)
1320 f << stringf("%s" "end\n", indent.c_str());
1321 }
1322
1323 void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw)
1324 {
1325 if (sw->signal.size() == 0) {
1326 f << stringf("%s" "begin\n", indent.c_str());
1327 for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) {
1328 if ((*it)->compare.size() == 0)
1329 dump_case_body(f, indent + " ", *it);
1330 }
1331 f << stringf("%s" "end\n", indent.c_str());
1332 return;
1333 }
1334
1335 f << stringf("%s" "casez (", indent.c_str());
1336 dump_sigspec(f, sw->signal);
1337 f << stringf(")\n");
1338
1339 bool got_default = false;
1340 for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) {
1341 if ((*it)->compare.size() == 0) {
1342 if (got_default)
1343 continue;
1344 f << stringf("%s default", indent.c_str());
1345 got_default = true;
1346 } else {
1347 f << stringf("%s ", indent.c_str());
1348 for (size_t i = 0; i < (*it)->compare.size(); i++) {
1349 if (i > 0)
1350 f << stringf(", ");
1351 dump_sigspec(f, (*it)->compare[i]);
1352 }
1353 }
1354 f << stringf(":\n");
1355 dump_case_body(f, indent + " ", *it);
1356 }
1357
1358 f << stringf("%s" "endcase\n", indent.c_str());
1359 }
1360
1361 void case_body_find_regs(RTLIL::CaseRule *cs)
1362 {
1363 for (auto it = cs->switches.begin(); it != cs->switches.end(); ++it)
1364 for (auto it2 = (*it)->cases.begin(); it2 != (*it)->cases.end(); it2++)
1365 case_body_find_regs(*it2);
1366
1367 for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it) {
1368 for (auto &c : it->first.chunks())
1369 if (c.wire != NULL)
1370 reg_wires.insert(c.wire->name);
1371 }
1372 }
1373
1374 void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, bool find_regs = false)
1375 {
1376 if (find_regs) {
1377 case_body_find_regs(&proc->root_case);
1378 for (auto it = proc->syncs.begin(); it != proc->syncs.end(); ++it)
1379 for (auto it2 = (*it)->actions.begin(); it2 != (*it)->actions.end(); it2++) {
1380 for (auto &c : it2->first.chunks())
1381 if (c.wire != NULL)
1382 reg_wires.insert(c.wire->name);
1383 }
1384 return;
1385 }
1386
1387 f << stringf("%s" "always @* begin\n", indent.c_str());
1388 dump_case_body(f, indent, &proc->root_case, true);
1389
1390 std::string backup_indent = indent;
1391
1392 for (size_t i = 0; i < proc->syncs.size(); i++)
1393 {
1394 RTLIL::SyncRule *sync = proc->syncs[i];
1395 indent = backup_indent;
1396
1397 if (sync->type == RTLIL::STa) {
1398 f << stringf("%s" "always @* begin\n", indent.c_str());
1399 } else if (sync->type == RTLIL::STi) {
1400 f << stringf("%s" "initial begin\n", indent.c_str());
1401 } else {
1402 f << stringf("%s" "always @(", indent.c_str());
1403 if (sync->type == RTLIL::STp || sync->type == RTLIL::ST1)
1404 f << stringf("posedge ");
1405 if (sync->type == RTLIL::STn || sync->type == RTLIL::ST0)
1406 f << stringf("negedge ");
1407 dump_sigspec(f, sync->signal);
1408 f << stringf(") begin\n");
1409 }
1410 std::string ends = indent + "end\n";
1411 indent += " ";
1412
1413 if (sync->type == RTLIL::ST0 || sync->type == RTLIL::ST1) {
1414 f << stringf("%s" "if (%s", indent.c_str(), sync->type == RTLIL::ST0 ? "!" : "");
1415 dump_sigspec(f, sync->signal);
1416 f << stringf(") begin\n");
1417 ends = indent + "end\n" + ends;
1418 indent += " ";
1419 }
1420
1421 if (sync->type == RTLIL::STp || sync->type == RTLIL::STn) {
1422 for (size_t j = 0; j < proc->syncs.size(); j++) {
1423 RTLIL::SyncRule *sync2 = proc->syncs[j];
1424 if (sync2->type == RTLIL::ST0 || sync2->type == RTLIL::ST1) {
1425 f << stringf("%s" "if (%s", indent.c_str(), sync2->type == RTLIL::ST1 ? "!" : "");
1426 dump_sigspec(f, sync2->signal);
1427 f << stringf(") begin\n");
1428 ends = indent + "end\n" + ends;
1429 indent += " ";
1430 }
1431 }
1432 }
1433
1434 for (auto it = sync->actions.begin(); it != sync->actions.end(); ++it) {
1435 if (it->first.size() == 0)
1436 continue;
1437 f << stringf("%s ", indent.c_str());
1438 dump_sigspec(f, it->first);
1439 f << stringf(" <= ");
1440 dump_sigspec(f, it->second);
1441 f << stringf(";\n");
1442 }
1443
1444 f << stringf("%s", ends.c_str());
1445 }
1446 }
1447
1448 void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
1449 {
1450 reg_wires.clear();
1451 reset_auto_counter(module);
1452 active_module = module;
1453 active_sigmap.set(module);
1454 active_initdata.clear();
1455
1456 for (auto wire : module->wires())
1457 if (wire->attributes.count("\\init")) {
1458 SigSpec sig = active_sigmap(wire);
1459 Const val = wire->attributes.at("\\init");
1460 for (int i = 0; i < GetSize(sig) && i < GetSize(val); i++)
1461 active_initdata[sig[i]] = val.bits.at(i);
1462 }
1463
1464 if (!module->processes.empty())
1465 log_warning("Module %s contains unmapped RTLIL processes. RTLIL processes\n"
1466 "can't always be mapped directly to Verilog always blocks. Unintended\n"
1467 "changes in simulation behavior are possible! Use \"proc\" to convert\n"
1468 "processes to logic networks and registers.\n", log_id(module));
1469
1470 f << stringf("\n");
1471 for (auto it = module->processes.begin(); it != module->processes.end(); ++it)
1472 dump_process(f, indent + " ", it->second, true);
1473
1474 if (!noexpr)
1475 {
1476 std::set<std::pair<RTLIL::Wire*,int>> reg_bits;
1477 for (auto &it : module->cells_)
1478 {
1479 RTLIL::Cell *cell = it.second;
1480 if (!reg_ct.count(cell->type) || !cell->hasPort("\\Q"))
1481 continue;
1482
1483 RTLIL::SigSpec sig = cell->getPort("\\Q");
1484
1485 if (sig.is_chunk()) {
1486 RTLIL::SigChunk chunk = sig.as_chunk();
1487 if (chunk.wire != NULL)
1488 for (int i = 0; i < chunk.width; i++)
1489 reg_bits.insert(std::pair<RTLIL::Wire*,int>(chunk.wire, chunk.offset+i));
1490 }
1491 }
1492 for (auto &it : module->wires_)
1493 {
1494 RTLIL::Wire *wire = it.second;
1495 for (int i = 0; i < wire->width; i++)
1496 if (reg_bits.count(std::pair<RTLIL::Wire*,int>(wire, i)) == 0)
1497 goto this_wire_aint_reg;
1498 if (wire->width)
1499 reg_wires.insert(wire->name);
1500 this_wire_aint_reg:;
1501 }
1502 }
1503
1504 dump_attributes(f, indent, module->attributes, '\n', true);
1505 f << stringf("%s" "module %s(", indent.c_str(), id(module->name, false).c_str());
1506 bool keep_running = true;
1507 for (int port_id = 1; keep_running; port_id++) {
1508 keep_running = false;
1509 for (auto it = module->wires_.begin(); it != module->wires_.end(); ++it) {
1510 RTLIL::Wire *wire = it->second;
1511 if (wire->port_id == port_id) {
1512 if (port_id != 1)
1513 f << stringf(", ");
1514 f << stringf("%s", id(wire->name).c_str());
1515 keep_running = true;
1516 continue;
1517 }
1518 }
1519 }
1520 f << stringf(");\n");
1521
1522 for (auto it = module->wires_.begin(); it != module->wires_.end(); ++it)
1523 dump_wire(f, indent + " ", it->second);
1524
1525 for (auto it = module->memories.begin(); it != module->memories.end(); ++it)
1526 dump_memory(f, indent + " ", it->second);
1527
1528 for (auto it = module->cells_.begin(); it != module->cells_.end(); ++it)
1529 dump_cell(f, indent + " ", it->second);
1530
1531 for (auto it = module->processes.begin(); it != module->processes.end(); ++it)
1532 dump_process(f, indent + " ", it->second);
1533
1534 for (auto it = module->connections().begin(); it != module->connections().end(); ++it)
1535 dump_conn(f, indent + " ", it->first, it->second);
1536
1537 f << stringf("%s" "endmodule\n", indent.c_str());
1538 active_module = NULL;
1539 active_sigmap.clear();
1540 active_initdata.clear();
1541 }
1542
1543 struct VerilogBackend : public Backend {
1544 VerilogBackend() : Backend("verilog", "write design to Verilog file") { }
1545 void help() YS_OVERRIDE
1546 {
1547 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
1548 log("\n");
1549 log(" write_verilog [options] [filename]\n");
1550 log("\n");
1551 log("Write the current design to a Verilog file.\n");
1552 log("\n");
1553 log(" -norename\n");
1554 log(" without this option all internal object names (the ones with a dollar\n");
1555 log(" instead of a backslash prefix) are changed to short names in the\n");
1556 log(" format '_<number>_'.\n");
1557 log("\n");
1558 log(" -renameprefix <prefix>\n");
1559 log(" insert this prefix in front of auto-generated instance names\n");
1560 log("\n");
1561 log(" -noattr\n");
1562 log(" with this option no attributes are included in the output\n");
1563 log("\n");
1564 log(" -attr2comment\n");
1565 log(" with this option attributes are included as comments in the output\n");
1566 log("\n");
1567 log(" -noexpr\n");
1568 log(" without this option all internal cells are converted to Verilog\n");
1569 log(" expressions.\n");
1570 log("\n");
1571 log(" -nodec\n");
1572 log(" 32-bit constant values are by default dumped as decimal numbers,\n");
1573 log(" not bit pattern. This option deactivates this feature and instead\n");
1574 log(" will write out all constants in binary.\n");
1575 log("\n");
1576 log(" -decimal\n");
1577 log(" dump 32-bit constants in decimal and without size and radix\n");
1578 log("\n");
1579 log(" -nohex\n");
1580 log(" constant values that are compatible with hex output are usually\n");
1581 log(" dumped as hex values. This option deactivates this feature and\n");
1582 log(" instead will write out all constants in binary.\n");
1583 log("\n");
1584 log(" -nostr\n");
1585 log(" Parameters and attributes that are specified as strings in the\n");
1586 log(" original input will be output as strings by this back-end. This\n");
1587 log(" deactivates this feature and instead will write string constants\n");
1588 log(" as binary numbers.\n");
1589 log("\n");
1590 log(" -defparam\n");
1591 log(" Use 'defparam' statements instead of the Verilog-2001 syntax for\n");
1592 log(" cell parameters.\n");
1593 log("\n");
1594 log(" -blackboxes\n");
1595 log(" usually modules with the 'blackbox' attribute are ignored. with\n");
1596 log(" this option set only the modules with the 'blackbox' attribute\n");
1597 log(" are written to the output file.\n");
1598 log("\n");
1599 log(" -selected\n");
1600 log(" only write selected modules. modules must be selected entirely or\n");
1601 log(" not at all.\n");
1602 log("\n");
1603 log(" -v\n");
1604 log(" verbose output (print new names of all renamed wires and cells)\n");
1605 log("\n");
1606 log("Note that RTLIL processes can't always be mapped directly to Verilog\n");
1607 log("always blocks. This frontend should only be used to export an RTLIL\n");
1608 log("netlist, i.e. after the \"proc\" pass has been used to convert all\n");
1609 log("processes to logic networks and registers. A warning is generated when\n");
1610 log("this command is called on a design with RTLIL processes.\n");
1611 log("\n");
1612 }
1613 void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
1614 {
1615 log_header(design, "Executing Verilog backend.\n");
1616
1617 verbose = false;
1618 norename = false;
1619 noattr = false;
1620 attr2comment = false;
1621 noexpr = false;
1622 nodec = false;
1623 nohex = false;
1624 nostr = false;
1625 defparam = false;
1626 decimal = false;
1627 auto_prefix = "";
1628
1629 bool blackboxes = false;
1630 bool selected = false;
1631
1632 reg_ct.clear();
1633
1634 reg_ct.insert("$dff");
1635 reg_ct.insert("$adff");
1636 reg_ct.insert("$dffe");
1637 reg_ct.insert("$dlatch");
1638
1639 reg_ct.insert("$_DFF_N_");
1640 reg_ct.insert("$_DFF_P_");
1641
1642 reg_ct.insert("$_DFF_NN0_");
1643 reg_ct.insert("$_DFF_NN1_");
1644 reg_ct.insert("$_DFF_NP0_");
1645 reg_ct.insert("$_DFF_NP1_");
1646 reg_ct.insert("$_DFF_PN0_");
1647 reg_ct.insert("$_DFF_PN1_");
1648 reg_ct.insert("$_DFF_PP0_");
1649 reg_ct.insert("$_DFF_PP1_");
1650
1651 reg_ct.insert("$_DFFSR_NNN_");
1652 reg_ct.insert("$_DFFSR_NNP_");
1653 reg_ct.insert("$_DFFSR_NPN_");
1654 reg_ct.insert("$_DFFSR_NPP_");
1655 reg_ct.insert("$_DFFSR_PNN_");
1656 reg_ct.insert("$_DFFSR_PNP_");
1657 reg_ct.insert("$_DFFSR_PPN_");
1658 reg_ct.insert("$_DFFSR_PPP_");
1659
1660 size_t argidx;
1661 for (argidx = 1; argidx < args.size(); argidx++) {
1662 std::string arg = args[argidx];
1663 if (arg == "-norename") {
1664 norename = true;
1665 continue;
1666 }
1667 if (arg == "-renameprefix" && argidx+1 < args.size()) {
1668 auto_prefix = args[++argidx];
1669 continue;
1670 }
1671 if (arg == "-noattr") {
1672 noattr = true;
1673 continue;
1674 }
1675 if (arg == "-attr2comment") {
1676 attr2comment = true;
1677 continue;
1678 }
1679 if (arg == "-noexpr") {
1680 noexpr = true;
1681 continue;
1682 }
1683 if (arg == "-nodec") {
1684 nodec = true;
1685 continue;
1686 }
1687 if (arg == "-nohex") {
1688 nohex = true;
1689 continue;
1690 }
1691 if (arg == "-nostr") {
1692 nostr = true;
1693 continue;
1694 }
1695 if (arg == "-defparam") {
1696 defparam = true;
1697 continue;
1698 }
1699 if (arg == "-decimal") {
1700 decimal = true;
1701 continue;
1702 }
1703 if (arg == "-blackboxes") {
1704 blackboxes = true;
1705 continue;
1706 }
1707 if (arg == "-selected") {
1708 selected = true;
1709 continue;
1710 }
1711 if (arg == "-v") {
1712 verbose = true;
1713 continue;
1714 }
1715 break;
1716 }
1717 extra_args(f, filename, args, argidx);
1718
1719 design->sort();
1720
1721 *f << stringf("/* Generated by %s */\n", yosys_version_str);
1722 for (auto it = design->modules_.begin(); it != design->modules_.end(); ++it) {
1723 if (it->second->get_bool_attribute("\\blackbox") != blackboxes)
1724 continue;
1725 if (selected && !design->selected_whole_module(it->first)) {
1726 if (design->selected_module(it->first))
1727 log_cmd_error("Can't handle partially selected module %s!\n", RTLIL::id2cstr(it->first));
1728 continue;
1729 }
1730 log("Dumping module `%s'.\n", it->first.c_str());
1731 dump_module(*f, "", it->second);
1732 }
1733
1734 reg_ct.clear();
1735 }
1736 } VerilogBackend;
1737
1738 PRIVATE_NAMESPACE_END