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 <cstring>
32 #include <signal.h>
33 #include <fstream>
34 #include <sstream>
35 #include "include/cascade.h"
36 #include "cl/cl.h"
37 
38 using namespace cascade;
39 using namespace cascade::cl;
40 using namespace std;
41 
42 namespace {
43 
44 __attribute__((unused)) auto& g1 = Group::create("Cascade Runtime Options");
45 auto& march = StrArg<string>::create("--march")
46   .usage("sw|de10|ulx3s")
47   .description("Target architecture")
48   .initial("sw");
49 auto& fopen_dirs = StrArg<string>::create("-F")
50   .usage("<path1>:<path2>:...:<pathn>")
51   .description("Paths to search when resolving $fopen() statements")
52   .initial("");
53 auto& inc_dirs = StrArg<string>::create("-I")
54   .usage("<path1>:<path2>:...:<pathn>")
55   .description("Paths to search when resolving `include directives")
56   .initial("");
57 auto& input_path = StrArg<string>::create("-e")
58   .usage("path/to/file.v")
59   .description("Read input from file");
60 
61 __attribute__((unused)) auto& g2 = Group::create("Quartus Server Options");
62 auto& quartus_host = StrArg<string>::create("--quartus_host")
63   .usage("<host>")
64   .description("Location of quartus server")
65   .initial("localhost");
66 auto& quartus_port = StrArg<uint32_t>::create("--quartus_port")
67   .usage("<port>")
68   .description("Location of quartus server")
69   .initial(9900);
70 
71 __attribute__((unused)) auto& g3 = Group::create("Logging Options");
72 auto& profile = StrArg<int>::create("--profile")
73   .usage("<n>")
74   .description("Number of seconds to wait between profiling events; setting n to zero disables profiling; only effective with --enable_info")
75   .initial(0);
76 auto& enable_info = FlagArg::create("--enable_info")
77   .description("Turn on info messages");
78 auto& disable_warning = FlagArg::create("--disable_warning")
79   .description("Turn off warning messages");
80 auto& disable_error = FlagArg::create("--disable_error")
81   .description("Turn off error messages");
82 auto& enable_log = FlagArg::create("--enable_log")
83   .description("Prints debugging information to log file");
84 
85 __attribute__((unused)) auto& g4 = Group::create("Optimization Options");
86 auto& disable_inlining = FlagArg::create("--disable_inlining")
87   .description("Prevents cascade from inlining modules");
88 auto& open_loop_target = StrArg<size_t>::create("--open_loop_target")
89   .usage("<n>")
90   .description("Maximum number of seconds to run in open loop for before transferring control back to runtime")
91   .initial(1);
92 
93 __attribute__((unused)) auto& g5 = Group::create("REPL Options");
94 auto& disable_repl = FlagArg::create("--disable_repl")
95   .description("Disables the REPL and treats user input as stdin");
96 
97 class inbuf : public streambuf {
98   public:
inbuf(streambuf * sb)99     inbuf(streambuf* sb) : streambuf() {
100       sb_ = sb;
101       prompt_ = false;
102     }
103     ~inbuf() override = default;
104 
105   private:
106     streambuf* sb_;
107     bool prompt_;
108 
sync()109     int sync() override {
110       return sb_->pubsync();
111     }
uflow()112     int_type uflow() override {
113       const auto res =  sb_->sbumpc();
114       prompt(res);
115       prompt_ = false;
116       return res;
117     }
underflow()118     int_type underflow() override {
119       const auto res = sb_->sgetc();
120       prompt(res);
121       return res;
122     }
prompt(int_type c)123     void prompt(int_type c) {
124       if ((c == '\n') && !prompt_) {
125         prompt_ = true;
126         cout << ">>> ";
127         cout.flush();
128       }
129     }
130 };
131 
132 class outbuf : public streambuf {
133   public:
outbuf(const string & color="")134     outbuf(const string& color = "") : streambuf() {
135       color_ = color;
136     }
137     ~outbuf() override = default;
138 
139   private:
140     string color_;
141     stringstream ss_;
142 
overflow(int_type c=traits_type::eof ())143     int_type overflow(int_type c = traits_type::eof()) override {
144       ss_.put(c);
145       if (c == '\n') {
146         ss_ << ((color_ != "") ? "\033[00m" : "") << ">>> " << color_;
147       }
148       return c;
149     }
sync()150     int_type sync() override {
151       cout << color_ << ss_.str() << ((color_ != "") ? "\033[00m" : "");
152       cout.flush();
153       ss_.str(string());
154       return 0;
155     }
156 };
157 
158 // Allocate cascade on the heap so we can guarantee that it's torn down before
159 // stack or static variables.
160 Cascade* cascade_ = nullptr;
161 
162 // Signal Handlers:
int_handler(int sig)163 void int_handler(int sig) {
164   (void) sig;
165   ::cascade_->request_stop();
166 }
segv_handler(int sig)167 void segv_handler(int sig) {
168   (void) sig;
169   cerr << "\033[31mCASCADE SHUTDOWN UNEXPECTEDLY\033[00m" << endl;
170   cerr << "\033[31mPlease rerun with --enable_log and forward log file to developers\033[00m" << endl;
171   exit(1);
172 }
173 
174 } // namespace
175 
main(int argc,char ** argv)176 int main(int argc, char** argv) {
177   // Parse command line
178   Simple::read(argc, argv);
179 
180   // Wrap cin in inbuf (re-prints the prompt when the user types \n)
181   inbuf ib(cin.rdbuf());
182   cin.rdbuf(&ib);
183 
184   // Install signal handlers
185   { struct sigaction action;
186     memset(&action, 0, sizeof(action));
187     action.sa_handler = ::segv_handler;
188     sigaction(SIGSEGV, &action, nullptr);
189   }
190   { struct sigaction action;
191     memset(&action, 0, sizeof(action));
192     action.sa_handler = ::int_handler;
193     sigaction(SIGINT, &action, nullptr);
194   }
195 
196   // Create a new cascade
197   ::cascade_ = new Cascade();
198 
199   // Set command line flags
200   ::cascade_->set_fopen_dirs(::fopen_dirs.value());
201   ::cascade_->set_include_dirs(::inc_dirs.value());
202   ::cascade_->set_enable_inlining(!::disable_inlining.value());
203   ::cascade_->set_open_loop_target(::open_loop_target.value());
204   ::cascade_->set_quartus_server(::quartus_host.value(), ::quartus_port.value());
205   ::cascade_->set_profile_interval(::profile.value());
206 
207   // Map standard streams to colored outbufs
208   if (::disable_repl.value()) {
209     ::cascade_->set_stdin(cin.rdbuf());
210   }
211   ::cascade_->set_stdout(new outbuf());
212   if (!::disable_error.value()) {
213     ::cascade_->set_stderr(new outbuf("\033[31m"));
214   }
215   if (!::disable_warning.value()) {
216     ::cascade_->set_stdwarn(new outbuf("\033[33m"));
217   }
218   if (::enable_info.value()) {
219     ::cascade_->set_stdinfo(new outbuf("\033[37m"));
220   }
221   auto* fb = new filebuf();
222   if (::enable_log.value()) {
223     fb->open("cascade.log", ios::app | ios::out);
224   }
225   ::cascade_->set_stdlog(fb);
226 
227   // Print the initial prompt
228   cout << ">>> ";
229 
230   // Start cascade, and read the march file and -e file (if provided)
231   ::cascade_->run();
232   if (::input_path.value() != "") {
233     *::cascade_ << "`include \"share/cascade/march/" << ::march.value() << ".v\"\n"
234                 << "`include \"" << ::input_path.value() <<  "\"" << endl;
235   } else {
236     *::cascade_ << "`include \"share/cascade/march/" << ::march.value() << ".v\"" << endl;
237   }
238   ::cascade_->stop_now();
239 
240   // Switch to reading from cin if the REPL wasn't disable and wait for finish
241   if (!::disable_repl.value()) {
242     ::cascade_->rdbuf(cin.rdbuf());
243   }
244   ::cascade_->run();
245   ::cascade_->wait_for_stop();
246 
247   // If cascade isn't finished by now, it's because we've caught a signal.
248   // Either way, we can delete it now.
249   if (!::cascade_->is_finished()) {
250     cerr << "\033[31mCaught Signal\033[00m" << endl;
251   }
252   delete ::cascade_;
253 
254   cout << "Goodbye!" << endl;
255   return 0;
256 }
257