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