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