smtbmc: fix bmc with no assertions
[yosys.git] / backends / cxxrtl / cxxrtl_backend.cc
1 /*
2 * yosys -- Yosys Open SYnthesis Suite
3 *
4 * Copyright (C) 2019-2020 whitequark <whitequark@whitequark.org>
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 *
18 */
19
20 #include "kernel/rtlil.h"
21 #include "kernel/register.h"
22 #include "kernel/sigtools.h"
23 #include "kernel/utils.h"
24 #include "kernel/celltypes.h"
25 #include "kernel/mem.h"
26 #include "kernel/log.h"
27
28 USING_YOSYS_NAMESPACE
29 PRIVATE_NAMESPACE_BEGIN
30
31 // [[CITE]]
32 // Peter Eades; Xuemin Lin; W. F. Smyth, "A Fast Effective Heuristic For The Feedback Arc Set Problem"
33 // Information Processing Letters, Vol. 47, pp 319-323, 1993
34 // https://pdfs.semanticscholar.org/c7ed/d9acce96ca357876540e19664eb9d976637f.pdf
35
36 // A topological sort (on a cell/wire graph) is always possible in a fully flattened RTLIL design without
37 // processes or logic loops where every wire has a single driver. Logic loops are illegal in RTLIL and wires
38 // with multiple drivers can be split by the `splitnets` pass; however, interdependencies between processes
39 // or module instances can create strongly connected components without introducing evaluation nondeterminism.
40 // We wish to support designs with such benign SCCs (as well as designs with multiple drivers per wire), so
41 // we sort the graph in a way that minimizes feedback arcs. If there are no feedback arcs in the sorted graph,
42 // then a more efficient evaluation method is possible, since eval() will always immediately converge.
43 template<class T>
44 struct Scheduler {
45 struct Vertex {
46 T *data;
47 Vertex *prev, *next;
48 pool<Vertex*, hash_ptr_ops> preds, succs;
49
50 Vertex() : data(NULL), prev(this), next(this) {}
51 Vertex(T *data) : data(data), prev(NULL), next(NULL) {}
52
53 bool empty() const
54 {
55 log_assert(data == NULL);
56 if (next == this) {
57 log_assert(prev == next);
58 return true;
59 }
60 return false;
61 }
62
63 void link(Vertex *list)
64 {
65 log_assert(prev == NULL && next == NULL);
66 next = list;
67 prev = list->prev;
68 list->prev->next = this;
69 list->prev = this;
70 }
71
72 void unlink()
73 {
74 log_assert(prev->next == this && next->prev == this);
75 prev->next = next;
76 next->prev = prev;
77 next = prev = NULL;
78 }
79
80 int delta() const
81 {
82 return succs.size() - preds.size();
83 }
84 };
85
86 std::vector<Vertex*> vertices;
87 Vertex *sources = new Vertex;
88 Vertex *sinks = new Vertex;
89 dict<int, Vertex*> bins;
90
91 ~Scheduler()
92 {
93 delete sources;
94 delete sinks;
95 for (auto bin : bins)
96 delete bin.second;
97 for (auto vertex : vertices)
98 delete vertex;
99 }
100
101 Vertex *add(T *data)
102 {
103 Vertex *vertex = new Vertex(data);
104 vertices.push_back(vertex);
105 return vertex;
106 }
107
108 void relink(Vertex *vertex)
109 {
110 if (vertex->succs.empty())
111 vertex->link(sinks);
112 else if (vertex->preds.empty())
113 vertex->link(sources);
114 else {
115 int delta = vertex->delta();
116 if (!bins.count(delta))
117 bins[delta] = new Vertex;
118 vertex->link(bins[delta]);
119 }
120 }
121
122 Vertex *remove(Vertex *vertex)
123 {
124 vertex->unlink();
125 for (auto pred : vertex->preds) {
126 if (pred == vertex)
127 continue;
128 log_assert(pred->succs[vertex]);
129 pred->unlink();
130 pred->succs.erase(vertex);
131 relink(pred);
132 }
133 for (auto succ : vertex->succs) {
134 if (succ == vertex)
135 continue;
136 log_assert(succ->preds[vertex]);
137 succ->unlink();
138 succ->preds.erase(vertex);
139 relink(succ);
140 }
141 vertex->preds.clear();
142 vertex->succs.clear();
143 return vertex;
144 }
145
146 std::vector<Vertex*> schedule()
147 {
148 std::vector<Vertex*> s1, s2r;
149 for (auto vertex : vertices)
150 relink(vertex);
151 bool bins_empty = false;
152 while (!(sinks->empty() && sources->empty() && bins_empty)) {
153 while (!sinks->empty())
154 s2r.push_back(remove(sinks->next));
155 while (!sources->empty())
156 s1.push_back(remove(sources->next));
157 // Choosing u in this implementation isn't O(1), but the paper handwaves which data structure they suggest
158 // using to get O(1) relinking *and* find-max-key ("it is clear"... no it isn't), so this code uses a very
159 // naive implementation of find-max-key.
160 bins_empty = true;
161 bins.template sort<std::greater<int>>();
162 for (auto bin : bins) {
163 if (!bin.second->empty()) {
164 bins_empty = false;
165 s1.push_back(remove(bin.second->next));
166 break;
167 }
168 }
169 }
170 s1.insert(s1.end(), s2r.rbegin(), s2r.rend());
171 return s1;
172 }
173 };
174
175 bool is_unary_cell(RTLIL::IdString type)
176 {
177 return type.in(
178 ID($not), ID($logic_not), ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool),
179 ID($pos), ID($neg));
180 }
181
182 bool is_binary_cell(RTLIL::IdString type)
183 {
184 return type.in(
185 ID($and), ID($or), ID($xor), ID($xnor), ID($logic_and), ID($logic_or),
186 ID($shl), ID($sshl), ID($shr), ID($sshr), ID($shift), ID($shiftx),
187 ID($eq), ID($ne), ID($eqx), ID($nex), ID($gt), ID($ge), ID($lt), ID($le),
188 ID($add), ID($sub), ID($mul), ID($div), ID($mod));
189 }
190
191 bool is_extending_cell(RTLIL::IdString type)
192 {
193 return !type.in(
194 ID($logic_not), ID($logic_and), ID($logic_or),
195 ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool));
196 }
197
198 bool is_inlinable_cell(RTLIL::IdString type)
199 {
200 return is_unary_cell(type) || is_binary_cell(type) || type.in(
201 ID($mux), ID($concat), ID($slice), ID($pmux), ID($bmux), ID($demux));
202 }
203
204 bool is_ff_cell(RTLIL::IdString type)
205 {
206 return type.in(
207 ID($dff), ID($dffe), ID($sdff), ID($sdffe), ID($sdffce),
208 ID($adff), ID($adffe), ID($dffsr), ID($dffsre),
209 ID($aldff), ID($aldffe),
210 ID($dlatch), ID($adlatch), ID($dlatchsr), ID($sr));
211 }
212
213 bool is_internal_cell(RTLIL::IdString type)
214 {
215 return !type.isPublic() && !type.begins_with("$paramod");
216 }
217
218 bool is_effectful_cell(RTLIL::IdString type)
219 {
220 return type.isPublic();
221 }
222
223 bool is_cxxrtl_blackbox_cell(const RTLIL::Cell *cell)
224 {
225 RTLIL::Module *cell_module = cell->module->design->module(cell->type);
226 log_assert(cell_module != nullptr);
227 return cell_module->get_bool_attribute(ID(cxxrtl_blackbox));
228 }
229
230 bool is_memwr_process(const RTLIL::Process *process)
231 {
232 for (auto sync : process->syncs)
233 if (!sync->mem_write_actions.empty())
234 return true;
235 return false;
236 }
237
238 enum class CxxrtlPortType {
239 UNKNOWN = 0, // or mixed comb/sync
240 COMB = 1,
241 SYNC = 2,
242 };
243
244 CxxrtlPortType cxxrtl_port_type(RTLIL::Module *module, RTLIL::IdString port)
245 {
246 RTLIL::Wire *output_wire = module->wire(port);
247 log_assert(output_wire != nullptr);
248 bool is_comb = output_wire->get_bool_attribute(ID(cxxrtl_comb));
249 bool is_sync = output_wire->get_bool_attribute(ID(cxxrtl_sync));
250 if (is_comb && is_sync)
251 log_cmd_error("Port `%s.%s' is marked as both `cxxrtl_comb` and `cxxrtl_sync`.\n",
252 log_id(module), log_signal(output_wire));
253 else if (is_comb)
254 return CxxrtlPortType::COMB;
255 else if (is_sync)
256 return CxxrtlPortType::SYNC;
257 return CxxrtlPortType::UNKNOWN;
258 }
259
260 CxxrtlPortType cxxrtl_port_type(const RTLIL::Cell *cell, RTLIL::IdString port)
261 {
262 RTLIL::Module *cell_module = cell->module->design->module(cell->type);
263 if (cell_module == nullptr || !cell_module->get_bool_attribute(ID(cxxrtl_blackbox)))
264 return CxxrtlPortType::UNKNOWN;
265 return cxxrtl_port_type(cell_module, port);
266 }
267
268 bool is_cxxrtl_comb_port(const RTLIL::Cell *cell, RTLIL::IdString port)
269 {
270 return cxxrtl_port_type(cell, port) == CxxrtlPortType::COMB;
271 }
272
273 bool is_cxxrtl_sync_port(const RTLIL::Cell *cell, RTLIL::IdString port)
274 {
275 return cxxrtl_port_type(cell, port) == CxxrtlPortType::SYNC;
276 }
277
278 struct FlowGraph {
279 struct Node {
280 enum class Type {
281 CONNECT,
282 CELL_SYNC,
283 CELL_EVAL,
284 PROCESS_SYNC,
285 PROCESS_CASE,
286 MEM_RDPORT,
287 MEM_WRPORTS,
288 };
289
290 Type type;
291 RTLIL::SigSig connect = {};
292 const RTLIL::Cell *cell = nullptr;
293 const RTLIL::Process *process = nullptr;
294 const Mem *mem = nullptr;
295 int portidx;
296 };
297
298 std::vector<Node*> nodes;
299 dict<const RTLIL::Wire*, pool<Node*, hash_ptr_ops>> wire_comb_defs, wire_sync_defs, wire_uses;
300 dict<Node*, pool<const RTLIL::Wire*>, hash_ptr_ops> node_comb_defs, node_sync_defs, node_uses;
301 dict<const RTLIL::Wire*, bool> wire_def_inlinable;
302 dict<const RTLIL::Wire*, dict<Node*, bool, hash_ptr_ops>> wire_use_inlinable;
303 dict<RTLIL::SigBit, bool> bit_has_state;
304
305 ~FlowGraph()
306 {
307 for (auto node : nodes)
308 delete node;
309 }
310
311 void add_defs(Node *node, const RTLIL::SigSpec &sig, bool is_ff, bool inlinable)
312 {
313 for (auto chunk : sig.chunks())
314 if (chunk.wire) {
315 if (is_ff) {
316 // A sync def means that a wire holds design state because it is driven directly by
317 // a flip-flop output. Such a wire can never be unbuffered.
318 wire_sync_defs[chunk.wire].insert(node);
319 node_sync_defs[node].insert(chunk.wire);
320 } else {
321 // A comb def means that a wire doesn't hold design state. It might still be connected,
322 // indirectly, to a flip-flop output.
323 wire_comb_defs[chunk.wire].insert(node);
324 node_comb_defs[node].insert(chunk.wire);
325 }
326 }
327 for (auto bit : sig.bits())
328 bit_has_state[bit] |= is_ff;
329 // Only comb defs of an entire wire in the right order can be inlined.
330 if (!is_ff && sig.is_wire()) {
331 // Only a single def of a wire can be inlined. (Multiple defs of a wire are unsound, but we
332 // handle them anyway to avoid assertion failures later.)
333 if (!wire_def_inlinable.count(sig.as_wire()))
334 wire_def_inlinable[sig.as_wire()] = inlinable;
335 else
336 wire_def_inlinable[sig.as_wire()] = false;
337 }
338 }
339
340 void add_uses(Node *node, const RTLIL::SigSpec &sig)
341 {
342 for (auto chunk : sig.chunks())
343 if (chunk.wire) {
344 wire_uses[chunk.wire].insert(node);
345 node_uses[node].insert(chunk.wire);
346 // Only a single use of an entire wire in the right order can be inlined. (But the use can include
347 // other chunks.) This is tracked per-node because a wire used by multiple nodes can still be inlined
348 // if all but one of those nodes is dead.
349 if (!wire_use_inlinable[chunk.wire].count(node))
350 wire_use_inlinable[chunk.wire][node] = true;
351 else
352 wire_use_inlinable[chunk.wire][node] = false;
353 }
354 }
355
356 bool is_inlinable(const RTLIL::Wire *wire) const
357 {
358 // Can the wire be inlined at all?
359 if (wire_def_inlinable.count(wire))
360 return wire_def_inlinable.at(wire);
361 return false;
362 }
363
364 bool is_inlinable(const RTLIL::Wire *wire, const pool<Node*, hash_ptr_ops> &nodes) const
365 {
366 // Can the wire be inlined, knowing that the given nodes are reachable?
367 if (nodes.size() != 1)
368 return false;
369 Node *node = *nodes.begin();
370 log_assert(node_uses.at(node).count(wire));
371 if (is_inlinable(wire) && wire_use_inlinable.count(wire) && wire_use_inlinable.at(wire).count(node))
372 return wire_use_inlinable.at(wire).at(node);
373 return false;
374 }
375
376 // Connections
377 void add_connect_defs_uses(Node *node, const RTLIL::SigSig &conn)
378 {
379 add_defs(node, conn.first, /*is_ff=*/false, /*inlinable=*/true);
380 add_uses(node, conn.second);
381 }
382
383 Node *add_node(const RTLIL::SigSig &conn)
384 {
385 Node *node = new Node;
386 node->type = Node::Type::CONNECT;
387 node->connect = conn;
388 nodes.push_back(node);
389 add_connect_defs_uses(node, conn);
390 return node;
391 }
392
393 // Cells
394 void add_cell_sync_defs(Node *node, const RTLIL::Cell *cell)
395 {
396 // To understand why this node type is necessary and why it produces comb defs, consider a cell
397 // with input \i and sync output \o, used in a design such that \i is connected to \o. This does
398 // not result in a feedback arc because the output is synchronous. However, a naive implementation
399 // of code generation for cells that assigns to inputs, evaluates cells, assigns from outputs
400 // would not be able to immediately converge...
401 //
402 // wire<1> i_tmp;
403 // cell->p_i = i_tmp.curr;
404 // cell->eval();
405 // i_tmp.next = cell->p_o.curr;
406 //
407 // ... since the wire connecting the input and output ports would not be localizable. To solve
408 // this, the cell is split into two scheduling nodes; one exclusively for sync outputs, and
409 // another for inputs and all non-sync outputs. This way the generated code can be rearranged...
410 //
411 // value<1> i_tmp;
412 // i_tmp = cell->p_o.curr;
413 // cell->p_i = i_tmp;
414 // cell->eval();
415 //
416 // eliminating the unnecessary delta cycle. Conceptually, the CELL_SYNC node type is a series of
417 // connections of the form `connect \lhs \cell.\sync_output`; the right-hand side of these is not
418 // expressible as a wire in RTLIL. If it was expressible, then `\cell.\sync_output` would have
419 // a sync def, and this node would be an ordinary CONNECT node, with `\lhs` having a comb def.
420 // Because it isn't, a special node type is used, the right-hand side does not appear anywhere,
421 // and the left-hand side has a comb def.
422 for (auto conn : cell->connections())
423 if (cell->output(conn.first))
424 if (is_cxxrtl_sync_port(cell, conn.first)) {
425 // See note regarding inlinability below.
426 add_defs(node, conn.second, /*is_ff=*/false, /*inlinable=*/false);
427 }
428 }
429
430 void add_cell_eval_defs_uses(Node *node, const RTLIL::Cell *cell)
431 {
432 for (auto conn : cell->connections()) {
433 if (cell->output(conn.first)) {
434 if (is_inlinable_cell(cell->type))
435 add_defs(node, conn.second, /*is_ff=*/false, /*inlinable=*/true);
436 else if (is_ff_cell(cell->type))
437 add_defs(node, conn.second, /*is_ff=*/true, /*inlinable=*/false);
438 else if (is_internal_cell(cell->type))
439 add_defs(node, conn.second, /*is_ff=*/false, /*inlinable=*/false);
440 else if (!is_cxxrtl_sync_port(cell, conn.first)) {
441 // Although at first it looks like outputs of user-defined cells may always be inlined, the reality is
442 // more complex. Fully sync outputs produce no defs and so don't participate in inlining. Fully comb
443 // outputs are assigned in a different way depending on whether the cell's eval() immediately converged.
444 // Unknown/mixed outputs could be inlined, but should be rare in practical designs and don't justify
445 // the infrastructure required to inline outputs of cells with many of them.
446 add_defs(node, conn.second, /*is_ff=*/false, /*inlinable=*/false);
447 }
448 }
449 if (cell->input(conn.first))
450 add_uses(node, conn.second);
451 }
452 }
453
454 Node *add_node(const RTLIL::Cell *cell)
455 {
456 log_assert(cell->known());
457
458 bool has_fully_sync_outputs = false;
459 for (auto conn : cell->connections())
460 if (cell->output(conn.first) && is_cxxrtl_sync_port(cell, conn.first)) {
461 has_fully_sync_outputs = true;
462 break;
463 }
464 if (has_fully_sync_outputs) {
465 Node *node = new Node;
466 node->type = Node::Type::CELL_SYNC;
467 node->cell = cell;
468 nodes.push_back(node);
469 add_cell_sync_defs(node, cell);
470 }
471
472 Node *node = new Node;
473 node->type = Node::Type::CELL_EVAL;
474 node->cell = cell;
475 nodes.push_back(node);
476 add_cell_eval_defs_uses(node, cell);
477 return node;
478 }
479
480 // Processes
481 void add_case_rule_defs_uses(Node *node, const RTLIL::CaseRule *case_)
482 {
483 for (auto &action : case_->actions) {
484 add_defs(node, action.first, /*is_ff=*/false, /*inlinable=*/false);
485 add_uses(node, action.second);
486 }
487 for (auto sub_switch : case_->switches) {
488 add_uses(node, sub_switch->signal);
489 for (auto sub_case : sub_switch->cases) {
490 for (auto &compare : sub_case->compare)
491 add_uses(node, compare);
492 add_case_rule_defs_uses(node, sub_case);
493 }
494 }
495 }
496
497 void add_sync_rules_defs_uses(Node *node, const RTLIL::Process *process)
498 {
499 for (auto sync : process->syncs) {
500 for (auto &action : sync->actions) {
501 if (sync->type == RTLIL::STp || sync->type == RTLIL::STn || sync->type == RTLIL::STe)
502 add_defs(node, action.first, /*is_ff=*/true, /*inlinable=*/false);
503 else
504 add_defs(node, action.first, /*is_ff=*/false, /*inlinable=*/false);
505 add_uses(node, action.second);
506 }
507 for (auto &memwr : sync->mem_write_actions) {
508 add_uses(node, memwr.address);
509 add_uses(node, memwr.data);
510 add_uses(node, memwr.enable);
511 }
512 }
513 }
514
515 Node *add_node(const RTLIL::Process *process)
516 {
517 Node *node = new Node;
518 node->type = Node::Type::PROCESS_SYNC;
519 node->process = process;
520 nodes.push_back(node);
521 add_sync_rules_defs_uses(node, process);
522
523 node = new Node;
524 node->type = Node::Type::PROCESS_CASE;
525 node->process = process;
526 nodes.push_back(node);
527 add_case_rule_defs_uses(node, &process->root_case);
528 return node;
529 }
530
531 // Memories
532 void add_node(const Mem *mem) {
533 for (int i = 0; i < GetSize(mem->rd_ports); i++) {
534 auto &port = mem->rd_ports[i];
535 Node *node = new Node;
536 node->type = Node::Type::MEM_RDPORT;
537 node->mem = mem;
538 node->portidx = i;
539 nodes.push_back(node);
540 add_defs(node, port.data, /*is_ff=*/port.clk_enable, /*inlinable=*/false);
541 add_uses(node, port.clk);
542 add_uses(node, port.en);
543 add_uses(node, port.arst);
544 add_uses(node, port.srst);
545 add_uses(node, port.addr);
546 bool transparent = false;
547 for (int j = 0; j < GetSize(mem->wr_ports); j++) {
548 auto &wrport = mem->wr_ports[j];
549 if (port.transparency_mask[j]) {
550 // Our implementation of transparent read ports reads en, addr and data from every write port
551 // the read port is transparent with.
552 add_uses(node, wrport.en);
553 add_uses(node, wrport.addr);
554 add_uses(node, wrport.data);
555 transparent = true;
556 }
557 }
558 // Also we read the read address twice in this case (prevent inlining).
559 if (transparent)
560 add_uses(node, port.addr);
561 }
562 if (!mem->wr_ports.empty()) {
563 Node *node = new Node;
564 node->type = Node::Type::MEM_WRPORTS;
565 node->mem = mem;
566 nodes.push_back(node);
567 for (auto &port : mem->wr_ports) {
568 add_uses(node, port.clk);
569 add_uses(node, port.en);
570 add_uses(node, port.addr);
571 add_uses(node, port.data);
572 }
573 }
574 }
575 };
576
577 std::vector<std::string> split_by(const std::string &str, const std::string &sep)
578 {
579 std::vector<std::string> result;
580 size_t prev = 0;
581 while (true) {
582 size_t curr = str.find_first_of(sep, prev);
583 if (curr == std::string::npos) {
584 std::string part = str.substr(prev);
585 if (!part.empty()) result.push_back(part);
586 break;
587 } else {
588 std::string part = str.substr(prev, curr - prev);
589 if (!part.empty()) result.push_back(part);
590 prev = curr + 1;
591 }
592 }
593 return result;
594 }
595
596 std::string escape_cxx_string(const std::string &input)
597 {
598 std::string output = "\"";
599 for (auto c : input) {
600 if (::isprint(c)) {
601 if (c == '\\')
602 output.push_back('\\');
603 output.push_back(c);
604 } else {
605 char l = c & 0xf, h = (c >> 4) & 0xf;
606 output.append("\\x");
607 output.push_back((h < 10 ? '0' + h : 'a' + h - 10));
608 output.push_back((l < 10 ? '0' + l : 'a' + l - 10));
609 }
610 }
611 output.push_back('"');
612 if (output.find('\0') != std::string::npos) {
613 output.insert(0, "std::string {");
614 output.append(stringf(", %zu}", input.size()));
615 }
616 return output;
617 }
618
619 template<class T>
620 std::string get_hdl_name(T *object)
621 {
622 if (object->has_attribute(ID::hdlname))
623 return object->get_string_attribute(ID::hdlname);
624 else
625 return object->name.str().substr(1);
626 }
627
628 struct WireType {
629 enum Type {
630 // Non-referenced wire; is not a part of the design.
631 UNUSED,
632 // Double-buffered wire; is a class member, and holds design state.
633 BUFFERED,
634 // Single-buffered wire; is a class member, but holds no state.
635 MEMBER,
636 // Single-buffered wire; is a class member, and is computed on demand.
637 OUTLINE,
638 // Local wire; is a local variable in eval method.
639 LOCAL,
640 // Inline wire; is an unnamed temporary in eval method.
641 INLINE,
642 // Alias wire; is replaced with aliasee, except in debug info.
643 ALIAS,
644 // Const wire; is replaced with constant, except in debug info.
645 CONST,
646 };
647
648 Type type = UNUSED;
649 const RTLIL::Cell *cell_subst = nullptr; // for INLINE
650 RTLIL::SigSpec sig_subst = {}; // for INLINE, ALIAS, and CONST
651
652 WireType() = default;
653
654 WireType(Type type) : type(type) {
655 log_assert(type == UNUSED || type == BUFFERED || type == MEMBER || type == OUTLINE || type == LOCAL);
656 }
657
658 WireType(Type type, const RTLIL::Cell *cell) : type(type), cell_subst(cell) {
659 log_assert(type == INLINE && is_inlinable_cell(cell->type));
660 }
661
662 WireType(Type type, RTLIL::SigSpec sig) : type(type), sig_subst(sig) {
663 log_assert(type == INLINE || (type == ALIAS && sig.is_wire()) || (type == CONST && sig.is_fully_const()));
664 }
665
666 bool is_buffered() const { return type == BUFFERED; }
667 bool is_member() const { return type == BUFFERED || type == MEMBER || type == OUTLINE; }
668 bool is_outline() const { return type == OUTLINE; }
669 bool is_named() const { return is_member() || type == LOCAL; }
670 bool is_local() const { return type == LOCAL || type == INLINE; }
671 bool is_exact() const { return type == ALIAS || type == CONST; }
672 };
673
674 // Tests for a SigSpec that is a valid clock input, clocks have to have a backing wire and be a single bit
675 // using this instead of sig.is_wire() solves issues when the clock is a slice instead of a full wire
676 bool is_valid_clock(const RTLIL::SigSpec& sig) {
677 return sig.is_chunk() && sig.is_bit() && sig[0].wire;
678 }
679
680 struct CxxrtlWorker {
681 bool split_intf = false;
682 std::string intf_filename;
683 std::string design_ns = "cxxrtl_design";
684 std::ostream *impl_f = nullptr;
685 std::ostream *intf_f = nullptr;
686
687 bool print_wire_types = false;
688 bool print_debug_wire_types = false;
689 bool run_hierarchy = false;
690 bool run_flatten = false;
691 bool run_proc = false;
692
693 bool unbuffer_internal = false;
694 bool unbuffer_public = false;
695 bool localize_internal = false;
696 bool localize_public = false;
697 bool inline_internal = false;
698 bool inline_public = false;
699
700 bool debug_info = false;
701 bool debug_member = false;
702 bool debug_alias = false;
703 bool debug_eval = false;
704
705 std::ostringstream f;
706 std::string indent;
707 int temporary = 0;
708
709 dict<const RTLIL::Module*, SigMap> sigmaps;
710 dict<const RTLIL::Module*, std::vector<Mem>> mod_memories;
711 pool<std::pair<const RTLIL::Module*, RTLIL::IdString>> writable_memories;
712 pool<const RTLIL::Wire*> edge_wires;
713 dict<const RTLIL::Wire*, RTLIL::Const> wire_init;
714 dict<RTLIL::SigBit, RTLIL::SyncType> edge_types;
715 dict<const RTLIL::Module*, std::vector<FlowGraph::Node>> schedule, debug_schedule;
716 dict<const RTLIL::Wire*, WireType> wire_types, debug_wire_types;
717 dict<RTLIL::SigBit, bool> bit_has_state;
718 dict<const RTLIL::Module*, pool<std::string>> blackbox_specializations;
719 dict<const RTLIL::Module*, bool> eval_converges;
720
721 void inc_indent() {
722 indent += "\t";
723 }
724 void dec_indent() {
725 indent.resize(indent.size() - 1);
726 }
727
728 // RTLIL allows any characters in names other than whitespace. This presents an issue for generating C++ code
729 // because C++ identifiers may be only alphanumeric, cannot clash with C++ keywords, and cannot clash with cxxrtl
730 // identifiers. This issue can be solved with a name mangling scheme. We choose a name mangling scheme that results
731 // in readable identifiers, does not depend on an up-to-date list of C++ keywords, and is easy to apply. Its rules:
732 // 1. All generated identifiers start with `_`.
733 // 1a. Generated identifiers for public names (beginning with `\`) start with `p_`.
734 // 1b. Generated identifiers for internal names (beginning with `$`) start with `i_`.
735 // 2. An underscore is escaped with another underscore, i.e. `__`.
736 // 3. Any other non-alnum character is escaped with underscores around its lowercase hex code, e.g. `@` as `_40_`.
737 std::string mangle_name(const RTLIL::IdString &name)
738 {
739 std::string mangled;
740 bool first = true;
741 for (char c : name.str()) {
742 if (first) {
743 first = false;
744 if (c == '\\')
745 mangled += "p_";
746 else if (c == '$')
747 mangled += "i_";
748 else
749 log_assert(false);
750 } else {
751 if (isalnum(c)) {
752 mangled += c;
753 } else if (c == '_') {
754 mangled += "__";
755 } else {
756 char l = c & 0xf, h = (c >> 4) & 0xf;
757 mangled += '_';
758 mangled += (h < 10 ? '0' + h : 'a' + h - 10);
759 mangled += (l < 10 ? '0' + l : 'a' + l - 10);
760 mangled += '_';
761 }
762 }
763 }
764 return mangled;
765 }
766
767 std::string mangle_module_name(const RTLIL::IdString &name, bool is_blackbox = false)
768 {
769 // Class namespace.
770 if (is_blackbox)
771 return "bb_" + mangle_name(name);
772 return mangle_name(name);
773 }
774
775 std::string mangle_memory_name(const RTLIL::IdString &name)
776 {
777 // Class member namespace.
778 return "memory_" + mangle_name(name);
779 }
780
781 std::string mangle_cell_name(const RTLIL::IdString &name)
782 {
783 // Class member namespace.
784 return "cell_" + mangle_name(name);
785 }
786
787 std::string mangle_wire_name(const RTLIL::IdString &name)
788 {
789 // Class member namespace.
790 return mangle_name(name);
791 }
792
793 std::string mangle(const RTLIL::Module *module)
794 {
795 return mangle_module_name(module->name, /*is_blackbox=*/module->get_bool_attribute(ID(cxxrtl_blackbox)));
796 }
797
798 std::string mangle(const Mem *mem)
799 {
800 return mangle_memory_name(mem->memid);
801 }
802
803 std::string mangle(const RTLIL::Memory *memory)
804 {
805 return mangle_memory_name(memory->name);
806 }
807
808 std::string mangle(const RTLIL::Cell *cell)
809 {
810 return mangle_cell_name(cell->name);
811 }
812
813 std::string mangle(const RTLIL::Wire *wire)
814 {
815 return mangle_wire_name(wire->name);
816 }
817
818 std::string mangle(RTLIL::SigBit sigbit)
819 {
820 log_assert(sigbit.wire != NULL);
821 if (sigbit.wire->width == 1)
822 return mangle(sigbit.wire);
823 return mangle(sigbit.wire) + "_" + std::to_string(sigbit.offset);
824 }
825
826 std::vector<std::string> template_param_names(const RTLIL::Module *module)
827 {
828 if (!module->has_attribute(ID(cxxrtl_template)))
829 return {};
830
831 if (module->attributes.at(ID(cxxrtl_template)).flags != RTLIL::CONST_FLAG_STRING)
832 log_cmd_error("Attribute `cxxrtl_template' of module `%s' is not a string.\n", log_id(module));
833
834 std::vector<std::string> param_names = split_by(module->get_string_attribute(ID(cxxrtl_template)), " \t");
835 for (const auto &param_name : param_names) {
836 // Various lowercase prefixes (p_, i_, cell_, ...) are used for member variables, so require
837 // parameters to start with an uppercase letter to avoid name conflicts. (This is the convention
838 // in both Verilog and C++, anyway.)
839 if (!isupper(param_name[0]))
840 log_cmd_error("Attribute `cxxrtl_template' of module `%s' includes a parameter `%s', "
841 "which does not start with an uppercase letter.\n",
842 log_id(module), param_name.c_str());
843 }
844 return param_names;
845 }
846
847 std::string template_params(const RTLIL::Module *module, bool is_decl)
848 {
849 std::vector<std::string> param_names = template_param_names(module);
850 if (param_names.empty())
851 return "";
852
853 std::string params = "<";
854 bool first = true;
855 for (const auto &param_name : param_names) {
856 if (!first)
857 params += ", ";
858 first = false;
859 if (is_decl)
860 params += "size_t ";
861 params += param_name;
862 }
863 params += ">";
864 return params;
865 }
866
867 std::string template_args(const RTLIL::Cell *cell)
868 {
869 RTLIL::Module *cell_module = cell->module->design->module(cell->type);
870 log_assert(cell_module != nullptr);
871 if (!cell_module->get_bool_attribute(ID(cxxrtl_blackbox)))
872 return "";
873
874 std::vector<std::string> param_names = template_param_names(cell_module);
875 if (param_names.empty())
876 return "";
877
878 std::string params = "<";
879 bool first = true;
880 for (const auto &param_name : param_names) {
881 if (!first)
882 params += ", ";
883 first = false;
884 params += "/*" + param_name + "=*/";
885 RTLIL::IdString id_param_name = '\\' + param_name;
886 if (!cell->hasParam(id_param_name))
887 log_cmd_error("Cell `%s.%s' does not have a parameter `%s', which is required by the templated module `%s'.\n",
888 log_id(cell->module), log_id(cell), param_name.c_str(), log_id(cell_module));
889 RTLIL::Const param_value = cell->getParam(id_param_name);
890 if (((param_value.flags & ~RTLIL::CONST_FLAG_SIGNED) != 0) || param_value.as_int() < 0)
891 log_cmd_error("Parameter `%s' of cell `%s.%s', which is required by the templated module `%s', "
892 "is not a positive integer.\n",
893 param_name.c_str(), log_id(cell->module), log_id(cell), log_id(cell_module));
894 params += std::to_string(cell->getParam(id_param_name).as_int());
895 }
896 params += ">";
897 return params;
898 }
899
900 std::string fresh_temporary()
901 {
902 return stringf("tmp_%d", temporary++);
903 }
904
905 void dump_attrs(const RTLIL::AttrObject *object)
906 {
907 for (auto attr : object->attributes) {
908 f << indent << "// " << attr.first.str() << ": ";
909 if (attr.second.flags & RTLIL::CONST_FLAG_STRING) {
910 f << attr.second.decode_string();
911 } else {
912 f << attr.second.as_int(/*is_signed=*/attr.second.flags & RTLIL::CONST_FLAG_SIGNED);
913 }
914 f << "\n";
915 }
916 }
917
918 void dump_const_init(const RTLIL::Const &data, int width, int offset = 0, bool fixed_width = false)
919 {
920 const int CHUNK_SIZE = 32;
921 f << "{";
922 while (width > 0) {
923 int chunk_width = min(width, CHUNK_SIZE);
924 uint32_t chunk = data.extract(offset, chunk_width).as_int();
925 if (fixed_width)
926 f << stringf("0x%.*xu", (3 + chunk_width) / 4, chunk);
927 else
928 f << stringf("%#xu", chunk);
929 if (width > CHUNK_SIZE)
930 f << ',';
931 offset += CHUNK_SIZE;
932 width -= CHUNK_SIZE;
933 }
934 f << "}";
935 }
936
937 void dump_const(const RTLIL::Const &data, int width, int offset = 0, bool fixed_width = false)
938 {
939 f << "value<" << width << ">";
940 dump_const_init(data, width, offset, fixed_width);
941 }
942
943 void dump_const(const RTLIL::Const &data)
944 {
945 dump_const(data, data.size());
946 }
947
948 bool dump_sigchunk(const RTLIL::SigChunk &chunk, bool is_lhs, bool for_debug = false)
949 {
950 if (chunk.wire == NULL) {
951 dump_const(chunk.data, chunk.width, chunk.offset);
952 return false;
953 } else {
954 const auto &wire_type = (for_debug ? debug_wire_types : wire_types)[chunk.wire];
955 switch (wire_type.type) {
956 case WireType::BUFFERED:
957 f << mangle(chunk.wire) << (is_lhs ? ".next" : ".curr");
958 break;
959 case WireType::MEMBER:
960 case WireType::LOCAL:
961 case WireType::OUTLINE:
962 f << mangle(chunk.wire);
963 break;
964 case WireType::INLINE:
965 log_assert(!is_lhs);
966 if (wire_type.cell_subst != nullptr) {
967 dump_cell_expr(wire_type.cell_subst, for_debug);
968 break;
969 }
970 YS_FALLTHROUGH
971 case WireType::ALIAS:
972 case WireType::CONST:
973 log_assert(!is_lhs);
974 return dump_sigspec(wire_type.sig_subst.extract(chunk.offset, chunk.width), is_lhs, for_debug);
975 case WireType::UNUSED:
976 log_assert(is_lhs);
977 f << "value<" << chunk.width << ">()";
978 return false;
979 }
980 if (chunk.width == chunk.wire->width && chunk.offset == 0)
981 return false;
982 else if (chunk.width == 1)
983 f << ".slice<" << chunk.offset << ">()";
984 else
985 f << ".slice<" << chunk.offset+chunk.width-1 << "," << chunk.offset << ">()";
986 return true;
987 }
988 }
989
990 bool dump_sigspec(const RTLIL::SigSpec &sig, bool is_lhs, bool for_debug = false)
991 {
992 if (sig.empty()) {
993 f << "value<0>()";
994 return false;
995 } else if (sig.is_chunk()) {
996 return dump_sigchunk(sig.as_chunk(), is_lhs, for_debug);
997 } else {
998 bool first = true;
999 auto chunks = sig.chunks();
1000 for (auto it = chunks.rbegin(); it != chunks.rend(); it++) {
1001 if (!first)
1002 f << ".concat(";
1003 bool is_complex = dump_sigchunk(*it, is_lhs, for_debug);
1004 if (!is_lhs && it->width == 1) {
1005 size_t repeat = 1;
1006 while ((it + repeat) != chunks.rend() && *(it + repeat) == *it)
1007 repeat++;
1008 if (repeat > 1) {
1009 if (is_complex)
1010 f << ".val()";
1011 f << ".repeat<" << repeat << ">()";
1012 }
1013 it += repeat - 1;
1014 }
1015 if (!first)
1016 f << ")";
1017 first = false;
1018 }
1019 return true;
1020 }
1021 }
1022
1023 void dump_sigspec_lhs(const RTLIL::SigSpec &sig, bool for_debug = false)
1024 {
1025 dump_sigspec(sig, /*is_lhs=*/true, for_debug);
1026 }
1027
1028 void dump_sigspec_rhs(const RTLIL::SigSpec &sig, bool for_debug = false)
1029 {
1030 // In the contexts where we want template argument deduction to occur for `template<size_t Bits> ... value<Bits>`,
1031 // it is necessary to have the argument to already be a `value<N>`, since template argument deduction and implicit
1032 // type conversion are mutually exclusive. In these contexts, we use dump_sigspec_rhs() to emit an explicit
1033 // type conversion, but only if the expression needs it.
1034 bool is_complex = dump_sigspec(sig, /*is_lhs=*/false, for_debug);
1035 if (is_complex)
1036 f << ".val()";
1037 }
1038
1039 void dump_inlined_cells(const std::vector<const RTLIL::Cell*> &cells)
1040 {
1041 if (cells.empty()) {
1042 f << indent << "// connection\n";
1043 } else if (cells.size() == 1) {
1044 dump_attrs(cells.front());
1045 f << indent << "// cell " << cells.front()->name.str() << "\n";
1046 } else {
1047 f << indent << "// cells";
1048 for (auto cell : cells)
1049 f << " " << cell->name.str();
1050 f << "\n";
1051 }
1052 }
1053
1054 void collect_sigspec_rhs(const RTLIL::SigSpec &sig, bool for_debug, std::vector<const RTLIL::Cell*> &cells)
1055 {
1056 for (auto chunk : sig.chunks()) {
1057 if (!chunk.wire)
1058 continue;
1059 const auto &wire_type = wire_types[chunk.wire];
1060 switch (wire_type.type) {
1061 case WireType::INLINE:
1062 if (wire_type.cell_subst != nullptr) {
1063 collect_cell_eval(wire_type.cell_subst, for_debug, cells);
1064 break;
1065 }
1066 YS_FALLTHROUGH
1067 case WireType::ALIAS:
1068 collect_sigspec_rhs(wire_type.sig_subst, for_debug, cells);
1069 break;
1070 default:
1071 break;
1072 }
1073 }
1074 }
1075
1076 void dump_connect_expr(const RTLIL::SigSig &conn, bool for_debug = false)
1077 {
1078 dump_sigspec_rhs(conn.second, for_debug);
1079 }
1080
1081 void dump_connect(const RTLIL::SigSig &conn, bool for_debug = false)
1082 {
1083 std::vector<const RTLIL::Cell*> inlined_cells;
1084 collect_sigspec_rhs(conn.second, for_debug, inlined_cells);
1085 dump_inlined_cells(inlined_cells);
1086
1087 f << indent;
1088 dump_sigspec_lhs(conn.first, for_debug);
1089 f << " = ";
1090 dump_connect_expr(conn, for_debug);
1091 f << ";\n";
1092 }
1093
1094 void collect_connect(const RTLIL::SigSig &conn, bool for_debug, std::vector<const RTLIL::Cell*> &cells)
1095 {
1096 collect_sigspec_rhs(conn.second, for_debug, cells);
1097 }
1098
1099 void dump_cell_sync(const RTLIL::Cell *cell, bool for_debug = false)
1100 {
1101 const char *access = is_cxxrtl_blackbox_cell(cell) ? "->" : ".";
1102 f << indent << "// cell " << cell->name.str() << " syncs\n";
1103 for (auto conn : cell->connections())
1104 if (cell->output(conn.first))
1105 if (is_cxxrtl_sync_port(cell, conn.first)) {
1106 f << indent;
1107 dump_sigspec_lhs(conn.second, for_debug);
1108 f << " = " << mangle(cell) << access << mangle_wire_name(conn.first) << ".curr;\n";
1109 }
1110 }
1111
1112 void dump_cell_expr(const RTLIL::Cell *cell, bool for_debug = false)
1113 {
1114 // Unary cells
1115 if (is_unary_cell(cell->type)) {
1116 f << cell->type.substr(1);
1117 if (is_extending_cell(cell->type))
1118 f << '_' << (cell->getParam(ID::A_SIGNED).as_bool() ? 's' : 'u');
1119 f << "<" << cell->getParam(ID::Y_WIDTH).as_int() << ">(";
1120 dump_sigspec_rhs(cell->getPort(ID::A), for_debug);
1121 f << ")";
1122 // Binary cells
1123 } else if (is_binary_cell(cell->type)) {
1124 f << cell->type.substr(1);
1125 if (is_extending_cell(cell->type))
1126 f << '_' << (cell->getParam(ID::A_SIGNED).as_bool() ? 's' : 'u') <<
1127 (cell->getParam(ID::B_SIGNED).as_bool() ? 's' : 'u');
1128 f << "<" << cell->getParam(ID::Y_WIDTH).as_int() << ">(";
1129 dump_sigspec_rhs(cell->getPort(ID::A), for_debug);
1130 f << ", ";
1131 dump_sigspec_rhs(cell->getPort(ID::B), for_debug);
1132 f << ")";
1133 // Muxes
1134 } else if (cell->type == ID($mux)) {
1135 f << "(";
1136 dump_sigspec_rhs(cell->getPort(ID::S), for_debug);
1137 f << " ? ";
1138 dump_sigspec_rhs(cell->getPort(ID::B), for_debug);
1139 f << " : ";
1140 dump_sigspec_rhs(cell->getPort(ID::A), for_debug);
1141 f << ")";
1142 // Parallel (one-hot) muxes
1143 } else if (cell->type == ID($pmux)) {
1144 int width = cell->getParam(ID::WIDTH).as_int();
1145 int s_width = cell->getParam(ID::S_WIDTH).as_int();
1146 for (int part = 0; part < s_width; part++) {
1147 f << "(";
1148 dump_sigspec_rhs(cell->getPort(ID::S).extract(part), for_debug);
1149 f << " ? ";
1150 dump_sigspec_rhs(cell->getPort(ID::B).extract(part * width, width), for_debug);
1151 f << " : ";
1152 }
1153 dump_sigspec_rhs(cell->getPort(ID::A), for_debug);
1154 for (int part = 0; part < s_width; part++) {
1155 f << ")";
1156 }
1157 // Big muxes
1158 } else if (cell->type == ID($bmux)) {
1159 dump_sigspec_rhs(cell->getPort(ID::A), for_debug);
1160 f << ".bmux<";
1161 f << cell->getParam(ID::WIDTH).as_int();
1162 f << ">(";
1163 dump_sigspec_rhs(cell->getPort(ID::S), for_debug);
1164 f << ").val()";
1165 // Demuxes
1166 } else if (cell->type == ID($demux)) {
1167 dump_sigspec_rhs(cell->getPort(ID::A), for_debug);
1168 f << ".demux<";
1169 f << GetSize(cell->getPort(ID::Y));
1170 f << ">(";
1171 dump_sigspec_rhs(cell->getPort(ID::S), for_debug);
1172 f << ").val()";
1173 // Concats
1174 } else if (cell->type == ID($concat)) {
1175 dump_sigspec_rhs(cell->getPort(ID::B), for_debug);
1176 f << ".concat(";
1177 dump_sigspec_rhs(cell->getPort(ID::A), for_debug);
1178 f << ").val()";
1179 // Slices
1180 } else if (cell->type == ID($slice)) {
1181 dump_sigspec_rhs(cell->getPort(ID::A), for_debug);
1182 f << ".slice<";
1183 f << cell->getParam(ID::OFFSET).as_int() + cell->getParam(ID::Y_WIDTH).as_int() - 1;
1184 f << ",";
1185 f << cell->getParam(ID::OFFSET).as_int();
1186 f << ">().val()";
1187 } else {
1188 log_assert(false);
1189 }
1190 }
1191
1192 void dump_cell_eval(const RTLIL::Cell *cell, bool for_debug = false)
1193 {
1194 std::vector<const RTLIL::Cell*> inlined_cells;
1195 collect_cell_eval(cell, for_debug, inlined_cells);
1196 dump_inlined_cells(inlined_cells);
1197
1198 // Elidable cells
1199 if (is_inlinable_cell(cell->type)) {
1200 f << indent;
1201 dump_sigspec_lhs(cell->getPort(ID::Y), for_debug);
1202 f << " = ";
1203 dump_cell_expr(cell, for_debug);
1204 f << ";\n";
1205 // Flip-flops
1206 } else if (is_ff_cell(cell->type)) {
1207 log_assert(!for_debug);
1208 // Clocks might be slices of larger signals but should only ever be single bit
1209 if (cell->hasPort(ID::CLK) && is_valid_clock(cell->getPort(ID::CLK))) {
1210 // Edge-sensitive logic
1211 RTLIL::SigBit clk_bit = cell->getPort(ID::CLK)[0];
1212 clk_bit = sigmaps[clk_bit.wire->module](clk_bit);
1213 if (clk_bit.wire) {
1214 f << indent << "if (" << (cell->getParam(ID::CLK_POLARITY).as_bool() ? "posedge_" : "negedge_")
1215 << mangle(clk_bit) << ") {\n";
1216 } else {
1217 f << indent << "if (false) {\n";
1218 }
1219 inc_indent();
1220 if (cell->hasPort(ID::EN)) {
1221 f << indent << "if (";
1222 dump_sigspec_rhs(cell->getPort(ID::EN));
1223 f << " == value<1> {" << cell->getParam(ID::EN_POLARITY).as_bool() << "u}) {\n";
1224 inc_indent();
1225 }
1226 f << indent;
1227 dump_sigspec_lhs(cell->getPort(ID::Q));
1228 f << " = ";
1229 dump_sigspec_rhs(cell->getPort(ID::D));
1230 f << ";\n";
1231 if (cell->hasPort(ID::EN) && cell->type != ID($sdffce)) {
1232 dec_indent();
1233 f << indent << "}\n";
1234 }
1235 if (cell->hasPort(ID::SRST)) {
1236 f << indent << "if (";
1237 dump_sigspec_rhs(cell->getPort(ID::SRST));
1238 f << " == value<1> {" << cell->getParam(ID::SRST_POLARITY).as_bool() << "u}) {\n";
1239 inc_indent();
1240 f << indent;
1241 dump_sigspec_lhs(cell->getPort(ID::Q));
1242 f << " = ";
1243 dump_const(cell->getParam(ID::SRST_VALUE));
1244 f << ";\n";
1245 dec_indent();
1246 f << indent << "}\n";
1247 }
1248 if (cell->hasPort(ID::EN) && cell->type == ID($sdffce)) {
1249 dec_indent();
1250 f << indent << "}\n";
1251 }
1252 dec_indent();
1253 f << indent << "}\n";
1254 } else if (cell->hasPort(ID::EN)) {
1255 // Level-sensitive logic
1256 f << indent << "if (";
1257 dump_sigspec_rhs(cell->getPort(ID::EN));
1258 f << " == value<1> {" << cell->getParam(ID::EN_POLARITY).as_bool() << "u}) {\n";
1259 inc_indent();
1260 f << indent;
1261 dump_sigspec_lhs(cell->getPort(ID::Q));
1262 f << " = ";
1263 dump_sigspec_rhs(cell->getPort(ID::D));
1264 f << ";\n";
1265 dec_indent();
1266 f << indent << "}\n";
1267 }
1268 if (cell->hasPort(ID::ARST)) {
1269 // Asynchronous reset (entire coarse cell at once)
1270 f << indent << "if (";
1271 dump_sigspec_rhs(cell->getPort(ID::ARST));
1272 f << " == value<1> {" << cell->getParam(ID::ARST_POLARITY).as_bool() << "u}) {\n";
1273 inc_indent();
1274 f << indent;
1275 dump_sigspec_lhs(cell->getPort(ID::Q));
1276 f << " = ";
1277 dump_const(cell->getParam(ID::ARST_VALUE));
1278 f << ";\n";
1279 dec_indent();
1280 f << indent << "}\n";
1281 }
1282 if (cell->hasPort(ID::ALOAD)) {
1283 // Asynchronous load
1284 f << indent << "if (";
1285 dump_sigspec_rhs(cell->getPort(ID::ALOAD));
1286 f << " == value<1> {" << cell->getParam(ID::ALOAD_POLARITY).as_bool() << "u}) {\n";
1287 inc_indent();
1288 f << indent;
1289 dump_sigspec_lhs(cell->getPort(ID::Q));
1290 f << " = ";
1291 dump_sigspec_rhs(cell->getPort(ID::AD));
1292 f << ";\n";
1293 dec_indent();
1294 f << indent << "}\n";
1295 }
1296 if (cell->hasPort(ID::SET)) {
1297 // Asynchronous set (for individual bits)
1298 f << indent;
1299 dump_sigspec_lhs(cell->getPort(ID::Q));
1300 f << " = ";
1301 dump_sigspec_lhs(cell->getPort(ID::Q));
1302 f << ".update(";
1303 dump_const(RTLIL::Const(RTLIL::S1, cell->getParam(ID::WIDTH).as_int()));
1304 f << ", ";
1305 dump_sigspec_rhs(cell->getPort(ID::SET));
1306 f << (cell->getParam(ID::SET_POLARITY).as_bool() ? "" : ".bit_not()") << ");\n";
1307 }
1308 if (cell->hasPort(ID::CLR)) {
1309 // Asynchronous clear (for individual bits; priority over set)
1310 f << indent;
1311 dump_sigspec_lhs(cell->getPort(ID::Q));
1312 f << " = ";
1313 dump_sigspec_lhs(cell->getPort(ID::Q));
1314 f << ".update(";
1315 dump_const(RTLIL::Const(RTLIL::S0, cell->getParam(ID::WIDTH).as_int()));
1316 f << ", ";
1317 dump_sigspec_rhs(cell->getPort(ID::CLR));
1318 f << (cell->getParam(ID::CLR_POLARITY).as_bool() ? "" : ".bit_not()") << ");\n";
1319 }
1320 // Internal cells
1321 } else if (is_internal_cell(cell->type)) {
1322 log_cmd_error("Unsupported internal cell `%s'.\n", cell->type.c_str());
1323 // User cells
1324 } else {
1325 log_assert(!for_debug);
1326 log_assert(cell->known());
1327 bool buffered_inputs = false;
1328 const char *access = is_cxxrtl_blackbox_cell(cell) ? "->" : ".";
1329 for (auto conn : cell->connections())
1330 if (cell->input(conn.first)) {
1331 RTLIL::Module *cell_module = cell->module->design->module(cell->type);
1332 log_assert(cell_module != nullptr && cell_module->wire(conn.first));
1333 RTLIL::Wire *cell_module_wire = cell_module->wire(conn.first);
1334 f << indent << mangle(cell) << access << mangle_wire_name(conn.first);
1335 if (!is_cxxrtl_blackbox_cell(cell) && wire_types[cell_module_wire].is_buffered()) {
1336 buffered_inputs = true;
1337 f << ".next";
1338 }
1339 f << " = ";
1340 dump_sigspec_rhs(conn.second);
1341 f << ";\n";
1342 if (getenv("CXXRTL_VOID_MY_WARRANTY") && conn.second.is_wire()) {
1343 // Until we have proper clock tree detection, this really awful hack that opportunistically
1344 // propagates prev_* values for clocks can be used to estimate how much faster a design could
1345 // be if only one clock edge was simulated by replacing:
1346 // top.p_clk = value<1>{0u}; top.step();
1347 // top.p_clk = value<1>{1u}; top.step();
1348 // with:
1349 // top.prev_p_clk = value<1>{0u}; top.p_clk = value<1>{1u}; top.step();
1350 // Don't rely on this; it will be removed without warning.
1351 if (edge_wires[conn.second.as_wire()] && edge_wires[cell_module_wire]) {
1352 f << indent << mangle(cell) << access << "prev_" << mangle(cell_module_wire) << " = ";
1353 f << "prev_" << mangle(conn.second.as_wire()) << ";\n";
1354 }
1355 }
1356 }
1357 auto assign_from_outputs = [&](bool cell_converged) {
1358 for (auto conn : cell->connections()) {
1359 if (cell->output(conn.first)) {
1360 if (conn.second.empty())
1361 continue; // ignore disconnected ports
1362 if (is_cxxrtl_sync_port(cell, conn.first))
1363 continue; // fully sync ports are handled in CELL_SYNC nodes
1364 f << indent;
1365 dump_sigspec_lhs(conn.second);
1366 f << " = " << mangle(cell) << access << mangle_wire_name(conn.first);
1367 // Similarly to how there is no purpose to buffering cell inputs, there is also no purpose to buffering
1368 // combinatorial cell outputs in case the cell converges within one cycle. (To convince yourself that
1369 // this optimization is valid, consider that, since the cell converged within one cycle, it would not
1370 // have any buffered wires if they were not output ports. Imagine inlining the cell's eval() function,
1371 // and consider the fate of the localized wires that used to be output ports.)
1372 //
1373 // It is not possible to know apriori whether the cell (which may be late bound) will converge immediately.
1374 // Because of this, the choice between using .curr (appropriate for buffered outputs) and .next (appropriate
1375 // for unbuffered outputs) is made at runtime.
1376 if (cell_converged && is_cxxrtl_comb_port(cell, conn.first))
1377 f << ".next;\n";
1378 else
1379 f << ".curr;\n";
1380 }
1381 }
1382 };
1383 if (buffered_inputs) {
1384 // If we have any buffered inputs, there's no chance of converging immediately.
1385 f << indent << mangle(cell) << access << "eval();\n";
1386 f << indent << "converged = false;\n";
1387 assign_from_outputs(/*cell_converged=*/false);
1388 } else {
1389 f << indent << "if (" << mangle(cell) << access << "eval()) {\n";
1390 inc_indent();
1391 assign_from_outputs(/*cell_converged=*/true);
1392 dec_indent();
1393 f << indent << "} else {\n";
1394 inc_indent();
1395 f << indent << "converged = false;\n";
1396 assign_from_outputs(/*cell_converged=*/false);
1397 dec_indent();
1398 f << indent << "}\n";
1399 }
1400 }
1401 }
1402
1403 void collect_cell_eval(const RTLIL::Cell *cell, bool for_debug, std::vector<const RTLIL::Cell*> &cells)
1404 {
1405 cells.push_back(cell);
1406 for (auto port : cell->connections())
1407 if (cell->input(port.first))
1408 collect_sigspec_rhs(port.second, for_debug, cells);
1409 }
1410
1411 void dump_assign(const RTLIL::SigSig &sigsig, bool for_debug = false)
1412 {
1413 f << indent;
1414 dump_sigspec_lhs(sigsig.first, for_debug);
1415 f << " = ";
1416 dump_sigspec_rhs(sigsig.second, for_debug);
1417 f << ";\n";
1418 }
1419
1420 void dump_case_rule(const RTLIL::CaseRule *rule, bool for_debug = false)
1421 {
1422 for (auto action : rule->actions)
1423 dump_assign(action, for_debug);
1424 for (auto switch_ : rule->switches)
1425 dump_switch_rule(switch_, for_debug);
1426 }
1427
1428 void dump_switch_rule(const RTLIL::SwitchRule *rule, bool for_debug = false)
1429 {
1430 // The switch attributes are printed before the switch condition is captured.
1431 dump_attrs(rule);
1432 std::string signal_temp = fresh_temporary();
1433 f << indent << "const value<" << rule->signal.size() << "> &" << signal_temp << " = ";
1434 dump_sigspec(rule->signal, /*is_lhs=*/false, for_debug);
1435 f << ";\n";
1436
1437 bool first = true;
1438 for (auto case_ : rule->cases) {
1439 // The case attributes (for nested cases) are printed before the if/else if/else statement.
1440 dump_attrs(rule);
1441 f << indent;
1442 if (!first)
1443 f << "} else ";
1444 first = false;
1445 if (!case_->compare.empty()) {
1446 f << "if (";
1447 bool first = true;
1448 for (auto &compare : case_->compare) {
1449 if (!first)
1450 f << " || ";
1451 first = false;
1452 if (compare.is_fully_def()) {
1453 f << signal_temp << " == ";
1454 dump_sigspec(compare, /*is_lhs=*/false, for_debug);
1455 } else if (compare.is_fully_const()) {
1456 RTLIL::Const compare_mask, compare_value;
1457 for (auto bit : compare.as_const()) {
1458 switch (bit) {
1459 case RTLIL::S0:
1460 case RTLIL::S1:
1461 compare_mask.bits.push_back(RTLIL::S1);
1462 compare_value.bits.push_back(bit);
1463 break;
1464
1465 case RTLIL::Sx:
1466 case RTLIL::Sz:
1467 case RTLIL::Sa:
1468 compare_mask.bits.push_back(RTLIL::S0);
1469 compare_value.bits.push_back(RTLIL::S0);
1470 break;
1471
1472 default:
1473 log_assert(false);
1474 }
1475 }
1476 f << "and_uu<" << compare.size() << ">(" << signal_temp << ", ";
1477 dump_const(compare_mask);
1478 f << ") == ";
1479 dump_const(compare_value);
1480 } else {
1481 log_assert(false);
1482 }
1483 }
1484 f << ") ";
1485 }
1486 f << "{\n";
1487 inc_indent();
1488 dump_case_rule(case_, for_debug);
1489 dec_indent();
1490 }
1491 f << indent << "}\n";
1492 }
1493
1494 void dump_process_case(const RTLIL::Process *proc, bool for_debug = false)
1495 {
1496 dump_attrs(proc);
1497 f << indent << "// process " << proc->name.str() << " case\n";
1498 // The case attributes (for root case) are always empty.
1499 log_assert(proc->root_case.attributes.empty());
1500 dump_case_rule(&proc->root_case, for_debug);
1501 }
1502
1503 void dump_process_syncs(const RTLIL::Process *proc, bool for_debug = false)
1504 {
1505 dump_attrs(proc);
1506 f << indent << "// process " << proc->name.str() << " syncs\n";
1507 for (auto sync : proc->syncs) {
1508 log_assert(!for_debug || sync->type == RTLIL::STa);
1509
1510 RTLIL::SigBit sync_bit;
1511 if (!sync->signal.empty()) {
1512 sync_bit = sync->signal[0];
1513 sync_bit = sigmaps[sync_bit.wire->module](sync_bit);
1514 if (!sync_bit.is_wire())
1515 continue; // a clock, or more commonly a reset, can be tied to a constant driver
1516 }
1517
1518 pool<std::string> events;
1519 switch (sync->type) {
1520 case RTLIL::STp:
1521 log_assert(sync_bit.wire != nullptr);
1522 events.insert("posedge_" + mangle(sync_bit));
1523 break;
1524 case RTLIL::STn:
1525 log_assert(sync_bit.wire != nullptr);
1526 events.insert("negedge_" + mangle(sync_bit));
1527 break;
1528 case RTLIL::STe:
1529 log_assert(sync_bit.wire != nullptr);
1530 events.insert("posedge_" + mangle(sync_bit));
1531 events.insert("negedge_" + mangle(sync_bit));
1532 break;
1533
1534 case RTLIL::STa:
1535 events.insert("true");
1536 break;
1537
1538 case RTLIL::ST0:
1539 case RTLIL::ST1:
1540 case RTLIL::STg:
1541 case RTLIL::STi:
1542 log_assert(false);
1543 }
1544 if (!events.empty()) {
1545 f << indent << "if (";
1546 bool first = true;
1547 for (auto &event : events) {
1548 if (!first)
1549 f << " || ";
1550 first = false;
1551 f << event;
1552 }
1553 f << ") {\n";
1554 inc_indent();
1555 for (auto &action : sync->actions)
1556 dump_assign(action, for_debug);
1557 for (auto &memwr : sync->mem_write_actions) {
1558 RTLIL::Memory *memory = proc->module->memories.at(memwr.memid);
1559 std::string valid_index_temp = fresh_temporary();
1560 f << indent << "auto " << valid_index_temp << " = memory_index(";
1561 dump_sigspec_rhs(memwr.address);
1562 f << ", " << memory->start_offset << ", " << memory->size << ");\n";
1563 // See below for rationale of having both the assert and the condition.
1564 //
1565 // If assertions are disabled, out of bounds writes are defined to do nothing.
1566 f << indent << "CXXRTL_ASSERT(" << valid_index_temp << ".valid && \"out of bounds write\");\n";
1567 f << indent << "if (" << valid_index_temp << ".valid) {\n";
1568 inc_indent();
1569 f << indent << mangle(memory) << ".update(" << valid_index_temp << ".index, ";
1570 dump_sigspec_rhs(memwr.data);
1571 f << ", ";
1572 dump_sigspec_rhs(memwr.enable);
1573 f << ");\n";
1574 dec_indent();
1575 f << indent << "}\n";
1576 }
1577 dec_indent();
1578 f << indent << "}\n";
1579 }
1580 }
1581 }
1582
1583 void dump_mem_rdport(const Mem *mem, int portidx, bool for_debug = false)
1584 {
1585 auto &port = mem->rd_ports[portidx];
1586 dump_attrs(&port);
1587 f << indent << "// memory " << mem->memid.str() << " read port " << portidx << "\n";
1588 if (port.clk_enable) {
1589 log_assert(!for_debug);
1590 RTLIL::SigBit clk_bit = port.clk[0];
1591 clk_bit = sigmaps[clk_bit.wire->module](clk_bit);
1592 if (clk_bit.wire) {
1593 f << indent << "if (" << (port.clk_polarity ? "posedge_" : "negedge_")
1594 << mangle(clk_bit) << ") {\n";
1595 } else {
1596 f << indent << "if (false) {\n";
1597 }
1598 inc_indent();
1599 }
1600 std::vector<const RTLIL::Cell*> inlined_cells_addr;
1601 collect_sigspec_rhs(port.addr, for_debug, inlined_cells_addr);
1602 if (!inlined_cells_addr.empty())
1603 dump_inlined_cells(inlined_cells_addr);
1604 std::string valid_index_temp = fresh_temporary();
1605 f << indent << "auto " << valid_index_temp << " = memory_index(";
1606 // Almost all non-elidable cells cannot appear in debug_eval(), but $memrd is an exception; asynchronous
1607 // memory read ports can.
1608 dump_sigspec_rhs(port.addr, for_debug);
1609 f << ", " << mem->start_offset << ", " << mem->size << ");\n";
1610 bool has_enable = port.clk_enable && !port.en.is_fully_ones();
1611 if (has_enable) {
1612 std::vector<const RTLIL::Cell*> inlined_cells_en;
1613 collect_sigspec_rhs(port.en, for_debug, inlined_cells_en);
1614 if (!inlined_cells_en.empty())
1615 dump_inlined_cells(inlined_cells_en);
1616 f << indent << "if (";
1617 dump_sigspec_rhs(port.en);
1618 f << ") {\n";
1619 inc_indent();
1620 }
1621 // The generated code has two bounds checks; one in an assertion, and another that guards the read.
1622 // This is done so that the code does not invoke undefined behavior under any conditions, but nevertheless
1623 // loudly crashes if an illegal condition is encountered. The assert may be turned off with -DCXXRTL_NDEBUG
1624 // not only for release builds, but also to make sure the simulator (which is presumably embedded in some
1625 // larger program) will never crash the code that calls into it.
1626 //
1627 // If assertions are disabled, out of bounds reads are defined to return zero.
1628 f << indent << "CXXRTL_ASSERT(" << valid_index_temp << ".valid && \"out of bounds read\");\n";
1629 f << indent << "if(" << valid_index_temp << ".valid) {\n";
1630 inc_indent();
1631 if (!mem->wr_ports.empty()) {
1632 std::string lhs_temp = fresh_temporary();
1633 f << indent << "value<" << mem->width << "> " << lhs_temp << " = "
1634 << mangle(mem) << "[" << valid_index_temp << ".index];\n";
1635 bool transparent = false;
1636 for (auto bit : port.transparency_mask)
1637 if (bit)
1638 transparent = true;
1639 if (transparent) {
1640 std::string addr_temp = fresh_temporary();
1641 f << indent << "const value<" << port.addr.size() << "> &" << addr_temp << " = ";
1642 dump_sigspec_rhs(port.addr);
1643 f << ";\n";
1644 for (int i = 0; i < GetSize(mem->wr_ports); i++) {
1645 auto &wrport = mem->wr_ports[i];
1646 if (!port.transparency_mask[i])
1647 continue;
1648 f << indent << "if (" << addr_temp << " == ";
1649 dump_sigspec_rhs(wrport.addr);
1650 f << ") {\n";
1651 inc_indent();
1652 f << indent << lhs_temp << " = " << lhs_temp;
1653 f << ".update(";
1654 dump_sigspec_rhs(wrport.data);
1655 f << ", ";
1656 dump_sigspec_rhs(wrport.en);
1657 f << ");\n";
1658 dec_indent();
1659 f << indent << "}\n";
1660 }
1661 }
1662 f << indent;
1663 dump_sigspec_lhs(port.data);
1664 f << " = " << lhs_temp << ";\n";
1665 } else {
1666 f << indent;
1667 dump_sigspec_lhs(port.data);
1668 f << " = " << mangle(mem) << "[" << valid_index_temp << ".index];\n";
1669 }
1670 dec_indent();
1671 f << indent << "} else {\n";
1672 inc_indent();
1673 f << indent;
1674 dump_sigspec_lhs(port.data);
1675 f << " = value<" << mem->width << "> {};\n";
1676 dec_indent();
1677 f << indent << "}\n";
1678 if (has_enable && !port.ce_over_srst) {
1679 dec_indent();
1680 f << indent << "}\n";
1681 }
1682 if (port.srst != State::S0) {
1683 // Synchronous reset
1684 std::vector<const RTLIL::Cell*> inlined_cells_srst;
1685 collect_sigspec_rhs(port.srst, for_debug, inlined_cells_srst);
1686 if (!inlined_cells_srst.empty())
1687 dump_inlined_cells(inlined_cells_srst);
1688 f << indent << "if (";
1689 dump_sigspec_rhs(port.srst);
1690 f << " == value<1> {1u}) {\n";
1691 inc_indent();
1692 f << indent;
1693 dump_sigspec_lhs(port.data);
1694 f << " = ";
1695 dump_const(port.srst_value);
1696 f << ";\n";
1697 dec_indent();
1698 f << indent << "}\n";
1699 }
1700 if (has_enable && port.ce_over_srst) {
1701 dec_indent();
1702 f << indent << "}\n";
1703 }
1704 if (port.clk_enable) {
1705 dec_indent();
1706 f << indent << "}\n";
1707 }
1708 if (port.arst != State::S0) {
1709 // Asynchronous reset
1710 std::vector<const RTLIL::Cell*> inlined_cells_arst;
1711 collect_sigspec_rhs(port.arst, for_debug, inlined_cells_arst);
1712 if (!inlined_cells_arst.empty())
1713 dump_inlined_cells(inlined_cells_arst);
1714 f << indent << "if (";
1715 dump_sigspec_rhs(port.arst);
1716 f << " == value<1> {1u}) {\n";
1717 inc_indent();
1718 f << indent;
1719 dump_sigspec_lhs(port.data);
1720 f << " = ";
1721 dump_const(port.arst_value);
1722 f << ";\n";
1723 dec_indent();
1724 f << indent << "}\n";
1725 }
1726 }
1727
1728 void dump_mem_wrports(const Mem *mem, bool for_debug = false)
1729 {
1730 log_assert(!for_debug);
1731 for (int portidx = 0; portidx < GetSize(mem->wr_ports); portidx++) {
1732 auto &port = mem->wr_ports[portidx];
1733 dump_attrs(&port);
1734 f << indent << "// memory " << mem->memid.str() << " write port " << portidx << "\n";
1735 if (port.clk_enable) {
1736 RTLIL::SigBit clk_bit = port.clk[0];
1737 clk_bit = sigmaps[clk_bit.wire->module](clk_bit);
1738 if (clk_bit.wire) {
1739 f << indent << "if (" << (port.clk_polarity ? "posedge_" : "negedge_")
1740 << mangle(clk_bit) << ") {\n";
1741 } else {
1742 f << indent << "if (false) {\n";
1743 }
1744 inc_indent();
1745 }
1746 std::vector<const RTLIL::Cell*> inlined_cells_addr;
1747 collect_sigspec_rhs(port.addr, for_debug, inlined_cells_addr);
1748 if (!inlined_cells_addr.empty())
1749 dump_inlined_cells(inlined_cells_addr);
1750 std::string valid_index_temp = fresh_temporary();
1751 f << indent << "auto " << valid_index_temp << " = memory_index(";
1752 dump_sigspec_rhs(port.addr);
1753 f << ", " << mem->start_offset << ", " << mem->size << ");\n";
1754 // See above for rationale of having both the assert and the condition.
1755 //
1756 // If assertions are disabled, out of bounds writes are defined to do nothing.
1757 f << indent << "CXXRTL_ASSERT(" << valid_index_temp << ".valid && \"out of bounds write\");\n";
1758 f << indent << "if (" << valid_index_temp << ".valid) {\n";
1759 inc_indent();
1760 std::vector<const RTLIL::Cell*> inlined_cells;
1761 collect_sigspec_rhs(port.data, for_debug, inlined_cells);
1762 collect_sigspec_rhs(port.en, for_debug, inlined_cells);
1763 if (!inlined_cells.empty())
1764 dump_inlined_cells(inlined_cells);
1765 f << indent << mangle(mem) << ".update(" << valid_index_temp << ".index, ";
1766 dump_sigspec_rhs(port.data);
1767 f << ", ";
1768 dump_sigspec_rhs(port.en);
1769 f << ", " << portidx << ");\n";
1770 dec_indent();
1771 f << indent << "}\n";
1772 if (port.clk_enable) {
1773 dec_indent();
1774 f << indent << "}\n";
1775 }
1776 }
1777 }
1778
1779 void dump_wire(const RTLIL::Wire *wire, bool is_local)
1780 {
1781 const auto &wire_type = wire_types[wire];
1782 if (!wire_type.is_named() || wire_type.is_local() != is_local)
1783 return;
1784
1785 dump_attrs(wire);
1786 f << indent;
1787 if (wire->port_input && wire->port_output)
1788 f << "/*inout*/ ";
1789 else if (wire->port_input)
1790 f << "/*input*/ ";
1791 else if (wire->port_output)
1792 f << "/*output*/ ";
1793 f << (wire_type.is_buffered() ? "wire" : "value");
1794 if (wire->module->has_attribute(ID(cxxrtl_blackbox)) && wire->has_attribute(ID(cxxrtl_width))) {
1795 f << "<" << wire->get_string_attribute(ID(cxxrtl_width)) << ">";
1796 } else {
1797 f << "<" << wire->width << ">";
1798 }
1799 f << " " << mangle(wire) << ";\n";
1800 if (edge_wires[wire]) {
1801 if (!wire_type.is_buffered()) {
1802 f << indent << "value<" << wire->width << "> prev_" << mangle(wire) << ";\n";
1803 }
1804 for (auto edge_type : edge_types) {
1805 if (edge_type.first.wire == wire) {
1806 std::string prev, next;
1807 if (!wire_type.is_buffered()) {
1808 prev = "prev_" + mangle(edge_type.first.wire);
1809 next = mangle(edge_type.first.wire);
1810 } else {
1811 prev = mangle(edge_type.first.wire) + ".curr";
1812 next = mangle(edge_type.first.wire) + ".next";
1813 }
1814 prev += ".slice<" + std::to_string(edge_type.first.offset) + ">().val()";
1815 next += ".slice<" + std::to_string(edge_type.first.offset) + ">().val()";
1816 if (edge_type.second != RTLIL::STn) {
1817 f << indent << "bool posedge_" << mangle(edge_type.first) << "() const {\n";
1818 inc_indent();
1819 f << indent << "return !" << prev << " && " << next << ";\n";
1820 dec_indent();
1821 f << indent << "}\n";
1822 }
1823 if (edge_type.second != RTLIL::STp) {
1824 f << indent << "bool negedge_" << mangle(edge_type.first) << "() const {\n";
1825 inc_indent();
1826 f << indent << "return " << prev << " && !" << next << ";\n";
1827 dec_indent();
1828 f << indent << "}\n";
1829 }
1830 }
1831 }
1832 }
1833 }
1834
1835 void dump_debug_wire(const RTLIL::Wire *wire, bool is_local)
1836 {
1837 const auto &wire_type = wire_types[wire];
1838 if (wire_type.is_member())
1839 return;
1840
1841 const auto &debug_wire_type = debug_wire_types[wire];
1842 if (!debug_wire_type.is_named() || debug_wire_type.is_local() != is_local)
1843 return;
1844
1845 dump_attrs(wire);
1846 f << indent;
1847 if (debug_wire_type.is_outline())
1848 f << "/*outline*/ ";
1849 f << "value<" << wire->width << "> " << mangle(wire) << ";\n";
1850 }
1851
1852 void dump_reset_method(RTLIL::Module *module)
1853 {
1854 int mem_init_idx = 0;
1855 inc_indent();
1856 for (auto wire : module->wires()) {
1857 const auto &wire_type = wire_types[wire];
1858 if (!wire_type.is_named() || wire_type.is_local()) continue;
1859 if (!wire_init.count(wire)) continue;
1860
1861 f << indent << mangle(wire) << " = ";
1862 if (wire_types[wire].is_buffered()) {
1863 f << "wire<" << wire->width << ">";
1864 } else {
1865 f << "value<" << wire->width << ">";
1866 }
1867 dump_const_init(wire_init.at(wire), wire->width);
1868 f << ";\n";
1869
1870 if (edge_wires[wire] && !wire_types[wire].is_buffered()) {
1871 f << indent << "prev_" << mangle(wire) << " = ";
1872 dump_const(wire_init.at(wire), wire->width);
1873 f << ";\n";
1874 }
1875 }
1876 for (auto &mem : mod_memories[module]) {
1877 for (auto &init : mem.inits) {
1878 if (init.removed)
1879 continue;
1880 dump_attrs(&init);
1881 int words = GetSize(init.data) / mem.width;
1882 f << indent << "static const value<" << mem.width << "> ";
1883 f << "mem_init_" << ++mem_init_idx << "[" << words << "] {";
1884 inc_indent();
1885 for (int n = 0; n < words; n++) {
1886 if (n % 4 == 0)
1887 f << "\n" << indent;
1888 else
1889 f << " ";
1890 dump_const(init.data, mem.width, n * mem.width, /*fixed_width=*/true);
1891 f << ",";
1892 }
1893 dec_indent();
1894 f << "\n";
1895 f << indent << "};\n";
1896 f << indent << "std::copy(std::begin(mem_init_" << mem_init_idx << "), ";
1897 f << "std::end(mem_init_" << mem_init_idx << "), ";
1898 f << "&" << mangle(&mem) << ".data[" << stringf("%#x", init.addr.as_int()) << "]);\n";
1899 }
1900 }
1901 for (auto cell : module->cells()) {
1902 if (is_internal_cell(cell->type))
1903 continue;
1904 f << indent << mangle(cell);
1905 RTLIL::Module *cell_module = module->design->module(cell->type);
1906 if (cell_module->get_bool_attribute(ID(cxxrtl_blackbox))) {
1907 f << "->reset();\n";
1908 } else {
1909 f << ".reset();\n";
1910 }
1911 }
1912 dec_indent();
1913 }
1914
1915 void dump_eval_method(RTLIL::Module *module)
1916 {
1917 inc_indent();
1918 f << indent << "bool converged = " << (eval_converges.at(module) ? "true" : "false") << ";\n";
1919 if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) {
1920 for (auto wire : module->wires()) {
1921 if (edge_wires[wire]) {
1922 for (auto edge_type : edge_types) {
1923 if (edge_type.first.wire == wire) {
1924 if (edge_type.second != RTLIL::STn) {
1925 f << indent << "bool posedge_" << mangle(edge_type.first) << " = ";
1926 f << "this->posedge_" << mangle(edge_type.first) << "();\n";
1927 }
1928 if (edge_type.second != RTLIL::STp) {
1929 f << indent << "bool negedge_" << mangle(edge_type.first) << " = ";
1930 f << "this->negedge_" << mangle(edge_type.first) << "();\n";
1931 }
1932 }
1933 }
1934 }
1935 }
1936 for (auto wire : module->wires())
1937 dump_wire(wire, /*is_local=*/true);
1938 for (auto node : schedule[module]) {
1939 switch (node.type) {
1940 case FlowGraph::Node::Type::CONNECT:
1941 dump_connect(node.connect);
1942 break;
1943 case FlowGraph::Node::Type::CELL_SYNC:
1944 dump_cell_sync(node.cell);
1945 break;
1946 case FlowGraph::Node::Type::CELL_EVAL:
1947 dump_cell_eval(node.cell);
1948 break;
1949 case FlowGraph::Node::Type::PROCESS_CASE:
1950 dump_process_case(node.process);
1951 break;
1952 case FlowGraph::Node::Type::PROCESS_SYNC:
1953 dump_process_syncs(node.process);
1954 break;
1955 case FlowGraph::Node::Type::MEM_RDPORT:
1956 dump_mem_rdport(node.mem, node.portidx);
1957 break;
1958 case FlowGraph::Node::Type::MEM_WRPORTS:
1959 dump_mem_wrports(node.mem);
1960 break;
1961 }
1962 }
1963 }
1964 f << indent << "return converged;\n";
1965 dec_indent();
1966 }
1967
1968 void dump_debug_eval_method(RTLIL::Module *module)
1969 {
1970 inc_indent();
1971 for (auto wire : module->wires())
1972 dump_debug_wire(wire, /*is_local=*/true);
1973 for (auto node : debug_schedule[module]) {
1974 switch (node.type) {
1975 case FlowGraph::Node::Type::CONNECT:
1976 dump_connect(node.connect, /*for_debug=*/true);
1977 break;
1978 case FlowGraph::Node::Type::CELL_SYNC:
1979 dump_cell_sync(node.cell, /*for_debug=*/true);
1980 break;
1981 case FlowGraph::Node::Type::CELL_EVAL:
1982 dump_cell_eval(node.cell, /*for_debug=*/true);
1983 break;
1984 case FlowGraph::Node::Type::PROCESS_CASE:
1985 dump_process_case(node.process, /*for_debug=*/true);
1986 break;
1987 case FlowGraph::Node::Type::PROCESS_SYNC:
1988 dump_process_syncs(node.process, /*for_debug=*/true);
1989 break;
1990 case FlowGraph::Node::Type::MEM_RDPORT:
1991 dump_mem_rdport(node.mem, node.portidx, /*for_debug=*/true);
1992 break;
1993 case FlowGraph::Node::Type::MEM_WRPORTS:
1994 dump_mem_wrports(node.mem, /*for_debug=*/true);
1995 break;
1996 default:
1997 log_abort();
1998 }
1999 }
2000 dec_indent();
2001 }
2002
2003 void dump_commit_method(RTLIL::Module *module)
2004 {
2005 inc_indent();
2006 f << indent << "bool changed = false;\n";
2007 for (auto wire : module->wires()) {
2008 const auto &wire_type = wire_types[wire];
2009 if (wire_type.type == WireType::MEMBER && edge_wires[wire])
2010 f << indent << "prev_" << mangle(wire) << " = " << mangle(wire) << ";\n";
2011 if (wire_type.is_buffered())
2012 f << indent << "if (" << mangle(wire) << ".commit()) changed = true;\n";
2013 }
2014 if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) {
2015 for (auto &mem : mod_memories[module]) {
2016 if (!writable_memories.count({module, mem.memid}))
2017 continue;
2018 f << indent << "if (" << mangle(&mem) << ".commit()) changed = true;\n";
2019 }
2020 for (auto cell : module->cells()) {
2021 if (is_internal_cell(cell->type))
2022 continue;
2023 const char *access = is_cxxrtl_blackbox_cell(cell) ? "->" : ".";
2024 f << indent << "if (" << mangle(cell) << access << "commit()) changed = true;\n";
2025 }
2026 }
2027 f << indent << "return changed;\n";
2028 dec_indent();
2029 }
2030
2031 void dump_debug_info_method(RTLIL::Module *module)
2032 {
2033 size_t count_public_wires = 0;
2034 size_t count_member_wires = 0;
2035 size_t count_undriven = 0;
2036 size_t count_driven_sync = 0;
2037 size_t count_driven_comb = 0;
2038 size_t count_mixed_driver = 0;
2039 size_t count_alias_wires = 0;
2040 size_t count_const_wires = 0;
2041 size_t count_inline_wires = 0;
2042 size_t count_skipped_wires = 0;
2043 inc_indent();
2044 f << indent << "assert(path.empty() || path[path.size() - 1] == ' ');\n";
2045 for (auto wire : module->wires()) {
2046 const auto &debug_wire_type = debug_wire_types[wire];
2047 if (!wire->name.isPublic())
2048 continue;
2049 count_public_wires++;
2050 switch (debug_wire_type.type) {
2051 case WireType::BUFFERED:
2052 case WireType::MEMBER: {
2053 // Member wire
2054 std::vector<std::string> flags;
2055
2056 if (wire->port_input && wire->port_output)
2057 flags.push_back("INOUT");
2058 else if (wire->port_output)
2059 flags.push_back("OUTPUT");
2060 else if (wire->port_input)
2061 flags.push_back("INPUT");
2062
2063 bool has_driven_sync = false;
2064 bool has_driven_comb = false;
2065 bool has_undriven = false;
2066 if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) {
2067 for (auto bit : SigSpec(wire))
2068 if (!bit_has_state.count(bit))
2069 has_undriven = true;
2070 else if (bit_has_state[bit])
2071 has_driven_sync = true;
2072 else
2073 has_driven_comb = true;
2074 } else if (wire->port_output) {
2075 switch (cxxrtl_port_type(module, wire->name)) {
2076 case CxxrtlPortType::SYNC:
2077 has_driven_sync = true;
2078 break;
2079 case CxxrtlPortType::COMB:
2080 has_driven_comb = true;
2081 break;
2082 case CxxrtlPortType::UNKNOWN:
2083 has_driven_sync = has_driven_comb = true;
2084 break;
2085 }
2086 } else {
2087 has_undriven = true;
2088 }
2089 if (has_undriven)
2090 flags.push_back("UNDRIVEN");
2091 if (!has_driven_sync && !has_driven_comb && has_undriven)
2092 count_undriven++;
2093 if (has_driven_sync)
2094 flags.push_back("DRIVEN_SYNC");
2095 if (has_driven_sync && !has_driven_comb && !has_undriven)
2096 count_driven_sync++;
2097 if (has_driven_comb)
2098 flags.push_back("DRIVEN_COMB");
2099 if (!has_driven_sync && has_driven_comb && !has_undriven)
2100 count_driven_comb++;
2101 if (has_driven_sync + has_driven_comb + has_undriven > 1)
2102 count_mixed_driver++;
2103
2104 f << indent << "items.add(path + " << escape_cxx_string(get_hdl_name(wire));
2105 f << ", debug_item(" << mangle(wire) << ", " << wire->start_offset;
2106 bool first = true;
2107 for (auto flag : flags) {
2108 if (first) {
2109 first = false;
2110 f << ", ";
2111 } else {
2112 f << "|";
2113 }
2114 f << "debug_item::" << flag;
2115 }
2116 f << "));\n";
2117 count_member_wires++;
2118 break;
2119 }
2120 case WireType::ALIAS: {
2121 // Alias of a member wire
2122 const RTLIL::Wire *aliasee = debug_wire_type.sig_subst.as_wire();
2123 f << indent << "items.add(path + " << escape_cxx_string(get_hdl_name(wire));
2124 f << ", debug_item(";
2125 // If the aliasee is an outline, then the alias must be an outline, too; otherwise downstream
2126 // tooling has no way to find out about the outline.
2127 if (debug_wire_types[aliasee].is_outline())
2128 f << "debug_eval_outline";
2129 else
2130 f << "debug_alias()";
2131 f << ", " << mangle(aliasee) << ", " << wire->start_offset << "));\n";
2132 count_alias_wires++;
2133 break;
2134 }
2135 case WireType::CONST: {
2136 // Wire tied to a constant
2137 f << indent << "static const value<" << wire->width << "> const_" << mangle(wire) << " = ";
2138 dump_const(debug_wire_type.sig_subst.as_const());
2139 f << ";\n";
2140 f << indent << "items.add(path + " << escape_cxx_string(get_hdl_name(wire));
2141 f << ", debug_item(const_" << mangle(wire) << ", " << wire->start_offset << "));\n";
2142 count_const_wires++;
2143 break;
2144 }
2145 case WireType::OUTLINE: {
2146 // Localized or inlined, but rematerializable wire
2147 f << indent << "items.add(path + " << escape_cxx_string(get_hdl_name(wire));
2148 f << ", debug_item(debug_eval_outline, " << mangle(wire) << ", " << wire->start_offset << "));\n";
2149 count_inline_wires++;
2150 break;
2151 }
2152 default: {
2153 // Localized or inlined wire with no debug information
2154 count_skipped_wires++;
2155 break;
2156 }
2157 }
2158 }
2159 if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) {
2160 for (auto &mem : mod_memories[module]) {
2161 if (!mem.memid.isPublic())
2162 continue;
2163 f << indent << "items.add(path + " << escape_cxx_string(mem.packed ? get_hdl_name(mem.cell) : get_hdl_name(mem.mem));
2164 f << ", debug_item(" << mangle(&mem) << ", ";
2165 f << mem.start_offset << "));\n";
2166 }
2167 for (auto cell : module->cells()) {
2168 if (is_internal_cell(cell->type))
2169 continue;
2170 const char *access = is_cxxrtl_blackbox_cell(cell) ? "->" : ".";
2171 f << indent << mangle(cell) << access << "debug_info(items, ";
2172 f << "path + " << escape_cxx_string(get_hdl_name(cell) + ' ') << ");\n";
2173 }
2174 }
2175 dec_indent();
2176
2177 log_debug("Debug information statistics for module `%s':\n", log_id(module));
2178 log_debug(" Public wires: %zu, of which:\n", count_public_wires);
2179 log_debug(" Member wires: %zu, of which:\n", count_member_wires);
2180 log_debug(" Undriven: %zu (incl. inputs)\n", count_undriven);
2181 log_debug(" Driven sync: %zu\n", count_driven_sync);
2182 log_debug(" Driven comb: %zu\n", count_driven_comb);
2183 log_debug(" Mixed driver: %zu\n", count_mixed_driver);
2184 if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) {
2185 log_debug(" Inline wires: %zu\n", count_inline_wires);
2186 log_debug(" Alias wires: %zu\n", count_alias_wires);
2187 log_debug(" Const wires: %zu\n", count_const_wires);
2188 log_debug(" Other wires: %zu%s\n", count_skipped_wires,
2189 count_skipped_wires > 0 ? " (debug unavailable)" : "");
2190 }
2191 }
2192
2193 void dump_metadata_map(const dict<RTLIL::IdString, RTLIL::Const> &metadata_map)
2194 {
2195 if (metadata_map.empty()) {
2196 f << "metadata_map()";
2197 return;
2198 }
2199 f << "metadata_map({\n";
2200 inc_indent();
2201 for (auto metadata_item : metadata_map) {
2202 if (!metadata_item.first.begins_with("\\"))
2203 continue;
2204 f << indent << "{ " << escape_cxx_string(metadata_item.first.str().substr(1)) << ", ";
2205 if (metadata_item.second.flags & RTLIL::CONST_FLAG_REAL) {
2206 f << std::showpoint << std::stod(metadata_item.second.decode_string()) << std::noshowpoint;
2207 } else if (metadata_item.second.flags & RTLIL::CONST_FLAG_STRING) {
2208 f << escape_cxx_string(metadata_item.second.decode_string());
2209 } else {
2210 f << metadata_item.second.as_int(/*is_signed=*/metadata_item.second.flags & RTLIL::CONST_FLAG_SIGNED);
2211 if (!(metadata_item.second.flags & RTLIL::CONST_FLAG_SIGNED))
2212 f << "u";
2213 }
2214 f << " },\n";
2215 }
2216 dec_indent();
2217 f << indent << "})";
2218 }
2219
2220 void dump_module_intf(RTLIL::Module *module)
2221 {
2222 dump_attrs(module);
2223 if (module->get_bool_attribute(ID(cxxrtl_blackbox))) {
2224 if (module->has_attribute(ID(cxxrtl_template)))
2225 f << indent << "template" << template_params(module, /*is_decl=*/true) << "\n";
2226 f << indent << "struct " << mangle(module) << " : public module {\n";
2227 inc_indent();
2228 for (auto wire : module->wires()) {
2229 if (wire->port_id != 0)
2230 dump_wire(wire, /*is_local=*/false);
2231 }
2232 f << "\n";
2233 f << indent << "void reset() override {\n";
2234 dump_reset_method(module);
2235 f << indent << "}\n";
2236 f << "\n";
2237 f << indent << "bool eval() override {\n";
2238 dump_eval_method(module);
2239 f << indent << "}\n";
2240 f << "\n";
2241 f << indent << "bool commit() override {\n";
2242 dump_commit_method(module);
2243 f << indent << "}\n";
2244 f << "\n";
2245 if (debug_info) {
2246 f << indent << "void debug_info(debug_items &items, std::string path = \"\") override {\n";
2247 dump_debug_info_method(module);
2248 f << indent << "}\n";
2249 f << "\n";
2250 }
2251 f << indent << "static std::unique_ptr<" << mangle(module);
2252 f << template_params(module, /*is_decl=*/false) << "> ";
2253 f << "create(std::string name, metadata_map parameters, metadata_map attributes);\n";
2254 dec_indent();
2255 f << indent << "}; // struct " << mangle(module) << "\n";
2256 f << "\n";
2257 if (blackbox_specializations.count(module)) {
2258 // If templated black boxes are used, the constructor of any module which includes the black box cell
2259 // (which calls the declared but not defined in the generated code `create` function) may only be used
2260 // if (a) the create function is defined in the same translation unit, or (b) the create function has
2261 // a forward-declared explicit specialization.
2262 //
2263 // Option (b) makes it possible to have the generated code and the black box implementation in different
2264 // translation units, which is convenient. Of course, its downside is that black boxes must predefine
2265 // a specialization for every combination of parameters the generated code may use; but since the main
2266 // purpose of templated black boxes is abstracting over datapath width, it is expected that there would
2267 // be very few such combinations anyway.
2268 for (auto specialization : blackbox_specializations[module]) {
2269 f << indent << "template<>\n";
2270 f << indent << "std::unique_ptr<" << mangle(module) << specialization << "> ";
2271 f << mangle(module) << specialization << "::";
2272 f << "create(std::string name, metadata_map parameters, metadata_map attributes);\n";
2273 f << "\n";
2274 }
2275 }
2276 } else {
2277 f << indent << "struct " << mangle(module) << " : public module {\n";
2278 inc_indent();
2279 for (auto wire : module->wires())
2280 dump_wire(wire, /*is_local=*/false);
2281 for (auto wire : module->wires())
2282 dump_debug_wire(wire, /*is_local=*/false);
2283 bool has_memories = false;
2284 for (auto &mem : mod_memories[module]) {
2285 dump_attrs(&mem);
2286 f << indent << "memory<" << mem.width << "> " << mangle(&mem)
2287 << " { " << mem.size << "u };\n";
2288 has_memories = true;
2289 }
2290 if (has_memories)
2291 f << "\n";
2292 bool has_cells = false;
2293 for (auto cell : module->cells()) {
2294 if (is_internal_cell(cell->type))
2295 continue;
2296 dump_attrs(cell);
2297 RTLIL::Module *cell_module = module->design->module(cell->type);
2298 log_assert(cell_module != nullptr);
2299 if (cell_module->get_bool_attribute(ID(cxxrtl_blackbox))) {
2300 f << indent << "std::unique_ptr<" << mangle(cell_module) << template_args(cell) << "> ";
2301 f << mangle(cell) << " = " << mangle(cell_module) << template_args(cell);
2302 f << "::create(" << escape_cxx_string(get_hdl_name(cell)) << ", ";
2303 dump_metadata_map(cell->parameters);
2304 f << ", ";
2305 dump_metadata_map(cell->attributes);
2306 f << ");\n";
2307 } else {
2308 f << indent << mangle(cell_module) << " " << mangle(cell) << " {interior()};\n";
2309 }
2310 has_cells = true;
2311 }
2312 if (has_cells)
2313 f << "\n";
2314 f << indent << mangle(module) << "(interior) {}\n";
2315 f << indent << mangle(module) << "() {\n";
2316 inc_indent();
2317 f << indent << "reset();\n";
2318 dec_indent();
2319 f << indent << "};\n";
2320 f << "\n";
2321 f << indent << "void reset() override;\n";
2322 f << indent << "bool eval() override;\n";
2323 f << indent << "bool commit() override;\n";
2324 if (debug_info) {
2325 if (debug_eval) {
2326 f << "\n";
2327 f << indent << "void debug_eval();\n";
2328 for (auto wire : module->wires())
2329 if (debug_wire_types[wire].is_outline()) {
2330 f << indent << "debug_outline debug_eval_outline { std::bind(&"
2331 << mangle(module) << "::debug_eval, this) };\n";
2332 break;
2333 }
2334 }
2335 f << "\n";
2336 f << indent << "void debug_info(debug_items &items, std::string path = \"\") override;\n";
2337 }
2338 dec_indent();
2339 f << indent << "}; // struct " << mangle(module) << "\n";
2340 f << "\n";
2341 }
2342 }
2343
2344 void dump_module_impl(RTLIL::Module *module)
2345 {
2346 if (module->get_bool_attribute(ID(cxxrtl_blackbox)))
2347 return;
2348 f << indent << "void " << mangle(module) << "::reset() {\n";
2349 dump_reset_method(module);
2350 f << indent << "}\n";
2351 f << "\n";
2352 f << indent << "bool " << mangle(module) << "::eval() {\n";
2353 dump_eval_method(module);
2354 f << indent << "}\n";
2355 f << "\n";
2356 f << indent << "bool " << mangle(module) << "::commit() {\n";
2357 dump_commit_method(module);
2358 f << indent << "}\n";
2359 f << "\n";
2360 if (debug_info) {
2361 if (debug_eval) {
2362 f << indent << "void " << mangle(module) << "::debug_eval() {\n";
2363 dump_debug_eval_method(module);
2364 f << indent << "}\n";
2365 f << "\n";
2366 }
2367 f << indent << "CXXRTL_EXTREMELY_COLD\n";
2368 f << indent << "void " << mangle(module) << "::debug_info(debug_items &items, std::string path) {\n";
2369 dump_debug_info_method(module);
2370 f << indent << "}\n";
2371 f << "\n";
2372 }
2373 }
2374
2375 void dump_design(RTLIL::Design *design)
2376 {
2377 RTLIL::Module *top_module = nullptr;
2378 std::vector<RTLIL::Module*> modules;
2379 TopoSort<RTLIL::Module*> topo_design;
2380 for (auto module : design->modules()) {
2381 if (!design->selected_module(module))
2382 continue;
2383 if (module->get_bool_attribute(ID(cxxrtl_blackbox)))
2384 modules.push_back(module); // cxxrtl blackboxes first
2385 if (module->get_blackbox_attribute() || module->get_bool_attribute(ID(cxxrtl_blackbox)))
2386 continue;
2387 if (module->get_bool_attribute(ID::top))
2388 top_module = module;
2389
2390 topo_design.node(module);
2391 for (auto cell : module->cells()) {
2392 if (is_internal_cell(cell->type) || is_cxxrtl_blackbox_cell(cell))
2393 continue;
2394 RTLIL::Module *cell_module = design->module(cell->type);
2395 log_assert(cell_module != nullptr);
2396 topo_design.edge(cell_module, module);
2397 }
2398 }
2399 bool no_loops = topo_design.sort();
2400 log_assert(no_loops);
2401 modules.insert(modules.end(), topo_design.sorted.begin(), topo_design.sorted.end());
2402
2403 if (split_intf) {
2404 // The only thing more depraved than include guards, is mangling filenames to turn them into include guards.
2405 std::string include_guard = design_ns + "_header";
2406 std::transform(include_guard.begin(), include_guard.end(), include_guard.begin(), ::toupper);
2407
2408 f << "#ifndef " << include_guard << "\n";
2409 f << "#define " << include_guard << "\n";
2410 f << "\n";
2411 if (top_module != nullptr && debug_info) {
2412 f << "#include <backends/cxxrtl/cxxrtl_capi.h>\n";
2413 f << "\n";
2414 f << "#ifdef __cplusplus\n";
2415 f << "extern \"C\" {\n";
2416 f << "#endif\n";
2417 f << "\n";
2418 f << "cxxrtl_toplevel " << design_ns << "_create();\n";
2419 f << "\n";
2420 f << "#ifdef __cplusplus\n";
2421 f << "}\n";
2422 f << "#endif\n";
2423 f << "\n";
2424 } else {
2425 f << "// The CXXRTL C API is not available because the design is built without debug information.\n";
2426 f << "\n";
2427 }
2428 f << "#ifdef __cplusplus\n";
2429 f << "\n";
2430 f << "#include <backends/cxxrtl/cxxrtl.h>\n";
2431 f << "\n";
2432 f << "using namespace cxxrtl;\n";
2433 f << "\n";
2434 f << "namespace " << design_ns << " {\n";
2435 f << "\n";
2436 for (auto module : modules)
2437 dump_module_intf(module);
2438 f << "} // namespace " << design_ns << "\n";
2439 f << "\n";
2440 f << "#endif // __cplusplus\n";
2441 f << "\n";
2442 f << "#endif\n";
2443 *intf_f << f.str(); f.str("");
2444 }
2445
2446 if (split_intf)
2447 f << "#include \"" << intf_filename << "\"\n";
2448 else
2449 f << "#include <backends/cxxrtl/cxxrtl.h>\n";
2450 f << "\n";
2451 f << "#if defined(CXXRTL_INCLUDE_CAPI_IMPL) || \\\n";
2452 f << " defined(CXXRTL_INCLUDE_VCD_CAPI_IMPL)\n";
2453 f << "#include <backends/cxxrtl/cxxrtl_capi.cc>\n";
2454 f << "#endif\n";
2455 f << "\n";
2456 f << "#if defined(CXXRTL_INCLUDE_VCD_CAPI_IMPL)\n";
2457 f << "#include <backends/cxxrtl/cxxrtl_vcd_capi.cc>\n";
2458 f << "#endif\n";
2459 f << "\n";
2460 f << "using namespace cxxrtl_yosys;\n";
2461 f << "\n";
2462 f << "namespace " << design_ns << " {\n";
2463 f << "\n";
2464 for (auto module : modules) {
2465 if (!split_intf)
2466 dump_module_intf(module);
2467 dump_module_impl(module);
2468 }
2469 f << "} // namespace " << design_ns << "\n";
2470 f << "\n";
2471 if (top_module != nullptr && debug_info) {
2472 f << "extern \"C\"\n";
2473 f << "cxxrtl_toplevel " << design_ns << "_create() {\n";
2474 inc_indent();
2475 std::string top_type = design_ns + "::" + mangle(top_module);
2476 f << indent << "return new _cxxrtl_toplevel { ";
2477 f << "std::unique_ptr<" << top_type << ">(new " + top_type + ")";
2478 f << " };\n";
2479 dec_indent();
2480 f << "}\n";
2481 }
2482
2483 *impl_f << f.str(); f.str("");
2484 }
2485
2486 // Edge-type sync rules require us to emit edge detectors, which require coordination between
2487 // eval and commit phases. To do this we need to collect them upfront.
2488 //
2489 // Note that the simulator commit phase operates at wire granularity but edge-type sync rules
2490 // operate at wire bit granularity; it is possible to have code similar to:
2491 // wire [3:0] clocks;
2492 // always @(posedge clocks[0]) ...
2493 // To handle this we track edge sensitivity both for wires and wire bits.
2494 void register_edge_signal(SigMap &sigmap, RTLIL::SigSpec signal, RTLIL::SyncType type)
2495 {
2496 signal = sigmap(signal);
2497 if (signal.is_fully_const())
2498 return; // a clock, or more commonly a reset, can be tied to a constant driver
2499 log_assert(is_valid_clock(signal));
2500 log_assert(type == RTLIL::STp || type == RTLIL::STn || type == RTLIL::STe);
2501
2502 RTLIL::SigBit sigbit = signal[0];
2503 if (!edge_types.count(sigbit))
2504 edge_types[sigbit] = type;
2505 else if (edge_types[sigbit] != type)
2506 edge_types[sigbit] = RTLIL::STe;
2507 // Cannot use as_wire because signal might not be a full wire, instead extract the wire from the sigbit
2508 edge_wires.insert(sigbit.wire);
2509 }
2510
2511 void analyze_design(RTLIL::Design *design)
2512 {
2513 bool has_feedback_arcs = false;
2514 bool has_buffered_comb_wires = false;
2515
2516 for (auto module : design->modules()) {
2517 if (!design->selected_module(module))
2518 continue;
2519
2520 SigMap &sigmap = sigmaps[module];
2521 sigmap.set(module);
2522
2523 std::vector<Mem> &memories = mod_memories[module];
2524 memories = Mem::get_all_memories(module);
2525 for (auto &mem : memories) {
2526 mem.narrow();
2527 mem.coalesce_inits();
2528 }
2529
2530 if (module->get_bool_attribute(ID(cxxrtl_blackbox))) {
2531 for (auto port : module->ports) {
2532 RTLIL::Wire *wire = module->wire(port);
2533 if (wire->port_input && !wire->port_output) {
2534 wire_types[wire] = debug_wire_types[wire] = {WireType::MEMBER};
2535 } else if (wire->port_input || wire->port_output) {
2536 wire_types[wire] = debug_wire_types[wire] = {WireType::BUFFERED};
2537 }
2538 if (wire->has_attribute(ID(cxxrtl_edge))) {
2539 RTLIL::Const edge_attr = wire->attributes[ID(cxxrtl_edge)];
2540 if (!(edge_attr.flags & RTLIL::CONST_FLAG_STRING) || (int)edge_attr.decode_string().size() != GetSize(wire))
2541 log_cmd_error("Attribute `cxxrtl_edge' of port `%s.%s' is not a string with one character per bit.\n",
2542 log_id(module), log_signal(wire));
2543
2544 std::string edges = wire->get_string_attribute(ID(cxxrtl_edge));
2545 for (int i = 0; i < GetSize(wire); i++) {
2546 RTLIL::SigSpec wire_sig = wire;
2547 switch (edges[i]) {
2548 case '-': break;
2549 case 'p': register_edge_signal(sigmap, wire_sig[i], RTLIL::STp); break;
2550 case 'n': register_edge_signal(sigmap, wire_sig[i], RTLIL::STn); break;
2551 case 'a': register_edge_signal(sigmap, wire_sig[i], RTLIL::STe); break;
2552 default:
2553 log_cmd_error("Attribute `cxxrtl_edge' of port `%s.%s' contains specifiers "
2554 "other than '-', 'p', 'n', or 'a'.\n",
2555 log_id(module), log_signal(wire));
2556 }
2557 }
2558 }
2559 }
2560
2561 // Black boxes converge by default, since their implementations are quite unlikely to require
2562 // internal propagation of comb signals.
2563 eval_converges[module] = true;
2564 continue;
2565 }
2566
2567 for (auto wire : module->wires())
2568 if (wire->has_attribute(ID::init))
2569 wire_init[wire] = wire->attributes.at(ID::init);
2570
2571 // Construct a flow graph where each node is a basic computational operation generally corresponding
2572 // to a fragment of the RTLIL netlist.
2573 FlowGraph flow;
2574
2575 for (auto conn : module->connections())
2576 flow.add_node(conn);
2577
2578 for (auto cell : module->cells()) {
2579 if (!cell->known())
2580 log_cmd_error("Unknown cell `%s'.\n", log_id(cell->type));
2581
2582 if (cell->is_mem_cell())
2583 continue;
2584
2585 RTLIL::Module *cell_module = design->module(cell->type);
2586 if (cell_module &&
2587 cell_module->get_blackbox_attribute() &&
2588 !cell_module->get_bool_attribute(ID(cxxrtl_blackbox)))
2589 log_cmd_error("External blackbox cell `%s' is not marked as a CXXRTL blackbox.\n", log_id(cell->type));
2590
2591 if (cell_module &&
2592 cell_module->get_bool_attribute(ID(cxxrtl_blackbox)) &&
2593 cell_module->get_bool_attribute(ID(cxxrtl_template)))
2594 blackbox_specializations[cell_module].insert(template_args(cell));
2595
2596 flow.add_node(cell);
2597
2598 // Various DFF cells are treated like posedge/negedge processes, see above for details.
2599 if (cell->type.in(ID($dff), ID($dffe), ID($adff), ID($adffe), ID($aldff), ID($aldffe), ID($dffsr), ID($dffsre), ID($sdff), ID($sdffe), ID($sdffce))) {
2600 if (is_valid_clock(cell->getPort(ID::CLK)))
2601 register_edge_signal(sigmap, cell->getPort(ID::CLK),
2602 cell->parameters[ID::CLK_POLARITY].as_bool() ? RTLIL::STp : RTLIL::STn);
2603 }
2604 }
2605
2606 for (auto &mem : memories) {
2607 flow.add_node(&mem);
2608
2609 // Clocked memory cells are treated like posedge/negedge processes as well.
2610 for (auto &port : mem.rd_ports) {
2611 if (port.clk_enable)
2612 if (is_valid_clock(port.clk))
2613 register_edge_signal(sigmap, port.clk,
2614 port.clk_polarity ? RTLIL::STp : RTLIL::STn);
2615 // For read ports, also move initial value to wire_init (if any).
2616 for (int i = 0; i < GetSize(port.data); i++) {
2617 if (port.init_value[i] != State::Sx) {
2618 SigBit bit = port.data[i];
2619 if (bit.wire) {
2620 auto &init = wire_init[bit.wire];
2621 if (init == RTLIL::Const()) {
2622 init = RTLIL::Const(State::Sx, GetSize(bit.wire));
2623 }
2624 init[bit.offset] = port.init_value[i];
2625 }
2626 }
2627 }
2628 }
2629 for (auto &port : mem.wr_ports) {
2630 if (port.clk_enable)
2631 if (is_valid_clock(port.clk))
2632 register_edge_signal(sigmap, port.clk,
2633 port.clk_polarity ? RTLIL::STp : RTLIL::STn);
2634 }
2635
2636 if (!mem.wr_ports.empty())
2637 writable_memories.insert({module, mem.memid});
2638 }
2639
2640 for (auto proc : module->processes) {
2641 flow.add_node(proc.second);
2642
2643 for (auto sync : proc.second->syncs) {
2644 switch (sync->type) {
2645 // Edge-type sync rules require pre-registration.
2646 case RTLIL::STp:
2647 case RTLIL::STn:
2648 case RTLIL::STe:
2649 register_edge_signal(sigmap, sync->signal, sync->type);
2650 break;
2651
2652 // Level-type sync rules require no special handling.
2653 case RTLIL::ST0:
2654 case RTLIL::ST1:
2655 case RTLIL::STa:
2656 break;
2657
2658 case RTLIL::STg:
2659 log_cmd_error("Global clock is not supported.\n");
2660
2661 // Handling of init-type sync rules is delegated to the `proc_init` pass, so we can use the wire
2662 // attribute regardless of input.
2663 case RTLIL::STi:
2664 log_assert(false);
2665 }
2666 for (auto &memwr : sync->mem_write_actions) {
2667 writable_memories.insert({module, memwr.memid});
2668 }
2669 }
2670 }
2671
2672 // Construct a linear order of the flow graph that minimizes the amount of feedback arcs. A flow graph
2673 // without feedback arcs can generally be evaluated in a single pass, i.e. it always requires only
2674 // a single delta cycle.
2675 Scheduler<FlowGraph::Node> scheduler;
2676 dict<FlowGraph::Node*, Scheduler<FlowGraph::Node>::Vertex*, hash_ptr_ops> node_vertex_map;
2677 for (auto node : flow.nodes)
2678 node_vertex_map[node] = scheduler.add(node);
2679 for (auto node_comb_def : flow.node_comb_defs) {
2680 auto vertex = node_vertex_map[node_comb_def.first];
2681 for (auto wire : node_comb_def.second)
2682 for (auto succ_node : flow.wire_uses[wire]) {
2683 auto succ_vertex = node_vertex_map[succ_node];
2684 vertex->succs.insert(succ_vertex);
2685 succ_vertex->preds.insert(vertex);
2686 }
2687 }
2688
2689 // Find out whether the order includes any feedback arcs.
2690 std::vector<FlowGraph::Node*> node_order;
2691 pool<FlowGraph::Node*, hash_ptr_ops> evaluated_nodes;
2692 pool<const RTLIL::Wire*> feedback_wires;
2693 for (auto vertex : scheduler.schedule()) {
2694 auto node = vertex->data;
2695 node_order.push_back(node);
2696 // Any wire that is an output of node vo and input of node vi where vo is scheduled later than vi
2697 // is a feedback wire. Feedback wires indicate apparent logic loops in the design, which may be
2698 // caused by a true logic loop, but usually are a benign result of dependency tracking that works
2699 // on wire, not bit, level. Nevertheless, feedback wires cannot be unbuffered.
2700 evaluated_nodes.insert(node);
2701 for (auto wire : flow.node_comb_defs[node])
2702 for (auto succ_node : flow.wire_uses[wire])
2703 if (evaluated_nodes[succ_node])
2704 feedback_wires.insert(wire);
2705 }
2706 if (!feedback_wires.empty()) {
2707 has_feedback_arcs = true;
2708 log("Module `%s' contains feedback arcs through wires:\n", log_id(module));
2709 for (auto wire : feedback_wires)
2710 log(" %s\n", log_id(wire));
2711 }
2712
2713 // Conservatively assign wire types. Assignment of types BUFFERED and MEMBER is final, but assignment
2714 // of type LOCAL may be further refined to UNUSED or INLINE.
2715 for (auto wire : module->wires()) {
2716 auto &wire_type = wire_types[wire];
2717 wire_type = {WireType::BUFFERED};
2718
2719 if (feedback_wires[wire]) continue;
2720 if (wire->port_output && !module->get_bool_attribute(ID::top)) continue;
2721 if (!wire->name.isPublic() && !unbuffer_internal) continue;
2722 if (wire->name.isPublic() && !unbuffer_public) continue;
2723 if (flow.wire_sync_defs.count(wire) > 0) continue;
2724 wire_type = {WireType::MEMBER};
2725
2726 if (edge_wires[wire]) continue;
2727 if (wire->get_bool_attribute(ID::keep)) continue;
2728 if (wire->port_input || wire->port_output) continue;
2729 if (!wire->name.isPublic() && !localize_internal) continue;
2730 if (wire->name.isPublic() && !localize_public) continue;
2731 wire_type = {WireType::LOCAL};
2732 }
2733
2734 // Discover nodes reachable from primary outputs (i.e. members) and collect reachable wire users.
2735 pool<FlowGraph::Node*, hash_ptr_ops> worklist;
2736 for (auto node : flow.nodes) {
2737 if (node->type == FlowGraph::Node::Type::CELL_EVAL && is_effectful_cell(node->cell->type))
2738 worklist.insert(node); // node has effects
2739 else if (node->type == FlowGraph::Node::Type::MEM_WRPORTS)
2740 worklist.insert(node); // node is memory write
2741 else if (node->type == FlowGraph::Node::Type::PROCESS_SYNC && is_memwr_process(node->process))
2742 worklist.insert(node); // node is memory write
2743 else if (flow.node_sync_defs.count(node))
2744 worklist.insert(node); // node is a flip-flop
2745 else if (flow.node_comb_defs.count(node)) {
2746 for (auto wire : flow.node_comb_defs[node])
2747 if (wire_types[wire].is_member())
2748 worklist.insert(node); // node drives public wires
2749 }
2750 }
2751 dict<const RTLIL::Wire*, pool<FlowGraph::Node*, hash_ptr_ops>> live_wires;
2752 pool<FlowGraph::Node*, hash_ptr_ops> live_nodes;
2753 while (!worklist.empty()) {
2754 auto node = worklist.pop();
2755 live_nodes.insert(node);
2756 for (auto wire : flow.node_uses[node]) {
2757 live_wires[wire].insert(node);
2758 for (auto pred_node : flow.wire_comb_defs[wire])
2759 if (!live_nodes[pred_node])
2760 worklist.insert(pred_node);
2761 }
2762 }
2763
2764 // Refine wire types taking into account the amount of uses from reachable nodes only.
2765 for (auto wire : module->wires()) {
2766 auto &wire_type = wire_types[wire];
2767 if (!wire_type.is_local()) continue;
2768 if (live_wires[wire].empty()) {
2769 wire_type = {WireType::UNUSED}; // wire never used
2770 continue;
2771 }
2772
2773 if (!wire->name.isPublic() && !inline_internal) continue;
2774 if (wire->name.isPublic() && !inline_public) continue;
2775 if (flow.is_inlinable(wire, live_wires[wire])) {
2776 if (flow.wire_comb_defs[wire].size() > 1)
2777 log_cmd_error("Wire %s.%s has multiple drivers!\n", log_id(module), log_id(wire));
2778 log_assert(flow.wire_comb_defs[wire].size() == 1);
2779 FlowGraph::Node *node = *flow.wire_comb_defs[wire].begin();
2780 switch (node->type) {
2781 case FlowGraph::Node::Type::CELL_EVAL:
2782 if (!is_inlinable_cell(node->cell->type)) continue;
2783 wire_type = {WireType::INLINE, node->cell}; // wire replaced with cell
2784 break;
2785 case FlowGraph::Node::Type::CONNECT:
2786 wire_type = {WireType::INLINE, node->connect.second}; // wire replaced with sig
2787 break;
2788 default: continue;
2789 }
2790 live_nodes.erase(node);
2791 }
2792 }
2793
2794 // Emit reachable nodes in eval().
2795 for (auto node : node_order)
2796 if (live_nodes[node])
2797 schedule[module].push_back(*node);
2798
2799 // For maximum performance, the state of the simulation (which is the same as the set of its double buffered
2800 // wires, since using a singly buffered wire for any kind of state introduces a race condition) should contain
2801 // no wires attached to combinatorial outputs. Feedback wires, by definition, make that impossible. However,
2802 // it is possible that a design with no feedback arcs would end up with doubly buffered wires in such cases
2803 // as a wire with multiple drivers where one of them is combinatorial and the other is synchronous. Such designs
2804 // also require more than one delta cycle to converge.
2805 pool<const RTLIL::Wire*> buffered_comb_wires;
2806 for (auto wire : module->wires())
2807 if (wire_types[wire].is_buffered() && !feedback_wires[wire] && flow.wire_comb_defs[wire].size() > 0)
2808 buffered_comb_wires.insert(wire);
2809 if (!buffered_comb_wires.empty()) {
2810 has_buffered_comb_wires = true;
2811 log("Module `%s' contains buffered combinatorial wires:\n", log_id(module));
2812 for (auto wire : buffered_comb_wires)
2813 log(" %s\n", log_id(wire));
2814 }
2815
2816 // Record whether eval() requires only one delta cycle in this module.
2817 eval_converges[module] = feedback_wires.empty() && buffered_comb_wires.empty();
2818
2819 if (debug_info) {
2820 // Annotate wire bits with the type of their driver; this is exposed in the debug metadata.
2821 for (auto item : flow.bit_has_state)
2822 bit_has_state.insert(item);
2823
2824 // Assign debug information wire types to public wires according to the chosen debug level.
2825 // Unlike with optimized wire types, all assignments here are final.
2826 for (auto wire : module->wires()) {
2827 const auto &wire_type = wire_types[wire];
2828 auto &debug_wire_type = debug_wire_types[wire];
2829
2830 if (!debug_info) continue;
2831 if (wire->port_input || wire_type.is_buffered())
2832 debug_wire_type = wire_type; // wire contains state
2833 else if (!wire->name.isPublic())
2834 continue; // internal and stateless
2835
2836 if (!debug_member) continue;
2837 if (wire_type.is_member())
2838 debug_wire_type = wire_type; // wire is a member
2839
2840 if (!debug_alias) continue;
2841 const RTLIL::Wire *it = wire;
2842 while (flow.is_inlinable(it)) {
2843 log_assert(flow.wire_comb_defs[it].size() == 1);
2844 FlowGraph::Node *node = *flow.wire_comb_defs[it].begin();
2845 if (node->type != FlowGraph::Node::Type::CONNECT) break; // not an alias
2846 RTLIL::SigSpec rhs = node->connect.second;
2847 if (rhs.is_fully_const()) {
2848 debug_wire_type = {WireType::CONST, rhs}; // wire replaced with const
2849 } else if (rhs.is_wire()) {
2850 if (wire_types[rhs.as_wire()].is_member())
2851 debug_wire_type = {WireType::ALIAS, rhs}; // wire replaced with wire
2852 else if (debug_eval && rhs.as_wire()->name.isPublic())
2853 debug_wire_type = {WireType::ALIAS, rhs}; // wire replaced with outline
2854 it = rhs.as_wire(); // and keep looking
2855 continue;
2856 }
2857 break;
2858 }
2859
2860 if (!debug_eval) continue;
2861 if (!debug_wire_type.is_exact() && !wire_type.is_member())
2862 debug_wire_type = {WireType::OUTLINE}; // wire is local or inlined
2863 }
2864
2865 // Discover nodes reachable from primary outputs (i.e. outlines) up until primary inputs (i.e. members)
2866 // and collect reachable wire users.
2867 pool<FlowGraph::Node*, hash_ptr_ops> worklist;
2868 for (auto node : flow.nodes) {
2869 if (flow.node_comb_defs.count(node))
2870 for (auto wire : flow.node_comb_defs[node])
2871 if (debug_wire_types[wire].is_outline())
2872 worklist.insert(node); // node drives outline
2873 }
2874 dict<const RTLIL::Wire*, pool<FlowGraph::Node*, hash_ptr_ops>> debug_live_wires;
2875 pool<FlowGraph::Node*, hash_ptr_ops> debug_live_nodes;
2876 while (!worklist.empty()) {
2877 auto node = worklist.pop();
2878 debug_live_nodes.insert(node);
2879 for (auto wire : flow.node_uses[node]) {
2880 if (debug_wire_types[wire].is_member())
2881 continue; // node uses member
2882 if (debug_wire_types[wire].is_exact())
2883 continue; // node uses alias or const
2884 debug_live_wires[wire].insert(node);
2885 for (auto pred_node : flow.wire_comb_defs[wire])
2886 if (!debug_live_nodes[pred_node])
2887 worklist.insert(pred_node);
2888 }
2889 }
2890
2891 // Assign debug information wire types to internal wires used by reachable nodes. This is similar
2892 // to refining optimized wire types with the exception that the assignments here are first and final.
2893 for (auto wire : module->wires()) {
2894 const auto &wire_type = wire_types[wire];
2895 auto &debug_wire_type = debug_wire_types[wire];
2896 if (wire->name.isPublic()) continue;
2897
2898 if (debug_live_wires[wire].empty()) {
2899 continue; // wire never used
2900 } else if (flow.is_inlinable(wire, debug_live_wires[wire])) {
2901 log_assert(flow.wire_comb_defs[wire].size() == 1);
2902 FlowGraph::Node *node = *flow.wire_comb_defs[wire].begin();
2903 switch (node->type) {
2904 case FlowGraph::Node::Type::CELL_EVAL:
2905 if (!is_inlinable_cell(node->cell->type)) continue;
2906 debug_wire_type = {WireType::INLINE, node->cell}; // wire replaced with cell
2907 break;
2908 case FlowGraph::Node::Type::CONNECT:
2909 debug_wire_type = {WireType::INLINE, node->connect.second}; // wire replaced with sig
2910 break;
2911 default: continue;
2912 }
2913 debug_live_nodes.erase(node);
2914 } else if (wire_type.is_member() || wire_type.type == WireType::LOCAL) {
2915 debug_wire_type = wire_type; // wire not inlinable
2916 } else {
2917 log_assert(wire_type.type == WireType::INLINE ||
2918 wire_type.type == WireType::UNUSED);
2919 if (flow.wire_comb_defs[wire].size() == 0) {
2920 if (wire_init.count(wire)) { // wire never modified
2921 debug_wire_type = {WireType::CONST, wire_init.at(wire)};
2922 } else {
2923 debug_wire_type = {WireType::CONST, RTLIL::SigSpec(RTLIL::S0, wire->width)};
2924 }
2925 } else {
2926 debug_wire_type = {WireType::LOCAL}; // wire used only for debug
2927 }
2928 }
2929 }
2930
2931 // Emit reachable nodes in debug_eval().
2932 for (auto node : node_order)
2933 if (debug_live_nodes[node])
2934 debug_schedule[module].push_back(*node);
2935 }
2936
2937 auto show_wire_type = [&](const RTLIL::Wire* wire, const WireType &wire_type) {
2938 const char *type_str;
2939 switch (wire_type.type) {
2940 case WireType::UNUSED: type_str = "UNUSED"; break;
2941 case WireType::BUFFERED: type_str = "BUFFERED"; break;
2942 case WireType::MEMBER: type_str = "MEMBER"; break;
2943 case WireType::OUTLINE: type_str = "OUTLINE"; break;
2944 case WireType::LOCAL: type_str = "LOCAL"; break;
2945 case WireType::INLINE: type_str = "INLINE"; break;
2946 case WireType::ALIAS: type_str = "ALIAS"; break;
2947 case WireType::CONST: type_str = "CONST"; break;
2948 default: type_str = "(invalid)";
2949 }
2950 if (wire_type.sig_subst.empty())
2951 log_debug(" %s: %s\n", log_signal((RTLIL::Wire*)wire), type_str);
2952 else
2953 log_debug(" %s: %s = %s\n", log_signal((RTLIL::Wire*)wire), type_str, log_signal(wire_type.sig_subst));
2954 };
2955 if (print_wire_types && !wire_types.empty()) {
2956 log_debug("Wire types:\n");
2957 for (auto wire_type : wire_types)
2958 show_wire_type(wire_type.first, wire_type.second);
2959 }
2960 if (print_debug_wire_types && !debug_wire_types.empty()) {
2961 log_debug("Debug wire types:\n");
2962 for (auto debug_wire_type : debug_wire_types)
2963 show_wire_type(debug_wire_type.first, debug_wire_type.second);
2964 }
2965 }
2966 if (has_feedback_arcs || has_buffered_comb_wires) {
2967 // Although both non-feedback buffered combinatorial wires and apparent feedback wires may be eliminated
2968 // by optimizing the design, if after `proc; flatten` there are any feedback wires remaining, it is very
2969 // likely that these feedback wires are indicative of a true logic loop, so they get emphasized in the message.
2970 const char *why_pessimistic = nullptr;
2971 if (has_feedback_arcs)
2972 why_pessimistic = "feedback wires";
2973 else if (has_buffered_comb_wires)
2974 why_pessimistic = "buffered combinatorial wires";
2975 log_warning("Design contains %s, which require delta cycles during evaluation.\n", why_pessimistic);
2976 if (!run_flatten)
2977 log("Flattening may eliminate %s from the design.\n", why_pessimistic);
2978 if (!run_proc)
2979 log("Converting processes to netlists may eliminate %s from the design.\n", why_pessimistic);
2980 }
2981 }
2982
2983 void check_design(RTLIL::Design *design, bool &has_sync_init)
2984 {
2985 has_sync_init = false;
2986
2987 for (auto module : design->modules()) {
2988 if (module->get_blackbox_attribute() && !module->has_attribute(ID(cxxrtl_blackbox)))
2989 continue;
2990
2991 if (!design->selected_whole_module(module))
2992 if (design->selected_module(module))
2993 log_cmd_error("Can't handle partially selected module `%s'!\n", id2cstr(module->name));
2994 if (!design->selected_module(module))
2995 continue;
2996
2997 for (auto proc : module->processes)
2998 for (auto sync : proc.second->syncs)
2999 if (sync->type == RTLIL::STi)
3000 has_sync_init = true;
3001 }
3002 }
3003
3004 void prepare_design(RTLIL::Design *design)
3005 {
3006 bool did_anything = false;
3007 bool has_sync_init;
3008 log_push();
3009 check_design(design, has_sync_init);
3010 if (run_hierarchy) {
3011 Pass::call(design, "hierarchy -auto-top");
3012 did_anything = true;
3013 }
3014 if (run_flatten) {
3015 Pass::call(design, "flatten");
3016 did_anything = true;
3017 }
3018 if (run_proc) {
3019 Pass::call(design, "proc");
3020 did_anything = true;
3021 } else if (has_sync_init) {
3022 // We're only interested in proc_init, but it depends on proc_prune and proc_clean, so call those
3023 // in case they weren't already. (This allows `yosys foo.v -o foo.cc` to work.)
3024 Pass::call(design, "proc_prune");
3025 Pass::call(design, "proc_clean");
3026 Pass::call(design, "proc_init");
3027 did_anything = true;
3028 }
3029 // Recheck the design if it was modified.
3030 if (did_anything)
3031 check_design(design, has_sync_init);
3032 log_assert(!has_sync_init);
3033 log_pop();
3034 if (did_anything)
3035 log_spacer();
3036 analyze_design(design);
3037 }
3038 };
3039
3040 struct CxxrtlBackend : public Backend {
3041 static const int DEFAULT_OPT_LEVEL = 6;
3042 static const int DEFAULT_DEBUG_LEVEL = 4;
3043
3044 CxxrtlBackend() : Backend("cxxrtl", "convert design to C++ RTL simulation") { }
3045 void help() override
3046 {
3047 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
3048 log("\n");
3049 log(" write_cxxrtl [options] [filename]\n");
3050 log("\n");
3051 log("Write C++ code that simulates the design. The generated code requires a driver\n");
3052 log("that instantiates the design, toggles its clock, and interacts with its ports.\n");
3053 log("\n");
3054 log("The following driver may be used as an example for a design with a single clock\n");
3055 log("driving rising edge triggered flip-flops:\n");
3056 log("\n");
3057 log(" #include \"top.cc\"\n");
3058 log("\n");
3059 log(" int main() {\n");
3060 log(" cxxrtl_design::p_top top;\n");
3061 log(" top.step();\n");
3062 log(" while (1) {\n");
3063 log(" /* user logic */\n");
3064 log(" top.p_clk.set(false);\n");
3065 log(" top.step();\n");
3066 log(" top.p_clk.set(true);\n");
3067 log(" top.step();\n");
3068 log(" }\n");
3069 log(" }\n");
3070 log("\n");
3071 log("Note that CXXRTL simulations, just like the hardware they are simulating, are\n");
3072 log("subject to race conditions. If, in the example above, the user logic would run\n");
3073 log("simultaneously with the rising edge of the clock, the design would malfunction.\n");
3074 log("\n");
3075 log("This backend supports replacing parts of the design with black boxes implemented\n");
3076 log("in C++. If a module marked as a CXXRTL black box, its implementation is ignored,\n");
3077 log("and the generated code consists only of an interface and a factory function.\n");
3078 log("The driver must implement the factory function that creates an implementation of\n");
3079 log("the black box, taking into account the parameters it is instantiated with.\n");
3080 log("\n");
3081 log("For example, the following Verilog code defines a CXXRTL black box interface for\n");
3082 log("a synchronous debug sink:\n");
3083 log("\n");
3084 log(" (* cxxrtl_blackbox *)\n");
3085 log(" module debug(...);\n");
3086 log(" (* cxxrtl_edge = \"p\" *) input clk;\n");
3087 log(" input en;\n");
3088 log(" input [7:0] i_data;\n");
3089 log(" (* cxxrtl_sync *) output [7:0] o_data;\n");
3090 log(" endmodule\n");
3091 log("\n");
3092 log("For this HDL interface, this backend will generate the following C++ interface:\n");
3093 log("\n");
3094 log(" struct bb_p_debug : public module {\n");
3095 log(" value<1> p_clk;\n");
3096 log(" bool posedge_p_clk() const { /* ... */ }\n");
3097 log(" value<1> p_en;\n");
3098 log(" value<8> p_i_data;\n");
3099 log(" wire<8> p_o_data;\n");
3100 log("\n");
3101 log(" bool eval() override;\n");
3102 log(" bool commit() override;\n");
3103 log("\n");
3104 log(" static std::unique_ptr<bb_p_debug>\n");
3105 log(" create(std::string name, metadata_map parameters, metadata_map attributes);\n");
3106 log(" };\n");
3107 log("\n");
3108 log("The `create' function must be implemented by the driver. For example, it could\n");
3109 log("always provide an implementation logging the values to standard error stream:\n");
3110 log("\n");
3111 log(" namespace cxxrtl_design {\n");
3112 log("\n");
3113 log(" struct stderr_debug : public bb_p_debug {\n");
3114 log(" bool eval() override {\n");
3115 log(" if (posedge_p_clk() && p_en)\n");
3116 log(" fprintf(stderr, \"debug: %%02x\\n\", p_i_data.data[0]);\n");
3117 log(" p_o_data.next = p_i_data;\n");
3118 log(" return bb_p_debug::eval();\n");
3119 log(" }\n");
3120 log(" };\n");
3121 log("\n");
3122 log(" std::unique_ptr<bb_p_debug>\n");
3123 log(" bb_p_debug::create(std::string name, cxxrtl::metadata_map parameters,\n");
3124 log(" cxxrtl::metadata_map attributes) {\n");
3125 log(" return std::make_unique<stderr_debug>();\n");
3126 log(" }\n");
3127 log("\n");
3128 log(" }\n");
3129 log("\n");
3130 log("For complex applications of black boxes, it is possible to parameterize their\n");
3131 log("port widths. For example, the following Verilog code defines a CXXRTL black box\n");
3132 log("interface for a configurable width debug sink:\n");
3133 log("\n");
3134 log(" (* cxxrtl_blackbox, cxxrtl_template = \"WIDTH\" *)\n");
3135 log(" module debug(...);\n");
3136 log(" parameter WIDTH = 8;\n");
3137 log(" (* cxxrtl_edge = \"p\" *) input clk;\n");
3138 log(" input en;\n");
3139 log(" (* cxxrtl_width = \"WIDTH\" *) input [WIDTH - 1:0] i_data;\n");
3140 log(" (* cxxrtl_width = \"WIDTH\" *) output [WIDTH - 1:0] o_data;\n");
3141 log(" endmodule\n");
3142 log("\n");
3143 log("For this parametric HDL interface, this backend will generate the following C++\n");
3144 log("interface (only the differences are shown):\n");
3145 log("\n");
3146 log(" template<size_t WIDTH>\n");
3147 log(" struct bb_p_debug : public module {\n");
3148 log(" // ...\n");
3149 log(" value<WIDTH> p_i_data;\n");
3150 log(" wire<WIDTH> p_o_data;\n");
3151 log(" // ...\n");
3152 log(" static std::unique_ptr<bb_p_debug<WIDTH>>\n");
3153 log(" create(std::string name, metadata_map parameters, metadata_map attributes);\n");
3154 log(" };\n");
3155 log("\n");
3156 log("The `create' function must be implemented by the driver, specialized for every\n");
3157 log("possible combination of template parameters. (Specialization is necessary to\n");
3158 log("enable separate compilation of generated code and black box implementations.)\n");
3159 log("\n");
3160 log(" template<size_t SIZE>\n");
3161 log(" struct stderr_debug : public bb_p_debug<SIZE> {\n");
3162 log(" // ...\n");
3163 log(" };\n");
3164 log("\n");
3165 log(" template<>\n");
3166 log(" std::unique_ptr<bb_p_debug<8>>\n");
3167 log(" bb_p_debug<8>::create(std::string name, cxxrtl::metadata_map parameters,\n");
3168 log(" cxxrtl::metadata_map attributes) {\n");
3169 log(" return std::make_unique<stderr_debug<8>>();\n");
3170 log(" }\n");
3171 log("\n");
3172 log("The following attributes are recognized by this backend:\n");
3173 log("\n");
3174 log(" cxxrtl_blackbox\n");
3175 log(" only valid on modules. if specified, the module contents are ignored,\n");
3176 log(" and the generated code includes only the module interface and a factory\n");
3177 log(" function, which will be called to instantiate the module.\n");
3178 log("\n");
3179 log(" cxxrtl_edge\n");
3180 log(" only valid on inputs of black boxes. must be one of \"p\", \"n\", \"a\".\n");
3181 log(" if specified on signal `clk`, the generated code includes edge detectors\n");
3182 log(" `posedge_p_clk()` (if \"p\"), `negedge_p_clk()` (if \"n\"), or both (if\n");
3183 log(" \"a\"), simplifying implementation of clocked black boxes.\n");
3184 log("\n");
3185 log(" cxxrtl_template\n");
3186 log(" only valid on black boxes. must contain a space separated sequence of\n");
3187 log(" identifiers that have a corresponding black box parameters. for each\n");
3188 log(" of them, the generated code includes a `size_t` template parameter.\n");
3189 log("\n");
3190 log(" cxxrtl_width\n");
3191 log(" only valid on ports of black boxes. must be a constant expression, which\n");
3192 log(" is directly inserted into generated code.\n");
3193 log("\n");
3194 log(" cxxrtl_comb, cxxrtl_sync\n");
3195 log(" only valid on outputs of black boxes. if specified, indicates that every\n");
3196 log(" bit of the output port is driven, correspondingly, by combinatorial or\n");
3197 log(" synchronous logic. this knowledge is used for scheduling optimizations.\n");
3198 log(" if neither is specified, the output will be pessimistically treated as\n");
3199 log(" driven by both combinatorial and synchronous logic.\n");
3200 log("\n");
3201 log("The following options are supported by this backend:\n");
3202 log("\n");
3203 log(" -print-wire-types, -print-debug-wire-types\n");
3204 log(" enable additional debug logging, for pass developers.\n");
3205 log("\n");
3206 log(" -header\n");
3207 log(" generate separate interface (.h) and implementation (.cc) files.\n");
3208 log(" if specified, the backend must be called with a filename, and filename\n");
3209 log(" of the interface is derived from filename of the implementation.\n");
3210 log(" otherwise, interface and implementation are generated together.\n");
3211 log("\n");
3212 log(" -namespace <ns-name>\n");
3213 log(" place the generated code into namespace <ns-name>. if not specified,\n");
3214 log(" \"cxxrtl_design\" is used.\n");
3215 log("\n");
3216 log(" -nohierarchy\n");
3217 log(" use design hierarchy as-is. in most designs, a top module should be\n");
3218 log(" present as it is exposed through the C API and has unbuffered outputs\n");
3219 log(" for improved performance; it will be determined automatically if absent.\n");
3220 log("\n");
3221 log(" -noflatten\n");
3222 log(" don't flatten the design. fully flattened designs can evaluate within\n");
3223 log(" one delta cycle if they have no combinatorial feedback.\n");
3224 log(" note that the debug interface and waveform dumps use full hierarchical\n");
3225 log(" names for all wires even in flattened designs.\n");
3226 log("\n");
3227 log(" -noproc\n");
3228 log(" don't convert processes to netlists. in most designs, converting\n");
3229 log(" processes significantly improves evaluation performance at the cost of\n");
3230 log(" slight increase in compilation time.\n");
3231 log("\n");
3232 log(" -O <level>\n");
3233 log(" set the optimization level. the default is -O%d. higher optimization\n", DEFAULT_OPT_LEVEL);
3234 log(" levels dramatically decrease compile and run time, and highest level\n");
3235 log(" possible for a design should be used.\n");
3236 log("\n");
3237 log(" -O0\n");
3238 log(" no optimization.\n");
3239 log("\n");
3240 log(" -O1\n");
3241 log(" unbuffer internal wires if possible.\n");
3242 log("\n");
3243 log(" -O2\n");
3244 log(" like -O1, and localize internal wires if possible.\n");
3245 log("\n");
3246 log(" -O3\n");
3247 log(" like -O2, and inline internal wires if possible.\n");
3248 log("\n");
3249 log(" -O4\n");
3250 log(" like -O3, and unbuffer public wires not marked (*keep*) if possible.\n");
3251 log("\n");
3252 log(" -O5\n");
3253 log(" like -O4, and localize public wires not marked (*keep*) if possible.\n");
3254 log("\n");
3255 log(" -O6\n");
3256 log(" like -O5, and inline public wires not marked (*keep*) if possible.\n");
3257 log("\n");
3258 log(" -g <level>\n");
3259 log(" set the debug level. the default is -g%d. higher debug levels provide\n", DEFAULT_DEBUG_LEVEL);
3260 log(" more visibility and generate more code, but do not pessimize evaluation.\n");
3261 log("\n");
3262 log(" -g0\n");
3263 log(" no debug information. the C API is disabled.\n");
3264 log("\n");
3265 log(" -g1\n");
3266 log(" include bare minimum of debug information necessary to access all design\n");
3267 log(" state. the C API is enabled.\n");
3268 log("\n");
3269 log(" -g2\n");
3270 log(" like -g1, but include debug information for all public wires that are\n");
3271 log(" directly accessible through the C++ interface.\n");
3272 log("\n");
3273 log(" -g3\n");
3274 log(" like -g2, and include debug information for public wires that are tied\n");
3275 log(" to a constant or another public wire.\n");
3276 log("\n");
3277 log(" -g4\n");
3278 log(" like -g3, and compute debug information on demand for all public wires\n");
3279 log(" that were optimized out.\n");
3280 log("\n");
3281 }
3282
3283 void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
3284 {
3285 bool print_wire_types = false;
3286 bool print_debug_wire_types = false;
3287 bool nohierarchy = false;
3288 bool noflatten = false;
3289 bool noproc = false;
3290 int opt_level = DEFAULT_OPT_LEVEL;
3291 int debug_level = DEFAULT_DEBUG_LEVEL;
3292 CxxrtlWorker worker;
3293
3294 log_header(design, "Executing CXXRTL backend.\n");
3295
3296 size_t argidx;
3297 for (argidx = 1; argidx < args.size(); argidx++)
3298 {
3299 if (args[argidx] == "-print-wire-types") {
3300 print_wire_types = true;
3301 continue;
3302 }
3303 if (args[argidx] == "-print-debug-wire-types") {
3304 print_debug_wire_types = true;
3305 continue;
3306 }
3307 if (args[argidx] == "-nohierarchy") {
3308 nohierarchy = true;
3309 continue;
3310 }
3311 if (args[argidx] == "-noflatten") {
3312 noflatten = true;
3313 continue;
3314 }
3315 if (args[argidx] == "-noproc") {
3316 noproc = true;
3317 continue;
3318 }
3319 if (args[argidx] == "-Og") {
3320 log_warning("The `-Og` option has been removed. Use `-g3` instead for complete "
3321 "design coverage regardless of optimization level.\n");
3322 continue;
3323 }
3324 if (args[argidx] == "-O" && argidx+1 < args.size() && args[argidx+1] == "g") {
3325 argidx++;
3326 log_warning("The `-Og` option has been removed. Use `-g3` instead for complete "
3327 "design coverage regardless of optimization level.\n");
3328 continue;
3329 }
3330 if (args[argidx] == "-O" && argidx+1 < args.size()) {
3331 opt_level = std::stoi(args[++argidx]);
3332 continue;
3333 }
3334 if (args[argidx].substr(0, 2) == "-O" && args[argidx].size() == 3 && isdigit(args[argidx][2])) {
3335 opt_level = std::stoi(args[argidx].substr(2));
3336 continue;
3337 }
3338 if (args[argidx] == "-g" && argidx+1 < args.size()) {
3339 debug_level = std::stoi(args[++argidx]);
3340 continue;
3341 }
3342 if (args[argidx].substr(0, 2) == "-g" && args[argidx].size() == 3 && isdigit(args[argidx][2])) {
3343 debug_level = std::stoi(args[argidx].substr(2));
3344 continue;
3345 }
3346 if (args[argidx] == "-header") {
3347 worker.split_intf = true;
3348 continue;
3349 }
3350 if (args[argidx] == "-namespace" && argidx+1 < args.size()) {
3351 worker.design_ns = args[++argidx];
3352 continue;
3353 }
3354 break;
3355 }
3356 extra_args(f, filename, args, argidx);
3357
3358 worker.print_wire_types = print_wire_types;
3359 worker.print_debug_wire_types = print_debug_wire_types;
3360 worker.run_hierarchy = !nohierarchy;
3361 worker.run_flatten = !noflatten;
3362 worker.run_proc = !noproc;
3363 switch (opt_level) {
3364 // the highest level here must match DEFAULT_OPT_LEVEL
3365 case 6:
3366 worker.inline_public = true;
3367 YS_FALLTHROUGH
3368 case 5:
3369 worker.localize_public = true;
3370 YS_FALLTHROUGH
3371 case 4:
3372 worker.unbuffer_public = true;
3373 YS_FALLTHROUGH
3374 case 3:
3375 worker.inline_internal = true;
3376 YS_FALLTHROUGH
3377 case 2:
3378 worker.localize_internal = true;
3379 YS_FALLTHROUGH
3380 case 1:
3381 worker.unbuffer_internal = true;
3382 YS_FALLTHROUGH
3383 case 0:
3384 break;
3385 default:
3386 log_cmd_error("Invalid optimization level %d.\n", opt_level);
3387 }
3388 switch (debug_level) {
3389 // the highest level here must match DEFAULT_DEBUG_LEVEL
3390 case 4:
3391 worker.debug_eval = true;
3392 YS_FALLTHROUGH
3393 case 3:
3394 worker.debug_alias = true;
3395 YS_FALLTHROUGH
3396 case 2:
3397 worker.debug_member = true;
3398 YS_FALLTHROUGH
3399 case 1:
3400 worker.debug_info = true;
3401 YS_FALLTHROUGH
3402 case 0:
3403 break;
3404 default:
3405 log_cmd_error("Invalid debug information level %d.\n", debug_level);
3406 }
3407
3408 std::ofstream intf_f;
3409 if (worker.split_intf) {
3410 if (filename == "<stdout>")
3411 log_cmd_error("Option -header must be used with a filename.\n");
3412
3413 worker.intf_filename = filename.substr(0, filename.rfind('.')) + ".h";
3414 intf_f.open(worker.intf_filename, std::ofstream::trunc);
3415 if (intf_f.fail())
3416 log_cmd_error("Can't open file `%s' for writing: %s\n",
3417 worker.intf_filename.c_str(), strerror(errno));
3418
3419 worker.intf_f = &intf_f;
3420 }
3421 worker.impl_f = f;
3422
3423 worker.prepare_design(design);
3424 worker.dump_design(design);
3425 }
3426 } CxxrtlBackend;
3427
3428 PRIVATE_NAMESPACE_END