kernel/ff: Refactor FfData to enable FFs with async load.
[yosys.git] / backends / verilog / verilog_backend.cc
1 /*
2 * yosys -- Yosys Open SYnthesis Suite
3 *
4 * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
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 "kernel/ff.h"
29 #include "kernel/mem.h"
30 #include <string>
31 #include <sstream>
32 #include <set>
33 #include <map>
34
35 USING_YOSYS_NAMESPACE
36 PRIVATE_NAMESPACE_BEGIN
37
38 bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, extmem, defparam, decimal, siminit, systemverilog, simple_lhs;
39 int auto_name_counter, auto_name_offset, auto_name_digits, extmem_counter;
40 std::map<RTLIL::IdString, int> auto_name_map;
41 std::set<RTLIL::IdString> reg_wires;
42 std::string auto_prefix, extmem_prefix;
43
44 RTLIL::Module *active_module;
45 dict<RTLIL::SigBit, RTLIL::State> active_initdata;
46 SigMap active_sigmap;
47
48 void reset_auto_counter_id(RTLIL::IdString id, bool may_rename)
49 {
50 const char *str = id.c_str();
51
52 if (*str == '$' && may_rename && !norename)
53 auto_name_map[id] = auto_name_counter++;
54
55 if (str[0] != '\\' || str[1] != '_' || str[2] == 0)
56 return;
57
58 for (int i = 2; str[i] != 0; i++) {
59 if (str[i] == '_' && str[i+1] == 0)
60 continue;
61 if (str[i] < '0' || str[i] > '9')
62 return;
63 }
64
65 int num = atoi(str+2);
66 if (num >= auto_name_offset)
67 auto_name_offset = num + 1;
68 }
69
70 void reset_auto_counter(RTLIL::Module *module)
71 {
72 auto_name_map.clear();
73 auto_name_counter = 0;
74 auto_name_offset = 0;
75
76 reset_auto_counter_id(module->name, false);
77
78 for (auto w : module->wires())
79 reset_auto_counter_id(w->name, true);
80
81 for (auto cell : module->cells()) {
82 reset_auto_counter_id(cell->name, true);
83 reset_auto_counter_id(cell->type, false);
84 }
85
86 for (auto it = module->processes.begin(); it != module->processes.end(); ++it)
87 reset_auto_counter_id(it->second->name, false);
88
89 auto_name_digits = 1;
90 for (size_t i = 10; i < auto_name_offset + auto_name_map.size(); i = i*10)
91 auto_name_digits++;
92
93 if (verbose)
94 for (auto it = auto_name_map.begin(); it != auto_name_map.end(); ++it)
95 log(" renaming `%s' to `%s_%0*d_'.\n", it->first.c_str(), auto_prefix.c_str(), auto_name_digits, auto_name_offset + it->second);
96 }
97
98 std::string next_auto_id()
99 {
100 return stringf("%s_%0*d_", auto_prefix.c_str(), auto_name_digits, auto_name_offset + auto_name_counter++);
101 }
102
103 std::string id(RTLIL::IdString internal_id, bool may_rename = true)
104 {
105 const char *str = internal_id.c_str();
106 bool do_escape = false;
107
108 if (may_rename && auto_name_map.count(internal_id) != 0)
109 return stringf("%s_%0*d_", auto_prefix.c_str(), auto_name_digits, auto_name_offset + auto_name_map[internal_id]);
110
111 if (*str == '\\')
112 str++;
113
114 if ('0' <= *str && *str <= '9')
115 do_escape = true;
116
117 for (int i = 0; str[i]; i++)
118 {
119 if ('0' <= str[i] && str[i] <= '9')
120 continue;
121 if ('a' <= str[i] && str[i] <= 'z')
122 continue;
123 if ('A' <= str[i] && str[i] <= 'Z')
124 continue;
125 if (str[i] == '_')
126 continue;
127 do_escape = true;
128 break;
129 }
130
131 const pool<string> keywords = {
132 // IEEE 1800-2017 Annex B
133 "accept_on", "alias", "always", "always_comb", "always_ff", "always_latch", "and", "assert", "assign", "assume", "automatic", "before",
134 "begin", "bind", "bins", "binsof", "bit", "break", "buf", "bufif0", "bufif1", "byte", "case", "casex", "casez", "cell", "chandle",
135 "checker", "class", "clocking", "cmos", "config", "const", "constraint", "context", "continue", "cover", "covergroup", "coverpoint",
136 "cross", "deassign", "default", "defparam", "design", "disable", "dist", "do", "edge", "else", "end", "endcase", "endchecker",
137 "endclass", "endclocking", "endconfig", "endfunction", "endgenerate", "endgroup", "endinterface", "endmodule", "endpackage",
138 "endprimitive", "endprogram", "endproperty", "endsequence", "endspecify", "endtable", "endtask", "enum", "event", "eventually",
139 "expect", "export", "extends", "extern", "final", "first_match", "for", "force", "foreach", "forever", "fork", "forkjoin", "function",
140 "generate", "genvar", "global", "highz0", "highz1", "if", "iff", "ifnone", "ignore_bins", "illegal_bins", "implements", "implies",
141 "import", "incdir", "include", "initial", "inout", "input", "inside", "instance", "int", "integer", "interconnect", "interface",
142 "intersect", "join", "join_any", "join_none", "large", "let", "liblist", "library", "local", "localparam", "logic", "longint",
143 "macromodule", "matches", "medium", "modport", "module", "nand", "negedge", "nettype", "new", "nexttime", "nmos", "nor",
144 "noshowcancelled", "not", "notif0", "notif1", "null", "or", "output", "package", "packed", "parameter", "pmos", "posedge", "primitive",
145 "priority", "program", "property", "protected", "pull0", "pull1", "pulldown", "pullup", "pulsestyle_ondetect", "pulsestyle_onevent",
146 "pure", "rand", "randc", "randcase", "randsequence", "rcmos", "real", "realtime", "ref", "reg", "reject_on", "release", "repeat",
147 "restrict", "return", "rnmos", "rpmos", "rtran", "rtranif0", "rtranif1", "s_always", "s_eventually", "s_nexttime", "s_until",
148 "s_until_with", "scalared", "sequence", "shortint", "shortreal", "showcancelled", "signed", "small", "soft", "solve", "specify",
149 "specparam", "static", "string", "strong", "strong0", "strong1", "struct", "super", "supply0", "supply1", "sync_accept_on",
150 "sync_reject_on", "table", "tagged", "task", "this", "throughout", "time", "timeprecision", "timeunit", "tran", "tranif0", "tranif1",
151 "tri", "tri0", "tri1", "triand", "trior", "trireg", "type", "typedef", "union", "unique", "unique0", "unsigned", "until", "until_with",
152 "untyped", "use", "uwire", "var", "vectored", "virtual", "void", "wait", "wait_order", "wand", "weak", "weak0", "weak1", "while",
153 "wildcard", "wire", "with", "within", "wor", "xnor", "xor",
154 };
155 if (keywords.count(str))
156 do_escape = true;
157
158 if (do_escape)
159 return "\\" + std::string(str) + " ";
160 return std::string(str);
161 }
162
163 bool is_reg_wire(RTLIL::SigSpec sig, std::string &reg_name)
164 {
165 if (!sig.is_chunk() || sig.as_chunk().wire == NULL)
166 return false;
167
168 RTLIL::SigChunk chunk = sig.as_chunk();
169
170 if (reg_wires.count(chunk.wire->name) == 0)
171 return false;
172
173 reg_name = id(chunk.wire->name);
174 if (sig.size() != chunk.wire->width) {
175 if (sig.size() == 1)
176 reg_name += stringf("[%d]", chunk.wire->start_offset + chunk.offset);
177 else if (chunk.wire->upto)
178 reg_name += stringf("[%d:%d]", (chunk.wire->width - (chunk.offset + chunk.width - 1) - 1) + chunk.wire->start_offset,
179 (chunk.wire->width - chunk.offset - 1) + chunk.wire->start_offset);
180 else
181 reg_name += stringf("[%d:%d]", chunk.wire->start_offset + chunk.offset + chunk.width - 1,
182 chunk.wire->start_offset + chunk.offset);
183 }
184
185 return true;
186 }
187
188 void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int offset = 0, bool no_decimal = false, bool escape_comment = false)
189 {
190 bool set_signed = (data.flags & RTLIL::CONST_FLAG_SIGNED) != 0;
191 if (width < 0)
192 width = data.bits.size() - offset;
193 if (width == 0) {
194 // See IEEE 1364-2005 Clause 5.1.14.
195 f << "{0{1'b0}}";
196 return;
197 }
198 if (nostr)
199 goto dump_hex;
200 if ((data.flags & RTLIL::CONST_FLAG_STRING) == 0 || width != (int)data.bits.size()) {
201 if (width == 32 && !no_decimal && !nodec) {
202 int32_t val = 0;
203 for (int i = offset+width-1; i >= offset; i--) {
204 log_assert(i < (int)data.bits.size());
205 if (data.bits[i] != State::S0 && data.bits[i] != State::S1)
206 goto dump_hex;
207 if (data.bits[i] == State::S1)
208 val |= 1 << (i - offset);
209 }
210 if (decimal)
211 f << stringf("%d", val);
212 else if (set_signed && val < 0)
213 f << stringf("-32'sd%u", -val);
214 else
215 f << stringf("32'%sd%u", set_signed ? "s" : "", val);
216 } else {
217 dump_hex:
218 if (nohex)
219 goto dump_bin;
220 vector<char> bin_digits, hex_digits;
221 for (int i = offset; i < offset+width; i++) {
222 log_assert(i < (int)data.bits.size());
223 switch (data.bits[i]) {
224 case State::S0: bin_digits.push_back('0'); break;
225 case State::S1: bin_digits.push_back('1'); break;
226 case RTLIL::Sx: bin_digits.push_back('x'); break;
227 case RTLIL::Sz: bin_digits.push_back('z'); break;
228 case RTLIL::Sa: bin_digits.push_back('?'); break;
229 case RTLIL::Sm: log_error("Found marker state in final netlist.");
230 }
231 }
232 if (GetSize(bin_digits) == 0)
233 goto dump_bin;
234 while (GetSize(bin_digits) % 4 != 0)
235 if (bin_digits.back() == '1')
236 bin_digits.push_back('0');
237 else
238 bin_digits.push_back(bin_digits.back());
239 for (int i = 0; i < GetSize(bin_digits); i += 4)
240 {
241 char bit_3 = bin_digits[i+3];
242 char bit_2 = bin_digits[i+2];
243 char bit_1 = bin_digits[i+1];
244 char bit_0 = bin_digits[i+0];
245 if (bit_3 == 'x' || bit_2 == 'x' || bit_1 == 'x' || bit_0 == 'x') {
246 if (bit_3 != 'x' || bit_2 != 'x' || bit_1 != 'x' || bit_0 != 'x')
247 goto dump_bin;
248 hex_digits.push_back('x');
249 continue;
250 }
251 if (bit_3 == 'z' || bit_2 == 'z' || bit_1 == 'z' || bit_0 == 'z') {
252 if (bit_3 != 'z' || bit_2 != 'z' || bit_1 != 'z' || bit_0 != 'z')
253 goto dump_bin;
254 hex_digits.push_back('z');
255 continue;
256 }
257 if (bit_3 == '?' || bit_2 == '?' || bit_1 == '?' || bit_0 == '?') {
258 if (bit_3 != '?' || bit_2 != '?' || bit_1 != '?' || bit_0 != '?')
259 goto dump_bin;
260 hex_digits.push_back('?');
261 continue;
262 }
263 int val = 8*(bit_3 - '0') + 4*(bit_2 - '0') + 2*(bit_1 - '0') + (bit_0 - '0');
264 hex_digits.push_back(val < 10 ? '0' + val : 'a' + val - 10);
265 }
266 f << stringf("%d'%sh", width, set_signed ? "s" : "");
267 for (int i = GetSize(hex_digits)-1; i >= 0; i--)
268 f << hex_digits[i];
269 }
270 if (0) {
271 dump_bin:
272 f << stringf("%d'%sb", width, set_signed ? "s" : "");
273 if (width == 0)
274 f << stringf("0");
275 for (int i = offset+width-1; i >= offset; i--) {
276 log_assert(i < (int)data.bits.size());
277 switch (data.bits[i]) {
278 case State::S0: f << stringf("0"); break;
279 case State::S1: f << stringf("1"); break;
280 case RTLIL::Sx: f << stringf("x"); break;
281 case RTLIL::Sz: f << stringf("z"); break;
282 case RTLIL::Sa: f << stringf("?"); break;
283 case RTLIL::Sm: log_error("Found marker state in final netlist.");
284 }
285 }
286 }
287 } else {
288 if ((data.flags & RTLIL::CONST_FLAG_REAL) == 0)
289 f << stringf("\"");
290 std::string str = data.decode_string();
291 for (size_t i = 0; i < str.size(); i++) {
292 if (str[i] == '\n')
293 f << stringf("\\n");
294 else if (str[i] == '\t')
295 f << stringf("\\t");
296 else if (str[i] < 32)
297 f << stringf("\\%03o", str[i]);
298 else if (str[i] == '"')
299 f << stringf("\\\"");
300 else if (str[i] == '\\')
301 f << stringf("\\\\");
302 else if (str[i] == '/' && escape_comment && i > 0 && str[i-1] == '*')
303 f << stringf("\\/");
304 else
305 f << str[i];
306 }
307 if ((data.flags & RTLIL::CONST_FLAG_REAL) == 0)
308 f << stringf("\"");
309 }
310 }
311
312 void dump_reg_init(std::ostream &f, SigSpec sig)
313 {
314 Const initval;
315 bool gotinit = false;
316
317 for (auto bit : active_sigmap(sig)) {
318 if (active_initdata.count(bit)) {
319 initval.bits.push_back(active_initdata.at(bit));
320 gotinit = true;
321 } else {
322 initval.bits.push_back(State::Sx);
323 }
324 }
325
326 if (gotinit) {
327 f << " = ";
328 dump_const(f, initval);
329 }
330 }
331
332 void dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool no_decimal = false)
333 {
334 if (chunk.wire == NULL) {
335 dump_const(f, chunk.data, chunk.width, chunk.offset, no_decimal);
336 } else {
337 if (chunk.width == chunk.wire->width && chunk.offset == 0) {
338 f << stringf("%s", id(chunk.wire->name).c_str());
339 } else if (chunk.width == 1) {
340 if (chunk.wire->upto)
341 f << stringf("%s[%d]", id(chunk.wire->name).c_str(), (chunk.wire->width - chunk.offset - 1) + chunk.wire->start_offset);
342 else
343 f << stringf("%s[%d]", id(chunk.wire->name).c_str(), chunk.offset + chunk.wire->start_offset);
344 } else {
345 if (chunk.wire->upto)
346 f << stringf("%s[%d:%d]", id(chunk.wire->name).c_str(),
347 (chunk.wire->width - (chunk.offset + chunk.width - 1) - 1) + chunk.wire->start_offset,
348 (chunk.wire->width - chunk.offset - 1) + chunk.wire->start_offset);
349 else
350 f << stringf("%s[%d:%d]", id(chunk.wire->name).c_str(),
351 (chunk.offset + chunk.width - 1) + chunk.wire->start_offset,
352 chunk.offset + chunk.wire->start_offset);
353 }
354 }
355 }
356
357 void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig)
358 {
359 if (GetSize(sig) == 0) {
360 f << "\"\"";
361 return;
362 }
363 if (sig.is_chunk()) {
364 dump_sigchunk(f, sig.as_chunk());
365 } else {
366 f << stringf("{ ");
367 for (auto it = sig.chunks().rbegin(); it != sig.chunks().rend(); ++it) {
368 if (it != sig.chunks().rbegin())
369 f << stringf(", ");
370 dump_sigchunk(f, *it, true);
371 }
372 f << stringf(" }");
373 }
374 }
375
376 void dump_attributes(std::ostream &f, std::string indent, dict<RTLIL::IdString, RTLIL::Const> &attributes, char term = '\n', bool modattr = false, bool regattr = false, bool as_comment = false)
377 {
378 if (noattr)
379 return;
380 if (attr2comment)
381 as_comment = true;
382 for (auto it = attributes.begin(); it != attributes.end(); ++it) {
383 if (it->first == ID::init && regattr) continue;
384 f << stringf("%s" "%s %s", indent.c_str(), as_comment ? "/*" : "(*", id(it->first).c_str());
385 f << stringf(" = ");
386 if (modattr && (it->second == State::S0 || it->second == Const(0)))
387 f << stringf(" 0 ");
388 else if (modattr && (it->second == State::S1 || it->second == Const(1)))
389 f << stringf(" 1 ");
390 else
391 dump_const(f, it->second, -1, 0, false, as_comment);
392 f << stringf(" %s%c", as_comment ? "*/" : "*)", term);
393 }
394 }
395
396 void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire)
397 {
398 dump_attributes(f, indent, wire->attributes, '\n', /*modattr=*/false, /*regattr=*/reg_wires.count(wire->name));
399 #if 0
400 if (wire->port_input && !wire->port_output)
401 f << stringf("%s" "input %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : "");
402 else if (!wire->port_input && wire->port_output)
403 f << stringf("%s" "output %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : "");
404 else if (wire->port_input && wire->port_output)
405 f << stringf("%s" "inout %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : "");
406 else
407 f << stringf("%s" "%s ", indent.c_str(), reg_wires.count(wire->name) ? "reg" : "wire");
408 if (wire->width != 1)
409 f << stringf("[%d:%d] ", wire->width - 1 + wire->start_offset, wire->start_offset);
410 f << stringf("%s;\n", id(wire->name).c_str());
411 #else
412 // do not use Verilog-2k "output reg" syntax in Verilog export
413 std::string range = "";
414 if (wire->width != 1) {
415 if (wire->upto)
416 range = stringf(" [%d:%d]", wire->start_offset, wire->width - 1 + wire->start_offset);
417 else
418 range = stringf(" [%d:%d]", wire->width - 1 + wire->start_offset, wire->start_offset);
419 }
420 if (wire->port_input && !wire->port_output)
421 f << stringf("%s" "input%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str());
422 if (!wire->port_input && wire->port_output)
423 f << stringf("%s" "output%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str());
424 if (wire->port_input && wire->port_output)
425 f << stringf("%s" "inout%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str());
426 if (reg_wires.count(wire->name)) {
427 f << stringf("%s" "reg%s %s", indent.c_str(), range.c_str(), id(wire->name).c_str());
428 if (wire->attributes.count(ID::init)) {
429 f << stringf(" = ");
430 dump_const(f, wire->attributes.at(ID::init));
431 }
432 f << stringf(";\n");
433 } else if (!wire->port_input && !wire->port_output)
434 f << stringf("%s" "wire%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str());
435 #endif
436 }
437
438 void dump_memory(std::ostream &f, std::string indent, Mem &mem)
439 {
440 std::string mem_id = id(mem.memid);
441
442 dump_attributes(f, indent, mem.attributes);
443 f << stringf("%s" "reg [%d:0] %s [%d:%d];\n", indent.c_str(), mem.width-1, mem_id.c_str(), mem.size+mem.start_offset-1, mem.start_offset);
444
445 // for memory block make something like:
446 // reg [7:0] memid [3:0];
447 // initial begin
448 // memid[0] = ...
449 // end
450 if (!mem.inits.empty())
451 {
452 if (extmem)
453 {
454 std::string extmem_filename = stringf("%s-%d.mem", extmem_prefix.c_str(), extmem_counter++);
455
456 std::string extmem_filename_esc;
457 for (auto c : extmem_filename)
458 {
459 if (c == '\n')
460 extmem_filename_esc += "\\n";
461 else if (c == '\t')
462 extmem_filename_esc += "\\t";
463 else if (c < 32)
464 extmem_filename_esc += stringf("\\%03o", c);
465 else if (c == '"')
466 extmem_filename_esc += "\\\"";
467 else if (c == '\\')
468 extmem_filename_esc += "\\\\";
469 else
470 extmem_filename_esc += c;
471 }
472 f << stringf("%s" "initial $readmemb(\"%s\", %s);\n", indent.c_str(), extmem_filename_esc.c_str(), mem_id.c_str());
473
474 std::ofstream extmem_f(extmem_filename, std::ofstream::trunc);
475 if (extmem_f.fail())
476 log_error("Can't open file `%s' for writing: %s\n", extmem_filename.c_str(), strerror(errno));
477 else
478 {
479 Const data = mem.get_init_data();
480 for (int i=0; i<mem.size; i++)
481 {
482 RTLIL::Const element = data.extract(i*mem.width, mem.width);
483 for (int j=0; j<element.size(); j++)
484 {
485 switch (element[element.size()-j-1])
486 {
487 case State::S0: extmem_f << '0'; break;
488 case State::S1: extmem_f << '1'; break;
489 case State::Sx: extmem_f << 'x'; break;
490 case State::Sz: extmem_f << 'z'; break;
491 case State::Sa: extmem_f << '_'; break;
492 case State::Sm: log_error("Found marker state in final netlist.");
493 }
494 }
495 extmem_f << '\n';
496 }
497 }
498 }
499 else
500 {
501 f << stringf("%s" "initial begin\n", indent.c_str());
502 for (auto &init : mem.inits) {
503 int words = GetSize(init.data) / mem.width;
504 int start = init.addr.as_int();
505 for (int i=0; i<words; i++)
506 {
507 for (int j = 0; j < mem.width; j++)
508 {
509 if (init.en[j] != State::S1)
510 continue;
511
512 int start_j = j, width = 1;
513
514 while (j+1 < mem.width && init.en[j+1] == State::S1)
515 j++, width++;
516
517 if (width == mem.width) {
518 f << stringf("%s" " %s[%d] = ", indent.c_str(), mem_id.c_str(), i + start);
519 } else {
520 f << stringf("%s" " %s[%d][%d:%d] = ", indent.c_str(), mem_id.c_str(), i + start, j, start_j);
521 }
522 dump_const(f, init.data.extract(i*mem.width+start_j, width));
523 f << stringf(";\n");
524 }
525 }
526 }
527 f << stringf("%s" "end\n", indent.c_str());
528 }
529 }
530
531 // create a map : "edge clk" -> expressions within that clock domain
532 dict<std::string, std::vector<std::string>> clk_to_lof_body;
533 dict<std::string, std::string> clk_to_arst_cond;
534 dict<std::string, std::vector<std::string>> clk_to_arst_body;
535 clk_to_lof_body[""] = std::vector<std::string>();
536 std::string clk_domain_str;
537 // create a list of reg declarations
538 std::vector<std::string> lof_reg_declarations;
539
540 // read ports
541 for (auto &port : mem.rd_ports)
542 {
543 if (port.clk_enable)
544 {
545 {
546 std::ostringstream os;
547 dump_sigspec(os, port.clk);
548 clk_domain_str = stringf("%sedge %s", port.clk_polarity ? "pos" : "neg", os.str().c_str());
549 if (port.arst != State::S0) {
550 std::ostringstream os2;
551 dump_sigspec(os2, port.arst);
552 clk_domain_str += stringf(", posedge %s", os2.str().c_str());
553 clk_to_arst_cond[clk_domain_str] = os2.str();
554 }
555 }
556
557 // Decide how to represent the transparency; same idea as Mem::extract_rdff.
558 bool trans_use_addr = true;
559 for (auto bit : port.transparency_mask)
560 if (!bit)
561 trans_use_addr = false;
562
563 if (GetSize(mem.wr_ports) == 0)
564 trans_use_addr = false;
565
566 if (port.en != State::S1 || port.srst != State::S0 || port.arst != State::S0 || !port.init_value.is_fully_undef())
567 trans_use_addr = false;
568
569 if (!trans_use_addr)
570 {
571 // for clocked read ports make something like:
572 // reg [..] temp_id;
573 // always @(posedge clk)
574 // if (rd_en) temp_id <= array_reg[r_addr];
575 // assign r_data = temp_id;
576 std::string temp_id = next_auto_id();
577 lof_reg_declarations.push_back( stringf("reg [%d:0] %s;\n", port.data.size() - 1, temp_id.c_str()) );
578
579 bool has_indent = false;
580
581 if (port.arst != State::S0) {
582 std::ostringstream os;
583 os << stringf("%s <= ", temp_id.c_str());
584 dump_sigspec(os, port.arst_value);
585 os << ";\n";
586 clk_to_arst_body[clk_domain_str].push_back(os.str());
587 }
588
589 if (port.srst != State::S0 && !port.ce_over_srst) {
590 std::ostringstream os;
591 os << stringf("if (");
592 dump_sigspec(os, port.srst);
593 os << stringf(")\n");
594 clk_to_lof_body[clk_domain_str].push_back(os.str());
595 std::ostringstream os2;
596 os2 << stringf("%s" "%s <= ", indent.c_str(), temp_id.c_str());
597 dump_sigspec(os2, port.srst_value);
598 os2 << ";\n";
599 clk_to_lof_body[clk_domain_str].push_back(os2.str());
600 std::ostringstream os3;
601 if (port.en == State::S1) {
602 os3 << "else begin\n";
603 } else {
604 os3 << "else if (";
605 dump_sigspec(os3, port.en);
606 os3 << ") begin\n";
607 }
608 clk_to_lof_body[clk_domain_str].push_back(os3.str());
609 has_indent = true;
610 } else if (port.en != State::S1) {
611 std::ostringstream os;
612 os << stringf("if (");
613 dump_sigspec(os, port.en);
614 os << stringf(") begin\n");
615 clk_to_lof_body[clk_domain_str].push_back(os.str());
616 has_indent = true;
617 }
618
619 for (int sub = 0; sub < (1 << port.wide_log2); sub++)
620 {
621 SigSpec addr = port.sub_addr(sub);
622 std::ostringstream os;
623 if (has_indent)
624 os << indent;
625 os << temp_id;
626 if (port.wide_log2)
627 os << stringf("[%d:%d]", (sub + 1) * mem.width - 1, sub * mem.width);
628 os << stringf(" <= %s[", mem_id.c_str());
629 dump_sigspec(os, addr);
630 os << stringf("];\n");
631 clk_to_lof_body[clk_domain_str].push_back(os.str());
632 }
633
634 for (int i = 0; i < GetSize(mem.wr_ports); i++) {
635 auto &wport = mem.wr_ports[i];
636 if (!port.transparency_mask[i] && !port.collision_x_mask[i])
637 continue;
638 int min_wide_log2 = std::min(port.wide_log2, wport.wide_log2);
639 int max_wide_log2 = std::max(port.wide_log2, wport.wide_log2);
640 bool wide_write = wport.wide_log2 > port.wide_log2;
641 for (int sub = 0; sub < (1 << max_wide_log2); sub += (1 << min_wide_log2)) {
642 SigSpec raddr = port.addr;
643 SigSpec waddr = wport.addr;
644 if (wide_write)
645 waddr = wport.sub_addr(sub);
646 else
647 raddr = port.sub_addr(sub);
648 int pos = 0;
649 int ewidth = mem.width << min_wide_log2;
650 int wsub = wide_write ? sub : 0;
651 int rsub = wide_write ? 0 : sub;
652 while (pos < ewidth) {
653 int epos = pos;
654 while (epos < ewidth && wport.en[epos + wsub * mem.width] == wport.en[pos + wsub * mem.width])
655 epos++;
656
657 std::ostringstream os;
658 if (has_indent)
659 os << indent;
660 os << "if (";
661 dump_sigspec(os, wport.en[pos + wsub * mem.width]);
662 if (raddr != waddr) {
663 os << " && ";
664 dump_sigspec(os, raddr);
665 os << " == ";
666 dump_sigspec(os, waddr);
667 }
668 os << ")\n";
669 clk_to_lof_body[clk_domain_str].push_back(os.str());
670
671 std::ostringstream os2;
672 if (has_indent)
673 os2 << indent;
674 os2 << indent;
675 os2 << temp_id;
676 if (epos-pos != GetSize(port.data))
677 os2 << stringf("[%d:%d]", rsub * mem.width + epos-1, rsub * mem.width + pos);
678 os2 << " <= ";
679 if (port.transparency_mask[i])
680 dump_sigspec(os2, wport.data.extract(wsub * mem.width + pos, epos-pos));
681 else
682 dump_sigspec(os2, Const(State::Sx, epos - pos));
683 os2 << ";\n";
684 clk_to_lof_body[clk_domain_str].push_back(os2.str());
685
686 pos = epos;
687 }
688 }
689 }
690
691 if (port.srst != State::S0 && port.ce_over_srst)
692 {
693 std::ostringstream os;
694 if (has_indent)
695 os << indent;
696 os << stringf("if (");
697 dump_sigspec(os, port.srst);
698 os << stringf(")\n");
699 clk_to_lof_body[clk_domain_str].push_back(os.str());
700 std::ostringstream os2;
701 if (has_indent)
702 os2 << indent;
703 os2 << stringf("%s" "%s <= ", indent.c_str(), temp_id.c_str());
704 dump_sigspec(os2, port.srst_value);
705 os2 << ";\n";
706 clk_to_lof_body[clk_domain_str].push_back(os2.str());
707 }
708
709 if (has_indent)
710 clk_to_lof_body[clk_domain_str].push_back("end\n");
711
712 if (!port.init_value.is_fully_undef())
713 {
714 std::ostringstream os;
715 dump_sigspec(os, port.init_value);
716 std::string line = stringf("initial %s = %s;\n", temp_id.c_str(), os.str().c_str());
717 clk_to_lof_body[""].push_back(line);
718 }
719
720 {
721 std::ostringstream os;
722 dump_sigspec(os, port.data);
723 std::string line = stringf("assign %s = %s;\n", os.str().c_str(), temp_id.c_str());
724 clk_to_lof_body[""].push_back(line);
725 }
726 }
727 else
728 {
729 // for rd-transparent read-ports make something like:
730 // reg [..] temp_id;
731 // always @(posedge clk)
732 // temp_id <= r_addr;
733 // assign r_data = array_reg[temp_id];
734 std::string temp_id = next_auto_id();
735 lof_reg_declarations.push_back( stringf("reg [%d:0] %s;\n", port.addr.size() - 1 - port.wide_log2, temp_id.c_str()) );
736 {
737 std::ostringstream os;
738 dump_sigspec(os, port.addr.extract_end(port.wide_log2));
739 std::string line = stringf("%s <= %s;\n", temp_id.c_str(), os.str().c_str());
740 clk_to_lof_body[clk_domain_str].push_back(line);
741 }
742 for (int sub = 0; sub < (1 << port.wide_log2); sub++)
743 {
744 std::ostringstream os;
745 os << "assign ";
746 dump_sigspec(os, port.data.extract(sub * mem.width, mem.width));
747 os << stringf(" = %s[", mem_id.c_str());;
748 if (port.wide_log2) {
749 Const addr_lo;
750 for (int i = 0; i < port.wide_log2; i++)
751 addr_lo.bits.push_back(State(sub >> i & 1));
752 os << "{";
753 os << temp_id;
754 os << ", ";
755 dump_const(os, addr_lo);
756 os << "}";
757 } else {
758 os << temp_id;
759 }
760 os << "];\n";
761 clk_to_lof_body[""].push_back(os.str());
762 }
763 }
764 } else {
765 // for non-clocked read-ports make something like:
766 // assign r_data = array_reg[r_addr];
767 for (int sub = 0; sub < (1 << port.wide_log2); sub++)
768 {
769 SigSpec addr = port.sub_addr(sub);
770
771 std::ostringstream os, os2;
772 dump_sigspec(os, port.data.extract(sub * mem.width, mem.width));
773 dump_sigspec(os2, addr);
774 std::string line = stringf("assign %s = %s[%s];\n", os.str().c_str(), mem_id.c_str(), os2.str().c_str());
775 clk_to_lof_body[""].push_back(line);
776 }
777 }
778 }
779
780 // Write ports. Those are messy because we try to preserve priority, as much as we can:
781 //
782 // 1. We split all ports into several disjoint processes.
783 // 2. If a port has priority over another port, the two ports need to share
784 // a process, so that priority can be reconstructed on the other end.
785 // 3. We want each process to be as small as possible, to avoid extra
786 // priorities inferred on the other end.
787 pool<int> wr_ports_done;
788 for (int ridx = 0; ridx < GetSize(mem.wr_ports); ridx++)
789 {
790 if (wr_ports_done.count(ridx))
791 continue;
792
793 auto &root = mem.wr_ports[ridx];
794
795 // Start from a root.
796 pool<int> wr_ports_now;
797 wr_ports_now.insert(ridx);
798
799 // Transitively fill list of ports in this process by following priority edges.
800 while (true)
801 {
802 bool changed = false;
803
804 for (int i = 0; i < GetSize(mem.wr_ports); i++)
805 for (int j = 0; j < i; j++)
806 if (mem.wr_ports[i].priority_mask[j])
807 {
808 if (wr_ports_now.count(i) && !wr_ports_now.count(j)) {
809 wr_ports_now.insert(j);
810 changed = true;
811 }
812 if (!wr_ports_now.count(i) && wr_ports_now.count(j)) {
813 wr_ports_now.insert(i);
814 changed = true;
815 }
816 }
817
818 if (!changed)
819 break;
820 }
821
822 if (root.clk_enable) {
823 f << stringf("%s" "always%s @(%sedge ", indent.c_str(), systemverilog ? "_ff" : "", root.clk_polarity ? "pos" : "neg");
824 dump_sigspec(f, root.clk);
825 f << ") begin\n";
826 } else {
827 f << stringf("%s" "always%s begin\n", indent.c_str(), systemverilog ? "_latch" : " @*");
828 }
829
830 for (int pidx = 0; pidx < GetSize(mem.wr_ports); pidx++)
831 {
832 if (!wr_ports_now.count(pidx))
833 continue;
834 wr_ports_done.insert(pidx);
835
836 auto &port = mem.wr_ports[pidx];
837 log_assert(port.clk_enable == root.clk_enable);
838 if (port.clk_enable) {
839 log_assert(port.clk == root.clk);
840 log_assert(port.clk_polarity == root.clk_polarity);
841 }
842
843 // make something like:
844 // always @(posedge clk)
845 // if (wr_en_bit) memid[w_addr][??] <= w_data[??];
846 // ...
847 for (int sub = 0; sub < (1 << port.wide_log2); sub++)
848 {
849 SigSpec addr = port.sub_addr(sub);
850 for (int i = 0; i < mem.width; i++)
851 {
852 int start_i = i, width = 1;
853 SigBit wen_bit = port.en[sub * mem.width + i];
854
855 while (i+1 < mem.width && active_sigmap(port.en[sub * mem.width + i+1]) == active_sigmap(wen_bit))
856 i++, width++;
857
858 if (wen_bit == State::S0)
859 continue;
860
861 f << stringf("%s%s", indent.c_str(), indent.c_str());
862 if (wen_bit != State::S1)
863 {
864 f << stringf("if (");
865 dump_sigspec(f, wen_bit);
866 f << stringf(")\n");
867 f << stringf("%s%s%s", indent.c_str(), indent.c_str(), indent.c_str());
868 }
869 f << stringf("%s[", mem_id.c_str());
870 dump_sigspec(f, addr);
871 if (width == GetSize(port.en))
872 f << stringf("] <= ");
873 else
874 f << stringf("][%d:%d] <= ", i, start_i);
875 dump_sigspec(f, port.data.extract(sub * mem.width + start_i, width));
876 f << stringf(";\n");
877 }
878 }
879 }
880
881 f << stringf("%s" "end\n", indent.c_str());
882 }
883 // Output Verilog that looks something like this:
884 // reg [..] _3_;
885 // always @(posedge CLK2) begin
886 // _3_ <= memory[D1ADDR];
887 // if (A1EN)
888 // memory[A1ADDR] <= A1DATA;
889 // if (A2EN)
890 // memory[A2ADDR] <= A2DATA;
891 // ...
892 // end
893 // always @(negedge CLK1) begin
894 // if (C1EN)
895 // memory[C1ADDR] <= C1DATA;
896 // end
897 // ...
898 // assign D1DATA = _3_;
899 // assign D2DATA <= memory[D2ADDR];
900
901 // the reg ... definitions
902 for(auto &reg : lof_reg_declarations)
903 {
904 f << stringf("%s" "%s", indent.c_str(), reg.c_str());
905 }
906 // the block of expressions by clock domain
907 for(auto &pair : clk_to_lof_body)
908 {
909 std::string clk_domain = pair.first;
910 std::vector<std::string> lof_lines = pair.second;
911 if( clk_domain != "")
912 {
913 f << stringf("%s" "always%s @(%s) begin\n", indent.c_str(), systemverilog ? "_ff" : "", clk_domain.c_str());
914 bool has_arst = clk_to_arst_cond.count(clk_domain) != 0;
915 if (has_arst) {
916 f << stringf("%s%s" "if (%s) begin\n", indent.c_str(), indent.c_str(), clk_to_arst_cond[clk_domain].c_str());
917 for(auto &line : clk_to_arst_body[clk_domain])
918 f << stringf("%s%s%s" "%s", indent.c_str(), indent.c_str(), indent.c_str(), line.c_str());
919 f << stringf("%s%s" "end else begin\n", indent.c_str(), indent.c_str());
920 for(auto &line : lof_lines)
921 f << stringf("%s%s%s" "%s", indent.c_str(), indent.c_str(), indent.c_str(), line.c_str());
922 f << stringf("%s%s" "end\n", indent.c_str(), indent.c_str());
923 } else {
924 for(auto &line : lof_lines)
925 f << stringf("%s%s" "%s", indent.c_str(), indent.c_str(), line.c_str());
926 }
927 f << stringf("%s" "end\n", indent.c_str());
928 }
929 else
930 {
931 // the non-clocked assignments
932 for(auto &line : lof_lines)
933 f << stringf("%s" "%s", indent.c_str(), line.c_str());
934 }
935 }
936 }
937
938 void dump_cell_expr_port(std::ostream &f, RTLIL::Cell *cell, std::string port, bool gen_signed = true)
939 {
940 if (gen_signed && cell->parameters.count("\\" + port + "_SIGNED") > 0 && cell->parameters["\\" + port + "_SIGNED"].as_bool()) {
941 f << stringf("$signed(");
942 dump_sigspec(f, cell->getPort("\\" + port));
943 f << stringf(")");
944 } else
945 dump_sigspec(f, cell->getPort("\\" + port));
946 }
947
948 std::string cellname(RTLIL::Cell *cell)
949 {
950 if (!norename && cell->name[0] == '$' && RTLIL::builtin_ff_cell_types().count(cell->type) && cell->hasPort(ID::Q) && !cell->type.in(ID($ff), ID($_FF_)))
951 {
952 RTLIL::SigSpec sig = cell->getPort(ID::Q);
953 if (GetSize(sig) != 1 || sig.is_fully_const())
954 goto no_special_reg_name;
955
956 RTLIL::Wire *wire = sig[0].wire;
957
958 if (wire->name[0] != '\\')
959 goto no_special_reg_name;
960
961 std::string cell_name = wire->name.str();
962
963 size_t pos = cell_name.find('[');
964 if (pos != std::string::npos)
965 cell_name = cell_name.substr(0, pos) + "_reg" + cell_name.substr(pos);
966 else
967 cell_name = cell_name + "_reg";
968
969 if (wire->width != 1)
970 cell_name += stringf("[%d]", wire->start_offset + sig[0].offset);
971
972 if (active_module && active_module->count_id(cell_name) > 0)
973 goto no_special_reg_name;
974
975 return id(cell_name);
976 }
977 else
978 {
979 no_special_reg_name:
980 return id(cell->name).c_str();
981 }
982 }
983
984 void dump_cell_expr_uniop(std::ostream &f, std::string indent, RTLIL::Cell *cell, std::string op)
985 {
986 f << stringf("%s" "assign ", indent.c_str());
987 dump_sigspec(f, cell->getPort(ID::Y));
988 f << stringf(" = %s ", op.c_str());
989 dump_attributes(f, "", cell->attributes, ' ');
990 dump_cell_expr_port(f, cell, "A", true);
991 f << stringf(";\n");
992 }
993
994 void dump_cell_expr_binop(std::ostream &f, std::string indent, RTLIL::Cell *cell, std::string op)
995 {
996 f << stringf("%s" "assign ", indent.c_str());
997 dump_sigspec(f, cell->getPort(ID::Y));
998 f << stringf(" = ");
999 dump_cell_expr_port(f, cell, "A", true);
1000 f << stringf(" %s ", op.c_str());
1001 dump_attributes(f, "", cell->attributes, ' ');
1002 dump_cell_expr_port(f, cell, "B", true);
1003 f << stringf(";\n");
1004 }
1005
1006 bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
1007 {
1008 if (cell->type == ID($_NOT_)) {
1009 f << stringf("%s" "assign ", indent.c_str());
1010 dump_sigspec(f, cell->getPort(ID::Y));
1011 f << stringf(" = ");
1012 f << stringf("~");
1013 dump_attributes(f, "", cell->attributes, ' ');
1014 dump_cell_expr_port(f, cell, "A", false);
1015 f << stringf(";\n");
1016 return true;
1017 }
1018
1019 if (cell->type.in(ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_))) {
1020 f << stringf("%s" "assign ", indent.c_str());
1021 dump_sigspec(f, cell->getPort(ID::Y));
1022 f << stringf(" = ");
1023 if (cell->type.in(ID($_NAND_), ID($_NOR_), ID($_XNOR_)))
1024 f << stringf("~(");
1025 dump_cell_expr_port(f, cell, "A", false);
1026 f << stringf(" ");
1027 if (cell->type.in(ID($_AND_), ID($_NAND_), ID($_ANDNOT_)))
1028 f << stringf("&");
1029 if (cell->type.in(ID($_OR_), ID($_NOR_), ID($_ORNOT_)))
1030 f << stringf("|");
1031 if (cell->type.in(ID($_XOR_), ID($_XNOR_)))
1032 f << stringf("^");
1033 dump_attributes(f, "", cell->attributes, ' ');
1034 f << stringf(" ");
1035 if (cell->type.in(ID($_ANDNOT_), ID($_ORNOT_)))
1036 f << stringf("~(");
1037 dump_cell_expr_port(f, cell, "B", false);
1038 if (cell->type.in(ID($_NAND_), ID($_NOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_)))
1039 f << stringf(")");
1040 f << stringf(";\n");
1041 return true;
1042 }
1043
1044 if (cell->type == ID($_MUX_)) {
1045 f << stringf("%s" "assign ", indent.c_str());
1046 dump_sigspec(f, cell->getPort(ID::Y));
1047 f << stringf(" = ");
1048 dump_cell_expr_port(f, cell, "S", false);
1049 f << stringf(" ? ");
1050 dump_attributes(f, "", cell->attributes, ' ');
1051 dump_cell_expr_port(f, cell, "B", false);
1052 f << stringf(" : ");
1053 dump_cell_expr_port(f, cell, "A", false);
1054 f << stringf(";\n");
1055 return true;
1056 }
1057
1058 if (cell->type == ID($_NMUX_)) {
1059 f << stringf("%s" "assign ", indent.c_str());
1060 dump_sigspec(f, cell->getPort(ID::Y));
1061 f << stringf(" = !(");
1062 dump_cell_expr_port(f, cell, "S", false);
1063 f << stringf(" ? ");
1064 dump_attributes(f, "", cell->attributes, ' ');
1065 dump_cell_expr_port(f, cell, "B", false);
1066 f << stringf(" : ");
1067 dump_cell_expr_port(f, cell, "A", false);
1068 f << stringf(");\n");
1069 return true;
1070 }
1071
1072 if (cell->type.in(ID($_AOI3_), ID($_OAI3_))) {
1073 f << stringf("%s" "assign ", indent.c_str());
1074 dump_sigspec(f, cell->getPort(ID::Y));
1075 f << stringf(" = ~((");
1076 dump_cell_expr_port(f, cell, "A", false);
1077 f << stringf(cell->type == ID($_AOI3_) ? " & " : " | ");
1078 dump_cell_expr_port(f, cell, "B", false);
1079 f << stringf(cell->type == ID($_AOI3_) ? ") |" : ") &");
1080 dump_attributes(f, "", cell->attributes, ' ');
1081 f << stringf(" ");
1082 dump_cell_expr_port(f, cell, "C", false);
1083 f << stringf(");\n");
1084 return true;
1085 }
1086
1087 if (cell->type.in(ID($_AOI4_), ID($_OAI4_))) {
1088 f << stringf("%s" "assign ", indent.c_str());
1089 dump_sigspec(f, cell->getPort(ID::Y));
1090 f << stringf(" = ~((");
1091 dump_cell_expr_port(f, cell, "A", false);
1092 f << stringf(cell->type == ID($_AOI4_) ? " & " : " | ");
1093 dump_cell_expr_port(f, cell, "B", false);
1094 f << stringf(cell->type == ID($_AOI4_) ? ") |" : ") &");
1095 dump_attributes(f, "", cell->attributes, ' ');
1096 f << stringf(" (");
1097 dump_cell_expr_port(f, cell, "C", false);
1098 f << stringf(cell->type == ID($_AOI4_) ? " & " : " | ");
1099 dump_cell_expr_port(f, cell, "D", false);
1100 f << stringf("));\n");
1101 return true;
1102 }
1103
1104 #define HANDLE_UNIOP(_type, _operator) \
1105 if (cell->type ==_type) { dump_cell_expr_uniop(f, indent, cell, _operator); return true; }
1106 #define HANDLE_BINOP(_type, _operator) \
1107 if (cell->type ==_type) { dump_cell_expr_binop(f, indent, cell, _operator); return true; }
1108
1109 HANDLE_UNIOP(ID($not), "~")
1110 HANDLE_UNIOP(ID($pos), "+")
1111 HANDLE_UNIOP(ID($neg), "-")
1112
1113 HANDLE_BINOP(ID($and), "&")
1114 HANDLE_BINOP(ID($or), "|")
1115 HANDLE_BINOP(ID($xor), "^")
1116 HANDLE_BINOP(ID($xnor), "~^")
1117
1118 HANDLE_UNIOP(ID($reduce_and), "&")
1119 HANDLE_UNIOP(ID($reduce_or), "|")
1120 HANDLE_UNIOP(ID($reduce_xor), "^")
1121 HANDLE_UNIOP(ID($reduce_xnor), "~^")
1122 HANDLE_UNIOP(ID($reduce_bool), "|")
1123
1124 HANDLE_BINOP(ID($shl), "<<")
1125 HANDLE_BINOP(ID($shr), ">>")
1126 HANDLE_BINOP(ID($sshl), "<<<")
1127 HANDLE_BINOP(ID($sshr), ">>>")
1128
1129 HANDLE_BINOP(ID($lt), "<")
1130 HANDLE_BINOP(ID($le), "<=")
1131 HANDLE_BINOP(ID($eq), "==")
1132 HANDLE_BINOP(ID($ne), "!=")
1133 HANDLE_BINOP(ID($eqx), "===")
1134 HANDLE_BINOP(ID($nex), "!==")
1135 HANDLE_BINOP(ID($ge), ">=")
1136 HANDLE_BINOP(ID($gt), ">")
1137
1138 HANDLE_BINOP(ID($add), "+")
1139 HANDLE_BINOP(ID($sub), "-")
1140 HANDLE_BINOP(ID($mul), "*")
1141 HANDLE_BINOP(ID($div), "/")
1142 HANDLE_BINOP(ID($mod), "%")
1143 HANDLE_BINOP(ID($pow), "**")
1144
1145 HANDLE_UNIOP(ID($logic_not), "!")
1146 HANDLE_BINOP(ID($logic_and), "&&")
1147 HANDLE_BINOP(ID($logic_or), "||")
1148
1149 #undef HANDLE_UNIOP
1150 #undef HANDLE_BINOP
1151
1152 if (cell->type == ID($divfloor))
1153 {
1154 // wire [MAXLEN+1:0] _0_, _1_, _2_;
1155 // assign _0_ = $signed(A);
1156 // assign _1_ = $signed(B);
1157 // assign _2_ = (A[-1] == B[-1]) || A == 0 ? _0_ : $signed(_0_ - (B[-1] ? _1_ + 1 : _1_ - 1));
1158 // assign Y = $signed(_2_) / $signed(_1_);
1159
1160 if (cell->getParam(ID::A_SIGNED).as_bool() && cell->getParam(ID::B_SIGNED).as_bool()) {
1161 SigSpec sig_a = cell->getPort(ID::A);
1162 SigSpec sig_b = cell->getPort(ID::B);
1163
1164 std::string buf_a = next_auto_id();
1165 std::string buf_b = next_auto_id();
1166 std::string buf_num = next_auto_id();
1167 int size_a = GetSize(sig_a);
1168 int size_b = GetSize(sig_b);
1169 int size_y = GetSize(cell->getPort(ID::Y));
1170 int size_max = std::max(size_a, std::max(size_b, size_y));
1171
1172 // intentionally one wider than maximum width
1173 f << stringf("%s" "wire [%d:0] %s, %s, %s;\n", indent.c_str(), size_max, buf_a.c_str(), buf_b.c_str(), buf_num.c_str());
1174 f << stringf("%s" "assign %s = ", indent.c_str(), buf_a.c_str());
1175 dump_cell_expr_port(f, cell, "A", true);
1176 f << stringf(";\n");
1177 f << stringf("%s" "assign %s = ", indent.c_str(), buf_b.c_str());
1178 dump_cell_expr_port(f, cell, "B", true);
1179 f << stringf(";\n");
1180
1181 f << stringf("%s" "assign %s = ", indent.c_str(), buf_num.c_str());
1182 f << stringf("(");
1183 dump_sigspec(f, sig_a.extract(sig_a.size()-1));
1184 f << stringf(" == ");
1185 dump_sigspec(f, sig_b.extract(sig_b.size()-1));
1186 f << stringf(") || ");
1187 dump_sigspec(f, sig_a);
1188 f << stringf(" == 0 ? %s : ", buf_a.c_str());
1189 f << stringf("$signed(%s - (", buf_a.c_str());
1190 dump_sigspec(f, sig_b.extract(sig_b.size()-1));
1191 f << stringf(" ? %s + 1 : %s - 1));\n", buf_b.c_str(), buf_b.c_str());
1192
1193
1194 f << stringf("%s" "assign ", indent.c_str());
1195 dump_sigspec(f, cell->getPort(ID::Y));
1196 f << stringf(" = $signed(%s) / ", buf_num.c_str());
1197 dump_attributes(f, "", cell->attributes, ' ');
1198 f << stringf("$signed(%s);\n", buf_b.c_str());
1199 return true;
1200 } else {
1201 // same as truncating division
1202 dump_cell_expr_binop(f, indent, cell, "/");
1203 return true;
1204 }
1205 }
1206
1207 if (cell->type == ID($modfloor))
1208 {
1209 // wire truncated = $signed(A) % $signed(B);
1210 // assign Y = (A[-1] == B[-1]) || truncated == 0 ? truncated : $signed(B) + $signed(truncated);
1211
1212 if (cell->getParam(ID::A_SIGNED).as_bool() && cell->getParam(ID::B_SIGNED).as_bool()) {
1213 SigSpec sig_a = cell->getPort(ID::A);
1214 SigSpec sig_b = cell->getPort(ID::B);
1215
1216 std::string temp_id = next_auto_id();
1217 f << stringf("%s" "wire [%d:0] %s = ", indent.c_str(), GetSize(cell->getPort(ID::A))-1, temp_id.c_str());
1218 dump_cell_expr_port(f, cell, "A", true);
1219 f << stringf(" %% ");
1220 dump_attributes(f, "", cell->attributes, ' ');
1221 dump_cell_expr_port(f, cell, "B", true);
1222 f << stringf(";\n");
1223
1224 f << stringf("%s" "assign ", indent.c_str());
1225 dump_sigspec(f, cell->getPort(ID::Y));
1226 f << stringf(" = (");
1227 dump_sigspec(f, sig_a.extract(sig_a.size()-1));
1228 f << stringf(" == ");
1229 dump_sigspec(f, sig_b.extract(sig_b.size()-1));
1230 f << stringf(") || %s == 0 ? %s : ", temp_id.c_str(), temp_id.c_str());
1231 dump_cell_expr_port(f, cell, "B", true);
1232 f << stringf(" + $signed(%s);\n", temp_id.c_str());
1233 return true;
1234 } else {
1235 // same as truncating modulo
1236 dump_cell_expr_binop(f, indent, cell, "%");
1237 return true;
1238 }
1239 }
1240
1241 if (cell->type == ID($shift))
1242 {
1243 f << stringf("%s" "assign ", indent.c_str());
1244 dump_sigspec(f, cell->getPort(ID::Y));
1245 f << stringf(" = ");
1246 if (cell->getParam(ID::B_SIGNED).as_bool())
1247 {
1248 dump_cell_expr_port(f, cell, "B", true);
1249 f << stringf(" < 0 ? ");
1250 dump_cell_expr_port(f, cell, "A", true);
1251 f << stringf(" << - ");
1252 dump_sigspec(f, cell->getPort(ID::B));
1253 f << stringf(" : ");
1254 dump_cell_expr_port(f, cell, "A", true);
1255 f << stringf(" >> ");
1256 dump_sigspec(f, cell->getPort(ID::B));
1257 }
1258 else
1259 {
1260 dump_cell_expr_port(f, cell, "A", true);
1261 f << stringf(" >> ");
1262 dump_sigspec(f, cell->getPort(ID::B));
1263 }
1264 f << stringf(";\n");
1265 return true;
1266 }
1267
1268 if (cell->type == ID($shiftx))
1269 {
1270 std::string temp_id = next_auto_id();
1271 f << stringf("%s" "wire [%d:0] %s = ", indent.c_str(), GetSize(cell->getPort(ID::A))-1, temp_id.c_str());
1272 dump_sigspec(f, cell->getPort(ID::A));
1273 f << stringf(";\n");
1274
1275 f << stringf("%s" "assign ", indent.c_str());
1276 dump_sigspec(f, cell->getPort(ID::Y));
1277 f << stringf(" = %s[", temp_id.c_str());
1278 if (cell->getParam(ID::B_SIGNED).as_bool())
1279 f << stringf("$signed(");
1280 dump_sigspec(f, cell->getPort(ID::B));
1281 if (cell->getParam(ID::B_SIGNED).as_bool())
1282 f << stringf(")");
1283 f << stringf(" +: %d", cell->getParam(ID::Y_WIDTH).as_int());
1284 f << stringf("];\n");
1285 return true;
1286 }
1287
1288 if (cell->type == ID($mux))
1289 {
1290 f << stringf("%s" "assign ", indent.c_str());
1291 dump_sigspec(f, cell->getPort(ID::Y));
1292 f << stringf(" = ");
1293 dump_sigspec(f, cell->getPort(ID::S));
1294 f << stringf(" ? ");
1295 dump_attributes(f, "", cell->attributes, ' ');
1296 dump_sigspec(f, cell->getPort(ID::B));
1297 f << stringf(" : ");
1298 dump_sigspec(f, cell->getPort(ID::A));
1299 f << stringf(";\n");
1300 return true;
1301 }
1302
1303 if (cell->type == ID($pmux))
1304 {
1305 int width = cell->parameters[ID::WIDTH].as_int();
1306 int s_width = cell->getPort(ID::S).size();
1307 std::string func_name = cellname(cell);
1308
1309 f << stringf("%s" "function [%d:0] %s;\n", indent.c_str(), width-1, func_name.c_str());
1310 f << stringf("%s" " input [%d:0] a;\n", indent.c_str(), width-1);
1311 f << stringf("%s" " input [%d:0] b;\n", indent.c_str(), s_width*width-1);
1312 f << stringf("%s" " input [%d:0] s;\n", indent.c_str(), s_width-1);
1313
1314 dump_attributes(f, indent + " ", cell->attributes);
1315 if (!noattr)
1316 f << stringf("%s" " (* parallel_case *)\n", indent.c_str());
1317 f << stringf("%s" " casez (s)", indent.c_str());
1318 f << stringf(noattr ? " // synopsys parallel_case\n" : "\n");
1319
1320 for (int i = 0; i < s_width; i++)
1321 {
1322 f << stringf("%s" " %d'b", indent.c_str(), s_width);
1323
1324 for (int j = s_width-1; j >= 0; j--)
1325 f << stringf("%c", j == i ? '1' : '?');
1326
1327 f << stringf(":\n");
1328 f << stringf("%s" " %s = b[%d:%d];\n", indent.c_str(), func_name.c_str(), (i+1)*width-1, i*width);
1329 }
1330
1331 f << stringf("%s" " default:\n", indent.c_str());
1332 f << stringf("%s" " %s = a;\n", indent.c_str(), func_name.c_str());
1333
1334 f << stringf("%s" " endcase\n", indent.c_str());
1335 f << stringf("%s" "endfunction\n", indent.c_str());
1336
1337 f << stringf("%s" "assign ", indent.c_str());
1338 dump_sigspec(f, cell->getPort(ID::Y));
1339 f << stringf(" = %s(", func_name.c_str());
1340 dump_sigspec(f, cell->getPort(ID::A));
1341 f << stringf(", ");
1342 dump_sigspec(f, cell->getPort(ID::B));
1343 f << stringf(", ");
1344 dump_sigspec(f, cell->getPort(ID::S));
1345 f << stringf(");\n");
1346 return true;
1347 }
1348
1349 if (cell->type == ID($tribuf))
1350 {
1351 f << stringf("%s" "assign ", indent.c_str());
1352 dump_sigspec(f, cell->getPort(ID::Y));
1353 f << stringf(" = ");
1354 dump_sigspec(f, cell->getPort(ID::EN));
1355 f << stringf(" ? ");
1356 dump_sigspec(f, cell->getPort(ID::A));
1357 f << stringf(" : %d'bz;\n", cell->parameters.at(ID::WIDTH).as_int());
1358 return true;
1359 }
1360
1361 if (cell->type == ID($slice))
1362 {
1363 f << stringf("%s" "assign ", indent.c_str());
1364 dump_sigspec(f, cell->getPort(ID::Y));
1365 f << stringf(" = ");
1366 dump_sigspec(f, cell->getPort(ID::A));
1367 f << stringf(" >> %d;\n", cell->parameters.at(ID::OFFSET).as_int());
1368 return true;
1369 }
1370
1371 if (cell->type == ID($concat))
1372 {
1373 f << stringf("%s" "assign ", indent.c_str());
1374 dump_sigspec(f, cell->getPort(ID::Y));
1375 f << stringf(" = { ");
1376 dump_sigspec(f, cell->getPort(ID::B));
1377 f << stringf(" , ");
1378 dump_sigspec(f, cell->getPort(ID::A));
1379 f << stringf(" };\n");
1380 return true;
1381 }
1382
1383 if (cell->type == ID($lut))
1384 {
1385 f << stringf("%s" "assign ", indent.c_str());
1386 dump_sigspec(f, cell->getPort(ID::Y));
1387 f << stringf(" = ");
1388 dump_const(f, cell->parameters.at(ID::LUT));
1389 f << stringf(" >> ");
1390 dump_attributes(f, "", cell->attributes, ' ');
1391 dump_sigspec(f, cell->getPort(ID::A));
1392 f << stringf(";\n");
1393 return true;
1394 }
1395
1396 if (RTLIL::builtin_ff_cell_types().count(cell->type))
1397 {
1398 FfData ff(nullptr, cell);
1399
1400 // $ff / $_FF_ cell: not supported.
1401 if (ff.has_gclk)
1402 return false;
1403
1404 std::string reg_name = cellname(cell);
1405 bool out_is_reg_wire = is_reg_wire(ff.sig_q, reg_name);
1406
1407 if (!out_is_reg_wire) {
1408 if (ff.width == 1)
1409 f << stringf("%s" "reg %s", indent.c_str(), reg_name.c_str());
1410 else
1411 f << stringf("%s" "reg [%d:0] %s", indent.c_str(), ff.width-1, reg_name.c_str());
1412 dump_reg_init(f, ff.sig_q);
1413 f << ";\n";
1414 }
1415
1416 // If the FF has CLR/SET inputs, emit every bit slice separately.
1417 int chunks = ff.has_sr ? ff.width : 1;
1418 bool chunky = ff.has_sr && ff.width != 1;
1419
1420 for (int i = 0; i < chunks; i++)
1421 {
1422 SigSpec sig_d, sig_ad;
1423 Const val_arst, val_srst;
1424 std::string reg_bit_name, sig_set_name, sig_clr_name, sig_arst_name, sig_aload_name;
1425 if (chunky) {
1426 reg_bit_name = stringf("%s[%d]", reg_name.c_str(), i);
1427 if (ff.has_gclk || ff.has_clk)
1428 sig_d = ff.sig_d[i];
1429 if (ff.has_aload)
1430 sig_ad = ff.sig_ad[i];
1431 } else {
1432 reg_bit_name = reg_name;
1433 sig_d = ff.sig_d;
1434 sig_ad = ff.sig_ad;
1435 }
1436 if (ff.has_arst)
1437 val_arst = chunky ? ff.val_arst[i] : ff.val_arst;
1438 if (ff.has_srst)
1439 val_srst = chunky ? ff.val_srst[i] : ff.val_srst;
1440
1441 // If there are constants in the sensitivity list, replace them with an intermediate wire
1442 if (ff.has_clk) {
1443 if (ff.has_sr) {
1444 if (ff.sig_set[i].wire == NULL)
1445 {
1446 sig_set_name = next_auto_id();
1447 f << stringf("%s" "wire %s = ", indent.c_str(), sig_set_name.c_str());
1448 dump_const(f, ff.sig_set[i].data);
1449 f << stringf(";\n");
1450 }
1451 if (ff.sig_clr[i].wire == NULL)
1452 {
1453 sig_clr_name = next_auto_id();
1454 f << stringf("%s" "wire %s = ", indent.c_str(), sig_clr_name.c_str());
1455 dump_const(f, ff.sig_clr[i].data);
1456 f << stringf(";\n");
1457 }
1458 } else if (ff.has_arst) {
1459 if (ff.sig_arst[0].wire == NULL)
1460 {
1461 sig_arst_name = next_auto_id();
1462 f << stringf("%s" "wire %s = ", indent.c_str(), sig_arst_name.c_str());
1463 dump_const(f, ff.sig_arst[0].data);
1464 f << stringf(";\n");
1465 }
1466 } else if (ff.has_aload) {
1467 if (ff.sig_aload[0].wire == NULL)
1468 {
1469 sig_aload_name = next_auto_id();
1470 f << stringf("%s" "wire %s = ", indent.c_str(), sig_aload_name.c_str());
1471 dump_const(f, ff.sig_aload[0].data);
1472 f << stringf(";\n");
1473 }
1474 }
1475 }
1476
1477 dump_attributes(f, indent, cell->attributes);
1478 if (ff.has_clk)
1479 {
1480 // FFs.
1481 f << stringf("%s" "always%s @(%sedge ", indent.c_str(), systemverilog ? "_ff" : "", ff.pol_clk ? "pos" : "neg");
1482 dump_sigspec(f, ff.sig_clk);
1483 if (ff.has_sr) {
1484 f << stringf(", %sedge ", ff.pol_set ? "pos" : "neg");
1485 if (ff.sig_set[i].wire == NULL)
1486 f << stringf("%s", sig_set_name.c_str());
1487 else
1488 dump_sigspec(f, ff.sig_set[i]);
1489
1490 f << stringf(", %sedge ", ff.pol_clr ? "pos" : "neg");
1491 if (ff.sig_clr[i].wire == NULL)
1492 f << stringf("%s", sig_clr_name.c_str());
1493 else
1494 dump_sigspec(f, ff.sig_clr[i]);
1495 } else if (ff.has_arst) {
1496 f << stringf(", %sedge ", ff.pol_arst ? "pos" : "neg");
1497 if (ff.sig_arst[0].wire == NULL)
1498 f << stringf("%s", sig_arst_name.c_str());
1499 else
1500 dump_sigspec(f, ff.sig_arst);
1501 } else if (ff.has_aload) {
1502 f << stringf(", %sedge ", ff.pol_aload ? "pos" : "neg");
1503 if (ff.sig_aload[0].wire == NULL)
1504 f << stringf("%s", sig_aload_name.c_str());
1505 else
1506 dump_sigspec(f, ff.sig_aload);
1507 }
1508 f << stringf(")\n");
1509
1510 f << stringf("%s" " ", indent.c_str());
1511 if (ff.has_sr) {
1512 f << stringf("if (%s", ff.pol_clr ? "" : "!");
1513 if (ff.sig_clr[i].wire == NULL)
1514 f << stringf("%s", sig_clr_name.c_str());
1515 else
1516 dump_sigspec(f, ff.sig_clr[i]);
1517 f << stringf(") %s <= 1'b0;\n", reg_bit_name.c_str());
1518 f << stringf("%s" " else if (%s", indent.c_str(), ff.pol_set ? "" : "!");
1519 if (ff.sig_set[i].wire == NULL)
1520 f << stringf("%s", sig_set_name.c_str());
1521 else
1522 dump_sigspec(f, ff.sig_set[i]);
1523 f << stringf(") %s <= 1'b1;\n", reg_bit_name.c_str());
1524 f << stringf("%s" " else ", indent.c_str());
1525 } else if (ff.has_arst) {
1526 f << stringf("if (%s", ff.pol_arst ? "" : "!");
1527 if (ff.sig_arst[0].wire == NULL)
1528 f << stringf("%s", sig_arst_name.c_str());
1529 else
1530 dump_sigspec(f, ff.sig_arst);
1531 f << stringf(") %s <= ", reg_bit_name.c_str());
1532 dump_sigspec(f, val_arst);
1533 f << stringf(";\n");
1534 f << stringf("%s" " else ", indent.c_str());
1535 } else if (ff.has_aload) {
1536 f << stringf("if (%s", ff.pol_aload ? "" : "!");
1537 if (ff.sig_aload[0].wire == NULL)
1538 f << stringf("%s", sig_aload_name.c_str());
1539 else
1540 dump_sigspec(f, ff.sig_aload);
1541 f << stringf(") %s <= ", reg_bit_name.c_str());
1542 dump_sigspec(f, sig_ad);
1543 f << stringf(";\n");
1544 f << stringf("%s" " else ", indent.c_str());
1545 }
1546
1547 if (ff.has_srst && ff.has_ce && ff.ce_over_srst) {
1548 f << stringf("if (%s", ff.pol_ce ? "" : "!");
1549 dump_sigspec(f, ff.sig_ce);
1550 f << stringf(")\n");
1551 f << stringf("%s" " if (%s", indent.c_str(), ff.pol_srst ? "" : "!");
1552 dump_sigspec(f, ff.sig_srst);
1553 f << stringf(") %s <= ", reg_bit_name.c_str());
1554 dump_sigspec(f, val_srst);
1555 f << stringf(";\n");
1556 f << stringf("%s" " else ", indent.c_str());
1557 } else {
1558 if (ff.has_srst) {
1559 f << stringf("if (%s", ff.pol_srst ? "" : "!");
1560 dump_sigspec(f, ff.sig_srst);
1561 f << stringf(") %s <= ", reg_bit_name.c_str());
1562 dump_sigspec(f, val_srst);
1563 f << stringf(";\n");
1564 f << stringf("%s" " else ", indent.c_str());
1565 }
1566 if (ff.has_ce) {
1567 f << stringf("if (%s", ff.pol_ce ? "" : "!");
1568 dump_sigspec(f, ff.sig_ce);
1569 f << stringf(") ");
1570 }
1571 }
1572
1573 f << stringf("%s <= ", reg_bit_name.c_str());
1574 dump_sigspec(f, sig_d);
1575 f << stringf(";\n");
1576 }
1577 else
1578 {
1579 // Latches.
1580 f << stringf("%s" "always%s\n", indent.c_str(), systemverilog ? "_latch" : " @*");
1581
1582 f << stringf("%s" " ", indent.c_str());
1583 if (ff.has_sr) {
1584 f << stringf("if (%s", ff.pol_clr ? "" : "!");
1585 dump_sigspec(f, ff.sig_clr[i]);
1586 f << stringf(") %s = 1'b0;\n", reg_bit_name.c_str());
1587 f << stringf("%s" " else if (%s", indent.c_str(), ff.pol_set ? "" : "!");
1588 dump_sigspec(f, ff.sig_set[i]);
1589 f << stringf(") %s = 1'b1;\n", reg_bit_name.c_str());
1590 if (ff.has_aload)
1591 f << stringf("%s" " else ", indent.c_str());
1592 } else if (ff.has_arst) {
1593 f << stringf("if (%s", ff.pol_arst ? "" : "!");
1594 dump_sigspec(f, ff.sig_arst);
1595 f << stringf(") %s = ", reg_bit_name.c_str());
1596 dump_sigspec(f, val_arst);
1597 f << stringf(";\n");
1598 if (ff.has_aload)
1599 f << stringf("%s" " else ", indent.c_str());
1600 }
1601 if (ff.has_aload) {
1602 f << stringf("if (%s", ff.pol_aload ? "" : "!");
1603 dump_sigspec(f, ff.sig_aload);
1604 f << stringf(") %s = ", reg_bit_name.c_str());
1605 dump_sigspec(f, sig_ad);
1606 f << stringf(";\n");
1607 }
1608 }
1609 }
1610
1611 if (!out_is_reg_wire) {
1612 f << stringf("%s" "assign ", indent.c_str());
1613 dump_sigspec(f, ff.sig_q);
1614 f << stringf(" = %s;\n", reg_name.c_str());
1615 }
1616
1617 return true;
1618 }
1619
1620 if (cell->type.in(ID($assert), ID($assume), ID($cover)))
1621 {
1622 f << stringf("%s" "always%s if (", indent.c_str(), systemverilog ? "_comb" : " @*");
1623 dump_sigspec(f, cell->getPort(ID::EN));
1624 f << stringf(") %s(", cell->type.c_str()+1);
1625 dump_sigspec(f, cell->getPort(ID::A));
1626 f << stringf(");\n");
1627 return true;
1628 }
1629
1630 if (cell->type.in(ID($specify2), ID($specify3)))
1631 {
1632 f << stringf("%s" "specify\n%s ", indent.c_str(), indent.c_str());
1633
1634 SigSpec en = cell->getPort(ID::EN);
1635 if (en != State::S1) {
1636 f << stringf("if (");
1637 dump_sigspec(f, cell->getPort(ID::EN));
1638 f << stringf(") ");
1639 }
1640
1641 f << "(";
1642 if (cell->type == ID($specify3) && cell->getParam(ID::EDGE_EN).as_bool())
1643 f << (cell->getParam(ID::EDGE_POL).as_bool() ? "posedge ": "negedge ");
1644
1645 dump_sigspec(f, cell->getPort(ID::SRC));
1646
1647 f << " ";
1648 if (cell->getParam(ID::SRC_DST_PEN).as_bool())
1649 f << (cell->getParam(ID::SRC_DST_POL).as_bool() ? "+": "-");
1650 f << (cell->getParam(ID::FULL).as_bool() ? "*> ": "=> ");
1651
1652 if (cell->type == ID($specify3)) {
1653 f << "(";
1654 dump_sigspec(f, cell->getPort(ID::DST));
1655 f << " ";
1656 if (cell->getParam(ID::DAT_DST_PEN).as_bool())
1657 f << (cell->getParam(ID::DAT_DST_POL).as_bool() ? "+": "-");
1658 f << ": ";
1659 dump_sigspec(f, cell->getPort(ID::DAT));
1660 f << ")";
1661 } else {
1662 dump_sigspec(f, cell->getPort(ID::DST));
1663 }
1664
1665 bool bak_decimal = decimal;
1666 decimal = 1;
1667
1668 f << ") = (";
1669 dump_const(f, cell->getParam(ID::T_RISE_MIN));
1670 f << ":";
1671 dump_const(f, cell->getParam(ID::T_RISE_TYP));
1672 f << ":";
1673 dump_const(f, cell->getParam(ID::T_RISE_MAX));
1674 f << ", ";
1675 dump_const(f, cell->getParam(ID::T_FALL_MIN));
1676 f << ":";
1677 dump_const(f, cell->getParam(ID::T_FALL_TYP));
1678 f << ":";
1679 dump_const(f, cell->getParam(ID::T_FALL_MAX));
1680 f << ");\n";
1681
1682 decimal = bak_decimal;
1683
1684 f << stringf("%s" "endspecify\n", indent.c_str());
1685 return true;
1686 }
1687
1688 if (cell->type == ID($specrule))
1689 {
1690 f << stringf("%s" "specify\n%s ", indent.c_str(), indent.c_str());
1691
1692 IdString spec_type = cell->getParam(ID::TYPE).decode_string();
1693 f << stringf("%s(", spec_type.c_str());
1694
1695 if (cell->getParam(ID::SRC_PEN).as_bool())
1696 f << (cell->getParam(ID::SRC_POL).as_bool() ? "posedge ": "negedge ");
1697 dump_sigspec(f, cell->getPort(ID::SRC));
1698
1699 if (cell->getPort(ID::SRC_EN) != State::S1) {
1700 f << " &&& ";
1701 dump_sigspec(f, cell->getPort(ID::SRC_EN));
1702 }
1703
1704 f << ", ";
1705 if (cell->getParam(ID::DST_PEN).as_bool())
1706 f << (cell->getParam(ID::DST_POL).as_bool() ? "posedge ": "negedge ");
1707 dump_sigspec(f, cell->getPort(ID::DST));
1708
1709 if (cell->getPort(ID::DST_EN) != State::S1) {
1710 f << " &&& ";
1711 dump_sigspec(f, cell->getPort(ID::DST_EN));
1712 }
1713
1714 bool bak_decimal = decimal;
1715 decimal = 1;
1716
1717 f << ", ";
1718 dump_const(f, cell->getParam(ID::T_LIMIT_MIN));
1719 f << ": ";
1720 dump_const(f, cell->getParam(ID::T_LIMIT_TYP));
1721 f << ": ";
1722 dump_const(f, cell->getParam(ID::T_LIMIT_MAX));
1723
1724 if (spec_type.in(ID($setuphold), ID($recrem), ID($fullskew))) {
1725 f << ", ";
1726 dump_const(f, cell->getParam(ID::T_LIMIT2_MIN));
1727 f << ": ";
1728 dump_const(f, cell->getParam(ID::T_LIMIT2_TYP));
1729 f << ": ";
1730 dump_const(f, cell->getParam(ID::T_LIMIT2_MAX));
1731 }
1732
1733 f << ");\n";
1734 decimal = bak_decimal;
1735
1736 f << stringf("%s" "endspecify\n", indent.c_str());
1737 return true;
1738 }
1739
1740 // FIXME: $fsm
1741
1742 return false;
1743 }
1744
1745 void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell)
1746 {
1747 // Handled by dump_memory
1748 if (cell->is_mem_cell())
1749 return;
1750
1751 if (cell->type[0] == '$' && !noexpr) {
1752 if (dump_cell_expr(f, indent, cell))
1753 return;
1754 }
1755
1756 dump_attributes(f, indent, cell->attributes);
1757 f << stringf("%s" "%s", indent.c_str(), id(cell->type, false).c_str());
1758
1759 if (!defparam && cell->parameters.size() > 0) {
1760 f << stringf(" #(");
1761 for (auto it = cell->parameters.begin(); it != cell->parameters.end(); ++it) {
1762 if (it != cell->parameters.begin())
1763 f << stringf(",");
1764 f << stringf("\n%s .%s(", indent.c_str(), id(it->first).c_str());
1765 dump_const(f, it->second);
1766 f << stringf(")");
1767 }
1768 f << stringf("\n%s" ")", indent.c_str());
1769 }
1770
1771 std::string cell_name = cellname(cell);
1772 if (cell_name != id(cell->name))
1773 f << stringf(" %s /* %s */ (", cell_name.c_str(), id(cell->name).c_str());
1774 else
1775 f << stringf(" %s (", cell_name.c_str());
1776
1777 bool first_arg = true;
1778 std::set<RTLIL::IdString> numbered_ports;
1779 for (int i = 1; true; i++) {
1780 char str[16];
1781 snprintf(str, 16, "$%d", i);
1782 for (auto it = cell->connections().begin(); it != cell->connections().end(); ++it) {
1783 if (it->first != str)
1784 continue;
1785 if (!first_arg)
1786 f << stringf(",");
1787 first_arg = false;
1788 f << stringf("\n%s ", indent.c_str());
1789 dump_sigspec(f, it->second);
1790 numbered_ports.insert(it->first);
1791 goto found_numbered_port;
1792 }
1793 break;
1794 found_numbered_port:;
1795 }
1796 for (auto it = cell->connections().begin(); it != cell->connections().end(); ++it) {
1797 if (numbered_ports.count(it->first))
1798 continue;
1799 if (!first_arg)
1800 f << stringf(",");
1801 first_arg = false;
1802 f << stringf("\n%s .%s(", indent.c_str(), id(it->first).c_str());
1803 if (it->second.size() > 0)
1804 dump_sigspec(f, it->second);
1805 f << stringf(")");
1806 }
1807 f << stringf("\n%s" ");\n", indent.c_str());
1808
1809 if (defparam && cell->parameters.size() > 0) {
1810 for (auto it = cell->parameters.begin(); it != cell->parameters.end(); ++it) {
1811 f << stringf("%sdefparam %s.%s = ", indent.c_str(), cell_name.c_str(), id(it->first).c_str());
1812 dump_const(f, it->second);
1813 f << stringf(";\n");
1814 }
1815 }
1816
1817 if (siminit && RTLIL::builtin_ff_cell_types().count(cell->type) && cell->hasPort(ID::Q) && !cell->type.in(ID($ff), ID($_FF_))) {
1818 std::stringstream ss;
1819 dump_reg_init(ss, cell->getPort(ID::Q));
1820 if (!ss.str().empty()) {
1821 f << stringf("%sinitial %s.Q", indent.c_str(), cell_name.c_str());
1822 f << ss.str();
1823 f << ";\n";
1824 }
1825 }
1826 }
1827
1828 void dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right)
1829 {
1830 if (simple_lhs) {
1831 int offset = 0;
1832 for (auto &chunk : left.chunks()) {
1833 f << stringf("%s" "assign ", indent.c_str());
1834 dump_sigspec(f, chunk);
1835 f << stringf(" = ");
1836 dump_sigspec(f, right.extract(offset, GetSize(chunk)));
1837 f << stringf(";\n");
1838 offset += GetSize(chunk);
1839 }
1840 } else {
1841 f << stringf("%s" "assign ", indent.c_str());
1842 dump_sigspec(f, left);
1843 f << stringf(" = ");
1844 dump_sigspec(f, right);
1845 f << stringf(";\n");
1846 }
1847 }
1848
1849 void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw);
1850
1851 void dump_case_body(std::ostream &f, std::string indent, RTLIL::CaseRule *cs, bool omit_trailing_begin = false)
1852 {
1853 int number_of_stmts = cs->switches.size() + cs->actions.size();
1854
1855 if (!omit_trailing_begin && number_of_stmts >= 2)
1856 f << stringf("%s" "begin\n", indent.c_str());
1857
1858 for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it) {
1859 if (it->first.size() == 0)
1860 continue;
1861 f << stringf("%s ", indent.c_str());
1862 dump_sigspec(f, it->first);
1863 f << stringf(" = ");
1864 dump_sigspec(f, it->second);
1865 f << stringf(";\n");
1866 }
1867
1868 for (auto it = cs->switches.begin(); it != cs->switches.end(); ++it)
1869 dump_proc_switch(f, indent + " ", *it);
1870
1871 if (!omit_trailing_begin && number_of_stmts == 0)
1872 f << stringf("%s /* empty */;\n", indent.c_str());
1873
1874 if (omit_trailing_begin || number_of_stmts >= 2)
1875 f << stringf("%s" "end\n", indent.c_str());
1876 }
1877
1878 void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw)
1879 {
1880 if (sw->signal.size() == 0) {
1881 f << stringf("%s" "begin\n", indent.c_str());
1882 for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) {
1883 if ((*it)->compare.size() == 0)
1884 dump_case_body(f, indent + " ", *it);
1885 }
1886 f << stringf("%s" "end\n", indent.c_str());
1887 return;
1888 }
1889
1890 dump_attributes(f, indent, sw->attributes);
1891 f << stringf("%s" "casez (", indent.c_str());
1892 dump_sigspec(f, sw->signal);
1893 f << stringf(")\n");
1894
1895 bool got_default = false;
1896 for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) {
1897 dump_attributes(f, indent + " ", (*it)->attributes, '\n', /*modattr=*/false, /*regattr=*/false, /*as_comment=*/true);
1898 if ((*it)->compare.size() == 0) {
1899 if (got_default)
1900 continue;
1901 f << stringf("%s default", indent.c_str());
1902 got_default = true;
1903 } else {
1904 f << stringf("%s ", indent.c_str());
1905 for (size_t i = 0; i < (*it)->compare.size(); i++) {
1906 if (i > 0)
1907 f << stringf(", ");
1908 dump_sigspec(f, (*it)->compare[i]);
1909 }
1910 }
1911 f << stringf(":\n");
1912 dump_case_body(f, indent + " ", *it);
1913 }
1914
1915 f << stringf("%s" "endcase\n", indent.c_str());
1916 }
1917
1918 void case_body_find_regs(RTLIL::CaseRule *cs)
1919 {
1920 for (auto it = cs->switches.begin(); it != cs->switches.end(); ++it)
1921 for (auto it2 = (*it)->cases.begin(); it2 != (*it)->cases.end(); it2++)
1922 case_body_find_regs(*it2);
1923
1924 for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it) {
1925 for (auto &c : it->first.chunks())
1926 if (c.wire != NULL)
1927 reg_wires.insert(c.wire->name);
1928 }
1929 }
1930
1931 void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, bool find_regs = false)
1932 {
1933 if (find_regs) {
1934 case_body_find_regs(&proc->root_case);
1935 for (auto it = proc->syncs.begin(); it != proc->syncs.end(); ++it)
1936 for (auto it2 = (*it)->actions.begin(); it2 != (*it)->actions.end(); it2++) {
1937 for (auto &c : it2->first.chunks())
1938 if (c.wire != NULL)
1939 reg_wires.insert(c.wire->name);
1940 }
1941 return;
1942 }
1943
1944 f << stringf("%s" "always%s begin\n", indent.c_str(), systemverilog ? "_comb" : " @*");
1945 if (!systemverilog)
1946 f << indent + " " << "if (" << id("\\initial") << ") begin end\n";
1947 dump_case_body(f, indent, &proc->root_case, true);
1948
1949 std::string backup_indent = indent;
1950
1951 for (size_t i = 0; i < proc->syncs.size(); i++)
1952 {
1953 RTLIL::SyncRule *sync = proc->syncs[i];
1954 indent = backup_indent;
1955
1956 if (sync->type == RTLIL::STa) {
1957 f << stringf("%s" "always%s begin\n", indent.c_str(), systemverilog ? "_comb" : " @*");
1958 } else if (sync->type == RTLIL::STi) {
1959 f << stringf("%s" "initial begin\n", indent.c_str());
1960 } else {
1961 f << stringf("%s" "always%s @(", indent.c_str(), systemverilog ? "_ff" : "");
1962 if (sync->type == RTLIL::STp || sync->type == RTLIL::ST1)
1963 f << stringf("posedge ");
1964 if (sync->type == RTLIL::STn || sync->type == RTLIL::ST0)
1965 f << stringf("negedge ");
1966 dump_sigspec(f, sync->signal);
1967 f << stringf(") begin\n");
1968 }
1969 std::string ends = indent + "end\n";
1970 indent += " ";
1971
1972 if (sync->type == RTLIL::ST0 || sync->type == RTLIL::ST1) {
1973 f << stringf("%s" "if (%s", indent.c_str(), sync->type == RTLIL::ST0 ? "!" : "");
1974 dump_sigspec(f, sync->signal);
1975 f << stringf(") begin\n");
1976 ends = indent + "end\n" + ends;
1977 indent += " ";
1978 }
1979
1980 if (sync->type == RTLIL::STp || sync->type == RTLIL::STn) {
1981 for (size_t j = 0; j < proc->syncs.size(); j++) {
1982 RTLIL::SyncRule *sync2 = proc->syncs[j];
1983 if (sync2->type == RTLIL::ST0 || sync2->type == RTLIL::ST1) {
1984 f << stringf("%s" "if (%s", indent.c_str(), sync2->type == RTLIL::ST1 ? "!" : "");
1985 dump_sigspec(f, sync2->signal);
1986 f << stringf(") begin\n");
1987 ends = indent + "end\n" + ends;
1988 indent += " ";
1989 }
1990 }
1991 }
1992
1993 for (auto it = sync->actions.begin(); it != sync->actions.end(); ++it) {
1994 if (it->first.size() == 0)
1995 continue;
1996 f << stringf("%s ", indent.c_str());
1997 dump_sigspec(f, it->first);
1998 f << stringf(" <= ");
1999 dump_sigspec(f, it->second);
2000 f << stringf(";\n");
2001 }
2002
2003 f << stringf("%s", ends.c_str());
2004 }
2005 }
2006
2007 void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
2008 {
2009 reg_wires.clear();
2010 reset_auto_counter(module);
2011 active_module = module;
2012 active_sigmap.set(module);
2013 active_initdata.clear();
2014
2015 for (auto wire : module->wires())
2016 if (wire->attributes.count(ID::init)) {
2017 SigSpec sig = active_sigmap(wire);
2018 Const val = wire->attributes.at(ID::init);
2019 for (int i = 0; i < GetSize(sig) && i < GetSize(val); i++)
2020 if (val[i] == State::S0 || val[i] == State::S1)
2021 active_initdata[sig[i]] = val[i];
2022 }
2023
2024 if (!module->processes.empty())
2025 log_warning("Module %s contains unmapped RTLIL processes. RTLIL processes\n"
2026 "can't always be mapped directly to Verilog always blocks. Unintended\n"
2027 "changes in simulation behavior are possible! Use \"proc\" to convert\n"
2028 "processes to logic networks and registers.\n", log_id(module));
2029
2030 f << stringf("\n");
2031 for (auto it = module->processes.begin(); it != module->processes.end(); ++it)
2032 dump_process(f, indent + " ", it->second, true);
2033
2034 if (!noexpr)
2035 {
2036 std::set<std::pair<RTLIL::Wire*,int>> reg_bits;
2037 for (auto cell : module->cells())
2038 {
2039 if (!RTLIL::builtin_ff_cell_types().count(cell->type) || !cell->hasPort(ID::Q) || cell->type.in(ID($ff), ID($_FF_)))
2040 continue;
2041
2042 RTLIL::SigSpec sig = cell->getPort(ID::Q);
2043
2044 if (sig.is_chunk()) {
2045 RTLIL::SigChunk chunk = sig.as_chunk();
2046 if (chunk.wire != NULL)
2047 for (int i = 0; i < chunk.width; i++)
2048 reg_bits.insert(std::pair<RTLIL::Wire*,int>(chunk.wire, chunk.offset+i));
2049 }
2050 }
2051 for (auto wire : module->wires())
2052 {
2053 for (int i = 0; i < wire->width; i++)
2054 if (reg_bits.count(std::pair<RTLIL::Wire*,int>(wire, i)) == 0)
2055 goto this_wire_aint_reg;
2056 if (wire->width)
2057 reg_wires.insert(wire->name);
2058 this_wire_aint_reg:;
2059 }
2060 }
2061
2062 dump_attributes(f, indent, module->attributes, '\n', /*modattr=*/true);
2063 f << stringf("%s" "module %s(", indent.c_str(), id(module->name, false).c_str());
2064 bool keep_running = true;
2065 for (int port_id = 1; keep_running; port_id++) {
2066 keep_running = false;
2067 for (auto wire : module->wires()) {
2068 if (wire->port_id == port_id) {
2069 if (port_id != 1)
2070 f << stringf(", ");
2071 f << stringf("%s", id(wire->name).c_str());
2072 keep_running = true;
2073 continue;
2074 }
2075 }
2076 }
2077 f << stringf(");\n");
2078
2079 if (!systemverilog && !module->processes.empty())
2080 f << indent + " " << "reg " << id("\\initial") << " = 0;\n";
2081
2082 for (auto w : module->wires())
2083 dump_wire(f, indent + " ", w);
2084
2085 for (auto &mem : Mem::get_all_memories(module))
2086 dump_memory(f, indent + " ", mem);
2087
2088 for (auto cell : module->cells())
2089 dump_cell(f, indent + " ", cell);
2090
2091 for (auto it = module->processes.begin(); it != module->processes.end(); ++it)
2092 dump_process(f, indent + " ", it->second);
2093
2094 for (auto it = module->connections().begin(); it != module->connections().end(); ++it)
2095 dump_conn(f, indent + " ", it->first, it->second);
2096
2097 f << stringf("%s" "endmodule\n", indent.c_str());
2098 active_module = NULL;
2099 active_sigmap.clear();
2100 active_initdata.clear();
2101 }
2102
2103 struct VerilogBackend : public Backend {
2104 VerilogBackend() : Backend("verilog", "write design to Verilog file") { }
2105 void help() override
2106 {
2107 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
2108 log("\n");
2109 log(" write_verilog [options] [filename]\n");
2110 log("\n");
2111 log("Write the current design to a Verilog file.\n");
2112 log("\n");
2113 log(" -sv\n");
2114 log(" with this option, SystemVerilog constructs like always_comb are used\n");
2115 log("\n");
2116 log(" -norename\n");
2117 log(" without this option all internal object names (the ones with a dollar\n");
2118 log(" instead of a backslash prefix) are changed to short names in the\n");
2119 log(" format '_<number>_'.\n");
2120 log("\n");
2121 log(" -renameprefix <prefix>\n");
2122 log(" insert this prefix in front of auto-generated instance names\n");
2123 log("\n");
2124 log(" -noattr\n");
2125 log(" with this option no attributes are included in the output\n");
2126 log("\n");
2127 log(" -attr2comment\n");
2128 log(" with this option attributes are included as comments in the output\n");
2129 log("\n");
2130 log(" -noexpr\n");
2131 log(" without this option all internal cells are converted to Verilog\n");
2132 log(" expressions.\n");
2133 log("\n");
2134 log(" -siminit\n");
2135 log(" add initial statements with hierarchical refs to initialize FFs when\n");
2136 log(" in -noexpr mode.\n");
2137 log("\n");
2138 log(" -nodec\n");
2139 log(" 32-bit constant values are by default dumped as decimal numbers,\n");
2140 log(" not bit pattern. This option deactivates this feature and instead\n");
2141 log(" will write out all constants in binary.\n");
2142 log("\n");
2143 log(" -decimal\n");
2144 log(" dump 32-bit constants in decimal and without size and radix\n");
2145 log("\n");
2146 log(" -nohex\n");
2147 log(" constant values that are compatible with hex output are usually\n");
2148 log(" dumped as hex values. This option deactivates this feature and\n");
2149 log(" instead will write out all constants in binary.\n");
2150 log("\n");
2151 log(" -nostr\n");
2152 log(" Parameters and attributes that are specified as strings in the\n");
2153 log(" original input will be output as strings by this back-end. This\n");
2154 log(" deactivates this feature and instead will write string constants\n");
2155 log(" as binary numbers.\n");
2156 log("\n");
2157 log(" -simple-lhs\n");
2158 log(" Connection assignments with simple left hand side without concatenations.\n");
2159 log("\n");
2160 log(" -extmem\n");
2161 log(" instead of initializing memories using assignments to individual\n");
2162 log(" elements, use the '$readmemh' function to read initialization data\n");
2163 log(" from a file. This data is written to a file named by appending\n");
2164 log(" a sequential index to the Verilog filename and replacing the extension\n");
2165 log(" with '.mem', e.g. 'write_verilog -extmem foo.v' writes 'foo-1.mem',\n");
2166 log(" 'foo-2.mem' and so on.\n");
2167 log("\n");
2168 log(" -defparam\n");
2169 log(" use 'defparam' statements instead of the Verilog-2001 syntax for\n");
2170 log(" cell parameters.\n");
2171 log("\n");
2172 log(" -blackboxes\n");
2173 log(" usually modules with the 'blackbox' attribute are ignored. with\n");
2174 log(" this option set only the modules with the 'blackbox' attribute\n");
2175 log(" are written to the output file.\n");
2176 log("\n");
2177 log(" -selected\n");
2178 log(" only write selected modules. modules must be selected entirely or\n");
2179 log(" not at all.\n");
2180 log("\n");
2181 log(" -v\n");
2182 log(" verbose output (print new names of all renamed wires and cells)\n");
2183 log("\n");
2184 log("Note that RTLIL processes can't always be mapped directly to Verilog\n");
2185 log("always blocks. This frontend should only be used to export an RTLIL\n");
2186 log("netlist, i.e. after the \"proc\" pass has been used to convert all\n");
2187 log("processes to logic networks and registers. A warning is generated when\n");
2188 log("this command is called on a design with RTLIL processes.\n");
2189 log("\n");
2190 }
2191 void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
2192 {
2193 log_header(design, "Executing Verilog backend.\n");
2194
2195 verbose = false;
2196 norename = false;
2197 noattr = false;
2198 attr2comment = false;
2199 noexpr = false;
2200 nodec = false;
2201 nohex = false;
2202 nostr = false;
2203 extmem = false;
2204 defparam = false;
2205 decimal = false;
2206 siminit = false;
2207 simple_lhs = false;
2208 auto_prefix = "";
2209
2210 bool blackboxes = false;
2211 bool selected = false;
2212
2213 auto_name_map.clear();
2214 reg_wires.clear();
2215
2216 size_t argidx;
2217 for (argidx = 1; argidx < args.size(); argidx++) {
2218 std::string arg = args[argidx];
2219 if (arg == "-sv") {
2220 systemverilog = true;
2221 continue;
2222 }
2223 if (arg == "-norename") {
2224 norename = true;
2225 continue;
2226 }
2227 if (arg == "-renameprefix" && argidx+1 < args.size()) {
2228 auto_prefix = args[++argidx];
2229 continue;
2230 }
2231 if (arg == "-noattr") {
2232 noattr = true;
2233 continue;
2234 }
2235 if (arg == "-attr2comment") {
2236 attr2comment = true;
2237 continue;
2238 }
2239 if (arg == "-noexpr") {
2240 noexpr = true;
2241 continue;
2242 }
2243 if (arg == "-nodec") {
2244 nodec = true;
2245 continue;
2246 }
2247 if (arg == "-nohex") {
2248 nohex = true;
2249 continue;
2250 }
2251 if (arg == "-nostr") {
2252 nostr = true;
2253 continue;
2254 }
2255 if (arg == "-extmem") {
2256 extmem = true;
2257 extmem_counter = 1;
2258 continue;
2259 }
2260 if (arg == "-defparam") {
2261 defparam = true;
2262 continue;
2263 }
2264 if (arg == "-decimal") {
2265 decimal = true;
2266 continue;
2267 }
2268 if (arg == "-siminit") {
2269 siminit = true;
2270 continue;
2271 }
2272 if (arg == "-blackboxes") {
2273 blackboxes = true;
2274 continue;
2275 }
2276 if (arg == "-selected") {
2277 selected = true;
2278 continue;
2279 }
2280 if (arg == "-simple-lhs") {
2281 simple_lhs = true;
2282 continue;
2283 }
2284 if (arg == "-v") {
2285 verbose = true;
2286 continue;
2287 }
2288 break;
2289 }
2290 extra_args(f, filename, args, argidx);
2291 if (extmem)
2292 {
2293 if (filename == "<stdout>")
2294 log_cmd_error("Option -extmem must be used with a filename.\n");
2295 extmem_prefix = filename.substr(0, filename.rfind('.'));
2296 }
2297
2298 design->sort();
2299
2300 *f << stringf("/* Generated by %s */\n", yosys_version_str);
2301 for (auto module : design->modules()) {
2302 if (module->get_blackbox_attribute() != blackboxes)
2303 continue;
2304 if (selected && !design->selected_whole_module(module->name)) {
2305 if (design->selected_module(module->name))
2306 log_cmd_error("Can't handle partially selected module %s!\n", log_id(module->name));
2307 continue;
2308 }
2309 log("Dumping module `%s'.\n", module->name.c_str());
2310 dump_module(*f, "", module);
2311 }
2312
2313 auto_name_map.clear();
2314 reg_wires.clear();
2315 }
2316 } VerilogBackend;
2317
2318 PRIVATE_NAMESPACE_END