1 // Copyright 2017-2019 VMware, Inc.
2 // SPDX-License-Identifier: BSD-2-Clause
3 //
4 // The BSD-2 license (the License) set forth below applies to all parts of the
5 // Cascade project. You may not use this file except in compliance with the
6 // License.
7 //
8 // BSD-2 License
9 //
10 // Redistribution and use in source and binary forms, with or without
11 // modification, are permitted provided that the following conditions are met:
12 //
13 // 1. Redistributions of source code must retain the above copyright notice, this
14 // list of conditions and the following disclaimer.
15 //
16 // 2. Redistributions in binary form must reproduce the above copyright notice,
17 // this list of conditions and the following disclaimer in the documentation
18 // and/or other materials provided with the distribution.
19 //
20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND
21 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31 #include "runtime/runtime.h"
32
33 #include <cassert>
34 #include <cctype>
35 #include <fstream>
36 #include <iostream>
37 #include <limits>
38 #include <sstream>
39 #include "common/incstream.h"
40 #include "common/indstream.h"
41 #include "common/system.h"
42 #include "runtime/data_plane.h"
43 #include "runtime/isolate.h"
44 #include "runtime/module.h"
45 #include "runtime/nullbuf.h"
46 #include "target/compiler/local_compiler.h"
47 #include "target/engine.h"
48 #include "verilog/analyze/evaluate.h"
49 #include "verilog/analyze/module_info.h"
50 #include "verilog/analyze/navigate.h"
51 #include "verilog/analyze/resolve.h"
52 #include "verilog/build/ast_builder.h"
53 #include "verilog/parse/parser.h"
54 #include "verilog/print/print.h"
55 #include "verilog/program/inline.h"
56 #include "verilog/program/program.h"
57
58 using namespace std;
59
60 namespace cascade {
61
Runtime()62 Runtime::Runtime() : Thread() {
63 include_dirs_ = System::src_root();
64 fopen_dirs_ = "./";
65 disable_inlining_ = false;
66 enable_open_loop_ = false;
67 open_loop_itrs_ = 2;
68 open_loop_target_ = 1;
69 profile_interval_ = 0;
70
71 pool_.set_num_threads(4);
72 pool_.run();
73
74 log_ = new Log();
75 parser_ = new Parser(log_);
76 parser_->set_include_dirs(include_dirs_);
77 compiler_ = new LocalCompiler(this);
78 dp_ = new DataPlane();
79 isolate_ = new Isolate();
80
81 program_ = new Program();
82 root_ = nullptr;
83 next_id_ = 0;
84
85 finished_ = false;
86 item_evals_ = 0;
87
88 schedule_all_ = false;
89 clock_ = nullptr;
90 inlined_logic_ = nullptr;
91
92 begin_time_ = ::time(nullptr);
93 last_time_ = ::time(nullptr);
94 logical_time_ = 0;
95
96 for (size_t i = 0; i < 6; ++i) {
97 streambufs_.push_back(make_pair(new nullbuf(), true));
98 }
99 }
100
~Runtime()101 Runtime::~Runtime() {
102 // INVARIANT: The runtime should always be in a state where finish was called
103 // before teardown. This guarantees that outstanding async threads won't
104 // attempt to schedule interrupts that will never be serviced.
105
106 stop_now();
107 if (!finished_) {
108 finish(0);
109 run();
110 stop_now();
111 }
112
113 // INVARIANT: At this point we know that the interrupt queue is empty and no
114 // new asyncrhonous threads have been started (the only place this happens is
115 // inside interrupts). Stop any outstanding compilations and wait for them to
116 // return. When that's done, stop any asynchronous jobs associated with
117 // compilers.
118
119 compiler_->stop_compile();
120 pool_.stop_now();
121 compiler_->stop_async();
122
123 // INVARIANT: All outstanding asynchronous threads have finished executing,
124 // and any interrupts scheduled by those threads have either fizzled or had
125 // their alternate callbacks executed. It's now safe to tear down the
126 // runtime.
127
128 delete program_;
129 if (root_ != nullptr) {
130 delete root_;
131 }
132
133 delete log_;
134 delete parser_;
135 delete compiler_;
136 delete dp_;
137 delete isolate_;
138
139 for (auto& s : streambufs_) {
140 if (s.second) {
141 delete s.first;
142 }
143 }
144 }
145
set_fopen_dirs(const string & s)146 Runtime& Runtime::set_fopen_dirs(const string& s) {
147 fopen_dirs_ = string("./") + ":" + s;
148 return *this;
149 }
150
set_include_dirs(const string & s)151 Runtime& Runtime::set_include_dirs(const string& s) {
152 include_dirs_ = System::src_root() + ":" + s;
153 parser_->set_include_dirs(include_dirs_);
154 return *this;
155 }
156
set_open_loop_target(size_t olt)157 Runtime& Runtime::set_open_loop_target(size_t olt) {
158 open_loop_target_ = olt;
159 return *this;
160 }
161
set_disable_inlining(bool di)162 Runtime& Runtime::set_disable_inlining(bool di) {
163 disable_inlining_ = di;
164 return *this;
165 }
166
set_profile_interval(size_t n)167 Runtime& Runtime::set_profile_interval(size_t n) {
168 profile_interval_ = n;
169 last_check_ = ::time(nullptr);
170 return *this;
171 }
172
get_data_plane()173 DataPlane* Runtime::get_data_plane() {
174 return dp_;
175 }
176
get_compiler()177 Compiler* Runtime::get_compiler() {
178 return compiler_;
179 }
180
get_isolate()181 Isolate* Runtime::get_isolate() {
182 return isolate_;
183 }
184
get_next_id()185 Engine::Id Runtime::get_next_id() {
186 return next_id_++;
187 }
188
eval(istream & is)189 pair<bool, bool> Runtime::eval(istream& is) {
190 log_->clear();
191 const auto eof = parser_->parse(is);
192 auto err = log_->error();
193
194 if (err) {
195 log_parse_errors();
196 } else {
197 schedule_blocking_interrupt([this, &is, &err]{
198 err = !eval_nodes(parser_->begin(), parser_->end());
199 });
200 }
201 return make_pair(eof, err);
202 }
203
eval_all(istream & is)204 pair<bool, bool> Runtime::eval_all(istream& is) {
205 auto eof = false;
206 auto err = false;
207 schedule_blocking_interrupt([this, &is, &eof, &err]{
208 while (!eof && !err) {
209 log_->clear();
210 eof = parser_->parse(is);
211 err = log_->error();
212 if (err) {
213 log_parse_errors();
214 } else {
215 err = !eval_nodes(parser_->begin(), parser_->end());
216 }
217 }
218 });
219 return make_pair(eof, err);
220 }
221
schedule_interrupt(Interrupt int_)222 bool Runtime::schedule_interrupt(Interrupt int_) {
223 lock_guard<recursive_mutex> lg(int_lock_);
224 if (finished_) {
225 return false;
226 }
227 ints_.push_back([this, int_]{
228 if (!finished_) {
229 int_();
230 }
231 });
232 return true;
233 }
234
schedule_interrupt(Interrupt int_,Interrupt alt)235 bool Runtime::schedule_interrupt(Interrupt int_, Interrupt alt) {
236 lock_guard<recursive_mutex> lg(int_lock_);
237 if (finished_) {
238 alt();
239 return false;
240 }
241 ints_.push_back([this, int_, alt]{
242 if (!finished_) {
243 int_();
244 } else {
245 alt();
246 }
247 });
248 return true;
249 }
250
schedule_blocking_interrupt(Interrupt int_)251 void Runtime::schedule_blocking_interrupt(Interrupt int_) {
252 unique_lock<mutex> lg(block_lock_);
253 if (schedule_interrupt(int_)) {
254 block_cv_.wait(lg);
255 }
256 }
257
schedule_blocking_interrupt(Interrupt int_,Interrupt alt)258 void Runtime::schedule_blocking_interrupt(Interrupt int_, Interrupt alt) {
259 unique_lock<mutex> lg(block_lock_);
260 if (schedule_interrupt(int_, alt)) {
261 block_cv_.wait(lg);
262 }
263 }
264
schedule_state_safe_interrupt(Interrupt int__)265 void Runtime::schedule_state_safe_interrupt(Interrupt int__) {
266 schedule_blocking_interrupt(
267 [this, int__]{
268 // As with retarget(), invoking this method in a state where item_evals_ >
269 // 0 can be problematic. This condition guarantees safety.
270 if (item_evals_ > 0) {
271 return schedule_state_safe_interrupt(int__);
272 }
273 stringstream ss;
274 root_->save(ss);
275 int__();
276 root_->restart(ss);
277 },
278 int__
279 );
280 }
281
schedule_asynchronous(Asynchronous async)282 void Runtime::schedule_asynchronous(Asynchronous async) {
283 pool_.insert(async);
284 }
285
is_finished() const286 bool Runtime::is_finished() const {
287 return finished_;
288 }
289
reset_open_loop_itrs()290 void Runtime::reset_open_loop_itrs() {
291 schedule_interrupt([this]{
292 open_loop_itrs_ = 2;
293 });
294 }
295
debug(uint32_t action,const string & arg)296 void Runtime::debug(uint32_t action, const string& arg) {
297 schedule_interrupt([this, action, arg]{
298 const auto* r = resolve(arg);
299 if (r == nullptr) {
300 ostream(rdbuf(stderr_)) << "Unable to resolve " << arg << "!" << endl;
301 return;
302 }
303 switch (action) {
304 case 0:
305 list(r);
306 break;
307 case 1:
308 showscopes(r);
309 break;
310 case 2:
311 recursive_showscopes(r);
312 break;
313 case 3:
314 if (r->is_subclass_of(Node::Tag::declaration)) {
315 showvars(static_cast<const Declaration*>(r)->get_id());
316 } else {
317 recursive_showvars(r);
318 }
319 break;
320 default:
321 break;
322 }
323 });
324 }
325
finish(uint32_t arg)326 void Runtime::finish(uint32_t arg) {
327 if (arg > 0) {
328 ostream(rdbuf(stdout_))
329 << "Simulation Time: " << logical_time_ << "\n"
330 << "Wall Clock Time: " << (::time(nullptr) - begin_time_) << "s" << "\n"
331 << "Clock Frequency: " << overall_frequency() << endl;
332 }
333 request_stop();
334 finished_ = true;
335 }
336
restart(const string & path)337 void Runtime::restart(const string& path) {
338 schedule_interrupt([this, path]{
339 // As with retarget(), invoking this method in a state where item_evals_ >
340 // 0 can be problematic. This condition guarantees safety.
341 if (item_evals_ > 0) {
342 return restart(path);
343 }
344 ifstream ifs(path);
345 if (!ifs.is_open()) {
346 ostream(rdbuf(stderr_)) << "Unable to open save file '" << path << "'\"!" << endl;
347 finish(0);
348 return;
349 }
350 root_->restart(ifs);
351 });
352 }
353
retarget(const string & s)354 void Runtime::retarget(const string& s) {
355 schedule_interrupt([this, s]{
356 // An unfortunate corner case: Have some evals been processed by the
357 // interrupt queue? Remember that Module::rebuild() can only be invoked in
358 // a state where there are no unhandled evals. Fortunately, there's an easy
359 // fix: just reinvoke retarget(). This will reschedule us on the far side
360 // of Runtime::resync() and it'll be safe to invoke Module::rebuild().
361 if (item_evals_ > 0) {
362 return retarget(s);
363 }
364 // Give up if we can't open the march file which was requested
365 ifstream ifs(System::src_root() + "share/cascade/march/" + s + ".v");
366 if (!ifs.is_open()) {
367 ostream(rdbuf(stderr_)) << "Unrecognized march option '" << s << "'!" << endl;
368 finish(0);
369 return;
370 }
371
372 // Temporarily relocate program_ and root_ so that we can scan the contents
373 // of this file using the eval_stream() infrastructure. Also relocate the
374 // parser, as it will skip include guards that it's already seen.
375 auto* march = program_;
376 program_ = new Program();
377 auto* backup_root = root_;
378 root_ = nullptr;
379 auto* backup_parser = parser_;
380 parser_ = new Parser(log_);
381
382 // Read the march file
383 eval_stream(ifs);
384 assert(!log_->error());
385
386 // Wap everything back into place and restore original state
387 std::swap(march, program_);
388 std::swap(backup_root, root_);
389 std::swap(backup_parser, parser_);
390 delete backup_parser;
391 item_evals_ = 0;
392
393 // Replace attribute annotations for every elaborated module (this includes the
394 // root, which is where logic inherits its annotations from).
395 for (auto i = program_->elab_begin(), ie = program_->elab_end(); i != ie; ++i) {
396 auto* std1 = i->second->get_attrs()->get<String>("__std");
397 assert(std1 != nullptr);
398
399 auto found = false;
400 for (auto j = march->elab_begin(), je = march->elab_end(); j != je; ++j) {
401 auto* std2 = j->second->get_attrs()->get<String>("__std");
402 assert(std2 != nullptr);
403
404 if (std1->get_readable_val() == std2->get_readable_val()) {
405 i->second->replace_attrs(j->second->get_attrs()->clone());
406 found = true;
407 break;
408 }
409 }
410 if (!found) {
411 delete march;
412 delete backup_root;
413 ostream(rdbuf(stderr_)) << "New target does not support modules with standard type " << std1->get_readable_val() << "!" << endl;
414 finish(0);
415 return;
416 }
417 }
418
419 // Delete temporaries and rebuild the program
420 delete march;
421 delete backup_root;
422 root_->rebuild();
423 });
424 }
425
save(const string & path)426 void Runtime::save(const string& path) {
427 schedule_interrupt([this, path]{
428 // As with retarget(), invoking this method in a state where item_evals_ >
429 // 0 can be problematic. This condition guarantees safety.
430 if (item_evals_ > 0) {
431 return save(path);
432 }
433 ofstream ofs(path);
434 root_->save(ofs);
435 });
436 }
437
rdbuf(streambuf * sb)438 FId Runtime::rdbuf(streambuf* sb) {
439 streambufs_.push_back(make_pair(sb, false));
440 return streambufs_.size()-1;
441 }
442
rdbuf(FId id,streambuf * sb)443 void Runtime::rdbuf(FId id, streambuf* sb) {
444 const auto fid = id & 0x7fff'ffff;
445 while (fid >= streambufs_.size()) {
446 streambufs_.push_back(make_pair(new nullbuf(), true));
447 }
448 if (streambufs_[fid].second) {
449 delete streambufs_[fid].first;
450 }
451 if (sb != nullptr) {
452 streambufs_[fid] = make_pair(sb, false);
453 } else {
454 streambufs_[fid] = make_pair(new nullbuf(), true);
455 }
456 }
457
rdbuf(FId id) const458 streambuf* Runtime::rdbuf(FId id) const {
459 const auto fid = id & 0x7fff'ffff;
460 assert(fid < streambufs_.size());
461 return streambufs_[fid].first;
462 }
463
fopen(const std::string & path,uint8_t mode)464 FId Runtime::fopen(const std::string& path, uint8_t mode) {
465 incstream is(fopen_dirs_);
466 const auto full_path = is.find(path);
467 const auto target = full_path == "" ? path : full_path;
468
469 auto* fb = new filebuf();
470 auto m = ios_base::in;
471 switch (mode) {
472 case 1: m = ios_base::out; break;
473 case 2: m = ios_base::app; break;
474 case 3: m = ios_base::in | ios_base::out; break;
475 case 4: m = ios_base::in | ios_base::trunc; break;
476 case 5: m = ios_base::in | ios_base::app; break;
477 default: break;
478 }
479 fb->open(target.c_str(), m);
480 streambufs_.push_back(make_pair(fb, true));
481 return (streambufs_.size()-1);;
482 }
483
in_avail(FId id)484 int32_t Runtime::in_avail(FId id) {
485 return rdbuf(id)->in_avail();
486 }
487
pubseekoff(FId id,int32_t off,uint8_t way,uint8_t which)488 uint32_t Runtime::pubseekoff(FId id, int32_t off, uint8_t way, uint8_t which) {
489 auto d = ios_base::cur;
490 switch (way) {
491 case 1: d = ios_base::beg; break;
492 case 2: d = ios_base::end; break;
493 default: break;
494 }
495 auto o = ios_base::openmode();
496 switch (which) {
497 case 1: o = ios_base::in; break;
498 case 2: o = ios_base::out; break;
499 case 3: o = ios_base::in | ios_base::out; break;
500 default: break;
501 }
502 return rdbuf(id)->pubseekoff(off, d, o);
503 }
504
pubseekpos(FId id,int32_t pos,uint8_t which)505 uint32_t Runtime::pubseekpos(FId id, int32_t pos, uint8_t which) {
506 auto o = ios_base::openmode();
507 switch (which) {
508 case 1: o = ios_base::in; break;
509 case 2: o = ios_base::out; break;
510 case 3: o = ios_base::in | ios_base::out; break;
511 default: break;
512 }
513 return rdbuf(id)->pubseekpos(pos, o);
514 }
515
pubsync(FId id)516 int32_t Runtime::pubsync(FId id) {
517 return rdbuf(id)->pubsync();
518 }
519
sbumpc(FId id)520 int32_t Runtime::sbumpc(FId id) {
521 return rdbuf(id)->sbumpc();
522 }
523
sgetc(FId id)524 int32_t Runtime::sgetc(FId id) {
525 return rdbuf(id)->sgetc();
526 }
527
sgetn(FId id,char * c,uint32_t n)528 uint32_t Runtime::sgetn(FId id, char* c, uint32_t n) {
529 return rdbuf(id)->sgetn(c, n);
530 }
531
sputc(FId id,char c)532 int32_t Runtime::sputc(FId id, char c) {
533 // Squelch puts which take place after a call to finish
534 return !finished_ ? rdbuf(id)->sputc(c) : c;
535 }
536
sputn(FId id,const char * c,uint32_t n)537 uint32_t Runtime::sputn(FId id, const char* c, uint32_t n) {
538 // Squelch puts which take place after a call to finish
539 return !finished_ ? rdbuf(id)->sputn(c, n) : n;
540 }
541
run_logic()542 void Runtime::run_logic() {
543 if (logical_time_ == 0) {
544 log_event("BEGIN");
545 ostream os(rdbuf(stdinfo_));
546 os << "Started logical simulation..." << "\n";
547 os << "Installation Path: " << System::src_root() << "\n";
548 os << "Fopen dirs: " << fopen_dirs_ << "\n";
549 os << "Include dirs: " << include_dirs_ << "\n";
550 os << "C++ Compiler: " << System::cxx_compiler() << "\n";
551 os.flush();
552 }
553 if (finished_) {
554 return;
555 }
556 while (!stop_requested() && !finished_) {
557 if (enable_open_loop_ && !schedule_all_) {
558 open_loop_scheduler();
559 } else {
560 reference_scheduler();
561 }
562 log_freq();
563 }
564 if (finished_) {
565 done_simulation();
566 log_event("END");
567 ostream(rdbuf(stdinfo_)) << "Finished logical simulation" << endl;
568 }
569 }
570
eval_stream(istream & is)571 void Runtime::eval_stream(istream& is) {
572 for (auto res = true; res; ) {
573 log_->clear();
574 const auto eof = parser_->parse(is);
575
576 // Stop eval'ing as soon as we enounter a parse error, and return false.
577 if (log_->error()) {
578 log_parse_errors();
579 return;
580 }
581 // An eof marks end of stream, return the last result, and trigger finish
582 // if the eof appeared on the term
583 if (eof) {
584 return;
585 }
586 // Eval the code we just parsed; if this is the term, only loop for as
587 // long as we're inside of an include statement.
588 res = eval_nodes(parser_->begin(), parser_->end());
589 }
590 }
591
eval_node(Node * n)592 bool Runtime::eval_node(Node* n) {
593 log_event("PARSE", n);
594 if (n->is(Node::Tag::module_declaration)) {
595 auto* md = static_cast<ModuleDeclaration*>(n);
596 return eval_decl(md);
597 } else if (n->is_subclass_of(Node::Tag::module_item)) {
598 auto* mi = static_cast<ModuleItem*>(n);
599 return eval_item(mi);
600 } else {
601 assert(false);
602 return false;
603 }
604 }
605
eval_decl(ModuleDeclaration * md)606 bool Runtime::eval_decl(ModuleDeclaration* md) {
607 program_->declare(md, log_, parser_);
608 log_checker_warns();
609 if (log_->error()) {
610 log_checker_errors();
611 return false;
612 }
613 if (disable_inlining_) {
614 md->get_attrs()->set_or_replace("__no_inline", new String("true"));
615 }
616
617 log_event("DECL_OK");
618 return true;
619 }
620
eval_item(ModuleItem * mi)621 bool Runtime::eval_item(ModuleItem* mi) {
622 program_->eval(mi, log_, parser_);
623 log_checker_warns();
624 if (log_->error()) {
625 log_checker_errors();
626 return false;
627 }
628
629 // If the root has the standard six definitions, we just instantiated it.
630 // Otherwise, count this as an item instantiated within the root.
631 const auto* src = program_->root_elab()->second;
632 if (src->size_items() == 6) {
633 root_ = new Module(src, this);
634 item_evals_ = 6;
635 } else {
636 ++item_evals_;
637 }
638
639 log_event("ITEM_OK");
640 return true;
641 }
642
resync()643 void Runtime::resync() {
644 // If nothing has been evaled since the last call, we don't have to worry
645 // about recompilation. We might be here because of a jit handoff, in which
646 // case we need to check for compiler errors.
647 if (item_evals_ == 0) {
648 if (compiler_->error()) {
649 log_compiler_errors();
650 }
651 return;
652 }
653
654 // Inline as much as we can and compile whatever is new. Reset the
655 // item_evals_ counter as soon as we're done.
656 program_->inline_all();
657 root_->synchronize(item_evals_);
658 item_evals_ = 0;
659 if (compiler_->error()) {
660 log_compiler_errors();
661 }
662
663 // Clear scheduling state
664 logic_.clear();
665 done_logic_.clear();
666 clock_ = nullptr;
667 inlined_logic_ = nullptr;
668 // Reconfigure scheduling state
669 for (auto* m : *root_) {
670 if (m->engine()->is_stub()) {
671 continue;
672 }
673 logic_.push_back(m);
674 if (m->engine()->is_clock()) {
675 clock_ = m;
676 }
677 if (m->engine()->is_logic()) {
678 inlined_logic_ = m;
679 }
680 if (m->engine()->overrides_done_step()) {
681 done_logic_.push_back(m);
682 }
683 }
684 schedule_all_ = true;
685
686 // Determine whether we can reenter open loop in this state.
687 enable_open_loop_ = (logic_.size() == 2) && (clock_ != nullptr) && (inlined_logic_ != nullptr);
688 }
689
drain_active()690 void Runtime::drain_active() {
691 for (auto done = false; !done; ) {
692 done = true;
693 for (auto* m : logic_) {
694 if (schedule_all_ || m->engine()->there_are_reads()) {
695 m->engine()->evaluate();
696 done = false;
697 }
698 }
699 schedule_all_ = false;
700 }
701 }
702
drain_updates()703 bool Runtime::drain_updates() {
704 auto performed_update = false;
705 for (auto* m : logic_) {
706 if (m->engine()->conditional_update()) {
707 performed_update = true;
708 }
709 }
710 if (!performed_update) {
711 return false;
712 }
713 auto performed_evaluate = false;
714 for (auto* m : logic_) {
715 if (m->engine()->conditional_evaluate()) {
716 performed_evaluate = true;
717 }
718 }
719 return performed_evaluate;
720 }
721
done_step()722 void Runtime::done_step() {
723 for (auto* m : done_logic_) {
724 m->engine()->done_step();
725 }
726 }
727
done_simulation()728 void Runtime::done_simulation() {
729 for (auto* m : logic_) {
730 m->engine()->done_simulation();
731 }
732 }
733
drain_interrupts()734 void Runtime::drain_interrupts() {
735 lock_guard<recursive_mutex> lg(int_lock_);
736
737 // Fast Path: No interrupts
738 if (ints_.empty()) {
739 return;
740 }
741
742 // Slow Path:
743 // We have at least one interrupt, which could be an eval event or a jit
744 // handoff. Schedule an interrupt at the end of the queue to handle these.
745 schedule_interrupt([this]{
746 resync();
747 });
748 for (size_t i = 0; i < ints_.size(); ++i) {
749 ints_[i]();
750 }
751 ints_.clear();
752 block_cv_.notify_all();
753 }
754
open_loop_scheduler()755 void Runtime::open_loop_scheduler() {
756 // Record the current time, go open loop, and then record how long we were
757 // gone for.
758 const size_t then = ::time(nullptr);
759 const auto id = clock_->engine()->get_clock_id();
760 const auto val = clock_->engine()->get_clock_val();
761 const auto itrs = inlined_logic_->engine()->open_loop(id, val, open_loop_itrs_);
762 const size_t now = ::time(nullptr);
763
764 // If we ran for an odd number of iterations, flip the clock
765 if (itrs % 2) {
766 clock_->engine()->set_clock_val(!val);
767 }
768 // Drain the interrupt queue and fix up the logical time
769 drain_interrupts();
770 logical_time_ += itrs;
771
772 // Update open loop iterations based on our target
773 const auto delta = now - then;
774 auto next = open_loop_itrs_;
775 if ((delta < open_loop_target_) && (open_loop_itrs_ == itrs)) {
776 next <<= 1;
777 } else if (delta > open_loop_target_) {
778 next >>= 1;
779 }
780 open_loop_itrs_ = (next > 0) ? next : open_loop_itrs_;
781 }
782
reference_scheduler()783 void Runtime::reference_scheduler() {
784 while (schedule_all_ || drain_updates()) {
785 drain_active();
786 }
787 done_step();
788 drain_interrupts();
789 ++logical_time_;
790 }
791
log_parse_errors()792 void Runtime::log_parse_errors() {
793 ostream os(rdbuf(stderr_));
794 os << "Parse Error:";
795
796 indstream is(os);
797 is.tab();
798 for (auto e = log_->error_begin(), ee = log_->error_end(); e != ee; ++e) {
799 is << "\n> ";
800 is.tab();
801 is << *e;
802 is.untab();
803 }
804 os << endl;
805 }
806
log_checker_warns()807 void Runtime::log_checker_warns() {
808 if (log_->warn_begin() == log_->warn_end()) {
809 return;
810 }
811
812 ostream os(rdbuf(stdwarn_));
813 os << "Typechecker Warning:";
814
815 indstream is(os);
816 is.tab();
817 for (auto w = log_->warn_begin(), we = log_->warn_end(); w != we; ++w) {
818 is << "\n> ";
819 is.tab();
820 is << *w;
821 is.untab();
822 }
823 os << endl;
824 }
825
log_checker_errors()826 void Runtime::log_checker_errors() {
827 ostream os(rdbuf(stderr_));
828 os << "Typechecker Error:";
829
830 indstream is(os);
831 is.tab();
832 for (auto e = log_->error_begin(), ee = log_->error_end(); e != ee; ++e) {
833 is << "\n> ";
834 is.tab();
835 is << *e;
836 is.untab();
837 }
838 os << endl;
839 }
840
log_compiler_errors()841 void Runtime::log_compiler_errors() {
842 const auto what = compiler_->what();
843 if (what.first) {
844 ostream(rdbuf(stderr_))
845 << "Fatal Compiler Error:" << endl
846 << " > " << what.second << endl
847 << " > " << "Shutting Down!" << endl;
848 finish(0);
849 } else {
850 ostream(rdbuf(stderr_))
851 << "Compiler Error:" << endl
852 << " > " << what.second << endl
853 << " > " << "Control will remain in software-simulation!" << endl;
854 compiler_->clear();
855 }
856 }
857
log_event(const string & type,Node * n)858 void Runtime::log_event(const string& type, Node* n) {
859 stringstream ss;
860 ss << "*** " << type << " @ " << logical_time_;
861 if (n != nullptr) {
862 ss << endl << n;
863 }
864 auto s = ss.str();
865
866 auto event = [this, s]{
867 ostream(rdbuf(stdlog_)) << s << endl;
868 };
869 schedule_interrupt(event, event);
870 }
871
log_freq()872 void Runtime::log_freq() {
873 if (profile_interval_ == 0) {
874 return;
875 }
876 if ((::time(nullptr) - last_check_) < profile_interval_) {
877 return;
878 }
879 auto event = [this]{
880 last_check_ = ::time(nullptr);
881 ostream(rdbuf(stdinfo_)) << "Logical Time: " << logical_time_ << "\nVirtual Freq: " << current_frequency() << endl;
882 };
883 schedule_interrupt(event, event);
884 }
885
resolve(const string & arg)886 const Node* Runtime::resolve(const string& arg) {
887 // Create a new navigation object and point it at the root
888 Navigate nav(program_->root_elab()->second);
889 const Node* res = nav.where();
890
891 // Not exactly the prettiest way to do this, but it works.
892 const auto* temp = ItemBuilder("assign " + arg + " = 0;").get();
893 const auto* id = static_cast<const ContinuousAssign*>(temp)->get_lhs();
894
895 // Walk along the ids in this identifier, ignoring the first, which root
896 for (auto i = ++id->begin_ids(), ie = id->end_ids(); i != ie; ++i) {
897 // Update res on success
898 if (nav.down(*i)) {
899 res = nav.where();
900 }
901 // If we failed on the last element, we might have found an id
902 else if (i+1 == ie) {
903 res = nav.find_name(*i);
904 if (res != nullptr) {
905 res = Resolve().get_resolution(static_cast<const Identifier*>(res))->get_parent();
906 }
907 }
908 // If we failed anywhere else, we've just failed
909 else {
910 res = nullptr;
911 break;
912 }
913 }
914 delete temp;
915 return res;
916 }
917
list(const Node * n)918 void Runtime::list(const Node* n) {
919 ostream(rdbuf(stdout_)) << color << n << text << endl;
920 }
921
showscopes(const Node * n)922 void Runtime::showscopes(const Node* n) {
923 Navigate nav(n);
924 for (auto i = nav.child_begin(), ie = nav.child_end(); i != ie; ++i) {
925 const auto s = Resolve().get_readable_full_id(Navigate(*i).name());
926 ostream(rdbuf(stdout_)) << s << endl;
927 }
928 }
929
recursive_showscopes(const Node * n)930 void Runtime::recursive_showscopes(const Node* n) {
931 Navigate nav(n);
932 for (auto i = nav.child_begin(), ie = nav.child_end(); i != ie; ++i) {
933 Navigate child(*i);
934 const auto s = Resolve().get_readable_full_id(child.name());
935 ostream(rdbuf(stdout_)) << s << endl;
936 recursive_showscopes(child.where());
937 }
938 }
939
showvars(const Identifier * id)940 void Runtime::showvars(const Identifier* id) {
941 ostream os(rdbuf(stdout_));
942
943 // Print full id
944 os << Resolve().get_readable_full_id(id) << endl;
945
946 // Print text of original declaration
947 os << " " << color << id->get_parent() << text << endl;
948
949 // Print width, arity, and properties
950 const auto arity = Evaluate().get_arity(id);
951 os << " " << Evaluate().get_width(id) << " bit ";
952 if (arity.empty()) {
953 os << "scalar ";
954 } else {
955 for (auto i = arity.begin(), ie = arity.end(); i != ie; ) {
956 os << *i;
957 if (++i != ie) {
958 os << "x";
959 }
960 }
961 os << " element array ";
962 }
963 ModuleInfo info(Resolve().get_parent(id));
964 os << (info.is_input(id) ? "input " : "");
965 os << (info.is_output(id) ? "output " : "");
966 os << (info.is_stateful(id) ? "stateful " : "");
967 os << (info.is_implied_wire(id) ? "implied wire " : "");
968 os << (info.is_implied_latch(id) ? "implied latch " : "");
969 os << (info.is_read(id) ? "externally read " : "");
970 os << (info.is_write(id) ? "externally written " : "");
971 os << endl;
972 }
973
recursive_showvars(const Node * n)974 void Runtime::recursive_showvars(const Node* n) {
975 Navigate nav(n);
976 for (auto i = nav.name_begin(), ie = nav.name_end(); i != ie; ++i) {
977 showvars(*i);
978 }
979 for (auto i = nav.child_begin(), ie = nav.child_end(); i != ie; ++i) {
980 recursive_showvars(Navigate(*i).where());
981 }
982 }
983
current_frequency() const984 string Runtime::current_frequency() const {
985 const auto now = ::time(nullptr);
986 const auto den = (now == last_time_) ? 1 : (now - last_time_);
987 const auto res = (logical_time_ - last_logical_time_) / 2 / den;
988
989 *const_cast<time_t*>(&last_time_) = now;
990 *const_cast<uint64_t*>(&last_logical_time_) = logical_time_;
991
992 return format_freq(res);
993 }
994
overall_frequency() const995 string Runtime::overall_frequency() const {
996 const auto now = ::time(nullptr);
997 const auto den = (now == begin_time_) ? 1 : (now - begin_time_);
998 return format_freq(logical_time_ / 2 / den);
999 }
1000
format_freq(uint64_t f) const1001 string Runtime::format_freq(uint64_t f) const {
1002 stringstream ss;
1003 ss.precision(2);
1004 if (f > 1000000) {
1005 ss << fixed << (f/1000000) << " MHz";
1006 } else if (f > 1000) {
1007 ss << fixed << (f/1000) << " KHz";
1008 } else {
1009 ss << fixed << f << " Hz";
1010 }
1011 return ss.str();
1012 }
1013
1014 } // namespace cascade
1015