Error out if no top module given before 'sim'
[yosys.git] / passes / sat / sim.cc
index 47f1fe031b16d7fd24d15394534f3ffefcf5aa3f..4c3022c709d451d7a48d5f63800209025fb178ac 100644 (file)
@@ -28,8 +28,23 @@ 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;
@@ -46,6 +61,7 @@ struct SimInstance
        dict<SigBit, pool<Wire*>> upd_outports;
 
        pool<SigBit> dirty_bits;
+       pool<Cell*> dirty_cells;
        pool<SimInstance*, hash_ptr_ops> dirty_children;
 
        struct ff_state_t
@@ -54,13 +70,26 @@ struct SimInstance
                Const past_d;
        };
 
+       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;
@@ -109,6 +138,52 @@ struct SimInstance
                                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);
+                       }
                }
        }
 
@@ -174,6 +249,43 @@ struct SimInstance
                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);
@@ -230,9 +342,7 @@ 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_ph1()
@@ -240,6 +350,8 @@ struct SimInstance
                pool<Cell*> queue_cells;
                pool<Wire*> queue_outports;
 
+               queue_cells.swap(dirty_cells);
+
                while (1)
                {
                        for (auto bit : dirty_bits)
@@ -305,6 +417,58 @@ struct SimInstance
                        }
                }
 
+               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);
@@ -327,10 +491,86 @@ struct SimInstance
                        }
                }
 
+               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()));
@@ -414,7 +654,7 @@ struct SimWorker : SimShared
 
        void update()
        {
-               do
+               while (1)
                {
                        if (debug)
                                log("\n-- ph1 --\n");
@@ -423,8 +663,10 @@ struct SimWorker : SimShared
 
                        if (debug)
                                log("\n-- ph2 --\n");
+
+                       if (!top->update_ph2())
+                               break;
                }
-               while (top->update_ph2());
 
                if (debug)
                        log("\n-- ph3 --\n");
@@ -452,10 +694,15 @@ struct SimWorker : SimShared
 
                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();
@@ -474,11 +721,13 @@ struct SimWorker : SimShared
 
                        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 == 0) {
+                       if (cycle+1 == rstlen) {
                                set_inports(reset, State::S0);
                                set_inports(resetn, State::S1);
                        }
@@ -488,12 +737,17 @@ struct SimWorker : SimShared
                }
 
                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");
@@ -516,17 +770,26 @@ struct SimPass : public Pass {
                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 cycles to simulate (default: 20)\n");
                log("\n");
                log("    -a\n");
-               log("        include all nets in VCD output, nut just those with public names\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 numcycles = 20;
@@ -543,6 +806,10 @@ struct SimPass : public Pass {
                                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;
@@ -567,6 +834,14 @@ struct SimPass : public Pass {
                                worker.debug = true;
                                continue;
                        }
+                       if (args[argidx] == "-w") {
+                               worker.writeback = true;
+                               continue;
+                       }
+                       if (args[argidx] == "-zinit") {
+                               worker.zinit = true;
+                               continue;
+                       }
                        break;
                }
                extra_args(args, argidx, design);
@@ -575,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)