diff options
Diffstat (limited to 'passes')
-rw-r--r-- | passes/cmds/bugpoint.cc | 1 | ||||
-rw-r--r-- | passes/cmds/chformal.cc | 19 | ||||
-rw-r--r-- | passes/cmds/design.cc | 5 | ||||
-rw-r--r-- | passes/cmds/setundef.cc | 10 | ||||
-rw-r--r-- | passes/cmds/show.cc | 139 | ||||
-rw-r--r-- | passes/cmds/splitcells.cc | 172 | ||||
-rw-r--r-- | passes/cmds/stat.cc | 3 | ||||
-rw-r--r-- | passes/cmds/xprop.cc | 3 | ||||
-rw-r--r-- | passes/equiv/equiv_make.cc | 92 | ||||
-rw-r--r-- | passes/fsm/fsm_detect.cc | 29 | ||||
-rw-r--r-- | passes/opt/opt_ffinv.cc | 3 | ||||
-rw-r--r-- | passes/proc/proc_dff.cc | 2 | ||||
-rw-r--r-- | passes/sat/formalff.cc | 216 | ||||
-rw-r--r-- | passes/sat/sim.cc | 515 | ||||
-rw-r--r-- | passes/techmap/bmuxmap.cc | 39 |
15 files changed, 1057 insertions, 191 deletions
diff --git a/passes/cmds/bugpoint.cc b/passes/cmds/bugpoint.cc index e666023fa..c398afffa 100644 --- a/passes/cmds/bugpoint.cc +++ b/passes/cmds/bugpoint.cc @@ -393,6 +393,7 @@ struct BugpointPass : public Pass { } } } + delete design_copy; return nullptr; } diff --git a/passes/cmds/chformal.cc b/passes/cmds/chformal.cc index 66044b161..da97ff71d 100644 --- a/passes/cmds/chformal.cc +++ b/passes/cmds/chformal.cc @@ -55,6 +55,14 @@ struct ChformalPass : public Pass { log(" -skip <N>\n"); log(" ignore activation of the constraint in the first <N> clock cycles\n"); log("\n"); + log(" -coverenable\n"); + log(" add cover statements for the enable signals of the constraints\n"); + log("\n"); +#ifdef YOSYS_ENABLE_VERIFIC + log(" Note: For the Verific frontend it is currently not guaranteed that a\n"); + log(" reachable SVA statement corresponds to an active enable signal.\n"); + log("\n"); +#endif log(" -assert2assume\n"); log(" -assume2assert\n"); log(" -live2fair\n"); @@ -114,6 +122,10 @@ struct ChformalPass : public Pass { mode_arg = atoi(args[++argidx].c_str()); continue; } + if (mode == 0 && args[argidx] == "-coverenable") { + mode = 'p'; + continue; + } if ((mode == 0 || mode == 'c') && args[argidx] == "-assert2assume") { assert2assume = true; mode = 'c'; @@ -263,6 +275,13 @@ struct ChformalPass : public Pass { cell->setPort(ID::EN, module->LogicAnd(NEW_ID, en, cell->getPort(ID::EN))); } else + if (mode =='p') + { + for (auto cell : constr_cells) + module->addCover(NEW_ID_SUFFIX("coverenable"), + cell->getPort(ID::EN), State::S1, cell->get_src_attribute()); + } + else if (mode == 'c') { for (auto cell : constr_cells) diff --git a/passes/cmds/design.cc b/passes/cmds/design.cc index 169f7cc4a..168d38563 100644 --- a/passes/cmds/design.cc +++ b/passes/cmds/design.cc @@ -118,6 +118,9 @@ struct DesignPass : public Pass { std::string save_name, load_name, as_name, delete_name; std::vector<RTLIL::Module*> copy_src_modules; + if (!design) + log_cmd_error("No default design.\n"); + size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -280,7 +283,7 @@ struct DesignPass : public Pass { done[mod->name] = prefix; } - while (!queue.empty()) + while (!queue.empty() && copy_from_design) { pool<Module*> old_queue; old_queue.swap(queue); diff --git a/passes/cmds/setundef.cc b/passes/cmds/setundef.cc index 590a7eb1d..7293002f3 100644 --- a/passes/cmds/setundef.cc +++ b/passes/cmds/setundef.cc @@ -502,7 +502,15 @@ struct SetundefPass : public Pass { } } - module->rewrite_sigspecs(worker); + for (auto &it : module->cells_) + if (!it.second->get_bool_attribute(ID::xprop_decoder)) + it.second->rewrite_sigspecs(worker); + for (auto &it : module->processes) + it.second->rewrite_sigspecs(worker); + for (auto &it : module->connections_) { + worker(it.first); + worker(it.second); + } if (worker.next_bit_mode == MODE_ANYSEQ || worker.next_bit_mode == MODE_ANYCONST) { diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc index b186e5db2..dd7de8273 100644 --- a/passes/cmds/show.cc +++ b/passes/cmds/show.cc @@ -233,77 +233,101 @@ struct ShowWorker return std::string(); } + // Return the pieces of a label joined by a '|' separator + std::string join_label_pieces(std::vector<std::string> pieces) + { + std::string ret = ""; + bool first_piece = true; + + for (auto &piece : pieces) { + if (!first_piece) + ret += "|"; + ret += piece; + first_piece = false; + } + + return ret; + } + std::string gen_portbox(std::string port, RTLIL::SigSpec sig, bool driver, std::string *node = nullptr) { std::string code; std::string net = gen_signode_simple(sig); if (net.empty()) { - std::string label_string; - int pos = sig.size()-1; - int idx = single_idx_count++; - for (int rep, i = int(sig.chunks().size())-1; i >= 0; i -= rep) { - const RTLIL::SigChunk &c = sig.chunks().at(i); + int dot_idx = single_idx_count++; + std::vector<std::string> label_pieces; + int bitpos = sig.size()-1; + + for (int rep, chunk_idx = ((int) sig.chunks().size()) - 1; chunk_idx >= 0; chunk_idx -= rep) { + const RTLIL::SigChunk &c = sig.chunks().at(chunk_idx); + + // Find the number of times this chunk is repeating + for (rep = 1; chunk_idx - rep >= 0 && c == sig.chunks().at(chunk_idx - rep); rep++); + int cl, cr; - if (c.wire) { + cl = c.offset + c.width - 1; + cr = c.offset; + + if (c.is_wire()) { if (c.wire->upto) { - cr = c.wire->start_offset + (c.wire->width - c.offset - 1); + cr = (c.wire->width - 1) - c.offset; cl = cr - (c.width - 1); - } else { - cr = c.wire->start_offset + c.offset; - cl = cr + c.width - 1; } - } else { - cl = c.offset + c.width - 1; - cr = c.offset; + + cl += c.wire->start_offset; + cr += c.wire->start_offset; } - if (!driver && c.wire == nullptr) { - RTLIL::State s1 = c.data.front(); - for (auto s2 : c.data) - if (s1 != s2) - goto not_const_stream; - net.clear(); - } else { - not_const_stream: + + // Is this chunk a constant filled with one kind of bit state? + bool no_signode = !driver && !c.is_wire() \ + && std::equal(c.data.begin() + 1, c.data.end(), c.data.begin()); + + if (!no_signode) { net = gen_signode_simple(c, false); log_assert(!net.empty()); } - for (rep = 1; i-rep >= 0 && c == sig.chunks().at(i-rep); rep++) {} + std::string repinfo = rep > 1 ? stringf("%dx ", rep) : ""; + std::string portside = stringf("%d:%d", bitpos, bitpos - rep*c.width + 1); + std::string remoteside = stringf("%s%d:%d", repinfo.c_str(), cl, cr); + if (driver) { log_assert(!net.empty()); - label_string += stringf("<s%d> %d:%d - %s%d:%d |", i, pos, pos-c.width+1, repinfo.c_str(), cl, cr); - net_conn_map[net].in.insert({stringf("x%d:s%d", idx, i), rep*c.width}); + label_pieces.push_back(stringf("<s%d> %s - %s ", chunk_idx, portside.c_str(), remoteside.c_str())); + net_conn_map[net].in.insert({stringf("x%d:s%d", dot_idx, chunk_idx), rep*c.width}); net_conn_map[net].color = nextColor(c, net_conn_map[net].color); - } else - if (net.empty()) { - log_assert(rep == 1); - label_string += stringf("%c -> %d:%d |", - c.data.front() == State::S0 ? '0' : - c.data.front() == State::S1 ? '1' : - c.data.front() == State::Sx ? 'X' : - c.data.front() == State::Sz ? 'Z' : '?', - pos, pos-rep*c.width+1); } else { - label_string += stringf("<s%d> %s%d:%d - %d:%d |", i, repinfo.c_str(), cl, cr, pos, pos-rep*c.width+1); - net_conn_map[net].out.insert({stringf("x%d:s%d", idx, i), rep*c.width}); - net_conn_map[net].color = nextColor(c, net_conn_map[net].color); + if (no_signode) { + log_assert(rep == 1); + label_pieces.push_back(stringf("%c -> %d:%d ", + c.data.front() == State::S0 ? '0' : + c.data.front() == State::S1 ? '1' : + c.data.front() == State::Sx ? 'X' : + c.data.front() == State::Sz ? 'Z' : '?', + bitpos, bitpos-rep*c.width+1)); + } else { + label_pieces.push_back(stringf("<s%d> %s - %s ", chunk_idx, remoteside.c_str(), portside.c_str())); + net_conn_map[net].out.insert({stringf("x%d:s%d", dot_idx, chunk_idx), rep*c.width}); + net_conn_map[net].color = nextColor(c, net_conn_map[net].color); + } } - pos -= rep * c.width; + + bitpos -= rep * c.width; } - if (label_string[label_string.size()-1] == '|') - label_string = label_string.substr(0, label_string.size()-1); - code += stringf("x%d [ shape=record, style=rounded, label=\"%s\" ];\n", idx, label_string.c_str()); + + code += stringf("x%d [ shape=record, style=rounded, label=\"", dot_idx) \ + + join_label_pieces(label_pieces) + "\" ];\n"; + if (!port.empty()) { currentColor = xorshift32(currentColor); - log_warning("WIDTHLABEL %s %d\n", log_signal(sig), GetSize(sig)); if (driver) - code += stringf("%s:e -> x%d:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, %s, %s];\n", port.c_str(), idx, nextColor(sig).c_str(), widthLabel(sig.size()).c_str()); + code += stringf("%s:e -> x%d:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, %s, %s];\n", port.c_str(), dot_idx, nextColor(sig).c_str(), widthLabel(sig.size()).c_str()); else - code += stringf("x%d:e -> %s:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, %s, %s];\n", idx, port.c_str(), nextColor(sig).c_str(), widthLabel(sig.size()).c_str()); + code += stringf("x%d:e -> %s:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, %s, %s];\n", dot_idx, port.c_str(), nextColor(sig).c_str(), widthLabel(sig.size()).c_str()); } if (node != nullptr) - *node = stringf("x%d", idx); + *node = stringf("x%d", dot_idx); } else { @@ -418,6 +442,7 @@ struct ShowWorker for (auto cell : module->selected_cells()) { std::vector<RTLIL::IdString> in_ports, out_ports; + std::vector<std::string> in_label_pieces, out_label_pieces; for (auto &conn : cell->connections()) { if (!ct.cell_output(cell->type, conn.first)) @@ -429,23 +454,23 @@ struct ShowWorker std::sort(in_ports.begin(), in_ports.end(), RTLIL::sort_by_id_str()); std::sort(out_ports.begin(), out_ports.end(), RTLIL::sort_by_id_str()); - std::string label_string = "{{"; + for (auto &p : in_ports) { + bool signed_suffix = genSignedLabels && cell->hasParam(p.str() + "_SIGNED") + && cell->getParam(p.str() + "_SIGNED").as_bool(); - for (auto &p : in_ports) - label_string += stringf("<p%d> %s%s|", id2num(p), escape(p.str()), - genSignedLabels && cell->hasParam(p.str() + "_SIGNED") && - cell->getParam(p.str() + "_SIGNED").as_bool() ? "*" : ""); - if (label_string[label_string.size()-1] == '|') - label_string = label_string.substr(0, label_string.size()-1); - - label_string += stringf("}|%s\\n%s|{", findLabel(cell->name.str()), escape(cell->type.str())); + in_label_pieces.push_back(stringf("<p%d> %s%s", id2num(p), escape(p.str()), + signed_suffix ? "*" : "")); + } for (auto &p : out_ports) - label_string += stringf("<p%d> %s|", id2num(p), escape(p.str())); - if (label_string[label_string.size()-1] == '|') - label_string = label_string.substr(0, label_string.size()-1); + out_label_pieces.push_back(stringf("<p%d> %s", id2num(p), escape(p.str()))); + + std::string in_label = join_label_pieces(in_label_pieces); + std::string out_label = join_label_pieces(out_label_pieces); - label_string += "}}"; + std::string label_string = stringf("{{%s}|%s\\n%s|{%s}}", in_label.c_str(), + findLabel(cell->name.str()), escape(cell->type.str()), + out_label.c_str()); std::string code; for (auto &conn : cell->connections()) { diff --git a/passes/cmds/splitcells.cc b/passes/cmds/splitcells.cc index de6df6142..82ed49074 100644 --- a/passes/cmds/splitcells.cc +++ b/passes/cmds/splitcells.cc @@ -68,70 +68,132 @@ struct SplitcellsWorker int split(Cell *cell, const std::string &format) { - if (!cell->type.in("$and", "$mux", "$not", "$or", "$pmux", "$xnor", "$xor")) return 0; + if (cell->type.in("$and", "$mux", "$not", "$or", "$pmux", "$xnor", "$xor")) + { + SigSpec outsig = sigmap(cell->getPort(ID::Y)); + if (GetSize(outsig) <= 1) return 0; + + std::vector<int> slices; + slices.push_back(0); + + int width = GetSize(outsig); + width = std::min(width, GetSize(cell->getPort(ID::A))); + if (cell->hasPort(ID::B)) + width = std::min(width, GetSize(cell->getPort(ID::B))); + + for (int i = 1; i < width; i++) { + auto &last_users = bit_users_db[outsig[slices.back()]]; + auto &this_users = bit_users_db[outsig[i]]; + if (last_users != this_users) slices.push_back(i); + } + if (GetSize(slices) <= 1) return 0; + slices.push_back(GetSize(outsig)); + + log("Splitting %s cell %s/%s into %d slices:\n", log_id(cell->type), log_id(module), log_id(cell), GetSize(slices)-1); + for (int i = 1; i < GetSize(slices); i++) + { + int slice_msb = slices[i]-1; + int slice_lsb = slices[i-1]; - SigSpec outsig = sigmap(cell->getPort(ID::Y)); - if (GetSize(outsig) <= 1) return 0; + IdString slice_name = module->uniquify(cell->name.str() + (slice_msb == slice_lsb ? + stringf("%c%d%c", format[0], slice_lsb, format[1]) : + stringf("%c%d%c%d%c", format[0], slice_msb, format[2], slice_lsb, format[1]))); + + Cell *slice = module->addCell(slice_name, cell); + + auto slice_signal = [&](SigSpec old_sig) -> SigSpec { + SigSpec new_sig; + for (int i = 0; i < GetSize(old_sig); i += GetSize(outsig)) { + int offset = i+slice_lsb; + int length = std::min(GetSize(old_sig)-offset, slice_msb-slice_lsb+1); + new_sig.append(old_sig.extract(offset, length)); + } + return new_sig; + }; + + slice->setPort(ID::A, slice_signal(slice->getPort(ID::A))); + if (slice->hasParam(ID::A_WIDTH)) + slice->setParam(ID::A_WIDTH, GetSize(slice->getPort(ID::A))); + + if (slice->hasPort(ID::B)) { + slice->setPort(ID::B, slice_signal(slice->getPort(ID::B))); + if (slice->hasParam(ID::B_WIDTH)) + slice->setParam(ID::B_WIDTH, GetSize(slice->getPort(ID::B))); + } - std::vector<int> slices; - slices.push_back(0); + slice->setPort(ID::Y, slice_signal(slice->getPort(ID::Y))); + if (slice->hasParam(ID::Y_WIDTH)) + slice->setParam(ID::Y_WIDTH, GetSize(slice->getPort(ID::Y))); + if (slice->hasParam(ID::WIDTH)) + slice->setParam(ID::WIDTH, GetSize(slice->getPort(ID::Y))); - int width = GetSize(outsig); - width = std::min(width, GetSize(cell->getPort(ID::A))); - if (cell->hasPort(ID::B)) - width = std::min(width, GetSize(cell->getPort(ID::B))); + log(" slice %d: %s => %s\n", i, log_id(slice_name), log_signal(slice->getPort(ID::Y))); + } - for (int i = 1; i < width; i++) { - auto &last_users = bit_users_db[outsig[slices.back()]]; - auto &this_users = bit_users_db[outsig[i]]; - if (last_users != this_users) slices.push_back(i); + module->remove(cell); + return GetSize(slices)-1; } - if (GetSize(slices) <= 1) return 0; - slices.push_back(GetSize(outsig)); - log("Splitting %s cell %s/%s into %d slices:\n", log_id(cell->type), log_id(module), log_id(cell), GetSize(slices)-1); - for (int i = 1; i < GetSize(slices); i++) + if (cell->type.in("$ff", "$dff", "$dffe", "$dffsr", "$dffsre", "$adff", "$adffe", "$aldffe", + "$sdff", "$sdffce", "$sdffe", "$dlatch", "$dlatchsr", "$adlatch")) { - int slice_msb = slices[i]-1; - int slice_lsb = slices[i-1]; + auto splitports = {ID::D, ID::Q, ID::AD, ID::SET, ID::CLR}; + auto splitparams = {ID::ARST_VALUE, ID::SRST_VALUE}; + + SigSpec outsig = sigmap(cell->getPort(ID::Q)); + if (GetSize(outsig) <= 1) return 0; + int width = GetSize(outsig); + + std::vector<int> slices; + slices.push_back(0); + + for (int i = 1; i < width; i++) { + auto &last_users = bit_users_db[outsig[slices.back()]]; + auto &this_users = bit_users_db[outsig[i]]; + if (last_users != this_users) slices.push_back(i); + } + + if (GetSize(slices) <= 1) return 0; + slices.push_back(GetSize(outsig)); - IdString slice_name = module->uniquify(cell->name.str() + (slice_msb == slice_lsb ? - stringf("%c%d%c", format[0], slice_lsb, format[1]) : - stringf("%c%d%c%d%c", format[0], slice_msb, format[2], slice_lsb, format[1]))); + log("Splitting %s cell %s/%s into %d slices:\n", log_id(cell->type), log_id(module), log_id(cell), GetSize(slices)-1); + for (int i = 1; i < GetSize(slices); i++) + { + int slice_msb = slices[i]-1; + int slice_lsb = slices[i-1]; - Cell *slice = module->addCell(slice_name, cell); + IdString slice_name = module->uniquify(cell->name.str() + (slice_msb == slice_lsb ? + stringf("%c%d%c", format[0], slice_lsb, format[1]) : + stringf("%c%d%c%d%c", format[0], slice_msb, format[2], slice_lsb, format[1]))); - auto slice_signal = [&](SigSpec old_sig) -> SigSpec { - SigSpec new_sig; - for (int i = 0; i < GetSize(old_sig); i += GetSize(outsig)) { - int offset = i+slice_lsb; - int length = std::min(GetSize(old_sig)-offset, slice_msb-slice_lsb+1); - new_sig.append(old_sig.extract(offset, length)); + Cell *slice = module->addCell(slice_name, cell); + + for (IdString portname : splitports) { + if (slice->hasPort(portname)) { + SigSpec sig = slice->getPort(portname); + sig = sig.extract(slice_lsb, slice_msb-slice_lsb+1); + slice->setPort(portname, sig); + } } - return new_sig; - }; - slice->setPort(ID::A, slice_signal(slice->getPort(ID::A))); - if (slice->hasParam(ID::A_WIDTH)) - slice->setParam(ID::A_WIDTH, GetSize(slice->getPort(ID::A))); + for (IdString paramname : splitparams) { + if (slice->hasParam(paramname)) { + Const val = slice->getParam(paramname); + val = val.extract(slice_lsb, slice_msb-slice_lsb+1); + slice->setParam(paramname, val); + } + } - if (slice->hasPort(ID::B)) { - slice->setPort(ID::B, slice_signal(slice->getPort(ID::B))); - if (slice->hasParam(ID::B_WIDTH)) - slice->setParam(ID::B_WIDTH, GetSize(slice->getPort(ID::B))); - } + slice->setParam(ID::WIDTH, GetSize(slice->getPort(ID::Q))); - slice->setPort(ID::Y, slice_signal(slice->getPort(ID::Y))); - if (slice->hasParam(ID::Y_WIDTH)) - slice->setParam(ID::Y_WIDTH, GetSize(slice->getPort(ID::Y))); - if (slice->hasParam(ID::WIDTH)) - slice->setParam(ID::WIDTH, GetSize(slice->getPort(ID::Y))); + log(" slice %d: %s => %s\n", i, log_id(slice_name), log_signal(slice->getPort(ID::Q))); + } - log(" slice %d: %s => %s\n", i, log_id(slice_name), log_signal(slice->getPort(ID::Y))); + module->remove(cell); + return GetSize(slices)-1; } - module->remove(cell); - return GetSize(slices)-1; + return 0; } }; @@ -179,14 +241,20 @@ struct SplitcellsPass : public Pass { for (auto module : design->selected_modules()) { - SplitcellsWorker worker(module); int count_split_pre = 0; int count_split_post = 0; - for (auto cell : module->selected_cells()) { - int n = worker.split(cell, format); - count_split_pre += (n != 0); - count_split_post += n; + while (1) { + SplitcellsWorker worker(module); + bool did_something = false; + for (auto cell : module->selected_cells()) { + int n = worker.split(cell, format); + did_something |= (n != 0); + count_split_pre += (n != 0); + count_split_post += n; + } + if (!did_something) + break; } if (count_split_pre) diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index 10e98952e..522957f39 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -275,6 +275,9 @@ struct statdata_t log(" \"num_memory_bits\": %u,\n", num_memory_bits); log(" \"num_processes\": %u,\n", num_processes); log(" \"num_cells\": %u,\n", num_cells); + if (area != 0) { + log(" \"area\": %f,\n", area); + } log(" \"num_cells_by_type\": {\n"); bool first_line = true; for (auto &it : num_cells_by_type) diff --git a/passes/cmds/xprop.cc b/passes/cmds/xprop.cc index 5dee72e1b..5e78ff9fc 100644 --- a/passes/cmds/xprop.cc +++ b/passes/cmds/xprop.cc @@ -252,7 +252,8 @@ struct XpropWorker } if (!driven_orig.empty()) { - module->addBwmux(NEW_ID, driven_enc.is_1, Const(State::Sx, GetSize(driven_orig)), driven_enc.is_x, driven_orig); + auto decoder = module->addBwmux(NEW_ID, driven_enc.is_1, Const(State::Sx, GetSize(driven_orig)), driven_enc.is_x, driven_orig); + decoder->set_bool_attribute(ID::xprop_decoder); } if (!driven_never_x.first.empty()) { module->connect(driven_never_x); diff --git a/passes/equiv/equiv_make.cc b/passes/equiv/equiv_make.cc index 27cec7549..e15e510be 100644 --- a/passes/equiv/equiv_make.cc +++ b/passes/equiv/equiv_make.cc @@ -33,6 +33,7 @@ struct EquivMakeWorker bool inames; vector<string> blacklists; vector<string> encfiles; + bool make_assert; pool<IdString> blacklist_names; dict<IdString, dict<Const, Const>> encdata; @@ -133,6 +134,12 @@ struct EquivMakeWorker delete gate_clone; } + void add_eq_assertion(const SigSpec &gold_sig, const SigSpec &gate_sig) + { + auto eq_wire = equiv_mod->Eqx(NEW_ID, gold_sig, gate_sig); + equiv_mod->addAssert(NEW_ID_SUFFIX("assert"), eq_wire, State::S1); + } + void find_same_wires() { SigMap assign_map(equiv_mod); @@ -231,15 +238,24 @@ struct EquivMakeWorker if (gold_wire->port_output || gate_wire->port_output) { - Wire *wire = equiv_mod->addWire(id, gold_wire->width); - wire->port_output = true; gold_wire->port_input = false; gate_wire->port_input = false; gold_wire->port_output = false; gate_wire->port_output = false; - for (int i = 0; i < wire->width; i++) - equiv_mod->addEquiv(NEW_ID, SigSpec(gold_wire, i), SigSpec(gate_wire, i), SigSpec(wire, i)); + Wire *wire = equiv_mod->addWire(id, gold_wire->width); + wire->port_output = true; + + if (make_assert) + { + add_eq_assertion(gold_wire, gate_wire); + equiv_mod->connect(wire, gold_wire); + } + else + { + for (int i = 0; i < wire->width; i++) + equiv_mod->addEquiv(NEW_ID, SigSpec(gold_wire, i), SigSpec(gate_wire, i), SigSpec(wire, i)); + } rd_signal_map.add(assign_map(gold_wire), wire); rd_signal_map.add(assign_map(gate_wire), wire); @@ -259,26 +275,31 @@ struct EquivMakeWorker } else { - Wire *wire = equiv_mod->addWire(id, gold_wire->width); - SigSpec rdmap_gold, rdmap_gate, rdmap_equiv; + if (make_assert) + add_eq_assertion(gold_wire, gate_wire); - for (int i = 0; i < wire->width; i++) { - if (undriven_bits.count(assign_map(SigBit(gold_wire, i)))) { - log(" Skipping signal bit %s [%d]: undriven on gold side.\n", id2cstr(gold_wire->name), i); - continue; - } - if (undriven_bits.count(assign_map(SigBit(gate_wire, i)))) { - log(" Skipping signal bit %s [%d]: undriven on gate side.\n", id2cstr(gate_wire->name), i); - continue; + else { + Wire *wire = equiv_mod->addWire(id, gold_wire->width); + SigSpec rdmap_gold, rdmap_gate, rdmap_equiv; + + for (int i = 0; i < wire->width; i++) { + if (undriven_bits.count(assign_map(SigBit(gold_wire, i)))) { + log(" Skipping signal bit %s [%d]: undriven on gold side.\n", id2cstr(gold_wire->name), i); + continue; + } + if (undriven_bits.count(assign_map(SigBit(gate_wire, i)))) { + log(" Skipping signal bit %s [%d]: undriven on gate side.\n", id2cstr(gate_wire->name), i); + continue; + } + equiv_mod->addEquiv(NEW_ID, SigSpec(gold_wire, i), SigSpec(gate_wire, i), SigSpec(wire, i)); + rdmap_gold.append(SigBit(gold_wire, i)); + rdmap_gate.append(SigBit(gate_wire, i)); + rdmap_equiv.append(SigBit(wire, i)); } - equiv_mod->addEquiv(NEW_ID, SigSpec(gold_wire, i), SigSpec(gate_wire, i), SigSpec(wire, i)); - rdmap_gold.append(SigBit(gold_wire, i)); - rdmap_gate.append(SigBit(gate_wire, i)); - rdmap_equiv.append(SigBit(wire, i)); - } - rd_signal_map.add(rdmap_gold, rdmap_equiv); - rd_signal_map.add(rdmap_gate, rdmap_equiv); + rd_signal_map.add(rdmap_gold, rdmap_equiv); + rd_signal_map.add(rdmap_gate, rdmap_equiv); + } } } @@ -335,12 +356,20 @@ struct EquivMakeWorker continue; } - for (int i = 0; i < GetSize(gold_sig); i++) - if (gold_sig[i] != gate_sig[i]) { - Wire *w = equiv_mod->addWire(NEW_ID); - equiv_mod->addEquiv(NEW_ID, gold_sig[i], gate_sig[i], w); - gold_sig[i] = w; - } + if (make_assert) + { + if (gold_sig != gate_sig) + add_eq_assertion(gold_sig, gate_sig); + } + else + { + for (int i = 0; i < GetSize(gold_sig); i++) + if (gold_sig[i] != gate_sig[i]) { + Wire *w = equiv_mod->addWire(NEW_ID); + equiv_mod->addEquiv(NEW_ID, gold_sig[i], gate_sig[i], w); + gold_sig[i] = w; + } + } gold_cell->setPort(gold_conn.first, gold_sig); } @@ -417,6 +446,10 @@ struct EquivMakePass : public Pass { log(" Match FSM encodings using the description from the file.\n"); log(" See 'help fsm_recode' for details.\n"); log("\n"); + log(" -make_assert\n"); + log(" Check equivalence with $assert cells instead of $equiv.\n"); + log(" $eqx (===) is used to compare signals."); + log("\n"); log("Note: The circuit created by this command is not a miter (with something like\n"); log("a trigger output), but instead uses $equiv cells to encode the equivalence\n"); log("checking problem. Use 'miter -equiv' if you want to create a miter circuit.\n"); @@ -427,6 +460,7 @@ struct EquivMakePass : public Pass { EquivMakeWorker worker; worker.ct.setup(design); worker.inames = false; + worker.make_assert = false; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -443,6 +477,10 @@ struct EquivMakePass : public Pass { worker.encfiles.push_back(args[++argidx]); continue; } + if (args[argidx] == "-make_assert") { + worker.make_assert = true; + continue; + } break; } diff --git a/passes/fsm/fsm_detect.cc b/passes/fsm/fsm_detect.cc index 5378ec89e..86d654cc4 100644 --- a/passes/fsm/fsm_detect.cc +++ b/passes/fsm/fsm_detect.cc @@ -118,7 +118,7 @@ static bool check_state_users(RTLIL::SigSpec sig) return true; } -static void detect_fsm(RTLIL::Wire *wire) +static void detect_fsm(RTLIL::Wire *wire, bool ignore_self_reset=false) { bool has_fsm_encoding_attr = wire->attributes.count(ID::fsm_encoding) > 0 && wire->attributes.at(ID::fsm_encoding).decode_string() != "none"; bool has_fsm_encoding_none = wire->attributes.count(ID::fsm_encoding) > 0 && wire->attributes.at(ID::fsm_encoding).decode_string() == "none"; @@ -199,7 +199,7 @@ static void detect_fsm(RTLIL::Wire *wire) } SigSpec sig_y = sig_d, sig_undef; - if (ce.eval(sig_y, sig_undef)) + if (!ignore_self_reset && ce.eval(sig_y, sig_undef)) is_self_resetting = true; } @@ -261,12 +261,15 @@ struct FsmDetectPass : public Pass { { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" fsm_detect [selection]\n"); + log(" fsm_detect [options] [selection]\n"); log("\n"); log("This pass detects finite state machines by identifying the state signal.\n"); log("The state signal is then marked by setting the attribute 'fsm_encoding'\n"); log("on the state signal to \"auto\".\n"); log("\n"); + log(" -ignore-self-reset\n"); + log(" Mark FSMs even if they are self-resetting\n"); + log("\n"); log("Existing 'fsm_encoding' attributes are not changed by this pass.\n"); log("\n"); log("Signals can be protected from being detected by this pass by setting the\n"); @@ -276,16 +279,28 @@ struct FsmDetectPass : public Pass { log("before this pass to prepare the design for fsm_detect.\n"); log("\n"); #ifdef YOSYS_ENABLE_VERIFIC - log("The Verific frontend may merge multiplexers in a way that interferes with FSM\n"); + log("The Verific frontend may optimize the design in a way that interferes with FSM\n"); log("detection. Run 'verific -cfg db_infer_wide_muxes_post_elaboration 0' before\n"); - log("reading the source, and 'bmuxmap' after 'proc' for best results.\n"); + log("reading the source, and 'bmuxmap -pmux' after 'proc' for best results.\n"); log("\n"); #endif } void execute(std::vector<std::string> args, RTLIL::Design *design) override { log_header(design, "Executing FSM_DETECT pass (finding FSMs in design).\n"); - extra_args(args, 1, design); + + bool ignore_self_reset = false; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-ignore-self-reset") { + ignore_self_reset = true; + continue; + } + break; + } + extra_args(args, argidx, design); CellTypes ct; ct.setup_internals(); @@ -321,7 +336,7 @@ struct FsmDetectPass : public Pass { sig_at_port.add(assign_map(wire)); for (auto wire : module->selected_wires()) - detect_fsm(wire); + detect_fsm(wire, ignore_self_reset); } assign_map.clear(); diff --git a/passes/opt/opt_ffinv.cc b/passes/opt/opt_ffinv.cc index 5d989dafd..3f7b4bc4a 100644 --- a/passes/opt/opt_ffinv.cc +++ b/passes/opt/opt_ffinv.cc @@ -64,6 +64,7 @@ struct OptFfInvWorker log_assert(d_inv == nullptr); d_inv = port.cell; } + if (!d_inv) return false; if (index.query_is_output(ff.sig_q)) return false; @@ -140,6 +141,7 @@ struct OptFfInvWorker log_assert(d_lut == nullptr); d_lut = port.cell; } + if (!d_lut) return false; if (index.query_is_output(ff.sig_q)) return false; @@ -167,6 +169,7 @@ struct OptFfInvWorker log_assert(q_inv == nullptr); q_inv = port.cell; } + if (!q_inv) return false; ff.flip_rst_bits({0}); ff.sig_q = q_inv->getPort(ID::Y); diff --git a/passes/proc/proc_dff.cc b/passes/proc/proc_dff.cc index 234671df5..fd56786f2 100644 --- a/passes/proc/proc_dff.cc +++ b/passes/proc/proc_dff.cc @@ -302,7 +302,7 @@ void proc_dff(RTLIL::Module *mod, RTLIL::Process *proc, ConstEval &ce) ce.assign_map.apply(rstval); ce.assign_map.apply(sig); - if (rstval == sig) { + if (rstval == sig && sync_level) { if (sync_level->type == RTLIL::SyncType::ST1) insig = mod->Mux(NEW_ID, insig, sig, sync_level->signal); else diff --git a/passes/sat/formalff.cc b/passes/sat/formalff.cc index e36379096..264a9fb3b 100644 --- a/passes/sat/formalff.cc +++ b/passes/sat/formalff.cc @@ -317,6 +317,172 @@ struct InitValWorker } }; +struct ReplacedPort { + IdString name; + int offset; + bool clk_pol; +}; + +struct HierarchyWorker +{ + Design *design; + pool<Module *> pending; + + dict<Module *, std::vector<ReplacedPort>> replaced_clk_inputs; + + HierarchyWorker(Design *design) : + design(design) + { + for (auto module : design->modules()) + pending.insert(module); + } + + void propagate(); + + const std::vector<ReplacedPort> &find_replaced_clk_inputs(IdString cell_type); +}; + +// Propagates replaced clock signals +struct PropagateWorker +{ + HierarchyWorker &hierarchy; + + Module *module; + SigMap sigmap; + + dict<SigBit, bool> replaced_clk_bits; + dict<SigBit, SigBit> not_drivers; + + std::vector<ReplacedPort> replaced_clk_inputs; + std::vector<std::pair<SigBit, bool>> pending; + + PropagateWorker(Module *module, HierarchyWorker &hierarchy) : + hierarchy(hierarchy), module(module), sigmap(module) + { + hierarchy.pending.erase(module); + + for (auto wire : module->wires()) + if (wire->has_attribute(ID::replaced_by_gclk)) + replace_clk_bit(SigBit(wire), wire->attributes[ID::replaced_by_gclk].bits.at(0) == State::S1, false); + + for (auto cell : module->cells()) { + if (cell->type.in(ID($not), ID($_NOT_))) { + auto sig_a = cell->getPort(ID::A); + auto &sig_y = cell->getPort(ID::Y); + sig_a.extend_u0(GetSize(sig_y), cell->hasParam(ID::A_SIGNED) && cell->parameters.at(ID::A_SIGNED).as_bool()); + + for (int i = 0; i < GetSize(sig_a); i++) + if (sig_a[i].is_wire()) + not_drivers.emplace(sigmap(sig_y[i]), sigmap(sig_a[i])); + } else { + for (auto &port_bit : hierarchy.find_replaced_clk_inputs(cell->type)) + replace_clk_bit(cell->getPort(port_bit.name)[port_bit.offset], port_bit.clk_pol, true); + } + } + + while (!pending.empty()) { + auto current = pending.back(); + pending.pop_back(); + auto it = not_drivers.find(current.first); + if (it == not_drivers.end()) + continue; + + replace_clk_bit(it->second, !current.second, true); + } + + for (auto cell : module->cells()) { + if (cell->type.in(ID($not), ID($_NOT_))) + continue; + for (auto &conn : cell->connections()) { + if (!cell->output(conn.first)) + continue; + for (SigBit bit : conn.second) { + sigmap.apply(bit); + if (replaced_clk_bits.count(bit)) + log_error("derived signal %s driven by %s (%s) from module %s is used as clock, derived clocks are only supported with clk2fflogic.\n", + log_signal(bit), log_id(cell->name), log_id(cell->type), log_id(module)); + } + } + } + + for (auto port : module->ports) { + auto wire = module->wire(port); + if (!wire->port_input) + continue; + for (int i = 0; i < GetSize(wire); i++) { + SigBit bit(wire, i); + sigmap.apply(bit); + auto it = replaced_clk_bits.find(bit); + if (it == replaced_clk_bits.end()) + continue; + replaced_clk_inputs.emplace_back(ReplacedPort {port, i, it->second}); + + if (it->second) { + bit = module->Not(NEW_ID, bit); + } + } + } + } + + void replace_clk_bit(SigBit bit, bool polarity, bool add_attribute) + { + sigmap.apply(bit); + if (!bit.is_wire()) + log_error("XXX todo\n"); + + auto it = replaced_clk_bits.find(bit); + if (it != replaced_clk_bits.end()) { + if (it->second != polarity) + log_error("signal %s from module %s is used as clock with different polarities, run clk2fflogic instead.\n", + log_signal(bit), log_id(module)); + return; + } + + replaced_clk_bits.emplace(bit, polarity); + + if (add_attribute) { + Wire *clk_wire = bit.wire; + if (bit.offset != 0 || GetSize(bit.wire) != 1) { + clk_wire = module->addWire(NEW_ID); + module->connect(RTLIL::SigBit(clk_wire), bit); + } + clk_wire->attributes[ID::replaced_by_gclk] = polarity ? State::S1 : State::S0; + clk_wire->set_bool_attribute(ID::keep); + } + + pending.emplace_back(bit, polarity); + } +}; + +const std::vector<ReplacedPort> &HierarchyWorker::find_replaced_clk_inputs(IdString cell_type) +{ + static const std::vector<ReplacedPort> empty; + if (!cell_type.isPublic()) + return empty; + + Module *module = design->module(cell_type); + if (module == nullptr) + return empty; + + auto it = replaced_clk_inputs.find(module); + if (it != replaced_clk_inputs.end()) + return it->second; + + if (pending.count(module)) { + PropagateWorker worker(module, *this); + return replaced_clk_inputs.emplace(module, std::move(worker.replaced_clk_inputs)).first->second; + } + + return empty; +} + + +void HierarchyWorker::propagate() +{ + while (!pending.empty()) + PropagateWorker worker(pending.pop(), *this); +} + struct FormalFfPass : public Pass { FormalFfPass() : Pass("formalff", "prepare FFs for formal") { } void help() override @@ -362,6 +528,15 @@ struct FormalFfPass : public Pass { log(" them. For -ff2anyinit, this reduces the number of generated $anyinit\n"); log(" cells that drive wires with private names.\n"); log("\n"); + log(" -hierarchy\n"); + log(" Propagates the 'replaced_by_gclk' attribute set by clk2ff upwards\n"); + log(" through the design hierarchy towards the toplevel inputs. This option\n"); + log(" works on the whole design and ignores the selection.\n"); + log("\n"); + log(" -assume\n"); + log(" Add assumptions that constrain wires with the 'replaced_by_gclk'\n"); + log(" attribute to the value they would have before an active clock edge.\n"); + log("\n"); // TODO: An option to check whether all FFs use the same clock before changing it to the global clock } @@ -372,6 +547,8 @@ struct FormalFfPass : public Pass { bool flag_anyinit2ff = false; bool flag_fine = false; bool flag_setundef = false; + bool flag_hierarchy = false; + bool flag_assume = false; log_header(design, "Executing FORMALFF pass.\n"); @@ -398,12 +575,20 @@ struct FormalFfPass : public Pass { flag_setundef = true; continue; } + if (args[argidx] == "-hierarchy") { + flag_hierarchy = true; + continue; + } + if (args[argidx] == "-assume") { + flag_assume = true; + continue; + } break; } extra_args(args, argidx, design); - if (!(flag_clk2ff || flag_ff2anyinit || flag_anyinit2ff)) - log_cmd_error("One of the options -clk2ff, -ff2anyinit, or -anyinit2ff must be specified.\n"); + if (!(flag_clk2ff || flag_ff2anyinit || flag_anyinit2ff || flag_hierarchy || flag_assume)) + log_cmd_error("One of the options -clk2ff, -ff2anyinit, -anyinit2ff, -hierarchy or -assume must be specified.\n"); if (flag_ff2anyinit && flag_anyinit2ff) log_cmd_error("The options -ff2anyinit and -anyinit2ff are exclusive.\n"); @@ -548,6 +733,33 @@ struct FormalFfPass : public Pass { ff.emit(); } } + + if (flag_hierarchy) { + HierarchyWorker worker(design); + worker.propagate(); + } + + if (flag_assume) { + for (auto module : design->selected_modules()) { + std::vector<pair<SigBit, bool>> found; + for (auto wire : module->wires()) { + if (!wire->has_attribute(ID::replaced_by_gclk)) + continue; + bool clk_pol = wire->attributes[ID::replaced_by_gclk].bits.at(0) == State::S1; + + found.emplace_back(SigSpec(wire), clk_pol); + } + for (auto pair : found) { + SigBit clk = pair.first; + + if (pair.second) + clk = module->Not(NEW_ID, clk); + + module->addAssume(NEW_ID, clk, State::S1); + + } + } + } } } FormalFfPass; diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc index 0084a1f28..7c209f516 100644 --- a/passes/sat/sim.cc +++ b/passes/sat/sim.cc @@ -23,6 +23,8 @@ #include "kernel/mem.h" #include "kernel/fstdata.h" #include "kernel/ff.h" +#include "kernel/yw.h" +#include "kernel/json.h" #include <ctime> @@ -74,6 +76,17 @@ struct OutputWriter SimWorker *worker; }; +struct SimInstance; +struct TriggeredAssertion { + int step; + SimInstance *instance; + Cell *cell; + + TriggeredAssertion(int step, SimInstance *instance, Cell *cell) : + step(step), instance(instance), cell(cell) + { } +}; + struct SimShared { bool debug = false; @@ -93,6 +106,9 @@ struct SimShared bool ignore_x = false; bool date = false; bool multiclock = false; + int next_output_id = 0; + int step = 0; + std::vector<TriggeredAssertion> triggered_assertions; }; void zinit(State &v) @@ -123,6 +139,8 @@ struct SimInstance dict<SigBit, pool<Cell*>> upd_cells; dict<SigBit, pool<Wire*>> upd_outports; + dict<SigBit, SigBit> in_parent_drivers; + pool<SigBit> dirty_bits; pool<Cell*> dirty_cells; pool<IdString> dirty_memories; @@ -152,11 +170,14 @@ struct SimInstance dict<Cell*, ff_state_t> ff_database; dict<IdString, mem_state_t> mem_database; pool<Cell*> formal_database; + pool<Cell*> initstate_database; dict<Cell*, IdString> mem_cells; std::vector<Mem> memories; dict<Wire*, pair<int, Const>> signal_database; + dict<IdString, std::map<int, pair<int, Const>>> trace_mem_database; + dict<std::pair<IdString, int>, Const> trace_mem_init_database; dict<Wire*, fstHandle> fst_handles; dict<Wire*, fstHandle> fst_inputs; dict<IdString, dict<int,fstHandle>> fst_memories; @@ -199,6 +220,12 @@ struct SimInstance dirty_bits.insert(sig[i]); } } + + if (wire->port_input && instance != nullptr && parent != nullptr) { + for (int i = 0; i < GetSize(sig); i++) { + in_parent_drivers.emplace(sig[i], parent->sigmap(instance->getPort(wire->name)[i])); + } + } } memories = Mem::get_all_memories(module); @@ -254,6 +281,8 @@ struct SimInstance if (cell->type.in(ID($assert), ID($cover), ID($assume))) { formal_database.insert(cell); } + if (cell->type == ID($initstate)) + initstate_database.insert(cell); } if (shared->zinit) @@ -300,6 +329,21 @@ struct SimInstance return log_id(module->name); } + vector<std::string> witness_full_path() const + { + if (instance != nullptr) + return parent->witness_full_path(instance); + return vector<std::string>(); + } + + vector<std::string> witness_full_path(Cell *cell) const + { + auto result = witness_full_path(); + auto cell_path = witness_path(cell); + result.insert(result.end(), cell_path.begin(), cell_path.end()); + return result; + } + Const get_state(SigSpec sig) { Const value; @@ -325,7 +369,7 @@ struct SimInstance log_assert(GetSize(sig) <= GetSize(value)); for (int i = 0; i < GetSize(sig); i++) - if (state_nets.at(sig[i]) != value[i]) { + if (value[i] != State::Sa && state_nets.at(sig[i]) != value[i]) { state_nets.at(sig[i]) = value[i]; dirty_bits.insert(sig[i]); did_something = true; @@ -336,14 +380,41 @@ struct SimInstance return did_something; } + void set_state_parent_drivers(SigSpec sig, Const value) + { + sigmap.apply(sig); + + for (int i = 0; i < GetSize(sig); i++) { + auto sigbit = sig[i]; + auto sigval = value[i]; + + auto in_parent_driver = in_parent_drivers.find(sigbit); + if (in_parent_driver == in_parent_drivers.end()) + set_state(sigbit, sigval); + else + parent->set_state_parent_drivers(in_parent_driver->second, sigval); + } + } + void set_memory_state(IdString memid, Const addr, Const data) { + set_memory_state(memid, addr.as_int(), data); + } + + void set_memory_state(IdString memid, int addr, Const data) + { auto &state = mem_database[memid]; - int offset = (addr.as_int() - state.mem->start_offset) * state.mem->width; + bool dirty = false; + + int offset = (addr - state.mem->start_offset) * state.mem->width; for (int i = 0; i < GetSize(data); i++) - if (0 <= i+offset && i+offset < state.mem->size * state.mem->width) - state.data.bits[i+offset] = data.bits[i]; + if (0 <= i+offset && i+offset < state.mem->size * state.mem->width && data.bits[i] != State::Sa) + if (state.data.bits[i+offset] != data.bits[i]) + dirty = true, state.data.bits[i+offset] = data.bits[i]; + + if (dirty) + dirty_memories.insert(memid); } void set_memory_state_bit(IdString memid, int offset, State data) @@ -351,7 +422,10 @@ struct SimInstance auto &state = mem_database[memid]; if (offset >= state.mem->size * state.mem->width) log_error("Addressing out of bounds bit %d/%d of memory %s\n", offset, state.mem->size * state.mem->width, log_id(memid)); - state.data.bits[offset] = data; + if (state.data.bits[offset] != data) { + state.data.bits[offset] = data; + dirty_memories.insert(memid); + } } void update_cell(Cell *cell) @@ -447,9 +521,14 @@ struct SimInstance log_error("Memory %s.%s has clocked read ports. Run 'memory' with -nordff.\n", log_id(module), log_id(mem.memid)); if (addr.is_fully_def()) { - int index = addr.as_int() - mem.start_offset; + int addr_int = addr.as_int(); + int index = addr_int - mem.start_offset; if (index >= 0 && index < mem.size) data = mdb.data.extract(index*mem.width, mem.width << port.wide_log2); + + for (int offset = 0; offset < 1 << port.wide_log2; offset++) { + register_memory_addr(id, addr_int + offset); + } } set_state(port.data, data); @@ -604,7 +683,8 @@ struct SimInstance if (addr.is_fully_def()) { - int index = addr.as_int() - mem.start_offset; + int addr_int = addr.as_int(); + int index = addr_int - mem.start_offset; if (index >= 0 && index < mem.size) for (int i = 0; i < (mem.width << port.wide_log2); i++) if (enable[i] == State::S1 && mdb.data.bits.at(index*mem.width+i) != data[i]) { @@ -612,6 +692,9 @@ struct SimInstance dirty_memories.insert(mem.memid); did_something = true; } + + for (int i = 0; i < 1 << port.wide_log2; i++) + register_memory_addr(it.first, addr_int + i); } } } @@ -625,7 +708,7 @@ struct SimInstance return did_something; } - void update_ph3() + void update_ph3(bool check_assertions) { for (auto &it : ff_database) { @@ -660,27 +743,42 @@ struct SimInstance } } - for (auto cell : formal_database) + if (check_assertions) { - string label = log_id(cell); - if (cell->attributes.count(ID::src)) - label = cell->attributes.at(ID::src).decode_string(); + for (auto cell : formal_database) + { + string label = log_id(cell); + if (cell->attributes.count(ID::src)) + label = cell->attributes.at(ID::src).decode_string(); + + State a = get_state(cell->getPort(ID::A))[0]; + State en = get_state(cell->getPort(ID::EN))[0]; - State a = get_state(cell->getPort(ID::A))[0]; - State en = get_state(cell->getPort(ID::EN))[0]; + if (en == State::S1 && (cell->type == ID($cover) ? a == State::S1 : a != State::S1)) { + shared->triggered_assertions.emplace_back(shared->step, this, cell); + } - if (cell->type == ID($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 == ID($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 == ID($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 == ID($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 == ID($assert) && en == State::S1 && a != State::S1) - log_warning("Assert %s.%s (%s) failed.\n", hiername().c_str(), log_id(cell), label.c_str()); + if (cell->type == ID($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(); + it.second->update_ph3(check_assertions); + } + + void set_initstate_outputs(State state) + { + for (auto cell : initstate_database) + set_state(cell->getPort(ID::Y), state); + for (auto child : children) + child.second->set_initstate_outputs(state); } void writeback(pool<Module*> &wbmods) @@ -741,7 +839,7 @@ struct SimInstance child.second->register_signals(id); } - void write_output_header(std::function<void(IdString)> enter_scope, std::function<void()> exit_scope, std::function<void(const char*, Wire*, int, bool)> register_signal) + void write_output_header(std::function<void(IdString)> enter_scope, std::function<void()> exit_scope, std::function<void(const char*, int, Wire*, int, bool)> register_signal) { int exit_scopes = 1; if (shared->hdlname && instance != nullptr && instance->name.isPublic() && instance->has_attribute(ID::hdlname)) { @@ -774,11 +872,45 @@ struct SimInstance hdlname.pop_back(); for (auto name : hdlname) enter_scope("\\" + name); - register_signal(signal_name.c_str(), signal.first, signal.second.first, registers.count(signal.first)!=0); + register_signal(signal_name.c_str(), GetSize(signal.first), signal.first, signal.second.first, registers.count(signal.first)!=0); for (auto name : hdlname) exit_scope(); } else - register_signal(log_id(signal.first->name), signal.first, signal.second.first, registers.count(signal.first)!=0); + register_signal(log_id(signal.first->name), GetSize(signal.first), signal.first, signal.second.first, registers.count(signal.first)!=0); + } + + for (auto &trace_mem : trace_mem_database) + { + auto memid = trace_mem.first; + auto &mdb = mem_database.at(memid); + Cell *cell = mdb.mem->cell; + + std::vector<std::string> hdlname; + std::string signal_name; + bool has_hdlname = shared->hdlname && cell != nullptr && cell->name.isPublic() && cell->has_attribute(ID::hdlname); + + if (has_hdlname) { + hdlname = cell->get_hdlname_attribute(); + log_assert(!hdlname.empty()); + signal_name = std::move(hdlname.back()); + hdlname.pop_back(); + for (auto name : hdlname) + enter_scope("\\" + name); + } else { + signal_name = log_id(memid); + } + + for (auto &trace_index : trace_mem.second) { + int output_id = trace_index.second.first; + int index = trace_index.first; + register_signal( + stringf("%s[%d]", signal_name.c_str(), (index + mdb.mem->start_offset)).c_str(), + mdb.mem->width, nullptr, output_id, true); + } + + if (has_hdlname) + for (auto name : hdlname) + exit_scope(); } for (auto child : children) @@ -788,6 +920,30 @@ struct SimInstance exit_scope(); } + void register_memory_addr(IdString memid, int addr) + { + auto &mdb = mem_database.at(memid); + auto &mem = *mdb.mem; + int index = addr - mem.start_offset; + if (index < 0 || index >= mem.size) + return; + auto it = trace_mem_database.find(memid); + if (it != trace_mem_database.end() && it->second.count(index)) + return; + int output_id = shared->next_output_id++; + Const data; + if (!shared->output_data.empty()) { + auto init_it = trace_mem_init_database.find(std::make_pair(memid, addr)); + if (init_it != trace_mem_init_database.end()) + data = init_it->second; + else + data = mem.get_init_data().extract(index * mem.width, mem.width); + shared->output_data.front().second.emplace(output_id, data); + } + trace_mem_database[memid].emplace(index, make_pair(output_id, data)); + + } + void register_output_step_values(std::map<int,Const> *data) { for (auto &it : signal_database) @@ -803,6 +959,26 @@ struct SimInstance data->emplace(id, value); } + for (auto &trace_mem : trace_mem_database) + { + auto memid = trace_mem.first; + auto &mdb = mem_database.at(memid); + auto &mem = *mdb.mem; + for (auto &trace_index : trace_mem.second) + { + int output_id = trace_index.second.first; + int index = trace_index.first; + + auto value = mdb.data.extract(index * mem.width, mem.width); + + if (trace_index.second.second == value) + continue; + + trace_index.second.second = value; + data->emplace(output_id, value); + } + } + for (auto child : children) child.second->register_output_step_values(data); } @@ -946,6 +1122,7 @@ struct SimWorker : SimShared std::string timescale; std::string sim_filename; std::string map_filename; + std::string summary_filename; std::string scope; ~SimWorker() @@ -956,8 +1133,8 @@ struct SimWorker : SimShared void register_signals() { - int id = 1; - top->register_signals(id); + next_output_id = 1; + top->register_signals(top->shared->next_output_id); } void register_output_step(int t) @@ -989,6 +1166,9 @@ struct SimWorker : SimShared void update(bool gclk) { + if (gclk) + step += 1; + while (1) { if (debug) @@ -1006,7 +1186,7 @@ struct SimWorker : SimShared if (debug) log("\n-- ph3 --\n"); - top->update_ph3(); + top->update_ph3(gclk); } void initialize_stable_past() @@ -1016,7 +1196,7 @@ struct SimWorker : SimShared top->update_ph1(); if (debug) log("\n-- ph3 (initialize) --\n"); - top->update_ph3(); + top->update_ph3(true); } void set_inports(pool<IdString> ports, State value) @@ -1490,6 +1670,242 @@ struct SimWorker : SimShared write_output_files(); } + struct FoundYWPath + { + SimInstance *instance; + Wire *wire; + IdString memid; + int addr; + }; + + struct YwHierarchy { + dict<IdPath, FoundYWPath> paths; + }; + + YwHierarchy prepare_yw_hierarchy(const ReadWitness &yw) + { + YwHierarchy hierarchy; + pool<IdPath> paths; + dict<IdPath, pool<IdString>> mem_paths; + + for (auto &signal : yw.signals) + paths.insert(signal.path); + + for (auto &clock : yw.clocks) + paths.insert(clock.path); + + for (auto &path : paths) + if (path.has_address()) + mem_paths[path.prefix()].insert(path.back()); + + witness_hierarchy(top->module, top, [&](IdPath const &path, WitnessHierarchyItem item, SimInstance *instance) { + if (item.cell != nullptr) + return instance->children.at(item.cell); + if (item.wire != nullptr) { + if (paths.count(path)) { + if (debug) + log("witness hierarchy: found wire %s\n", path.str().c_str()); + bool inserted = hierarchy.paths.emplace(path, {instance, item.wire, {}, INT_MIN}).second; + if (!inserted) + log_warning("Yosys witness path `%s` is ambiguous in this design\n", path.str().c_str()); + } + } else if (item.mem) { + auto it = mem_paths.find(path); + if (it != mem_paths.end()) { + if (debug) + log("witness hierarchy: found mem %s\n", path.str().c_str()); + IdPath word_path = path; + word_path.emplace_back(); + for (auto addr_part : it->second) { + word_path.back() = addr_part; + int addr; + word_path.get_address(addr); + if (addr < item.mem->start_offset || (addr - item.mem->start_offset) >= item.mem->size) + continue; + bool inserted = hierarchy.paths.emplace(word_path, {instance, nullptr, item.mem->memid, addr}).second; + if (!inserted) + log_warning("Yosys witness path `%s` is ambiguous in this design\n", path.str().c_str()); + } + } + } + return instance; + }); + + for (auto &path : paths) + if (!hierarchy.paths.count(path)) + log_warning("Yosys witness path `%s` was not found in this design, ignoring\n", path.str().c_str()); + + dict<IdPath, dict<int, bool>> clock_inputs; + + for (auto &clock : yw.clocks) { + if (clock.is_negedge == clock.is_posedge) + continue; + clock_inputs[clock.path].emplace(clock.offset, clock.is_posedge); + } + for (auto &signal : yw.signals) { + auto it = clock_inputs.find(signal.path); + if (it == clock_inputs.end()) + continue; + + for (auto &clock_input : it->second) { + int offset = clock_input.first; + if (offset >= signal.offset && (offset - signal.offset) < signal.width) { + int clock_bits_offset = signal.bits_offset + (offset - signal.offset); + + State expected = clock_input.second ? State::S0 : State::S1; + + for (int t = 0; t < GetSize(yw.steps); t++) { + if (yw.get_bits(t, clock_bits_offset, 1) != expected) + log_warning("Yosys witness trace has an unexpected value for the clock input `%s` in step %d.\n", signal.path.str().c_str(), t); + } + } + } + } + // TODO add checks and warnings for witness signals (toplevel inputs, $any*) not present in the witness file + + return hierarchy; + } + + void set_yw_state(const ReadWitness &yw, const YwHierarchy &hierarchy, int t) + { + log_assert(t >= 0 && t < GetSize(yw.steps)); + + for (auto &signal : yw.signals) { + if (signal.init_only && t >= 1) + continue; + auto found_path_it = hierarchy.paths.find(signal.path); + if (found_path_it == hierarchy.paths.end()) + continue; + auto &found_path = found_path_it->second; + + Const value = yw.get_bits(t, signal.bits_offset, signal.width); + + if (debug) + log("yw: set %s to %s\n", signal.path.str().c_str(), log_const(value)); + + if (found_path.wire != nullptr) { + found_path.instance->set_state_parent_drivers( + SigChunk(found_path.wire, signal.offset, signal.width), + value); + } else if (!found_path.memid.empty()) { + if (t >= 1) + found_path.instance->register_memory_addr(found_path.memid, found_path.addr); + else + found_path.instance->trace_mem_init_database.emplace(make_pair(found_path.memid, found_path.addr), value); + found_path.instance->set_memory_state( + found_path.memid, found_path.addr, + value); + } + } + } + + void set_yw_clocks(const ReadWitness &yw, const YwHierarchy &hierarchy, bool active_edge) + { + for (auto &clock : yw.clocks) { + if (clock.is_negedge == clock.is_posedge) + continue; + auto found_path_it = hierarchy.paths.find(clock.path); + if (found_path_it == hierarchy.paths.end()) + continue; + auto &found_path = found_path_it->second; + + if (found_path.wire != nullptr) { + found_path.instance->set_state( + SigChunk(found_path.wire, clock.offset, 1), + active_edge == clock.is_posedge ? State::S1 : State::S0); + } + } + } + + void run_cosim_yw_witness(Module *topmod, int append) + { + if (!clock.empty()) + log_cmd_error("The -clock option is not required nor supported when reading a Yosys witness file.\n"); + if (!reset.empty()) + log_cmd_error("The -reset option is not required nor supported when reading a Yosys witness file.\n"); + if (multiclock) + log_warning("The -multiclock option is not required and ignored when reading a Yosys witness file.\n"); + + ReadWitness yw(sim_filename); + + top = new SimInstance(this, scope, topmod); + register_signals(); + + YwHierarchy hierarchy = prepare_yw_hierarchy(yw); + + if (yw.steps.empty()) { + log_warning("Yosys witness file `%s` contains no time steps\n", yw.filename.c_str()); + } else { + top->set_initstate_outputs(State::S1); + set_yw_state(yw, hierarchy, 0); + set_yw_clocks(yw, hierarchy, true); + initialize_stable_past(); + register_output_step(0); + + if (!yw.clocks.empty()) { + if (debug) + log("Simulating non-active clock edge.\n"); + set_yw_clocks(yw, hierarchy, false); + update(false); + register_output_step(5); + } + top->set_initstate_outputs(State::S0); + } + + for (int cycle = 1; cycle < GetSize(yw.steps) + append; cycle++) + { + if (verbose) + log("Simulating cycle %d.\n", cycle); + if (cycle < GetSize(yw.steps)) + set_yw_state(yw, hierarchy, cycle); + set_yw_clocks(yw, hierarchy, true); + update(true); + register_output_step(10 * cycle); + + if (!yw.clocks.empty()) { + if (debug) + log("Simulating non-active clock edge.\n"); + set_yw_clocks(yw, hierarchy, false); + update(false); + register_output_step(5 + 10 * cycle); + } + } + + register_output_step(10 * (GetSize(yw.steps) + append)); + write_output_files(); + } + + void write_summary() + { + if (summary_filename.empty()) + return; + + PrettyJson json; + if (!json.write_to_file(summary_filename)) + log_error("Can't open file `%s' for writing: %s\n", summary_filename.c_str(), strerror(errno)); + + json.begin_object(); + json.entry("version", "Yosys sim summary"); + json.entry("generator", yosys_version_str); + json.entry("steps", step); + json.entry("top", log_id(top->module->name)); + json.name("assertions"); + json.begin_array(); + for (auto &assertion : triggered_assertions) { + json.begin_object(); + json.entry("step", assertion.step); + json.entry("type", log_id(assertion.cell->type)); + json.entry("path", assertion.instance->witness_full_path(assertion.cell)); + auto src = assertion.cell->get_string_attribute(ID::src); + if (!src.empty()) { + json.entry("src", src); + } + json.end_object(); + } + json.end_array(); + json.end_object(); + } + std::string define_signal(Wire *wire) { std::stringstream f; @@ -1734,9 +2150,15 @@ struct VCDWriter : public OutputWriter worker->top->write_output_header( [this](IdString name) { vcdfile << stringf("$scope module %s $end\n", log_id(name)); }, [this]() { vcdfile << stringf("$upscope $end\n");}, - [this,use_signal](const char *name, Wire *wire, int id, bool is_reg) { + [this,use_signal](const char *name, int size, Wire *, int id, bool is_reg) { if (use_signal.at(id)) { - vcdfile << stringf("$var %s %d n%d %s%s $end\n", is_reg ? "reg" : "wire", GetSize(wire), id, name[0] == '$' ? "\\" : "", name); + // Works around gtkwave trying to parse everything past the last [ in a signal + // name. While the emitted range doesn't necessarily match the wire's range, + // this is consistent with the range gtkwave makes up if it doesn't find a + // range + std::string range = strchr(name, '[') ? stringf("[%d:0]", size - 1) : std::string(); + vcdfile << stringf("$var %s %d n%d %s%s%s $end\n", is_reg ? "reg" : "wire", size, id, name[0] == '$' ? "\\" : "", name, range.c_str()); + } } ); @@ -1796,9 +2218,9 @@ struct FSTWriter : public OutputWriter worker->top->write_output_header( [this](IdString name) { fstWriterSetScope(fstfile, FST_ST_VCD_MODULE, stringf("%s",log_id(name)).c_str(), nullptr); }, [this]() { fstWriterSetUpscope(fstfile); }, - [this,use_signal](const char *name, Wire *wire, int id, bool is_reg) { + [this,use_signal](const char *name, int size, Wire *, int id, bool is_reg) { if (!use_signal.at(id)) return; - fstHandle fst_id = fstWriterCreateVar(fstfile, is_reg ? FST_VT_VCD_REG : FST_VT_VCD_WIRE, FST_VD_IMPLICIT, GetSize(wire), + fstHandle fst_id = fstWriterCreateVar(fstfile, is_reg ? FST_VT_VCD_REG : FST_VT_VCD_WIRE, FST_VD_IMPLICIT, size, name, 0); mapping.emplace(id, fst_id); } @@ -1881,7 +2303,7 @@ struct AIWWriter : public OutputWriter worker->top->write_output_header( [](IdString) {}, []() {}, - [this](const char */*name*/, Wire *wire, int id, bool) { mapping[wire] = id; } + [this](const char */*name*/, int /*size*/, Wire *wire, int id, bool) { if (wire != nullptr) mapping[wire] = id; } ); std::map<int, Yosys::RTLIL::Const> current; @@ -2013,11 +2435,17 @@ struct SimPass : public Pass { log(" -w\n"); log(" writeback mode: use final simulation state as new init state\n"); log("\n"); - log(" -r\n"); - log(" read simulation results file\n"); - log(" File formats supported: FST, VCD, AIW and WIT\n"); + log(" -r <filename>\n"); + log(" read simulation or formal results file\n"); + log(" File formats supported: FST, VCD, AIW, WIT and .yw\n"); log(" VCD support requires vcd2fst external tool to be present\n"); log("\n"); + log(" -append <integer>\n"); + log(" number of extra clock cycles to simulate for a Yosys witness input\n"); + log("\n"); + log(" -summary <filename>\n"); + log(" write a JSON summary to the given file\n"); + log("\n"); log(" -map <filename>\n"); log(" read file with port and latch symbols, needed for AIGER witness input\n"); log("\n"); @@ -2063,6 +2491,7 @@ struct SimPass : public Pass { { SimWorker worker; int numcycles = 20; + int append = 0; bool start_set = false, stop_set = false, at_set = false; log_header(design, "Executing SIM pass (simulate the circuit).\n"); @@ -2146,12 +2575,22 @@ struct SimPass : public Pass { worker.sim_filename = sim_filename; continue; } + if (args[argidx] == "-append" && argidx+1 < args.size()) { + append = atoi(args[++argidx].c_str()); + continue; + } if (args[argidx] == "-map" && argidx+1 < args.size()) { std::string map_filename = args[++argidx]; rewrite_filename(map_filename); worker.map_filename = map_filename; continue; } + if (args[argidx] == "-summary" && argidx+1 < args.size()) { + std::string summary_filename = args[++argidx]; + rewrite_filename(summary_filename); + worker.summary_filename = summary_filename; + continue; + } if (args[argidx] == "-scope" && argidx+1 < args.size()) { worker.scope = args[++argidx]; continue; @@ -2235,10 +2674,14 @@ struct SimPass : public Pass { worker.run_cosim_aiger_witness(top_mod); } else if (filename_trim.size() > 4 && filename_trim.compare(filename_trim.size()-4, std::string::npos, ".wit") == 0) { worker.run_cosim_btor2_witness(top_mod); + } else if (filename_trim.size() > 3 && filename_trim.compare(filename_trim.size()-3, std::string::npos, ".yw") == 0) { + worker.run_cosim_yw_witness(top_mod, append); } else { log_cmd_error("Unhandled extension for simulation input file `%s`.\n", worker.sim_filename.c_str()); } } + + worker.write_summary(); } } SimPass; diff --git a/passes/techmap/bmuxmap.cc b/passes/techmap/bmuxmap.cc index 03673c278..7aa67d3c0 100644 --- a/passes/techmap/bmuxmap.cc +++ b/passes/techmap/bmuxmap.cc @@ -33,13 +33,22 @@ struct BmuxmapPass : public Pass { log("\n"); log("This pass transforms $bmux cells to trees of $mux cells.\n"); log("\n"); + log(" -pmux\n"); + log(" transform to $pmux instead of $mux cells.\n"); + log("\n"); } void execute(std::vector<std::string> args, RTLIL::Design *design) override { + bool pmux_mode = false; + log_header(design, "Executing BMUXMAP pass.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-pmux") { + pmux_mode = true; + continue; + } break; } extra_args(args, argidx, design); @@ -53,18 +62,36 @@ struct BmuxmapPass : public Pass { SigSpec sel = cell->getPort(ID::S); SigSpec data = cell->getPort(ID::A); int width = GetSize(cell->getPort(ID::Y)); + int s_width = GetSize(cell->getPort(ID::S)); - for (int idx = 0; idx < GetSize(sel); idx++) { - SigSpec new_data = module->addWire(NEW_ID, GetSize(data)/2); - for (int i = 0; i < GetSize(new_data); i += width) { - RTLIL::Cell *mux = module->addMux(NEW_ID, + if(pmux_mode) + { + int num_cases = 1 << s_width; + SigSpec new_a = SigSpec(State::Sx, width); + SigSpec new_s = module->addWire(NEW_ID, num_cases); + SigSpec new_data = module->addWire(NEW_ID, width); + for (int val = 0; val < num_cases; val++) + { + module->addEq(NEW_ID, sel, SigSpec(val, GetSize(sel)), new_s[val]); + } + RTLIL::Cell *pmux = module->addPmux(NEW_ID, new_a, data, new_s, new_data); + pmux->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); + data = new_data; + } + else + { + for (int idx = 0; idx < GetSize(sel); idx++) { + SigSpec new_data = module->addWire(NEW_ID, GetSize(data)/2); + for (int i = 0; i < GetSize(new_data); i += width) { + RTLIL::Cell *mux = module->addMux(NEW_ID, data.extract(i*2, width), data.extract(i*2+width, width), sel[idx], new_data.extract(i, width)); - mux->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); + mux->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); + } + data = new_data; } - data = new_data; } module->connect(cell->getPort(ID::Y), data); |