Add a few more filename rewrites
[yosys.git] / kernel / register.cc
index 1dd6087542d0bd9d73060593a584f0742e0219b4..26da96b95b6d9e62a217f3577050a25d510c4d38 100644 (file)
@@ -2,11 +2,11 @@
  *  yosys -- Yosys Open SYnthesis Suite
  *
  *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at>
- *  
+ *
  *  Permission to use, copy, modify, and/or distribute this software for any
  *  purpose with or without fee is hereby granted, provided that the above
  *  copyright notice and this permission notice appear in all copies.
- *  
+ *
  *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  *
  */
 
-#include "register.h"
-#include "log.h"
-#include <assert.h>
+#include "kernel/yosys.h"
+#include "kernel/satgen.h"
+
 #include <string.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include <errno.h>
+
+YOSYS_NAMESPACE_BEGIN
 
-using namespace REGISTER_INTERN;
 #define MAX_REG_COUNT 1000
 
-namespace REGISTER_INTERN
-{
-       int raw_register_count = 0;
-       bool raw_register_done = false;
-       Pass *raw_register_array[MAX_REG_COUNT];
+bool echo_mode = false;
+Pass *first_queued_pass;
+Pass *current_pass;
 
-       std::map<std::string, Frontend*> frontend_register;
-       std::map<std::string, Pass*> pass_register;
-       std::map<std::string, Backend*> backend_register;
-}
+std::map<std::string, Frontend*> frontend_register;
+std::map<std::string, Pass*> pass_register;
+std::map<std::string, Backend*> backend_register;
 
 std::vector<std::string> Frontend::next_args;
 
 Pass::Pass(std::string name, std::string short_help) : pass_name(name), short_help(short_help)
 {
-       assert(!raw_register_done);
-       assert(raw_register_count < MAX_REG_COUNT);
-       raw_register_array[raw_register_count++] = this;
+       next_queued_pass = first_queued_pass;
+       first_queued_pass = this;
+       call_counter = 0;
+       runtime_ns = 0;
 }
 
 void Pass::run_register()
 {
-       assert(pass_register.count(pass_name) == 0);
+       log_assert(pass_register.count(pass_name) == 0);
        pass_register[pass_name] = this;
 }
 
 void Pass::init_register()
 {
-       if (raw_register_done)
-               done_register();
-       while (raw_register_count > 0)
-               raw_register_array[--raw_register_count]->run_register();
-       raw_register_done = true;
+       while (first_queued_pass) {
+               first_queued_pass->run_register();
+               first_queued_pass = first_queued_pass->next_queued_pass;
+       }
 }
 
 void Pass::done_register()
@@ -67,13 +66,36 @@ void Pass::done_register()
        frontend_register.clear();
        pass_register.clear();
        backend_register.clear();
-       raw_register_done = false;
+       log_assert(first_queued_pass == NULL);
 }
 
 Pass::~Pass()
 {
 }
 
+Pass::pre_post_exec_state_t Pass::pre_execute()
+{
+       pre_post_exec_state_t state;
+       call_counter++;
+       state.begin_ns = PerformanceTimer::query();
+       state.parent_pass = current_pass;
+       current_pass = this;
+       clear_flags();
+       return state;
+}
+
+void Pass::post_execute(Pass::pre_post_exec_state_t state)
+{
+       IdString::checkpoint();
+       log_suppressed();
+
+       int64_t time_ns = PerformanceTimer::query() - state.begin_ns;
+       runtime_ns += time_ns;
+       current_pass = state.parent_pass;
+       if (current_pass)
+               current_pass->runtime_ns -= time_ns;
+}
+
 void Pass::help()
 {
        log("\n");
@@ -81,6 +103,10 @@ void Pass::help()
        log("\n");
 }
 
+void Pass::clear_flags()
+{
+}
+
 void Pass::cmd_log_args(const std::vector<std::string> &args)
 {
        if (args.size() <= 1)
@@ -109,9 +135,6 @@ void Pass::cmd_error(const std::vector<std::string> &args, size_t argidx, std::s
                        msg.c_str(), command_text.c_str(), error_pos, "");
 }
 
