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;
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
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;
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()
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);
return;
}
- // FIXME
-
log_error("Unsupported cell type: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell));
}
pool<Cell*> queue_cells;
pool<Wire*> queue_outports;
+ queue_cells.swap(dirty_cells);
+
while (1)
{
for (auto bit : dirty_bits)
}
}
+ 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);
}
}
+ 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);
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);
}
}
+ 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);
}
set_inports(reset, State::S1);
set_inports(resetn, State::S0);
+ set_inports(clock, State::Sx);
+ set_inports(clockn, State::Sx);
+
update();
write_vcd_header();
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);
}
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");
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(" 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;
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;
worker.writeback = true;
continue;
}
+ if (args[argidx] == "-zinit") {
+ worker.zinit = true;
+ continue;
+ }
break;
}
extra_args(args, argidx, design);
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)