1 /*
2  *  yosys -- Yosys Open SYnthesis Suite
3  *
4  *  Copyright (C) 2012  Claire Xenia Wolf <claire@yosyshq.com>
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/satgen.h"
22 
23 #include <string.h>
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <errno.h>
27 
28 #ifdef YOSYS_ENABLE_ZLIB
29 #include <zlib.h>
30 
31 PRIVATE_NAMESPACE_BEGIN
32 #define GZ_BUFFER_SIZE 8192
decompress_gzip(const std::string & filename,std::stringstream & out)33 void decompress_gzip(const std::string &filename, std::stringstream &out)
34 {
35 	char buffer[GZ_BUFFER_SIZE];
36 	int bytes_read;
37 	gzFile gzf = gzopen(filename.c_str(), "rb");
38 	while(!gzeof(gzf)) {
39 		bytes_read = gzread(gzf, reinterpret_cast<void *>(buffer), GZ_BUFFER_SIZE);
40 		out.write(buffer, bytes_read);
41 	}
42 	gzclose(gzf);
43 }
44 
45 /*
46 An output stream that uses a stringbuf to buffer data internally,
47 using zlib to write gzip-compressed data every time the stream is flushed.
48 */
49 class gzip_ostream : public std::ostream  {
50 public:
gzip_ostream()51 	gzip_ostream() : std::ostream(nullptr)
52 	{
53 		rdbuf(&outbuf);
54 	}
open(const std::string & filename)55 	bool open(const std::string &filename)
56 	{
57 		return outbuf.open(filename);
58 	}
59 private:
60 	class gzip_streambuf : public std::stringbuf {
61 	public:
gzip_streambuf()62 		gzip_streambuf() { };
open(const std::string & filename)63 		bool open(const std::string &filename)
64 		{
65 			gzf = gzopen(filename.c_str(), "wb");
66 			return gzf != nullptr;
67 		}
sync()68 		virtual int sync() override
69 		{
70 			gzwrite(gzf, reinterpret_cast<const void *>(str().c_str()), unsigned(str().size()));
71 			str("");
72 			return 0;
73 		}
~gzip_streambuf()74 		virtual ~gzip_streambuf()
75 		{
76 			sync();
77 			gzclose(gzf);
78 		}
79 	private:
80 		gzFile gzf = nullptr;
81 	} outbuf;
82 };
83 PRIVATE_NAMESPACE_END
84 
85 #endif
86 
87 YOSYS_NAMESPACE_BEGIN
88 
89 #define MAX_REG_COUNT 1000
90 
91 bool echo_mode = false;
92 Pass *first_queued_pass;
93 Pass *current_pass;
94 
95 std::map<std::string, Frontend*> frontend_register;
96 std::map<std::string, Pass*> pass_register;
97 std::map<std::string, Backend*> backend_register;
98 
99 std::vector<std::string> Frontend::next_args;
100 
Pass(std::string name,std::string short_help)101 Pass::Pass(std::string name, std::string short_help) : pass_name(name), short_help(short_help)
102 {
103 	next_queued_pass = first_queued_pass;
104 	first_queued_pass = this;
105 	call_counter = 0;
106 	runtime_ns = 0;
107 }
108 
run_register()109 void Pass::run_register()
110 {
111 	log_assert(pass_register.count(pass_name) == 0);
112 	pass_register[pass_name] = this;
113 }
114 
init_register()115 void Pass::init_register()
116 {
117 	vector<Pass*> added_passes;
118 	while (first_queued_pass) {
119 		added_passes.push_back(first_queued_pass);
120 		first_queued_pass->run_register();
121 		first_queued_pass = first_queued_pass->next_queued_pass;
122 	}
123 	for (auto added_pass : added_passes)
124 		added_pass->on_register();
125 }
126 
done_register()127 void Pass::done_register()
128 {
129 	for (auto &it : pass_register)
130 		it.second->on_shutdown();
131 
132 	frontend_register.clear();
133 	pass_register.clear();
134 	backend_register.clear();
135 	log_assert(first_queued_pass == NULL);
136 }
137 
on_register()138 void Pass::on_register()
139 {
140 }
141 
on_shutdown()142 void Pass::on_shutdown()
143 {
144 }
145 
~Pass()146 Pass::~Pass()
147 {
148 }
149 
pre_execute()150 Pass::pre_post_exec_state_t Pass::pre_execute()
151 {
152 	pre_post_exec_state_t state;
153 	call_counter++;
154 	state.begin_ns = PerformanceTimer::query();
155 	state.parent_pass = current_pass;
156 	current_pass = this;
157 	clear_flags();
158 	return state;
159 }
160 
post_execute(Pass::pre_post_exec_state_t state)161 void Pass::post_execute(Pass::pre_post_exec_state_t state)
162 {
163 	IdString::checkpoint();
164 	log_suppressed();
165 
166 	int64_t time_ns = PerformanceTimer::query() - state.begin_ns;
167 	runtime_ns += time_ns;
168 	current_pass = state.parent_pass;
169 	if (current_pass)
170 		current_pass->runtime_ns -= time_ns;
171 }
172 
help()173 void Pass::help()
174 {
175 	log("\n");
176 	log("No help message for command `%s'.\n", pass_name.c_str());
177 	log("\n");
178 }
179 
clear_flags()180 void Pass::clear_flags()
181 {
182 }
183 
cmd_log_args(const std::vector<std::string> & args)184 void Pass::cmd_log_args(const std::vector<std::string> &args)
185 {
186 	if (args.size() <= 1)
187 		return;
188 	log("Full command line:");
189 	for (size_t i = 0; i < args.size(); i++)
190 		log(" %s", args[i].c_str());
191 	log("\n");
192 }
193 
cmd_error(const std::vector<std::string> & args,size_t argidx,std::string msg)194 void Pass::cmd_error(const std::vector<std::string> &args, size_t argidx, std::string msg)
195 {
196 	std::string command_text;
197 	int error_pos = 0;
198 
199 	for (size_t i = 0; i < args.size(); i++) {
200 		if (i < argidx)
201 			error_pos += args[i].size() + 1;
202 		command_text = command_text + (command_text.empty() ? "" : " ") + args[i];
203 	}
204 
205 	log("\nSyntax error in command `%s':\n", command_text.c_str());
206 	help();
207 
208 	log_cmd_error("Command syntax error: %s\n> %s\n> %*s^\n",
209 			msg.c_str(), command_text.c_str(), error_pos, "");
210 }
211 
extra_args(std::vector<std::string> args,size_t argidx,RTLIL::Design * design,bool select)212 void Pass::extra_args(std::vector<std::string> args, size_t argidx, RTLIL::Design *design, bool select)
213 {
214 	for (; argidx < args.size(); argidx++)
215 	{
216 		std::string arg = args[argidx];
217 
218 		if (arg.compare(0, 1, "-") == 0)
219 			cmd_error(args, argidx, "Unknown option or option in arguments.");
220 
221 		if (!select)
222 			cmd_error(args, argidx, "Extra argument.");
223 
224 		handle_extra_select_args(this, args, argidx, args.size(), design);
225 		break;
226 	}
227 	// cmd_log_args(args);
228 }
229 
call(RTLIL::Design * design,std::string command)230 void Pass::call(RTLIL::Design *design, std::string command)
231 {
232 	std::vector<std::string> args;
233 
234 	std::string cmd_buf = command;
235 	std::string tok = next_token(cmd_buf, " \t\r\n", true);
236 
237 	if (tok.empty())
238 		return;
239 
240 	if (tok[0] == '!') {
241 #if !defined(YOSYS_DISABLE_SPAWN)
242 		cmd_buf = command.substr(command.find('!') + 1);
243 		while (!cmd_buf.empty() && (cmd_buf.back() == ' ' || cmd_buf.back() == '\t' ||
244 				cmd_buf.back() == '\r' || cmd_buf.back() == '\n'))
245 			cmd_buf.resize(cmd_buf.size()-1);
246 		log_header(design, "Shell command: %s\n", cmd_buf.c_str());
247 		int retCode = run_command(cmd_buf);
248 		if (retCode != 0)
249 			log_cmd_error("Shell command returned error code %d.\n", retCode);
250 		return;
251 #else
252 		log_cmd_error("Shell is not available.\n");
253 #endif
254 	}
255 
256 	while (!tok.empty()) {
257 		if (tok[0] == '#') {
258 			int stop;
259 			for (stop = 0; stop < GetSize(cmd_buf); stop++)
260 				if (cmd_buf[stop] == '\r' || cmd_buf[stop] == '\n')
261 					break;
262 			cmd_buf = cmd_buf.substr(stop);
263 		} else
264 		if (tok.back() == ';') {
265 			int num_semikolon = 0;
266 			while (!tok.empty() && tok.back() == ';')
267 				tok.resize(tok.size()-1), num_semikolon++;
268 			if (!tok.empty())
269 				args.push_back(tok);
270 			call(design, args);
271 			args.clear();
272 			if (num_semikolon == 2)
273 				call(design, "clean");
274 			if (num_semikolon == 3)
275 				call(design, "clean -purge");
276 		} else
277 			args.push_back(tok);
278 		bool found_nl = false;
279 		for (auto c : cmd_buf) {
280 			if (c == ' ' || c == '\t')
281 				continue;
282 			if (c == '\r' || c == '\n')
283 				found_nl = true;
284 			break;
285 		}
286 		if (found_nl) {
287 			call(design, args);
288 			args.clear();
289 		}
290 		tok = next_token(cmd_buf, " \t\r\n", true);
291 	}
292 
293 	call(design, args);
294 }
295 
call(RTLIL::Design * design,std::vector<std::string> args)296 void Pass::call(RTLIL::Design *design, std::vector<std::string> args)
297 {
298 	if (args.size() == 0 || args[0][0] == '#' || args[0][0] == ':')
299 		return;
300 
301 	if (echo_mode) {
302 		log("%s", create_prompt(design, 0));
303 		for (size_t i = 0; i < args.size(); i++)
304 			log("%s%s", i ? " " : "", args[i].c_str());
305 		log("\n");
306 	}
307 
308 	if (pass_register.count(args[0]) == 0)
309 		log_cmd_error("No such command: %s (type 'help' for a command overview)\n", args[0].c_str());
310 
311 	if (pass_register[args[0]]->experimental_flag)
312 		log_experimental("%s", args[0].c_str());
313 
314 	size_t orig_sel_stack_pos = design->selection_stack.size();
315 	auto state = pass_register[args[0]]->pre_execute();
316 	pass_register[args[0]]->execute(args, design);
317 	pass_register[args[0]]->post_execute(state);
318 	while (design->selection_stack.size() > orig_sel_stack_pos)
319 		design->selection_stack.pop_back();
320 }
321 
call_on_selection(RTLIL::Design * design,const RTLIL::Selection & selection,std::string command)322 void Pass::call_on_selection(RTLIL::Design *design, const RTLIL::Selection &selection, std::string command)
323 {
324 	std::string backup_selected_active_module = design->selected_active_module;
325 	design->selected_active_module.clear();
326 	design->selection_stack.push_back(selection);
327 
328 	Pass::call(design, command);
329 
330 	design->selection_stack.pop_back();
331 	design->selected_active_module = backup_selected_active_module;
332 }
333 
call_on_selection(RTLIL::Design * design,const RTLIL::Selection & selection,std::vector<std::string> args)334 void Pass::call_on_selection(RTLIL::Design *design, const RTLIL::Selection &selection, std::vector<std::string> args)
335 {
336 	std::string backup_selected_active_module = design->selected_active_module;
337 	design->selected_active_module.clear();
338 	design->selection_stack.push_back(selection);
339 
340 	Pass::call(design, args);
341 
342 	design->selection_stack.pop_back();
343 	design->selected_active_module = backup_selected_active_module;
344 }
345 
call_on_module(RTLIL::Design * design,RTLIL::Module * module,std::string command)346 void Pass::call_on_module(RTLIL::Design *design, RTLIL::Module *module, std::string command)
347 {
348 	std::string backup_selected_active_module = design->selected_active_module;
349 	design->selected_active_module = module->name.str();
350 	design->selection_stack.push_back(RTLIL::Selection(false));
351 	design->selection_stack.back().select(module);
352 
353 	Pass::call(design, command);
354 
355 	design->selection_stack.pop_back();
356 	design->selected_active_module = backup_selected_active_module;
357 }
358 
call_on_module(RTLIL::Design * design,RTLIL::Module * module,std::vector<std::string> args)359 void Pass::call_on_module(RTLIL::Design *design, RTLIL::Module *module, std::vector<std::string> args)
360 {
361 	std::string backup_selected_active_module = design->selected_active_module;
362 	design->selected_active_module = module->name.str();
363 	design->selection_stack.push_back(RTLIL::Selection(false));
364 	design->selection_stack.back().select(module);
365 
366 	Pass::call(design, args);
367 
368 	design->selection_stack.pop_back();
369 	design->selected_active_module = backup_selected_active_module;
370 }
371 
check_label(std::string label,std::string info)372 bool ScriptPass::check_label(std::string label, std::string info)
373 {
374 	if (active_design == nullptr) {
375 		log("\n");
376 		if (info.empty())
377 			log("    %s:\n", label.c_str());
378 		else
379 			log("    %s:    %s\n", label.c_str(), info.c_str());
380 		return true;
381 	} else {
382 		if (!active_run_from.empty() && active_run_from == active_run_to) {
383 			block_active = (label == active_run_from);
384 		} else {
385 			if (label == active_run_from)
386 				block_active = true;
387 			if (label == active_run_to)
388 				block_active = false;
389 		}
390 		return block_active;
391 	}
392 }
393 
run(std::string command,std::string info)394 void ScriptPass::run(std::string command, std::string info)
395 {
396 	if (active_design == nullptr) {
397 		if (info.empty())
398 			log("        %s\n", command.c_str());
399 		else
400 			log("        %s    %s\n", command.c_str(), info.c_str());
401 	} else {
402 		Pass::call(active_design, command);
403 		active_design->check();
404 	}
405 }
406 
run_nocheck(std::string command,std::string info)407 void ScriptPass::run_nocheck(std::string command, std::string info)
408 {
409 	if (active_design == nullptr) {
410 		if (info.empty())
411 			log("        %s\n", command.c_str());
412 		else
413 			log("        %s    %s\n", command.c_str(), info.c_str());
414 	} else {
415 		Pass::call(active_design, command);
416 	}
417 }
418 
run_script(RTLIL::Design * design,std::string run_from,std::string run_to)419 void ScriptPass::run_script(RTLIL::Design *design, std::string run_from, std::string run_to)
420 {
421 	help_mode = false;
422 	active_design = design;
423 	block_active = run_from.empty();
424 	active_run_from = run_from;
425 	active_run_to = run_to;
426 	script();
427 }
428 
help_script()429 void ScriptPass::help_script()
430 {
431 	clear_flags();
432 	help_mode = true;
433 	active_design = nullptr;
434 	block_active = true;
435 	active_run_from.clear();
436 	active_run_to.clear();
437 	script();
438 }
439 
Frontend(std::string name,std::string short_help)440 Frontend::Frontend(std::string name, std::string short_help) :
441 		Pass(name.rfind("=", 0) == 0 ? name.substr(1) : "read_" + name, short_help),
442 		frontend_name(name.rfind("=", 0) == 0 ? name.substr(1) : name)
443 {
444 }
445 
run_register()446 void Frontend::run_register()
447 {
448 	log_assert(pass_register.count(pass_name) == 0);
449 	pass_register[pass_name] = this;
450 
451 	log_assert(frontend_register.count(frontend_name) == 0);
452 	frontend_register[frontend_name] = this;
453 }
454 
~Frontend()455 Frontend::~Frontend()
456 {
457 }
458 
execute(std::vector<std::string> args,RTLIL::Design * design)459 void Frontend::execute(std::vector<std::string> args, RTLIL::Design *design)
460 {
461 	log_assert(next_args.empty());
462 	do {
463 		std::istream *f = NULL;
464 		next_args.clear();
465 		auto state = pre_execute();
466 		execute(f, std::string(), args, design);
467 		post_execute(state);
468 		args = next_args;
469 		delete f;
470 	} while (!args.empty());
471 }
472 
473 FILE *Frontend::current_script_file = NULL;
474 std::string Frontend::last_here_document;
475 
extra_args(std::istream * & f,std::string & filename,std::vector<std::string> args,size_t argidx,bool bin_input)476 void Frontend::extra_args(std::istream *&f, std::string &filename, std::vector<std::string> args, size_t argidx, bool bin_input)
477 {
478 	bool called_with_fp = f != NULL;
479 
480 	next_args.clear();
481 
482 	if (argidx < args.size())
483 	{
484 		std::string arg = args[argidx];
485 
486 		if (arg.compare(0, 1, "-") == 0)
487 			cmd_error(args, argidx, "Unknown option or option in arguments.");
488 		if (f != NULL)
489 			cmd_error(args, argidx, "Extra filename argument in direct file mode.");
490 
491 		filename = arg;
492 		//Accommodate heredocs with EOT marker spaced out from "<<", e.g. "<< EOT" vs. "<<EOT"
493 		if (filename == "<<" && argidx+1 < args.size())
494 			filename += args[++argidx];
495 		if (filename.compare(0, 2, "<<") == 0) {
496 			if (filename.size() <= 2)
497 				log_error("Missing EOT marker in here document!\n");
498 			std::string eot_marker = filename.substr(2);
499 			if (Frontend::current_script_file == nullptr)
500 				filename = "<stdin>";
501 			last_here_document.clear();
502 			while (1) {
503 				std::string buffer;
504 				char block[4096];
505 				while (1) {
506 					if (fgets(block, 4096, Frontend::current_script_file == nullptr? stdin : Frontend::current_script_file) == nullptr)
507 						log_error("Unexpected end of file in here document '%s'!\n", filename.c_str());
508 					buffer += block;
509 					if (buffer.size() > 0 && (buffer[buffer.size() - 1] == '\n' || buffer[buffer.size() - 1] == '\r'))
510 						break;
511 				}
512 				size_t indent = buffer.find_first_not_of(" \t\r\n");
513 				if (indent != std::string::npos && buffer.compare(indent, eot_marker.size(), eot_marker) == 0)
514 					break;
515 				last_here_document += buffer;
516 			}
517 			f = new std::istringstream(last_here_document);
518 		} else {
519 			rewrite_filename(filename);
520 			vector<string> filenames = glob_filename(filename);
521 			filename = filenames.front();
522 			if (GetSize(filenames) > 1) {
523 				next_args.insert(next_args.end(), args.begin(), args.begin()+argidx);
524 				next_args.insert(next_args.end(), filenames.begin()+1, filenames.end());
525 			}
526 			std::ifstream *ff = new std::ifstream;
527 			ff->open(filename.c_str(), bin_input ? std::ifstream::binary : std::ifstream::in);
528 			yosys_input_files.insert(filename);
529 			if (ff->fail())
530 				delete ff;
531 			else
532 				f = ff;
533 			if (f != NULL) {
534 				// Check for gzip magic
535 				unsigned char magic[3];
536 				int n = 0;
537 				while (n < 3)
538 				{
539 					int c = ff->get();
540 					if (c != EOF) {
541 						magic[n] = (unsigned char) c;
542 					}
543 					n++;
544 				}
545 				if (n == 3 && magic[0] == 0x1f && magic[1] == 0x8b) {
546 	#ifdef YOSYS_ENABLE_ZLIB
547 					log("Found gzip magic in file `%s', decompressing using zlib.\n", filename.c_str());
548 					if (magic[2] != 8)
549 						log_cmd_error("gzip file `%s' uses unsupported compression type %02x\n",
550 							filename.c_str(), unsigned(magic[2]));
551 					delete ff;
552 					std::stringstream *df = new std::stringstream();
553 					decompress_gzip(filename, *df);
554 					f = df;
555 	#else
556 					log_cmd_error("File `%s' is a gzip file, but Yosys is compiled without zlib.\n", filename.c_str());
557 	#endif
558 				} else {
559 					ff->clear();
560 					ff->seekg(0, std::ios::beg);
561 				}
562 			}
563 		}
564 		if (f == NULL)
565 			log_cmd_error("Can't open input file `%s' for reading: %s\n", filename.c_str(), strerror(errno));
566 
567 		for (size_t i = argidx+1; i < args.size(); i++)
568 			if (args[i].compare(0, 1, "-") == 0)
569 				cmd_error(args, i, "Found option, expected arguments.");
570 
571 		if (argidx+1 < args.size()) {
572 			if (next_args.empty())
573 				next_args.insert(next_args.end(), args.begin(), args.begin()+argidx);
574 			next_args.insert(next_args.end(), args.begin()+argidx+1, args.end());
575 			args.erase(args.begin()+argidx+1, args.end());
576 		}
577 	}
578 
579 	if (f == NULL)
580 		cmd_error(args, argidx, "No filename given.");
581 
582 	if (called_with_fp)
583 		args.push_back(filename);
584 	args[0] = pass_name;
585 	// cmd_log_args(args);
586 }
587 
frontend_call(RTLIL::Design * design,std::istream * f,std::string filename,std::string command)588 void Frontend::frontend_call(RTLIL::Design *design, std::istream *f, std::string filename, std::string command)
589 {
590 	std::vector<std::string> args;
591 	char *s = strdup(command.c_str());
592 	for (char *p = strtok(s, " \t\r\n"); p; p = strtok(NULL, " \t\r\n"))
593 		args.push_back(p);
594 	free(s);
595 	frontend_call(design, f, filename, args);
596 }
597 
frontend_call(RTLIL::Design * design,std::istream * f,std::string filename,std::vector<std::string> args)598 void Frontend::frontend_call(RTLIL::Design *design, std::istream *f, std::string filename, std::vector<std::string> args)
599 {
600 	if (args.size() == 0)
601 		return;
602 	if (frontend_register.count(args[0]) == 0)
603 		log_cmd_error("No such frontend: %s\n", args[0].c_str());
604 
605 	if (f != NULL) {
606 		auto state = frontend_register[args[0]]->pre_execute();
607 		frontend_register[args[0]]->execute(f, filename, args, design);
608 		frontend_register[args[0]]->post_execute(state);
609 	} else if (filename == "-") {
610 		std::istream *f_cin = &std::cin;
611 		auto state = frontend_register[args[0]]->pre_execute();
612 		frontend_register[args[0]]->execute(f_cin, "<stdin>", args, design);
613 		frontend_register[args[0]]->post_execute(state);
614 	} else {
615 		if (!filename.empty())
616 			args.push_back(filename);
617 		frontend_register[args[0]]->execute(args, design);
618 	}
619 }
620 
Backend(std::string name,std::string short_help)621 Backend::Backend(std::string name, std::string short_help) :
622 		Pass(name.rfind("=", 0) == 0 ? name.substr(1) : "write_" + name, short_help),
623 		backend_name(name.rfind("=", 0) == 0 ? name.substr(1) : name)
624 {
625 }
626 
run_register()627 void Backend::run_register()
628 {
629 	log_assert(pass_register.count(pass_name) == 0);
630 	pass_register[pass_name] = this;
631 
632 	log_assert(backend_register.count(backend_name) == 0);
633 	backend_register[backend_name] = this;
634 }
635 
~Backend()636 Backend::~Backend()
637 {
638 }
639 
execute(std::vector<std::string> args,RTLIL::Design * design)640 void Backend::execute(std::vector<std::string> args, RTLIL::Design *design)
641 {
642 	std::ostream *f = NULL;
643 	auto state = pre_execute();
644 	execute(f, std::string(), args, design);
645 	post_execute(state);
646 	if (f != &std::cout)
647 		delete f;
648 }
649 
extra_args(std::ostream * & f,std::string & filename,std::vector<std::string> args,size_t argidx,bool bin_output)650 void Backend::extra_args(std::ostream *&f, std::string &filename, std::vector<std::string> args, size_t argidx, bool bin_output)
651 {
652 	bool called_with_fp = f != NULL;
653 
654 	for (; argidx < args.size(); argidx++)
655 	{
656 		std::string arg = args[argidx];
657 
658 		if (arg.compare(0, 1, "-") == 0 && arg != "-")
659 			cmd_error(args, argidx, "Unknown option or option in arguments.");
660 		if (f != NULL)
661 			cmd_error(args, argidx, "Extra filename argument in direct file mode.");
662 
663 		if (arg == "-") {
664 			filename = "<stdout>";
665 			f = &std::cout;
666 			continue;
667 		}
668 
669 		filename = arg;
670 		rewrite_filename(filename);
671 		if (filename.size() > 3 && filename.compare(filename.size()-3, std::string::npos, ".gz") == 0) {
672 #ifdef YOSYS_ENABLE_ZLIB
673 			gzip_ostream *gf = new gzip_ostream;
674 			if (!gf->open(filename)) {
675 				delete gf;
676 				log_cmd_error("Can't open output file `%s' for writing: %s\n", filename.c_str(), strerror(errno));
677 			}
678 			yosys_output_files.insert(filename);
679 			f = gf;
680 #else
681 			log_cmd_error("Yosys is compiled without zlib support, unable to write gzip output.\n");
682 #endif
683 		} else {
684 			std::ofstream *ff = new std::ofstream;
685 			ff->open(filename.c_str(), bin_output ? (std::ofstream::trunc | std::ofstream::binary) : std::ofstream::trunc);
686 			yosys_output_files.insert(filename);
687 			if (ff->fail()) {
688 				delete ff;
689 				log_cmd_error("Can't open output file `%s' for writing: %s\n", filename.c_str(), strerror(errno));
690 			}
691 			f = ff;
692 		}
693 	}
694 
695 	if (called_with_fp)
696 		args.push_back(filename);
697 	args[0] = pass_name;
698 	// cmd_log_args(args);
699 
700 	if (f == NULL) {
701 		filename = "<stdout>";
702 		f = &std::cout;
703 	}
704 }
705 
backend_call(RTLIL::Design * design,std::ostream * f,std::string filename,std::string command)706 void Backend::backend_call(RTLIL::Design *design, std::ostream *f, std::string filename, std::string command)
707 {
708 	std::vector<std::string> args;
709 	char *s = strdup(command.c_str());
710 	for (char *p = strtok(s, " \t\r\n"); p; p = strtok(NULL, " \t\r\n"))
711 		args.push_back(p);
712 	free(s);
713 	backend_call(design, f, filename, args);
714 }
715 
backend_call(RTLIL::Design * design,std::ostream * f,std::string filename,std::vector<std::string> args)716 void Backend::backend_call(RTLIL::Design *design, std::ostream *f, std::string filename, std::vector<std::string> args)
717 {
718 	if (args.size() == 0)
719 		return;
720 	if (backend_register.count(args[0]) == 0)
721 		log_cmd_error("No such backend: %s\n", args[0].c_str());
722 
723 	size_t orig_sel_stack_pos = design->selection_stack.size();
724 
725 	if (f != NULL) {
726 		auto state = backend_register[args[0]]->pre_execute();
727 		backend_register[args[0]]->execute(f, filename, args, design);
728 		backend_register[args[0]]->post_execute(state);
729 	} else if (filename == "-") {
730 		std::ostream *f_cout = &std::cout;
731 		auto state = backend_register[args[0]]->pre_execute();
732 		backend_register[args[0]]->execute(f_cout, "<stdout>", args, design);
733 		backend_register[args[0]]->post_execute(state);
734 	} else {
735 		if (!filename.empty())
736 			args.push_back(filename);
737 		backend_register[args[0]]->execute(args, design);
738 	}
739 
740 	while (design->selection_stack.size() > orig_sel_stack_pos)
741 		design->selection_stack.pop_back();
742 }
743 
744 static struct CellHelpMessages {
745 	dict<string, string> cell_help, cell_code;
CellHelpMessagesCellHelpMessages746 	CellHelpMessages() {
747 #include "techlibs/common/simlib_help.inc"
748 #include "techlibs/common/simcells_help.inc"
749 		cell_help.sort();
750 		cell_code.sort();
751 	}
752 } cell_help_messages;
753 
754 struct HelpPass : public Pass {
HelpPassHelpPass755 	HelpPass() : Pass("help", "display help messages") { }
helpHelpPass756 	void help() override
757 	{
758 		log("\n");
759 		log("    help  ................  list all commands\n");
760 		log("    help <command>  ......  print help message for given command\n");
761 		log("    help -all  ...........  print complete command reference\n");
762 		log("\n");
763 		log("    help -cells ..........  list all cell types\n");
764 		log("    help <celltype>  .....  print help message for given cell type\n");
765 		log("    help <celltype>+  ....  print verilog code for given cell type\n");
766 		log("\n");
767 	}
escape_texHelpPass768 	void escape_tex(std::string &tex)
769 	{
770 		for (size_t pos = 0; (pos = tex.find('_', pos)) != std::string::npos; pos += 2)
771 			tex.replace(pos, 1, "\\_");
772 		for (size_t pos = 0; (pos = tex.find('$', pos)) != std::string::npos; pos += 2)
773 			tex.replace(pos, 1, "\\$");
774 	}
write_texHelpPass775 	void write_tex(FILE *f, std::string cmd, std::string title, std::string text)
776 	{
777 		size_t begin = text.find_first_not_of("\n"), end = text.find_last_not_of("\n");
778 		if (begin != std::string::npos && end != std::string::npos && begin < end)
779 			text = text.substr(begin, end-begin+1);
780 		std::string cmd_unescaped = cmd;
781 		escape_tex(cmd);
782 		escape_tex(title);
783 		fprintf(f, "\\section{%s -- %s}\n", cmd.c_str(), title.c_str());
784 		fprintf(f, "\\label{cmd:%s}\n", cmd_unescaped.c_str());
785 		fprintf(f, "\\begin{lstlisting}[numbers=left,frame=single]\n");
786 		fprintf(f, "%s\n\\end{lstlisting}\n\n", text.c_str());
787 	}
escape_htmlHelpPass788 	void escape_html(std::string &html)
789 	{
790 		size_t pos = 0;
791 		while ((pos = html.find_first_of("<>&", pos)) != std::string::npos)
792 			switch (html[pos]) {
793 			case '<':
794 				html.replace(pos, 1, "&lt;");
795 				pos += 4;
796 				break;
797 			case '>':
798 				html.replace(pos, 1, "&gt;");
799 				pos += 4;
800 				break;
801 			case '&':
802 				html.replace(pos, 1, "&amp;");
803 				pos += 5;
804 				break;
805 			}
806 	}
write_htmlHelpPass807 	void write_html(FILE *idxf, std::string cmd, std::string title, std::string text)
808 	{
809 		FILE *f = fopen(stringf("cmd_%s.in", cmd.c_str()).c_str(), "wt");
810 		fprintf(idxf, "<li><a href=\"cmd_%s.html\"> ", cmd.c_str());
811 
812 		escape_html(cmd);
813 		escape_html(title);
814 		escape_html(text);
815 
816 		fprintf(idxf, "%s</a> <span>%s</span></a>\n", cmd.c_str(), title.c_str());
817 
818 		fprintf(f, "@cmd_header %s@\n", cmd.c_str());
819 		fprintf(f, "<h1>%s - %s</h1>\n", cmd.c_str(), title.c_str());
820 		fprintf(f, "<pre>%s</pre>\n", text.c_str());
821 		fprintf(f, "@footer@\n");
822 
823 		fclose(f);
824 	}
executeHelpPass825 	void execute(std::vector<std::string> args, RTLIL::Design*) override
826 	{
827 		if (args.size() == 1) {
828 			log("\n");
829 			for (auto &it : pass_register)
830 				log("    %-20s %s\n", it.first.c_str(), it.second->short_help.c_str());
831 			log("\n");
832 			log("Type 'help <command>' for more information on a command.\n");
833 			log("Type 'help -cells' for a list of all cell types.\n");
834 			log("\n");
835 			return;
836 		}
837 
838 		if (args.size() == 2) {
839 			if (args[1] == "-all") {
840 				for (auto &it : pass_register) {
841 					log("\n\n");
842 					log("%s  --  %s\n", it.first.c_str(), it.second->short_help.c_str());
843 					for (size_t i = 0; i < it.first.size() + it.second->short_help.size() + 6; i++)
844 						log("=");
845 					log("\n");
846 					it.second->help();
847 					if (it.second->experimental_flag) {
848 						log("\n");
849 						log("WARNING: THE '%s' COMMAND IS EXPERIMENTAL.\n", it.first.c_str());
850 						log("\n");
851 					}
852 				}
853 			}
854 			else if (args[1] == "-cells") {
855 				log("\n");
856 				for (auto &it : cell_help_messages.cell_help) {
857 					string line = split_tokens(it.second, "\n").at(0);
858 					string cell_name = next_token(line);
859 					log("    %-15s %s\n", cell_name.c_str(), line.c_str());
860 				}
861 				log("\n");
862 				log("Type 'help <cell_type>' for more information on a cell type.\n");
863 				log("\n");
864 				return;
865 			}
866 			// this option is undocumented as it is for internal use only
867 			else if (args[1] == "-write-tex-command-reference-manual") {
868 				FILE *f = fopen("command-reference-manual.tex", "wt");
869 				fprintf(f, "%% Generated using the yosys 'help -write-tex-command-reference-manual' command.\n\n");
870 				for (auto &it : pass_register) {
871 					std::ostringstream buf;
872 					log_streams.push_back(&buf);
873 					it.second->help();
874 					if (it.second->experimental_flag) {
875 						log("\n");
876 						log("WARNING: THE '%s' COMMAND IS EXPERIMENTAL.\n", it.first.c_str());
877 						log("\n");
878 					}
879 					log_streams.pop_back();
880 					write_tex(f, it.first, it.second->short_help, buf.str());
881 				}
882 				fclose(f);
883 			}
884 			// this option is undocumented as it is for internal use only
885 			else if (args[1] == "-write-web-command-reference-manual") {
886 				FILE *f = fopen("templates/cmd_index.in", "wt");
887 				for (auto &it : pass_register) {
888 					std::ostringstream buf;
889 					log_streams.push_back(&buf);
890 					it.second->help();
891 					if (it.second->experimental_flag) {
892 						log("\n");
893 						log("WARNING: THE '%s' COMMAND IS EXPERIMENTAL.\n", it.first.c_str());
894 						log("\n");
895 					}
896 					log_streams.pop_back();
897 					write_html(f, it.first, it.second->short_help, buf.str());
898 				}
899 				fclose(f);
900 			}
901 			else if (pass_register.count(args[1])) {
902 				pass_register.at(args[1])->help();
903 				if (pass_register.at(args[1])->experimental_flag) {
904 					log("\n");
905 					log("WARNING: THE '%s' COMMAND IS EXPERIMENTAL.\n", args[1].c_str());
906 					log("\n");
907 				}
908 			}
909 			else if (cell_help_messages.cell_help.count(args[1])) {
910 				log("%s", cell_help_messages.cell_help.at(args[1]).c_str());
911 				log("Run 'help %s+' to display the Verilog model for this cell type.\n", args[1].c_str());
912 				log("\n");
913 			}
914 			else if (cell_help_messages.cell_code.count(args[1])) {
915 				log("\n");
916 				log("%s", cell_help_messages.cell_code.at(args[1]).c_str());
917 			}
918 			else
919 				log("No such command or cell type: %s\n", args[1].c_str());
920 			return;
921 		}
922 
923 		help();
924 	}
925 } HelpPass;
926 
927 struct EchoPass : public Pass {
EchoPassEchoPass928 	EchoPass() : Pass("echo", "turning echoing back of commands on and off") { }
helpEchoPass929 	void help() override
930 	{
931 		log("\n");
932 		log("    echo on\n");
933 		log("\n");
934 		log("Print all commands to log before executing them.\n");
935 		log("\n");
936 		log("\n");
937 		log("    echo off\n");
938 		log("\n");
939 		log("Do not print all commands to log before executing them. (default)\n");
940 		log("\n");
941 	}
executeEchoPass942 	void execute(std::vector<std::string> args, RTLIL::Design*) override
943 	{
944 		if (args.size() > 2)
945 			cmd_error(args, 2, "Unexpected argument.");
946 
947 		if (args.size() == 2) {
948 			if (args[1] == "on")
949 				echo_mode = true;
950 			else if (args[1] == "off")
951 				echo_mode = false;
952 			else
953 				cmd_error(args, 1, "Unexpected argument.");
954 		}
955 
956 		log("echo %s\n", echo_mode ? "on" : "off");
957 	}
958 } EchoPass;
959 
960 SatSolver *yosys_satsolver_list;
961 SatSolver *yosys_satsolver;
962 
963 struct MinisatSatSolver : public SatSolver {
MinisatSatSolverMinisatSatSolver964 	MinisatSatSolver() : SatSolver("minisat") {
965 		yosys_satsolver = this;
966 	}
createMinisatSatSolver967 	ezSAT *create() override {
968 		return new ezMiniSAT();
969 	}
970 } MinisatSatSolver;
971 
972 YOSYS_NAMESPACE_END
973