Error out if no top module given before 'sim'
[yosys.git] / passes / sat / sim.cc
index 974f00dbc2350ccdbd6da4e7ca09832588f20b68..4c3022c709d451d7a48d5f63800209025fb178ac 100644 (file)
 USING_YOSYS_NAMESPACE
 PRIVATE_NAMESPACE_BEGIN
 
+struct SimShared
+{
+       bool debug = false;
+       bool hide_internal = true;
+       bool writeback = false;
+       bool zinit = false;
+       int rstlen = 1;
+};
+
+void zinit(State &v)
+{
+       if (v != State::S1)
+               v = State::S0;
+}
+
+void zinit(Const &v)
+{
+       for (auto &bit : v.bits)
+               zinit(bit);
+}
+
 struct SimInstance
 {
+       SimShared *shared;
+
        Module *module;
        Cell *instance;
 
@@ -38,13 +61,35 @@ struct SimInstance
        dict<SigBit, pool<Wire*>> upd_outports;
 
        pool<SigBit> dirty_bits;
-       dict<SigBit, State> next_state_nets;
+       pool<Cell*> dirty_cells;
+       pool<SimInstance*, hash_ptr_ops> dirty_children;
 
-       dict<Wire*, int> vcd_netids;
+       struct ff_state_t
+       {
+               State past_clock;
+               Const past_d;
+       };
 
-       SimInstance(Module *module, Cell *instance = nullptr, SimInstance *parent = nullptr) :
-                       module(module), instance(instance), parent(parent), sigmap(module)
+       struct mem_state_t
        {
+               Const past_wr_clk;
+               Const past_wr_en;
+               Const past_wr_addr;
+               Const past_wr_data;
+               Const data;
+       };
+
+       dict<Cell*, ff_state_t> ff_database;
+       dict<Cell*, mem_state_t> mem_database;
+       pool<Cell*> formal_database;
+
+       dict<Wire*, pair<int, Const>> vcd_database;
+
+       SimInstance(SimShared *shared, Module *module, Cell *instance = nullptr, SimInstance *parent = nullptr) :
+                       shared(shared), module(module), instance(instance), parent(parent), sigmap(module)
+       {
+               log_assert(module);
+
                if (parent) {
                        log_assert(parent->children.count(instance) == 0);
                        parent->children[instance] = this;
@@ -78,7 +123,7 @@ struct SimInstance
                        Module *mod = module->design->module(cell->type);
 
                        if (mod != nullptr) {
-                               new SimInstance(mod, cell, this);
+                               dirty_children.insert(new SimInstance(shared, mod, cell, this));
                        }
 
                        for (auto &port : cell->connections()) {
@@ -86,7 +131,66 @@ struct SimInstance
                                        for (auto bit : sigmap(port.second))
                                                upd_cells[bit].insert(cell);
                        }
+
+                       if (cell->type.in("$dff")) {
+                               ff_state_t ff;
+                               ff.past_clock = State::Sx;
+                               ff.past_d = Const(State::Sx, cell->getParam("\\WIDTH").as_int());
+                               ff_database[cell] = ff;
+                       }
+
+                       if (cell->type == "$mem")
+                       {
+                               mem_state_t mem;
+
+                               mem.past_wr_clk = Const(State::Sx, GetSize(cell->getPort("\\WR_CLK")));
+                               mem.past_wr_en = Const(State::Sx, GetSize(cell->getPort("\\WR_EN")));
+                               mem.past_wr_addr = Const(State::Sx, GetSize(cell->getPort("\\WR_ADDR")));
+                               mem.past_wr_data = Const(State::Sx, GetSize(cell->getPort("\\WR_DATA")));
+
+                               mem.data = cell->getParam("\\INIT");
+                               int sz = cell->getParam("\\SIZE").as_int() * cell->getParam("\\WIDTH").as_int();
+
+                               if (GetSize(mem.data) > sz)
+                                       mem.data.bits.resize(sz);
+
+                               while (GetSize(mem.data) < sz)
+                                       mem.data.bits.push_back(State::Sx);
+
+                               mem_database[cell] = mem;
+                       }
+
+                       if (cell->type.in("$assert", "$cover", "$assume")) {
+                               formal_database.insert(cell);
+                       }
                }
+
+               if (shared->zinit)
+               {
+                       for (auto &it : ff_database)
+                       {
+                               Cell *cell = it.first;
+                               ff_state_t &ff = it.second;
+                               zinit(ff.past_d);
+
+                               SigSpec qsig = cell->getPort("\\Q");
+                               Const qdata = get_state(qsig);
+                               zinit(qdata);
+                               set_state(qsig, qdata);
+                       }
+
+                       for (auto &it : mem_database) {
+                               mem_state_t &mem = it.second;
+                               zinit(mem.past_wr_en);
+                               zinit(mem.data);
+                       }
+               }
+       }
+
+       ~SimInstance()
+       {
+               for (auto child : children)
+                       delete child.second;
        }
 
        IdString name() const
@@ -109,17 +213,22 @@ struct SimInstance
                Const value;
 
                for (auto bit : sigmap(sig))
-                       if (state_nets.count(bit))
+                       if (bit.wire == nullptr)
+                               value.bits.push_back(bit.data);
+                       else if (state_nets.count(bit))
                                value.bits.push_back(state_nets.at(bit));
                        else
                                value.bits.push_back(State::Sz);
 
-               // log("[%s] get %s: %s\n", hiername().c_str(), log_signal(sig), log_signal(value));
+               if (shared->debug)
+                       log("[%s] get %s: %s\n", hiername().c_str(), log_signal(sig), log_signal(value));
                return value;
        }
 