-// implemented in kernel/select.cc
-extern void handle_extra_select_args(Pass *pass, std::vector<std::string> args, size_t argidx, RTLIL::Design *design);
-
 void Pass::extra_args(std::vector<std::string> args, size_t argidx, RTLIL::Design *design, bool select)
 {
        for (; argidx < args.size(); argidx++)
@@ -119,75 +142,220 @@ void Pass::extra_args(std::vector<std::string> args, size_t argidx, RTLIL::Desig
                std::string arg = args[argidx];
 
                if (arg.substr(0, 1) == "-")
-                       cmd_error(args, argidx, "Unkown option or option in arguments.");
+                       cmd_error(args, argidx, "Unknown option or option in arguments.");
 
                if (!select)
                        cmd_error(args, argidx, "Extra argument.");
 
-               handle_extra_select_args(this, args, argidx, design);
+               handle_extra_select_args(this, args, argidx, args.size(), design);
                break;
        }
-       cmd_log_args(args);
+       // cmd_log_args(args);
 }
 
 void Pass::call(RTLIL::Design *design, std::string command)
 {
        std::vector<std::string> args;
-       char *s = strdup(command.c_str()), *sstart = s, *saveptr;
-       s += strspn(s, " \t\r\n");
-       if (*s == 0 || *s == '#')
+
+       std::string cmd_buf = command;
+       std::string tok = next_token(cmd_buf, " \t\r\n", true);
+
+       if (tok.empty())
                return;
-       if (*s == '!') {
-               for (s++; *s == ' ' || *s == '\t'; s++) { }
-               char *p = s + strlen(s) - 1;
-               while (p >= s && (*p == '\r' || *p == '\n'))
-                       *(p--) = 0;
-               log_header("Shell command: %s\n", s);
-               int retCode = system(s);
+
+       if (tok[0] == '!') {
+               cmd_buf = command.substr(command.find('!') + 1);
+               while (!cmd_buf.empty() && (cmd_buf.back() == ' ' || cmd_buf.back() == '\t' ||
+                               cmd_buf.back() == '\r' || cmd_buf.back() == '\n'))
+                       cmd_buf.resize(cmd_buf.size()-1);
+               log_header(design, "Shell command: %s\n", cmd_buf.c_str());
+               int retCode = run_command(cmd_buf);
                if (retCode != 0)
                        log_cmd_error("Shell command returned error code %d.\n", retCode);
                return;
        }
-       for (char *p = strtok_r(s, " \t\r\n", &saveptr); p; p = strtok_r(NULL, " \t\r\n", &saveptr)) {
-               std::string str = p;
-               int strsz = str.size();
-               if (strsz > 0 && str[strsz-1] == ';') {
-                       while (strsz > 0 && str[strsz-1] == ';')
-                               strsz--;
-                       if (strsz > 0)
-                               args.push_back(str.substr(0, strsz));
+
+       while (!tok.empty()) {
+               if (tok[0] == '#') {
+                       int stop;
+                       for (stop = 0; stop < GetSize(cmd_buf); stop++)
+                               if (cmd_buf[stop] == '\r' || cmd_buf[stop] == '\n')
+                                       break;
+                       cmd_buf = cmd_buf.substr(stop);
+               } else
+               if (tok.back() == ';') {
+                       int num_semikolon = 0;
+                       while (!tok.empty() && tok.back() == ';')
+                               tok.resize(tok.size()-1), num_semikolon++;
+                       if (!tok.empty())
+                               args.push_back(tok);
                        call(design, args);
                        args.clear();
+                       if (num_semikolon == 2)
+                               call(design, "clean");
+                       if (num_semikolon == 3)
+                               call(design, "clean -purge");
                } else
-                       args.push_back(str);
+                       args.push_back(tok);
+               bool found_nl = false;
+               for (auto c : cmd_buf) {
+                       if (c == ' ' || c == '\t')
+                               continue;
+                       if (c == '\r' || c == '\n')
+                               found_nl = true;
+                       break;
+               }
+               if (found_nl) {
+                       call(design, args);
+                       args.clear();
+               }
+               tok = next_token(cmd_buf, " \t\r\n", true);
        }
-       free(sstart);
+
        call(design, args);
 }
 
 void Pass::call(RTLIL::Design *design, std::vector<std::string> args)
 {
-       if (args.size() == 0 || args[0][0] == '#')
+       if (args.size() == 0 || args[0][0] == '#' || args[0][0] == ':')
                return;
+
+       if (echo_mode) {
+               log("%s", create_prompt(design, 0));
+               for (size_t i = 0; i < args.size(); i++)
+                       log("%s%s", i ? " " : "", args[i].c_str());
+               log("\n");
+       }
+
        if (pass_register.count(args[0]) == 0)
                log_cmd_error("No such command: %s (type 'help' for a command overview)\n", args[0].c_str());
 
        size_t orig_sel_stack_pos = design->selection_stack.size();
+       auto state = pass_register[args[0]]->pre_execute();
        pass_register[args[0]]->execute(args, design);
+       pass_register[args[0]]->post_execute(state);
        while (design->selection_stack.size() > orig_sel_stack_pos)
                design->selection_stack.pop_back();
+
+       design->check();
+}
+
+void Pass::call_on_selection(RTLIL::Design *design, const RTLIL::Selection &selection, std::string command)
+{
+       std::string backup_selected_active_module = design->selected_active_module;
+       design->selected_active_module.clear();
+       design->selection_stack.push_back(selection);
+
+       Pass::call(design, command);
+
+       design->selection_stack.pop_back();
+       design->selected_active_module = backup_selected_active_module;
 }
 
-Frontend::Frontend(std::string name, std::string short_help) : Pass("read_"+name, short_help), frontend_name(name)
+void Pass::call_on_selection(RTLIL::Design *design, const RTLIL::Selection &selection, std::vector<std::string> args)
+{
+       std::string backup_selected_active_module = design->selected_active_module;
+       design->selected_active_module.clear();
+       design->selection_stack.push_back(selection);
+
+       Pass::call(design, args);
+
+       design->selection_stack.pop_back();
+       design->selected_active_module = backup_selected_active_module;
+}
+
+void Pass::call_on_module(RTLIL::Design *design, RTLIL::Module *module, std::string command)
+{
+       std::string backup_selected_active_module = design->selected_active_module;
+       design->selected_active_module = module->name.str();
+       design->selection_stack.push_back(RTLIL::Selection(false));
+       design->selection_stack.back().select(module);
+
+       Pass::call(design, command);
+
+       design->selection_stack.pop_back();
+       design->selected_active_module = backup_selected_active_module;
+}
+
+void Pass::call_on_module(RTLIL::Design *design, RTLIL::Module *module, std::vector<std::string> args)
+{
+       std::string backup_selected_active_module = design->selected_active_module;
+       design->selected_active_module = module->name.str();
+       design->selection_stack.push_back(RTLIL::Selection(false));
+       design->selection_stack.back().select(module);
+
+       Pass::call(design, args);
+
+       design->selection_stack.pop_back();
+       design->selected_active_module = backup_selected_active_module;
+}
+
+bool ScriptPass::check_label(std::string label, std::string info)
+{
+       if (active_design == nullptr) {
+               log("\n");
+               if (info.empty())
+                       log("    %s:\n", label.c_str());
+               else
+                       log("    %s:    %s\n", label.c_str(), info.c_str());
+               return true;
+       } else {
+               if (!active_run_from.empty() && active_run_from == active_run_to) {
+                       block_active = (label == active_run_from);
+               } else {
+                       if (label == active_run_from)
+                               block_active = true;
+                       if (label == active_run_to)
+                               block_active = false;
+               }
+               return block_active;
+       }
+}
+
+void ScriptPass::run(std::string command, std::string info)
+{
+       if (active_design == nullptr) {
+               if (info.empty())
+                       log("        %s\n", command.c_str());
+               else
+                       log("        %s    %s\n", command.c_str(), info.c_str());
+       } else
+               Pass::call(active_design, command);
+}
+
+void ScriptPass::run_script(RTLIL::Design *design, std::string run_from, std::string run_to)
+{
+       help_mode = false;
+       active_design = design;
+       block_active = run_from.empty();
+       active_run_from = run_from;
+       active_run_to = run_to;
+       script();
+}
+
+void ScriptPass::help_script()
+{
+       clear_flags();
+       help_mode = true;
+       active_design = nullptr;
+       block_active = true;
+       active_run_from.clear();
+       active_run_to.clear();
+       script();
+}
+
+Frontend::Frontend(std::string name, std::string short_help) :
+               Pass(name.rfind("=", 0) == 0 ? name.substr(1) : "read_" + name, short_help),
+               frontend_name(name.rfind("=", 0) == 0 ? name.substr(1) : name)
 {
 }
 
 void Frontend::run_register()
 {
-       assert(pass_register.count(pass_name) == 0);
+       log_assert(pass_register.count(pass_name) == 0);
        pass_register[pass_name] = this;
 
-       assert(frontend_register.count(frontend_name) == 0);
+       log_assert(frontend_register.count(frontend_name) == 0);
        frontend_register[frontend_name] = this;
 }
 
@@ -197,52 +365,103 @@ Frontend::~Frontend()
 
 void Frontend::execute(std::vector<std::string> args, RTLIL::Design *design)
 {
-       assert(next_args.empty());
+       log_assert(next_args.empty());
        do {
-               FILE *f = NULL;
+               std::istream *f = NULL;
                next_args.clear();
+               auto state = pre_execute();
                execute(f, std::string(), args, design);
+               post_execute(state);
                args = next_args;
-               fclose(f);
+               delete f;
        } while (!args.empty());
 }
 
-void Frontend::extra_args(FILE *&f, std::string &filename, std::vector<std::string> args, size_t argidx)
+FILE *Frontend::current_script_file = NULL;
+std::string Frontend::last_here_document;
+
+void Frontend::extra_args(std::istream *&f, std::string &filename, std::vector<std::string> args, size_t argidx)
 {
        bool called_with_fp = f != NULL;
 
        next_args.clear();
-       for (; argidx < args.size(); argidx++)
+
+       if (argidx < args.size())
        {
                std::string arg = args[argidx];
 
                if (arg.substr(0, 1) == "-")
-                       cmd_error(args, argidx, "Unkown option or option in arguments.");
+                       cmd_error(args, argidx, "Unknown option or option in arguments.");
                if (f != NULL)
                        cmd_error(args, argidx, "Extra filename argument in direct file mode.");
 
                filename = arg;
-               f = fopen(filename.c_str(), "r");
+               if (filename == "<<" && argidx+1 < args.size())
+                       filename += args[++argidx];
+               if (filename.substr(0, 2) == "<<") {
+                       if (Frontend::current_script_file == NULL)
+                               log_error("Unexpected here document '%s' outside of script!\n", filename.c_str());
+                       if (filename.size() <= 2)
+                               log_error("Missing EOT marker in here document!\n");
+                       std::string eot_marker = filename.substr(2);
+                       last_here_document.clear();
+                       while (1) {
+                               std::string buffer;
+                               char block[4096];
+                               while (1) {
+                                       if (fgets(block, 4096, Frontend::current_script_file) == NULL)
+                                               log_error("Unexpected end of file in here document '%s'!\n", filename.c_str());
+                                       buffer += block;
+                                       if (buffer.size() > 0 && (buffer[buffer.size() - 1] == '\n' || buffer[buffer.size() - 1] == '\r'))
+                                               break;
+                               }
+                               size_t indent = buffer.find_first_not_of(" \t\r\n");
+                               if (indent != std::string::npos && buffer.substr(indent, eot_marker.size()) == eot_marker)
+                                       break;
+                               last_here_document += buffer;
+                       }
+                       f = new std::istringstream(last_here_document);
+               } else {
+                       rewrite_filename(filename);
+                       vector<string> filenames = glob_filename(filename);
+                       filename = filenames.front();
+                       if (GetSize(filenames) > 1) {
+                               next_args.insert(next_args.end(), args.begin(), args.begin()+argidx);
+                               next_args.insert(next_args.end(), filenames.begin()+1, filenames.end());
+                       }
+                       std::ifstream *ff = new std::ifstream;
+                       ff->open(filename.c_str());
+                       yosys_input_files.insert(filename);
+                       if (ff->fail())
+                               delete ff;
+                       else
+                               f = ff;
+               }
                if (f == NULL)
                        log_cmd_error("Can't open input file `%s' for reading: %s\n", filename.c_str(), strerror(errno));
 
+               for (size_t i = argidx+1; i < args.size(); i++)
+                       if (args[i].substr(0, 1) == "-")
+                               cmd_error(args, i, "Found option, expected arguments.");
+
                if (argidx+1 < args.size()) {
-                       next_args.insert(next_args.begin(), args.begin(), args.begin()+argidx);
-                       next_args.insert(next_args.begin()+argidx, args.begin()+argidx+1, args.end());
+                       if (next_args.empty())
+                               next_args.insert(next_args.end(), args.begin(), args.begin()+argidx);
+                       next_args.insert(next_args.end(), args.begin()+argidx+1, args.end());
                        args.erase(args.begin()+argidx+1, args.end());
                }
-               break;
        }
+
        if (f == NULL)
                cmd_error(args, argidx, "No filename given.");
 
        if (called_with_fp)
                args.push_back(filename);
        args[0] = pass_name;
-       cmd_log_args(args);
+       // cmd_log_args(args);
 }
 
-void Frontend::frontend_call(RTLIL::Design *design, FILE *f, std::string filename, std::string command)
+void Frontend::frontend_call(RTLIL::Design *design, std::istream *f, std::string filename, std::string command)
 {
        std::vector<std::string> args;
        char *s = strdup(command.c_str());
@@ -252,7 +471,7 @@ void Frontend::frontend_call(RTLIL::Design *design, FILE *f, std::string filenam
        frontend_call(design, f, filename, args);
 }
 
-void Frontend::frontend_call(RTLIL::Design *design, FILE *f, std::string filename, std::vector<std::string> args)
+void Frontend::frontend_call(RTLIL::Design *design, std::istream *f, std::string filename, std::vector<std::string> args)
 {
        if (args.size() == 0)
                return;
@@ -260,26 +479,35 @@ void Frontend::frontend_call(RTLIL::Design *design, FILE *f, std::string filenam
                log_cmd_error("No such frontend: %s\n", args[0].c_str());
 
        if (f != NULL) {
+               auto state = frontend_register[args[0]]->pre_execute();
                frontend_register[args[0]]->execute(f, filename, args, design);
+               frontend_register[args[0]]->post_execute(state);
        } else if (filename == "-") {
-               frontend_register[args[0]]->execute(stdin, "<stdin>", args, design);
+               std::istream *f_cin = &std::cin;
+               auto state = frontend_register[args[0]]->pre_execute();
+               frontend_register[args[0]]->execute(f_cin, "<stdin>", args, design);
+               frontend_register[args[0]]->post_execute(state);
        } else {
                if (!filename.empty())
                        args.push_back(filename);
                frontend_register[args[0]]->execute(args, design);
        }
+
+       design->check();
 }
 
-Backend::Backend(std::string name, std::string short_help) : Pass("write_"+name, short_help), backend_name(name)
+Backend::Backend(std::string name, std::string short_help) :
+               Pass(name.rfind("=", 0) == 0 ? name.substr(1) : "write_" + name, short_help),
+               backend_name(name.rfind("=", 0) == 0 ? name.substr(1) : name)
 {
 }
 
 void Backend::run_register()
 {
-       assert(pass_register.count(pass_name) == 0);
+       log_assert(pass_register.count(pass_name) == 0);
        pass_register[pass_name] = this;
 
-       assert(backend_register.count(backend_name) == 0);
+       log_assert(backend_register.count(backend_name) == 0);
        backend_register[backend_name] = this;
 }
 
@@ -289,13 +517,15 @@ Backend::~Backend()
 
 void Backend::execute(std::vector<std::string> args, RTLIL::Design *design)
 {
-       FILE *f = NULL;
+       std::ostream *f = NULL;
+       auto state = pre_execute();
        execute(f, std::string(), args, design);
-       if (f != stdout)
-               fclose(f);
+       post_execute(state);
+       if (f != &std::cout)
+               delete f;
 }
 
-void Backend::extra_args(FILE *&f, std::string &filename, std::vector<std::string> args, size_t argidx)
+void Backend::extra_args(std::ostream *&f, std::string &filename, std::vector<std::string> args, size_t argidx)
 {
        bool called_with_fp = f != NULL;
 
@@ -304,34 +534,40 @@ void Backend::extra_args(FILE *&f, std::string &filename, std::vector<std::strin
                std::string arg = args[argidx];
 
                if (arg.substr(0, 1) == "-" && arg != "-")
-                       cmd_error(args, argidx, "Unkown option or option in arguments.");
+                       cmd_error(args, argidx, "Unknown option or option in arguments.");
                if (f != NULL)
                        cmd_error(args, argidx, "Extra filename argument in direct file mode.");
 
                if (arg == "-") {
                        filename = "<stdout>";
-                       f = stdout;
+                       f = &std::cout;
                        continue;
                }
 
                filename = arg;
-               f = fopen(filename.c_str(), "w");
-               if (f == NULL)
+               rewrite_filename(filename);
+               std::ofstream *ff = new std::ofstream;
+               ff->open(filename.c_str(), std::ofstream::trunc);
+               yosys_output_files.insert(filename);
+               if (ff->fail()) {
+                       delete ff;
                        log_cmd_error("Can't open output file `%s' for writing: %s\n", filename.c_str(), strerror(errno));
+               }
+               f = ff;
        }
 
        if (called_with_fp)
                args.push_back(filename);
        args[0] = pass_name;
-       cmd_log_args(args);
+       // cmd_log_args(args);
 
        if (f == NULL) {
                filename = "<stdout>";
-               f = stdout;
+               f = &std::cout;
        }
 }
 
-void Backend::backend_call(RTLIL::Design *design, FILE *f, std::string filename, std::string command)
+void Backend::backend_call(RTLIL::Design *design, std::ostream *f, std::string filename, std::string command)
 {
        std::vector<std::string> args;
        char *s = strdup(command.c_str());
@@ -341,7 +577,7 @@ void Backend::backend_call(RTLIL::Design *design, FILE *f, std::string filename,
        backend_call(design, f, filename, args);
 }
 
-void Backend::backend_call(RTLIL::Design *design, FILE *f, std::string filename, std::vector<std::string> args)
+void Backend::backend_call(RTLIL::Design *design, std::ostream *f, std::string filename, std::vector<std::string> args)
 {
        if (args.size() == 0)
                return;
@@ -351,9 +587,14 @@ void Backend::backend_call(RTLIL::Design *design, FILE *f, std::string filename,
        size_t orig_sel_stack_pos = design->selection_stack.size();
 
        if (f != NULL) {
+               auto state = backend_register[args[0]]->pre_execute();
                backend_register[args[0]]->execute(f, filename, args, design);
+               backend_register[args[0]]->post_execute(state);
        } else if (filename == "-") {
-               backend_register[args[0]]->execute(stdout, "<stdout>", args, design);
+               std::ostream *f_cout = &std::cout;
+               auto state = backend_register[args[0]]->pre_execute();
+               backend_register[args[0]]->execute(f_cout, "<stdout>", args, design);
+               backend_register[args[0]]->post_execute(state);
        } else {
                if (!filename.empty())
                        args.push_back(filename);
@@ -362,53 +603,107 @@ void Backend::backend_call(RTLIL::Design *design, FILE *f, std::string filename,
 
        while (design->selection_stack.size() > orig_sel_stack_pos)
                design->selection_stack.pop_back();
+
+       design->check();
 }
 
+static struct CellHelpMessages {
+       dict<string, string> cell_help, cell_code;
+       CellHelpMessages() {
+#include "techlibs/common/simlib_help.inc"
+#include "techlibs/common/simcells_help.inc"
+               cell_help.sort();
+               cell_code.sort();
+       }
+} cell_help_messages;
+
 struct HelpPass : public Pass {
        HelpPass() : Pass("help", "display help messages") { }
-       virtual void help()
+       void help() YS_OVERRIDE
        {
                log("\n");
-               log("    help  .............  list all commands\n");
-               log("    help <command>  ...  print help message for given command\n");
-               log("    help -all  ........  print complete command reference\n");
+               log("    help  ................  list all commands\n");
+               log("    help <command>  ......  print help message for given command\n");
+               log("    help -all  ...........  print complete command reference\n");
+               log("\n");
+               log("    help -cells ..........  list all cell types\n");
+               log("    help <celltype>  .....  print help message for given cell type\n");
+               log("    help <celltype>+  ....  print verilog code for given cell type\n");
                log("\n");
        }
        void escape_tex(std::string &tex)
        {
-               size_t pos = 0;
-               while ((pos = tex.find('_', pos)) != std::string::npos) {
+               for (size_t pos = 0; (pos = tex.find('_', pos)) != std::string::npos; pos += 2)
                        tex.replace(pos, 1, "\\_");
-                       pos += 2;
-               }
+               for (size_t pos = 0; (pos = tex.find('$', pos)) != std::string::npos; pos += 2)
+                       tex.replace(pos, 1, "\\$");
        }
        void write_tex(FILE *f, std::string cmd, std::string title, std::string text)
        {
                size_t begin = text.find_first_not_of("\n"), end = text.find_last_not_of("\n");
                if (begin != std::string::npos && end != std::string::npos && begin < end)
                        text = text.substr(begin, end-begin+1);
+               std::string cmd_unescaped = cmd;
                escape_tex(cmd);
                escape_tex(title);
                fprintf(f, "\\section{%s -- %s}\n", cmd.c_str(), title.c_str());
+               fprintf(f, "\\label{cmd:%s}\n", cmd_unescaped.c_str());
                fprintf(f, "\\begin{lstlisting}[numbers=left,frame=single]\n");
                fprintf(f, "%s\n\\end{lstlisting}\n\n", text.c_str());
+       }
+       void escape_html(std::string &html)
+       {
+               size_t pos = 0;
+               while ((pos = html.find_first_of("<>&", pos)) != std::string::npos)
+                       switch (html[pos]) {
+                       case '<':
+                               html.replace(pos, 1, "&lt;");
+                               pos += 4;
+                               break;
+                       case '>':
+                               html.replace(pos, 1, "&gt;");
+                               pos += 4;
+                               break;
+                       case '&':
+                               html.replace(pos, 1, "&amp;");
+                               pos += 5;
+                               break;
+                       }
+       }
+       void write_html(FILE *idxf, std::string cmd, std::string title, std::string text)
+       {
+               FILE *f = fopen(stringf("cmd_%s.in", cmd.c_str()).c_str(), "wt");
+               fprintf(idxf, "<li><a href=\"cmd_%s.html\"> ", cmd.c_str());
+
+               escape_html(cmd);
+               escape_html(title);
+               escape_html(text);
+
+               fprintf(idxf, "%s</a> <span>%s</span></a>\n", cmd.c_str(), title.c_str());
+
+               fprintf(f, "@cmd_header %s@\n", cmd.c_str());
+               fprintf(f, "<h1>%s - %s</h1>\n", cmd.c_str(), title.c_str());
+               fprintf(f, "<pre>%s</pre>\n", text.c_str());
+               fprintf(f, "@footer@\n");
 
+               fclose(f);
        }
-       virtual void execute(std::vector<std::string> args, RTLIL::Design*)
+       void execute(std::vector<std::string> args, RTLIL::Design*) YS_OVERRIDE
        {
                if (args.size() == 1) {
                        log("\n");
-                       for (auto &it : REGISTER_INTERN::pass_register)
+                       for (auto &it : pass_register)
                                log("    %-20s %s\n", it.first.c_str(), it.second->short_help.c_str());
                        log("\n");
                        log("Type 'help <command>' for more information on a command.\n");
+                       log("Type 'help -cells' for a list of all cell types.\n");
                        log("\n");
                        return;
                }
 
                if (args.size() == 2) {
                        if (args[1] == "-all") {
-                               for (auto &it : REGISTER_INTERN::pass_register) {
+                               for (auto &it : pass_register) {
                                        log("\n\n");
                                        log("%s  --  %s\n", it.first.c_str(), it.second->short_help.c_str());
                                        for (size_t i = 0; i < it.first.size() + it.second->short_help.size() + 6; i++)
@@ -417,31 +712,107 @@ struct HelpPass : public Pass {
                                        it.second->help();
                                }
                        }
+                       else if (args[1] == "-cells") {
+                               log("\n");
+                               for (auto &it : cell_help_messages.cell_help) {
+                                       string line = split_tokens(it.second, "\n").at(0);
+                                       string cell_name = next_token(line);
+                                       log("    %-15s %s\n", cell_name.c_str(), line.c_str());
+                               }
+                               log("\n");
+                               log("Type 'help <cell_type>' for more information on a cell type.\n");
+                               log("\n");
+                               return;
+                       }
                        // this option is undocumented as it is for internal use only
                        else if (args[1] == "-write-tex-command-reference-manual") {
                                FILE *f = fopen("command-reference-manual.tex", "wt");
                                fprintf(f, "%% Generated using the yosys 'help -write-tex-command-reference-manual' command.\n\n");
-                               for (auto &it : REGISTER_INTERN::pass_register) {
-                                       size_t memsize;
-                                       char *memptr;
-                                       FILE *memf = open_memstream(&memptr, &memsize);
-                                       log_files.push_back(memf);
+                               for (auto &it : pass_register) {
+                                       std::ostringstream buf;
+                                       log_streams.push_back(&buf);
+                                       it.second->help();
+                                       log_streams.pop_back();
+                                       write_tex(f, it.first, it.second->short_help, buf.str());
+                               }
+                               fclose(f);
+                       }
+                       // this option is undocumented as it is for internal use only
+                       else if (args[1] == "-write-web-command-reference-manual") {
+                               FILE *f = fopen("templates/cmd_index.in", "wt");
+                               for (auto &it : pass_register) {
+                                       std::ostringstream buf;
+                                       log_streams.push_back(&buf);
                                        it.second->help();
-                                       log_files.pop_back();
-                                       fclose(memf);
-                                       write_tex(f, it.first, it.second->short_help, memptr);
-                                       free(memptr);
+                                       log_streams.pop_back();
+                                       write_html(f, it.first, it.second->short_help, buf.str());
                                }
                                fclose(f);
                        }
-                       else if (REGISTER_INTERN::pass_register.count(args[1]) == 0)
-                               log("No such command: %s\n", args[1].c_str());
+                       else if (pass_register.count(args[1])) {
+                               pass_register.at(args[1])->help();
+                       }
+                       else if (cell_help_messages.cell_help.count(args[1])) {
+                               log("%s", cell_help_messages.cell_help.at(args[1]).c_str());
+                               log("Run 'help %s+' to display the Verilog model for this cell type.\n", args[1].c_str());
+                               log("\n");
+                       }
+                       else if (cell_help_messages.cell_code.count(args[1])) {
+                               log("\n");
+                               log("%s", cell_help_messages.cell_code.at(args[1]).c_str());
+                       }
                        else
-                               REGISTER_INTERN::pass_register.at(args[1])->help();
+                               log("No such command or cell type: %s\n", args[1].c_str());
                        return;
                }
 
                help();
        }
 } HelpPass;
+
+struct EchoPass : public Pass {
+       EchoPass() : Pass("echo", "turning echoing back of commands on and off") { }
+       void help() YS_OVERRIDE
+       {
+               log("\n");
+               log("    echo on\n");
+               log("\n");
+               log("Print all commands to log before executing them.\n");
+               log("\n");
+               log("\n");
+               log("    echo off\n");
+               log("\n");
+               log("Do not print all commands to log before executing them. (default)\n");
+               log("\n");
+       }
+       void execute(std::vector<std::string> args, RTLIL::Design*) YS_OVERRIDE
+       {
+               if (args.size() > 2)
+                       cmd_error(args, 2, "Unexpected argument.");
+
+               if (args.size() == 2) {
+                       if (args[1] == "on")
+                               echo_mode = true;
+                       else if (args[1] == "off")
+                               echo_mode = false;
+                       else
+                               cmd_error(args, 1, "Unexpected argument.");
+               }
+
+               log("echo %s\n", echo_mode ? "on" : "off");
+       }
+} EchoPass;
+
+SatSolver *yosys_satsolver_list;
+SatSolver *yosys_satsolver;
+
+struct MinisatSatSolver : public SatSolver {
+       MinisatSatSolver() : SatSolver("minisat") {
+               yosys_satsolver = this;
+       }
+       ezSAT *create() YS_OVERRIDE {
+               return new ezMiniSAT();
+       }
+} MinisatSatSolver;
+
+YOSYS_NAMESPACE_END