Add writeback mode to "sim" command
[yosys.git] / passes / sat / sim.cc
1 /*
2 * yosys -- Yosys Open SYnthesis Suite
3 *
4 * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 *
18 */
19
20 #include "kernel/yosys.h"
21 #include "kernel/sigtools.h"
22 #include "kernel/celltypes.h"
23
24 USING_YOSYS_NAMESPACE
25 PRIVATE_NAMESPACE_BEGIN
26
27 struct SimShared
28 {
29 bool debug = false;
30 bool hide_internal = true;
31 bool writeback = false;
32 };
33
34 struct SimInstance
35 {
36 SimShared *shared;
37
38 Module *module;
39 Cell *instance;
40
41 SimInstance *parent;
42 dict<Cell*, SimInstance*> children;
43
44 SigMap sigmap;
45 dict<SigBit, State> state_nets;
46 dict<SigBit, pool<Cell*>> upd_cells;
47 dict<SigBit, pool<Wire*>> upd_outports;
48
49 pool<SigBit> dirty_bits;
50 pool<SimInstance*, hash_ptr_ops> dirty_children;
51
52 struct ff_state_t
53 {
54 State past_clock;
55 Const past_d;
56 };
57
58 dict<Cell*, ff_state_t> ff_database;
59
60 dict<Wire*, pair<int, Const>> vcd_database;
61
62 SimInstance(SimShared *shared, Module *module, Cell *instance = nullptr, SimInstance *parent = nullptr) :
63 shared(shared), module(module), instance(instance), parent(parent), sigmap(module)
64 {
65 if (parent) {
66 log_assert(parent->children.count(instance) == 0);
67 parent->children[instance] = this;
68 }
69
70 for (auto wire : module->wires())
71 {
72 SigSpec sig = sigmap(wire);
73
74 for (int i = 0; i < GetSize(sig); i++) {
75 if (state_nets.count(sig[i]) == 0)
76 state_nets[sig[i]] = State::Sx;
77 if (wire->port_output) {
78 upd_outports[sig[i]].insert(wire);
79 dirty_bits.insert(sig[i]);
80 }
81 }
82
83 if (wire->attributes.count("\\init")) {
84 Const initval = wire->attributes.at("\\init");
85 for (int i = 0; i < GetSize(sig) && i < GetSize(initval); i++)
86 if (initval[i] == State::S0 || initval[i] == State::S1) {
87 state_nets[sig[i]] = initval[i];
88 dirty_bits.insert(sig[i]);
89 }
90 }
91 }
92
93 for (auto cell : module->cells())
94 {
95 Module *mod = module->design->module(cell->type);
96
97 if (mod != nullptr) {
98 dirty_children.insert(new SimInstance(shared, mod, cell, this));
99 }
100
101 for (auto &port : cell->connections()) {
102 if (cell->input(port.first))
103 for (auto bit : sigmap(port.second))
104 upd_cells[bit].insert(cell);
105 }
106
107 if (cell->type.in("$dff")) {
108 ff_state_t ff;
109 ff.past_clock = State::Sx;
110 ff.past_d = Const(State::Sx, cell->getParam("\\WIDTH").as_int());
111 ff_database[cell] = ff;
112 }
113 }
114 }
115
116 ~SimInstance()
117 {
118 for (auto child : children)
119 delete child.second;
120 }
121
122 IdString name() const
123 {
124 if (instance != nullptr)
125 return instance->name;
126 return module->name;
127 }
128
129 std::string hiername() const
130 {
131 if (instance != nullptr)
132 return parent->hiername() + "." + log_id(instance->name);
133
134 return log_id(module->name);
135 }
136
137 Const get_state(SigSpec sig)
138 {
139 Const value;
140
141 for (auto bit : sigmap(sig))
142 if (bit.wire == nullptr)
143 value.bits.push_back(bit.data);
144 else if (state_nets.count(bit))
145 value.bits.push_back(state_nets.at(bit));
146 else
147 value.bits.push_back(State::Sz);
148
149 if (shared->debug)
150 log("[%s] get %s: %s\n", hiername().c_str(), log_signal(sig), log_signal(value));
151 return value;
152 }
153
154 bool set_state(SigSpec sig, Const value)
155 {
156 bool did_something = false;
157
158 sig = sigmap(sig);
159 log_assert(GetSize(sig) == GetSize(value));
160
161 for (int i = 0; i < GetSize(sig); i++)
162 if (state_nets.at(sig[i]) != value[i]) {
163 state_nets.at(sig[i]) = value[i];
164 dirty_bits.insert(sig[i]);
165 did_something = true;
166 }
167
168 if (shared->debug)
169 log("[%s] set %s: %s\n", hiername().c_str(), log_signal(sig), log_signal(value));
170 return did_something;
171 }
172
173 void update_cell(Cell *cell)
174 {
175 if (ff_database.count(cell))
176 return;
177
178 if (children.count(cell))
179 {
180 auto child = children.at(cell);
181 for (auto &conn: cell->connections())
182 if (cell->input(conn.first)) {
183 Const value = get_state(conn.second);
184 child->set_state(child->module->wire(conn.first), value);
185 }
186 dirty_children.insert(child);
187 return;
188 }
189
190 if (yosys_celltypes.cell_evaluable(cell->type))
191 {
192 RTLIL::SigSpec sig_a, sig_b, sig_c, sig_d, sig_s, sig_y;
193 bool has_a, has_b, has_c, has_d, has_s, has_y;
194
195 has_a = cell->hasPort("\\A");
196 has_b = cell->hasPort("\\B");
197 has_c = cell->hasPort("\\C");
198 has_d = cell->hasPort("\\D");
199 has_s = cell->hasPort("\\S");
200 has_y = cell->hasPort("\\Y");
201
202 if (has_a) sig_a = cell->getPort("\\A");
203 if (has_b) sig_b = cell->getPort("\\B");
204 if (has_c) sig_c = cell->getPort("\\C");
205 if (has_d) sig_d = cell->getPort("\\D");
206 if (has_s) sig_s = cell->getPort("\\S");
207 if (has_y) sig_y = cell->getPort("\\Y");
208
209 if (shared->debug)
210 log("[%s] eval %s (%s)\n", hiername().c_str(), log_id(cell), log_id(cell->type));
211
212 // Simple (A -> Y) and (A,B -> Y) cells
213 if (has_a && !has_c && !has_d && !has_s && has_y) {
214 set_state(sig_y, CellTypes::eval(cell, get_state(sig_a), get_state(sig_b)));
215 return;
216 }
217
218 // (A,B,C -> Y) cells
219 if (has_a && has_b && has_c && !has_d && !has_s && has_y) {
220 set_state(sig_y, CellTypes::eval(cell, get_state(sig_a), get_state(sig_b), get_state(sig_c)));
221 return;
222 }
223
224 // (A,B,S -> Y) cells
225 if (has_a && has_b && !has_c && !has_d && has_s && has_y) {
226 set_state(sig_y, CellTypes::eval(cell, get_state(sig_a), get_state(sig_b), get_state(sig_s)));
227 return;
228 }
229
230 log_warning("Unsupported evaluable cell type: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell));
231 return;
232 }
233
234 // FIXME
235
236 log_warning("Unsupported cell type: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell));
237 }
238
239 void update_ph1()
240 {
241 pool<Cell*> queue_cells;
242 pool<Wire*> queue_outports;
243
244 while (1)
245 {
246 for (auto bit : dirty_bits)
247 {
248 if (upd_cells.count(bit))
249 for (auto cell : upd_cells.at(bit))
250 queue_cells.insert(cell);
251
252 if (upd_outports.count(bit) && parent != nullptr)
253 for (auto wire : upd_outports.at(bit))
254 queue_outports.insert(wire);
255 }
256
257 dirty_bits.clear();
258
259 if (!queue_cells.empty())
260 {
261 for (auto cell : queue_cells)
262 update_cell(cell);
263
264 queue_cells.clear();
265 continue;
266 }
267
268 for (auto wire : queue_outports)
269 if (instance->hasPort(wire->name)) {
270 Const value = get_state(wire);
271 parent->set_state(instance->getPort(wire->name), value);
272 }
273
274 queue_outports.clear();
275
276 for (auto child : dirty_children)
277 child->update_ph1();
278
279 dirty_children.clear();
280
281 if (dirty_bits.empty())
282 break;
283 }
284 }
285
286 bool update_ph2()
287 {
288 bool did_something = false;
289
290 for (auto &it : ff_database)
291 {
292 Cell *cell = it.first;
293 ff_state_t &ff = it.second;
294
295 if (cell->type.in("$dff"))
296 {
297 bool clkpol = cell->getParam("\\CLK_POLARITY").as_bool();
298 State current_clock = get_state(cell->getPort("\\CLK"))[0];
299
300 if (clkpol ? (ff.past_clock == State::S1 || current_clock != State::S1) :
301 (ff.past_clock == State::S0 || current_clock != State::S0))
302 continue;
303
304 if (set_state(cell->getPort("\\Q"), ff.past_d))
305 did_something = true;
306 }
307 }
308
309 for (auto it : children)
310 if (it.second->update_ph2()) {
311 dirty_children.insert(it.second);
312 did_something = true;
313 }
314
315 return did_something;
316 }
317
318 void update_ph3()
319 {
320 for (auto &it : ff_database)
321 {
322 Cell *cell = it.first;
323 ff_state_t &ff = it.second;
324
325 if (cell->type.in("$dff")) {
326 ff.past_clock = get_state(cell->getPort("\\CLK"))[0];
327 ff.past_d = get_state(cell->getPort("\\D"));
328 }
329 }
330
331 for (auto it : children)
332 it.second->update_ph3();
333 }
334
335 void writeback(pool<Module*> &wbmods)
336 {
337 if (wbmods.count(module))
338 log_error("Instance %s of module %s is not unique: Writeback not possible.\n", hiername().c_str(), log_id(module));
339
340 wbmods.insert(module);
341
342 for (auto wire : module->wires())
343 wire->attributes.erase("\\init");
344
345 for (auto &it : ff_database)
346 {
347 Cell *cell = it.first;
348 SigSpec sig_q = cell->getPort("\\Q");
349 Const initval = get_state(sig_q);
350
351 for (int i = 0; i < GetSize(sig_q); i++)
352 {
353 Wire *w = sig_q[i].wire;
354
355 if (w->attributes.count("\\init") == 0)
356 w->attributes["\\init"] = Const(State::Sx, GetSize(w));
357
358 w->attributes["\\init"][sig_q[i].offset] = initval[i];
359 }
360 }
361
362 for (auto it : children)
363 it.second->writeback(wbmods);
364 }
365
366 void write_vcd_header(std::ofstream &f, int &id)
367 {
368 f << stringf("$scope module %s $end\n", log_id(name()));
369
370 for (auto wire : module->wires())
371 {
372 if (shared->hide_internal && wire->name[0] == '$')
373 continue;
374
375 f << stringf("$var wire %d n%d %s%s $end\n", GetSize(wire), id, wire->name[0] == '$' ? "\\" : "", log_id(wire));
376 vcd_database[wire] = make_pair(id++, Const());
377 }
378
379 for (auto child : children)
380 child.second->write_vcd_header(f, id);
381
382 f << stringf("$upscope $end\n");
383 }
384
385 void write_vcd_step(std::ofstream &f)
386 {
387 for (auto &it : vcd_database)
388 {
389 Wire *wire = it.first;
390 Const value = get_state(wire);
391 int id = it.second.first;
392
393 if (it.second.second == value)
394 continue;
395
396 it.second.second = value;
397
398 f << "b";
399 for (int i = GetSize(value)-1; i >= 0; i--) {
400 switch (value[i]) {
401 case State::S0: f << "0"; break;
402 case State::S1: f << "1"; break;
403 case State::Sx: f << "x"; break;
404 default: f << "z";
405 }
406 }
407
408 f << stringf(" n%d\n", id);
409 }
410
411 for (auto child : children)
412 child.second->write_vcd_step(f);
413 }
414 };
415
416 struct SimWorker : SimShared
417 {
418 SimInstance *top = nullptr;
419 std::ofstream vcdfile;
420 pool<IdString> clock, clockn, reset, resetn;
421
422 ~SimWorker()
423 {
424 delete top;
425 }
426
427 void write_vcd_header()
428 {
429 if (!vcdfile.is_open())
430 return;
431
432 int id = 1;
433 top->write_vcd_header(vcdfile, id);
434
435 vcdfile << stringf("$enddefinitions $end\n");
436 }
437
438 void write_vcd_step(int t)
439 {
440 if (!vcdfile.is_open())
441 return;
442
443 vcdfile << stringf("#%d\n", t);
444 top->write_vcd_step(vcdfile);
445 }
446
447 void update()
448 {
449 do
450 {
451 if (debug)
452 log("\n-- ph1 --\n");
453
454 top->update_ph1();
455
456 if (debug)
457 log("\n-- ph2 --\n");
458 }
459 while (top->update_ph2());
460
461 if (debug)
462 log("\n-- ph3 --\n");
463
464 top->update_ph3();
465 }
466
467 void set_inports(pool<IdString> ports, State value)
468 {
469 for (auto portname : ports)
470 {
471 Wire *w = top->module->wire(portname);
472
473 if (w == nullptr)
474 log_error("Can't find port %s on module %s.\n", log_id(portname), log_id(top->module));
475
476 top->set_state(w, value);
477 }
478 }
479
480 void run(Module *topmod, int numcycles)
481 {
482 log_assert(top == nullptr);
483 top = new SimInstance(this, topmod);
484
485 if (debug)
486 log("\n===== 0 =====\n");
487
488 set_inports(reset, State::S1);
489 set_inports(resetn, State::S0);
490
491 update();
492
493 write_vcd_header();
494 write_vcd_step(0);
495
496 for (int cycle = 0; cycle < numcycles; cycle++)
497 {
498 if (debug)
499 log("\n===== %d =====\n", 10*cycle + 5);
500
501 set_inports(clock, State::S0);
502 set_inports(clockn, State::S1);
503
504 update();
505 write_vcd_step(10*cycle + 5);
506
507 if (debug)
508 log("\n===== %d =====\n", 10*cycle + 10);
509
510 set_inports(clock, State::S1);
511 set_inports(clockn, State::S0);
512
513 if (cycle == 0) {
514 set_inports(reset, State::S0);
515 set_inports(resetn, State::S1);
516 }
517
518 update();
519 write_vcd_step(10*cycle + 10);
520 }
521
522 write_vcd_step(10*numcycles + 2);
523
524 if (writeback) {
525 pool<Module*> wbmods;
526 top->writeback(wbmods);
527 }
528 }
529 };
530
531 struct SimPass : public Pass {
532 SimPass() : Pass("sim", "simulate the circuit") { }
533 virtual void help()
534 {
535 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
536 log("\n");
537 log(" sim [options] [top-level]\n");
538 log("\n");
539 log("This command simulates the circuit using the given top-level module.\n");
540 log("\n");
541 log(" -vcd <filename>\n");
542 log(" write the simulation results to the given VCD file\n");
543 log("\n");
544 log(" -clock <portname>\n");
545 log(" name of top-level clock input\n");
546 log("\n");
547 log(" -clockn <portname>\n");
548 log(" name of top-level clock input (inverse polarity)\n");
549 log("\n");
550 log(" -reset <portname>\n");
551 log(" name of top-level reset input (active high)\n");
552 log("\n");
553 log(" -resetn <portname>\n");
554 log(" name of top-level inverted reset input (active low)\n");
555 log("\n");
556 log(" -n <integer>\n");
557 log(" number of cycles to simulate (default: 20)\n");
558 log("\n");
559 log(" -a\n");
560 log(" include all nets in VCD output, nut just those with public names\n");
561 log("\n");
562 log(" -w\n");
563 log(" writeback mode: use final simulation state as new init state\n");
564 log("\n");
565 log(" -d\n");
566 log(" enable debug output\n");
567 log("\n");
568 }
569 virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
570 {
571 SimWorker worker;
572 int numcycles = 20;
573
574 log_header(design, "Executing SIM pass (simulate the circuit).\n");
575
576 size_t argidx;
577 for (argidx = 1; argidx < args.size(); argidx++) {
578 if (args[argidx] == "-vcd" && argidx+1 < args.size()) {
579 worker.vcdfile.open(args[++argidx].c_str());
580 continue;
581 }
582 if (args[argidx] == "-n" && argidx+1 < args.size()) {
583 numcycles = atoi(args[++argidx].c_str());
584 continue;
585 }
586 if (args[argidx] == "-clock" && argidx+1 < args.size()) {
587 worker.clock.insert(RTLIL::escape_id(args[++argidx]));
588 continue;
589 }
590 if (args[argidx] == "-clockn" && argidx+1 < args.size()) {
591 worker.clockn.insert(RTLIL::escape_id(args[++argidx]));
592 continue;
593 }
594 if (args[argidx] == "-reset" && argidx+1 < args.size()) {
595 worker.reset.insert(RTLIL::escape_id(args[++argidx]));
596 continue;
597 }
598 if (args[argidx] == "-resetn" && argidx+1 < args.size()) {
599 worker.resetn.insert(RTLIL::escape_id(args[++argidx]));
600 continue;
601 }
602 if (args[argidx] == "-a") {
603 worker.hide_internal = false;
604 continue;
605 }
606 if (args[argidx] == "-d") {
607 worker.debug = true;
608 continue;
609 }
610 if (args[argidx] == "-w") {
611 worker.writeback = true;
612 continue;
613 }
614 break;
615 }
616 extra_args(args, argidx, design);
617
618 Module *top_mod = nullptr;
619
620 if (design->full_selection()) {
621 top_mod = design->top_module();
622 } else {
623 auto mods = design->selected_whole_modules();
624 if (GetSize(mods) != 1)
625 log_cmd_error("Only one top module must be selected.\n");
626 top_mod = mods.front();
627 }
628
629 worker.run(top_mod, numcycles);
630 }
631 } SimPass;
632
633 PRIVATE_NAMESPACE_END