-       void set_state(SigSpec sig, Const value)
+       bool set_state(SigSpec sig, Const value)
        {
+               bool did_something = false;
+
                sig = sigmap(sig);
                log_assert(GetSize(sig) == GetSize(value));
 
@@ -127,13 +236,56 @@ struct SimInstance
                        if (state_nets.at(sig[i]) != value[i]) {
                                state_nets.at(sig[i]) = value[i];
                                dirty_bits.insert(sig[i]);
+                               did_something = true;
                        }
 
-               // log("[%s] set %s: %s\n", hiername().c_str(), log_signal(sig), log_signal(value));
+               if (shared->debug)
+                       log("[%s] set %s: %s\n", hiername().c_str(), log_signal(sig), log_signal(value));
+               return did_something;
        }
 
        void update_cell(Cell *cell)
        {
+               if (ff_database.count(cell))
+                       return;
+
+               if (formal_database.count(cell))
+                       return;
+
+               if (mem_database.count(cell))
+               {
+                       mem_state_t &mem = mem_database.at(cell);
+
+                       int num_rd_ports = cell->getParam("\\RD_PORTS").as_int();
+
+                       int size = cell->getParam("\\SIZE").as_int();
+                       int offset = cell->getParam("\\OFFSET").as_int();
+                       int abits = cell->getParam("\\ABITS").as_int();
+                       int width = cell->getParam("\\WIDTH").as_int();
+
+                       if (cell->getParam("\\RD_CLK_ENABLE").as_bool())
+                               log_error("Memory %s.%s has clocked read ports. Run 'memory' with -nordff.\n", log_id(module), log_id(cell));
+
+                       SigSpec rd_addr_sig = cell->getPort("\\RD_ADDR");
+                       SigSpec rd_data_sig = cell->getPort("\\RD_DATA");
+
+                       for (int port_idx = 0; port_idx < num_rd_ports; port_idx++)
+                       {
+                               Const addr = get_state(rd_addr_sig.extract(port_idx*abits, abits));
+                               Const data = Const(State::Sx, width);
+
+                               if (addr.is_fully_def()) {
+                                       int index = addr.as_int() - offset;
+                                       if (index >= 0 && index < size)
+                                               data = mem.data.extract(index*width, width);
+                               }
+
+                               set_state(rd_data_sig.extract(port_idx*width, width), data);
+                       }
+
+                       return;
+               }
+
                if (children.count(cell))
                {
                        auto child = children.at(cell);
@@ -142,13 +294,12 @@ struct SimInstance
                                        Const value = get_state(conn.second);
                                        child->set_state(child->module->wire(conn.first), value);
                                }
+                       dirty_children.insert(child);
                        return;
                }
 
                if (yosys_celltypes.cell_evaluable(cell->type))
                {
-                       // log("[%s] eval %s (%s)\n", hiername().c_str(), log_id(cell), log_id(cell->type));
-
                        RTLIL::SigSpec sig_a, sig_b, sig_c, sig_d, sig_s, sig_y;
                        bool has_a, has_b, has_c, has_d, has_s, has_y;
 
@@ -166,6 +317,9 @@ struct SimInstance
                        if (has_s) sig_s = cell->getPort("\\S");
                        if (has_y) sig_y = cell->getPort("\\Y");
 
+                       if (shared->debug)
+                               log("[%s] eval %s (%s)\n", hiername().c_str(), log_id(cell), log_id(cell->type));
+
                        // Simple (A -> Y) and (A,B -> Y) cells
                        if (has_a && !has_c && !has_d && !has_s && has_y) {
                                set_state(sig_y, CellTypes::eval(cell, get_state(sig_a), get_state(sig_b)));
@@ -188,55 +342,246 @@ struct SimInstance
                        return;
                }
 
-               // FIXME
-
-               log_warning("Unsupported cell type: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell));
+               log_error("Unsupported cell type: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell));
        }
 
-       void update()
+       void update_ph1()
        {
+               pool<Cell*> queue_cells;
+               pool<Wire*> queue_outports;
+
+               queue_cells.swap(dirty_cells);
+
                while (1)
                {
-                       while (!dirty_bits.empty())
+                       for (auto bit : dirty_bits)
                        {
-                               SigBit bit = *dirty_bits.begin();
-                               dirty_bits.erase(bit);
-
                                if (upd_cells.count(bit))
-                               {
                                        for (auto cell : upd_cells.at(bit))
-                                               update_cell(cell);
-                               }
+                                               queue_cells.insert(cell);
 
                                if (upd_outports.count(bit) && parent != nullptr)
-                               {
                                        for (auto wire : upd_outports.at(bit))
-                                               if (instance->hasPort(wire->name)) {
-                                                       Const value = get_state(wire);
-                                                       parent->set_state(instance->getPort(wire->name), value);
-                                               }
-                               }
+                                               queue_outports.insert(wire);
+                       }
+
+                       dirty_bits.clear();
+
+                       if (!queue_cells.empty())
+                       {
+                               for (auto cell : queue_cells)
+                                       update_cell(cell);
+
+                               queue_cells.clear();
+                               continue;
                        }
 
-                       for (auto child : children)
-                               child.second->update();
+                       for (auto wire : queue_outports)
+                               if (instance->hasPort(wire->name)) {
+                                       Const value = get_state(wire);
+                                       parent->set_state(instance->getPort(wire->name), value);
+                               }
+
+                       queue_outports.clear();
+
+                       for (auto child : dirty_children)
+                               child->update_ph1();
+
+                       dirty_children.clear();
 
                        if (dirty_bits.empty())
                                break;
                }
        }
 
+       bool update_ph2()
+       {
+               bool did_something = false;
+
+               for (auto &it : ff_database)
+               {
+                       Cell *cell = it.first;
+                       ff_state_t &ff = it.second;
+
+                       if (cell->type.in("$dff"))
+                       {
+                               bool clkpol = cell->getParam("\\CLK_POLARITY").as_bool();
+                               State current_clock = get_state(cell->getPort("\\CLK"))[0];
+
+                               if (clkpol ? (ff.past_clock == State::S1 || current_clock != State::S1) :
+                                               (ff.past_clock == State::S0 || current_clock != State::S0))
+                                       continue;
+
+                               if (set_state(cell->getPort("\\Q"), ff.past_d))
+                                       did_something = true;
+                       }
+               }
+
+               for (auto &it : mem_database)
+               {
+                       Cell *cell = it.first;
+                       mem_state_t &mem = it.second;
+
+                       int num_wr_ports = cell->getParam("\\WR_PORTS").as_int();
+
+                       int size = cell->getParam("\\SIZE").as_int();
+                       int offset = cell->getParam("\\OFFSET").as_int();
+                       int abits = cell->getParam("\\ABITS").as_int();
+                       int width = cell->getParam("\\WIDTH").as_int();
+
+                       Const wr_clk_enable = cell->getParam("\\WR_CLK_ENABLE");
+                       Const wr_clk_polarity = cell->getParam("\\WR_CLK_POLARITY");
+                       Const current_wr_clk  = get_state(cell->getPort("\\WR_CLK"));
+
+                       for (int port_idx = 0; port_idx < num_wr_ports; port_idx++)
+                       {
+                               Const addr, data, enable;
+
+                               if (wr_clk_enable[port_idx] == State::S0)
+                               {
+                                       addr = get_state(cell->getPort("\\WR_ADDR").extract(port_idx*abits, abits));
+                                       data = get_state(cell->getPort("\\WR_DATA").extract(port_idx*width, width));
+                                       enable = get_state(cell->getPort("\\WR_EN").extract(port_idx*width, width));
+                               }
+                               else
+                               {
+                                       if (wr_clk_polarity[port_idx] == State::S1 ?
+                                                       (mem.past_wr_clk[port_idx] == State::S1 || current_wr_clk[port_idx] != State::S1) :
+                                                       (mem.past_wr_clk[port_idx] == State::S0 || current_wr_clk[port_idx] != State::S0))
+                                               continue;
+
+                                       addr = mem.past_wr_addr.extract(port_idx*abits, abits);
+                                       data = mem.past_wr_data.extract(port_idx*width, width);
+                                       enable = mem.past_wr_en.extract(port_idx*width, width);
+                               }
+
+                               if (addr.is_fully_def())
+                               {
+                                       int index = addr.as_int() - offset;
+                                       if (index >= 0 && index < size)
+                                               for (int i = 0; i < width; i++)
+                                                       if (enable[i] == State::S1 && mem.data.bits.at(index*width+i) != data[i]) {
+                                                               mem.data.bits.at(index*width+i) = data[i];
+                                                               dirty_cells.insert(cell);
+                                                               did_something = true;
+                                                       }
+                               }
+                       }
+               }
+
+               for (auto it : children)
+                       if (it.second->update_ph2()) {
+                               dirty_children.insert(it.second);
+                               did_something = true;
+                       }
+
+               return did_something;
+       }
+
+       void update_ph3()
+       {
+               for (auto &it : ff_database)
+               {
+                       Cell *cell = it.first;
+                       ff_state_t &ff = it.second;
+
+                       if (cell->type.in("$dff")) {
+                               ff.past_clock = get_state(cell->getPort("\\CLK"))[0];
+                               ff.past_d = get_state(cell->getPort("\\D"));
+                       }
+               }
+
+               for (auto &it : mem_database)
+               {
+                       Cell *cell = it.first;
+                       mem_state_t &mem = it.second;
+
+                       mem.past_wr_clk  = get_state(cell->getPort("\\WR_CLK"));
+                       mem.past_wr_en   = get_state(cell->getPort("\\WR_EN"));
+                       mem.past_wr_addr = get_state(cell->getPort("\\WR_ADDR"));
+                       mem.past_wr_data = get_state(cell->getPort("\\WR_DATA"));
+               }
+
+               for (auto cell : formal_database)
+               {
+                       string label = log_id(cell);
+                       if (cell->attributes.count("\\src"))
+                               label = cell->attributes.at("\\src").decode_string();
+
+                       State a = get_state(cell->getPort("\\A"))[0];
+                       State en = get_state(cell->getPort("\\EN"))[0];
+
+                       if (cell->type == "$cover" && en == State::S1 && a != State::S1)
+                               log("Cover %s.%s (%s) reached.\n", hiername().c_str(), log_id(cell), label.c_str());
+
+                       if (cell->type == "$assume" && en == State::S1 && a != State::S1)
+                               log("Assumption %s.%s (%s) failed.\n", hiername().c_str(), log_id(cell), label.c_str());
+
+                       if (cell->type == "$assert" && en == State::S1 && a != State::S1)
+                               log_warning("Assert %s.%s (%s) failed.\n", hiername().c_str(), log_id(cell), label.c_str());
+               }
+
+               for (auto it : children)
+                       it.second->update_ph3();
+       }
+
+       void writeback(pool<Module*> &wbmods)
+       {
+               if (wbmods.count(module))
+                       log_error("Instance %s of module %s is not unique: Writeback not possible. (Fix by running 'uniquify'.)\n", hiername().c_str(), log_id(module));
+
+               wbmods.insert(module);
+
+               for (auto wire : module->wires())
+                       wire->attributes.erase("\\init");
+
+               for (auto &it : ff_database)
+               {
+                       Cell *cell = it.first;
+                       SigSpec sig_q = cell->getPort("\\Q");
+                       Const initval = get_state(sig_q);
+
+                       for (int i = 0; i < GetSize(sig_q); i++)
+                       {
+                               Wire *w = sig_q[i].wire;
+
+                               if (w->attributes.count("\\init") == 0)
+                                       w->attributes["\\init"] = Const(State::Sx, GetSize(w));
+
+                               w->attributes["\\init"][sig_q[i].offset] = initval[i];
+                       }
+               }
+
+               for (auto &it : mem_database)
+               {
+                       Cell *cell = it.first;
+                       mem_state_t &mem = it.second;
+                       Const initval = mem.data;
+
+                       while (GetSize(initval) >= 2) {
+                               if (initval[GetSize(initval)-1] != State::Sx) break;
+                               if (initval[GetSize(initval)-2] != State::Sx) break;
+                               initval.bits.pop_back();
+                       }
+
+                       cell->setParam("\\INIT", initval);
+               }
+
+               for (auto it : children)
+                       it.second->writeback(wbmods);
+       }
+
        void write_vcd_header(std::ofstream &f, int &id)
        {
                f << stringf("$scope module %s $end\n", log_id(name()));
 
                for (auto wire : module->wires())
                {
-                       if (wire->name[0] == '$')
+                       if (shared->hide_internal && wire->name[0] == '$')
                                continue;
 
-                       f << stringf("$var wire %d n%d %s $end\n", GetSize(wire), id, log_id(wire));
-                       vcd_netids[wire] = id++;
+                       f << stringf("$var wire %d n%d %s%s $end\n", GetSize(wire), id, wire->name[0] == '$' ? "\\" : "", log_id(wire));
+                       vcd_database[wire] = make_pair(id++, Const());
                }
 
                for (auto child : children)
@@ -247,11 +592,16 @@ struct SimInstance
 
        void write_vcd_step(std::ofstream &f)
        {
-               for (auto it : vcd_netids)
+               for (auto &it : vcd_database)
                {
                        Wire *wire = it.first;
                        Const value = get_state(wire);
-                       int id = it.second;
+                       int id = it.second.first;
+
+                       if (it.second.second == value)
+                               continue;
+
+                       it.second.second = value;
 
                        f << "b";
                        for (int i = GetSize(value)-1; i >= 0; i--) {
@@ -271,20 +621,15 @@ struct SimInstance
        }
 };
 
-struct SimWorker
+struct SimWorker : SimShared
 {
        SimInstance *top = nullptr;
        std::ofstream vcdfile;
+       pool<IdString> clock, clockn, reset, resetn;
 
-       void initialize(Module *topmod)
+       ~SimWorker()
        {
-               top = new SimInstance(topmod);
-               top->update();
-       }
-
-       void step()
-       {
-               // FIXME
+               delete top;
        }
 
        void write_vcd_header()
@@ -298,19 +643,111 @@ struct SimWorker
                vcdfile << stringf("$enddefinitions $end\n");
        }
 
-       void write_vcd_step(int n)
+       void write_vcd_step(int t)
        {
                if (!vcdfile.is_open())
                        return;
 
-               vcdfile << stringf("#%d\n", 10*n);
+               vcdfile << stringf("#%d\n", t);
                top->write_vcd_step(vcdfile);
        }
+
+       void update()
+       {
+               while (1)
+               {
+                       if (debug)
+                               log("\n-- ph1 --\n");
+
+                       top->update_ph1();
+
+                       if (debug)
+                               log("\n-- ph2 --\n");
+
+                       if (!top->update_ph2())
+                               break;
+               }
+
+               if (debug)
+                       log("\n-- ph3 --\n");
+
+               top->update_ph3();
+       }
+
+       void set_inports(pool<IdString> ports, State value)
+       {
+               for (auto portname : ports)
+               {
+                       Wire *w = top->module->wire(portname);
+
+                       if (w == nullptr)
+                               log_error("Can't find port %s on module %s.\n", log_id(portname), log_id(top->module));
+
+                       top->set_state(w, value);
+               }
+       }
+
+       void run(Module *topmod, int numcycles)
+       {
+               log_assert(top == nullptr);
+               top = new SimInstance(this, topmod);
+
+               if (debug)
+                       log("\n===== 0 =====\n");
+               else
+                       log("Simulating cycle 0.\n");
+
+               set_inports(reset, State::S1);
+               set_inports(resetn, State::S0);
+
+               set_inports(clock, State::Sx);
+               set_inports(clockn, State::Sx);
+
+               update();
+
+               write_vcd_header();
+               write_vcd_step(0);
+
+               for (int cycle = 0; cycle < numcycles; cycle++)
+               {
+                       if (debug)
+                               log("\n===== %d =====\n", 10*cycle + 5);
+
+                       set_inports(clock, State::S0);
+                       set_inports(clockn, State::S1);
+
+                       update();
+                       write_vcd_step(10*cycle + 5);
+
+                       if (debug)
+                               log("\n===== %d =====\n", 10*cycle + 10);
+                       else
+                               log("Simulating cycle %d.\n", cycle+1);
+
+                       set_inports(clock, State::S1);
+                       set_inports(clockn, State::S0);
+
+                       if (cycle+1 == rstlen) {
+                               set_inports(reset, State::S0);
+                               set_inports(resetn, State::S1);
+                       }
+
+                       update();
+                       write_vcd_step(10*cycle + 10);
+               }
+
+               write_vcd_step(10*numcycles + 2);
+
+               if (writeback) {
+                       pool<Module*> wbmods;
+                       top->writeback(wbmods);
+               }
+       }
 };
 
 struct SimPass : public Pass {
        SimPass() : Pass("sim", "simulate the circuit") { }
-       virtual void help()
+       void help() YS_OVERRIDE
        {
                //   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
                log("\n");
@@ -321,14 +758,41 @@ struct SimPass : public Pass {
                log("    -vcd <filename>\n");
                log("        write the simulation results to the given VCD file\n");
                log("\n");
+               log("    -clock <portname>\n");
+               log("        name of top-level clock input\n");
+               log("\n");
+               log("    -clockn <portname>\n");
+               log("        name of top-level clock input (inverse polarity)\n");
+               log("\n");
+               log("    -reset <portname>\n");
+               log("        name of top-level reset input (active high)\n");
+               log("\n");
+               log("    -resetn <portname>\n");
+               log("        name of top-level inverted reset input (active low)\n");
+               log("\n");
+               log("    -rstlen <integer>\n");
+               log("        number of cycles reset should stay active (default: 1)\n");
+               log("\n");
+               log("    -zinit\n");
+               log("        zero-initialize all uninitialized regs and memories\n");
+               log("\n");
                log("    -n <integer>\n");
-               log("        number of steps to simulate (default: 20)\n");
+               log("        number of cycles to simulate (default: 20)\n");
+               log("\n");
+               log("    -a\n");
+               log("        include all nets in VCD output, not just those with public names\n");
+               log("\n");
+               log("    -w\n");
+               log("        writeback mode: use final simulation state as new init state\n");
+               log("\n");
+               log("    -d\n");
+               log("        enable debug output\n");
                log("\n");
        }
-       virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+       void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
        {
                SimWorker worker;
-               int numsteps = 20;
+               int numcycles = 20;
 
                log_header(design, "Executing SIM pass (simulate the circuit).\n");
 
@@ -339,7 +803,43 @@ struct SimPass : public Pass {
                                continue;
                        }
                        if (args[argidx] == "-n" && argidx+1 < args.size()) {
-                               numsteps = atoi(args[++argidx].c_str());
+                               numcycles = atoi(args[++argidx].c_str());
+                               continue;
+                       }
+                       if (args[argidx] == "-rstlen" && argidx+1 < args.size()) {
+                               worker.rstlen = atoi(args[++argidx].c_str());
+                               continue;
+                       }
+                       if (args[argidx] == "-clock" && argidx+1 < args.size()) {
+                               worker.clock.insert(RTLIL::escape_id(args[++argidx]));
+                               continue;
+                       }
+                       if (args[argidx] == "-clockn" && argidx+1 < args.size()) {
+                               worker.clockn.insert(RTLIL::escape_id(args[++argidx]));
+                               continue;
+                       }
+                       if (args[argidx] == "-reset" && argidx+1 < args.size()) {
+                               worker.reset.insert(RTLIL::escape_id(args[++argidx]));
+                               continue;
+                       }
+                       if (args[argidx] == "-resetn" && argidx+1 < args.size()) {
+                               worker.resetn.insert(RTLIL::escape_id(args[++argidx]));
+                               continue;
+                       }
+                       if (args[argidx] == "-a") {
+                               worker.hide_internal = false;
+                               continue;
+                       }
+                       if (args[argidx] == "-d") {
+                               worker.debug = true;
+                               continue;
+                       }
+                       if (args[argidx] == "-w") {
+                               worker.writeback = true;
+                               continue;
+                       }
+                       if (args[argidx] == "-zinit") {
+                               worker.zinit = true;
                                continue;
                        }
                        break;
@@ -350,6 +850,9 @@ struct SimPass : public Pass {
 
                if (design->full_selection()) {
                        top_mod = design->top_module();
+
+                       if (!top_mod)
+                               log_cmd_error("Design has no top module, use the 'hierarchy' command to specify one.\n");
                } else {
                        auto mods = design->selected_whole_modules();
                        if (GetSize(mods) != 1)
@@ -357,14 +860,7 @@ struct SimPass : public Pass {
                        top_mod = mods.front();
                }
 
-               worker.initialize(top_mod);
-               worker.write_vcd_header();
-               worker.write_vcd_step(0);
-
-               for (int i = 1; i < numsteps; i++) {
-                       worker.step();
-                       worker.write_vcd_step(i);
-               }
+               worker.run(top_mod, numcycles);
        }
 } SimPass;