Error out if no top module given before 'sim'
[yosys.git] / passes / sat / sim.cc
index ae9a862a4e372c852eaaa98456be29204b859ebf..4c3022c709d451d7a48d5f63800209025fb178ac 100644 (file)
@@ -29,8 +29,22 @@ 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;
@@ -47,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
@@ -73,6 +88,8 @@ struct SimInstance
        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;
@@ -122,10 +139,52 @@ struct SimInstance
                                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()
@@ -193,6 +252,40 @@ struct SimInstance
                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);
@@ -249,8 +342,6 @@ struct SimInstance
                        return;
                }
 
-               // FIXME
-
                log_error("Unsupported cell type: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell));
        }
 
@@ -259,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)
@@ -324,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);
@@ -346,6 +491,17 @@ 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);
@@ -372,7 +528,7 @@ struct SimInstance
        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 'singleton'.)\n", hiername().c_str(), log_id(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);
 
@@ -396,6 +552,21 @@ struct SimInstance
                        }
                }
 
+               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);
        }
@@ -529,6 +700,9 @@ struct SimWorker : SimShared
                set_inports(reset, State::S1);
                set_inports(resetn, State::S0);
 
+               set_inports(clock, State::Sx);
+               set_inports(clockn, State::Sx);
+
                update();
 
                write_vcd_header();
@@ -553,7 +727,7 @@ struct SimWorker : SimShared
                        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);
                        }
@@ -573,7 +747,7 @@ struct SimWorker : SimShared
 
 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");
@@ -596,11 +770,17 @@ 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");
@@ -609,7 +789,7 @@ struct SimPass : public Pass {
                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;
@@ -626,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;
@@ -654,6 +838,10 @@ struct SimPass : public Pass {
                                worker.writeback = true;
                                continue;
                        }
+                       if (args[argidx] == "-zinit") {
+                               worker.zinit = true;
+                               continue;
+                       }
                        break;
                }
                extra_args(args, argidx, design);
@@ -662,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)