3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2026-07-14 19:25:40 +00:00

Merge branch 'main' into nella/latch-toggle

This commit is contained in:
nella 2026-07-08 11:41:08 +02:00 committed by GitHub
commit f5809a7c2c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
675 changed files with 10003 additions and 8149 deletions

View file

@ -249,7 +249,7 @@ struct CheckPass : public Pass {
const int threshold = 1024;
// if the multiplication may overflow we will catch it here
// if the multiplication may overflow we will catch it here
if (in_widths + out_widths >= threshold)
return true;
@ -441,7 +441,7 @@ struct CheckPass : public Pass {
message += stringf(" cell %s (%s)%s\n", driver, driver->type.unescape(), driver_src);
if (!coarsened_cells.count(driver)) {
if (!coarsened_cells.count(driver)) {
MatchingEdgePrinter printer(message, sigmap, prev, bit);
printer.add_edges_from_cell(driver);
} else {
@ -455,7 +455,7 @@ struct CheckPass : public Pass {
std::string src_attr = wire->get_src_attribute();
wire_src = stringf(" source: %s", src_attr);
}
message += stringf(" wire %s%s\n", log_signal(SigBit(wire, pair.second)), wire_src);
message += stringf(" wire %s%s\n", log_signal(SigBit(wire, pair.second)), wire_src);
}
prev = bit;
@ -543,12 +543,12 @@ struct CheckMemPass : public Pass {
for (auto &init : mem.inits) {
int start = init.addr.as_int();
if (start < min_addr) {
log_warning("Mem %s.%s starts at %d but initializes address %d.\n", log_id(module), log_id(mem.mem), min_addr, start);
log_warning("Mem %s.%s starts at %d but initializes address %d.\n", module, mem.mem, min_addr, start);
counter++;
}
int end = start + (GetSize(init.data) / mem.width) - 1;
if (end > max_addr) {
log_warning("Mem %s.%s ends at %d but initializes address %d.\n", log_id(module), log_id(mem.mem), max_addr, end);
log_warning("Mem %s.%s ends at %d but initializes address %d.\n", module, mem.mem, max_addr, end);
counter++;
}
}
@ -557,7 +557,7 @@ struct CheckMemPass : public Pass {
if (addr_sig.is_fully_const()) {
auto addr = addr_sig.as_int();
if (addr < min_addr || addr > max_addr) {
log_warning("Mem %s.%s contains entries for addresses %d..%d but %s address %d.\n", log_id(module), log_id(mem.mem), min_addr, max_addr, access, addr);
log_warning("Mem %s.%s contains entries for addresses %d..%d but %s address %d.\n", module, mem.mem, min_addr, max_addr, access, addr);
counter++;
}
} else if (nonconst_mode) {
@ -566,7 +566,7 @@ struct CheckMemPass : public Pass {
int addr_sig_min = 0;
int addr_sig_max = (1 << addr_sig.size()) - 1;
if (min_addr > addr_sig_min || max_addr < addr_sig_max) {
log_warning("Mem %s.%s contains entries for addresses %d..%d but has a potentially dangerous non-const input %s\n", log_id(module), log_id(mem.mem), min_addr, max_addr, log_signal(addr_sig));
log_warning("Mem %s.%s contains entries for addresses %d..%d but has a potentially dangerous non-const input %s\n", module, mem.mem, min_addr, max_addr, log_signal(addr_sig));
counter++;
}
}

View file

@ -401,4 +401,3 @@ struct DesignPass : public Pass {
} DesignPass;
YOSYS_NAMESPACE_END

View file

@ -90,7 +90,7 @@ struct CoveragePass : public Pass {
std::map<std::string, std::set<int>> uncovered_lines;
std::map<std::string, std::set<int>> all_lines;
for (auto module : design->modules())
{
log_debug("Module %s:\n", module);
@ -136,7 +136,7 @@ struct CoveragePass : public Pass {
fout << "DA:" << l << ",";
if (uncovered_lines.count(file_entry.first) && uncovered_lines[file_entry.first].count(l))
fout << "0";
else
else
fout << "1";
fout << "\n";
}

View file

@ -36,7 +36,7 @@ struct LinuxPerf : public Pass {
bool formatted_help() override
{
auto *help = PrettyHelp::get_current();
auto content_root = help->get_root();
content_root->usage("linux_perf [on|off]");

View file

@ -106,7 +106,7 @@ struct LoggerPass : public Pass {
}
if (args[argidx] == "-warn" && argidx+1 < args.size()) {
std::string pattern = args[++argidx];
if (pattern.front() == '\"' && pattern.back() == '\"') pattern = pattern.substr(1, pattern.size() - 2);
if (pattern.front() == '\"' && pattern.back() == '\"') pattern = pattern.substr(1, pattern.size() - 2);
try {
log("Added regex '%s' for warnings to warn list.\n", pattern);
log_warn_regexes.push_back(YS_REGEX_COMPILE(pattern));
@ -118,7 +118,7 @@ struct LoggerPass : public Pass {
}
if (args[argidx] == "-nowarn" && argidx+1 < args.size()) {
std::string pattern = args[++argidx];
if (pattern.front() == '\"' && pattern.back() == '\"') pattern = pattern.substr(1, pattern.size() - 2);
if (pattern.front() == '\"' && pattern.back() == '\"') pattern = pattern.substr(1, pattern.size() - 2);
try {
log("Added regex '%s' for warnings to nowarn list.\n", pattern);
log_nowarn_regexes.push_back(YS_REGEX_COMPILE(pattern));
@ -130,7 +130,7 @@ struct LoggerPass : public Pass {
}
if (args[argidx] == "-werror" && argidx+1 < args.size()) {
std::string pattern = args[++argidx];
if (pattern.front() == '\"' && pattern.back() == '\"') pattern = pattern.substr(1, pattern.size() - 2);
if (pattern.front() == '\"' && pattern.back() == '\"') pattern = pattern.substr(1, pattern.size() - 2);
try {
log("Added regex '%s' for warnings to werror list.\n", pattern);
log_werror_regexes.push_back(YS_REGEX_COMPILE(pattern));

View file

@ -1472,7 +1472,7 @@ struct SelectPass : public Pass {
const char *common_flagset = "-add, -del, -assert-none, -assert-any, -assert-mod-count, -assert-count, -assert-max, or -assert-min";
if (common_flagset_tally > 1)
log_cmd_error("Options %s can not be combined.\n", common_flagset);
log_cmd_error("Options %s can not be combined.\n", common_flagset);
if ((list_mode || !write_file.empty() || count_mode) && common_flagset_tally)
log_cmd_error("Options -list, -list-mod, -write and -count can not be combined with %s.\n", common_flagset);

View file

@ -41,6 +41,7 @@ struct setunset_t
if (!RTLIL::SigSpec::parse(sig_value, nullptr, set_value))
log_cmd_error("Can't decode value '%s'!\n", set_value);
value = sig_value.as_const();
value.tag_bare_integer_const(set_value);
}
}
};

View file

@ -47,14 +47,14 @@ struct SetenvPass : public Pass {
std::string name = args[1];
std::string value = args[2];
if (value.front() == '\"' && value.back() == '\"') value = value.substr(1, value.size() - 2);
#if defined(_WIN32)
_putenv_s(name.c_str(), value.c_str());
#else
if (setenv(name.c_str(), value.c_str(), 1))
log_cmd_error("Invalid name \"%s\".\n", name);
#endif
}
} SetenvPass;

View file

@ -122,7 +122,7 @@ struct EstimateSta {
if (aigs.at(fingerprint).name.empty()) {
log_error("Unsupported cell '%s' in module '%s'",
cell->type.unescape(), m);
}
}
}
combinational.push_back(cell);
@ -217,9 +217,9 @@ struct EstimateSta {
if (!topo.sort())
log_error("Module '%s' contains combinational loops", m);
// now we determine how long it takes for signals to stabilize
// `levels` records the time after a clock edge after which a signal is stable
dict<std::tuple<SigBit, Cell *, AigNode *>, arrivalint> levels;

View file

@ -69,7 +69,7 @@ struct EquivOptPass:public ScriptPass
}
std::string command, techmap_opts, make_opts;
bool assert, undef, multiclock, async2sync, nocheck;
bool assert, undef, ignore_unknown_cells, multiclock, async2sync, nocheck;
void clear_flags() override
{
@ -78,6 +78,7 @@ struct EquivOptPass:public ScriptPass
make_opts = "";
assert = false;
undef = false;
ignore_unknown_cells = false;
multiclock = false;
async2sync = false;
nocheck = false;
@ -114,6 +115,10 @@ struct EquivOptPass:public ScriptPass
undef = true;
continue;
}
if (args[argidx] == "-ignore-unknown-cells") {
ignore_unknown_cells = true;
continue;
}
if (args[argidx] == "-nocheck") {
nocheck = true;
continue;
@ -197,12 +202,16 @@ struct EquivOptPass:public ScriptPass
else
opts = make_opts;
run("equiv_make" + opts + " gold gate equiv");
string induct_opts;
if (help_mode)
run("equiv_induct [-undef] equiv");
else if (undef)
run("equiv_induct -undef equiv");
else
run("equiv_induct equiv");
induct_opts = " [-undef] [-ignore-unknown-cells]";
else {
if (undef)
induct_opts += " -undef";
if (ignore_unknown_cells)
induct_opts += " -ignore-unknown-cells";
}
run("equiv_induct" + induct_opts + " equiv");
if (help_mode)
run("equiv_status [-assert] equiv");
else if (assert)

View file

@ -985,7 +985,9 @@ struct HierarchyPass : public Pass {
SigSpec sig_value;
if (!RTLIL::SigSpec::parse(sig_value, NULL, para.second))
log_cmd_error("Can't decode value '%s'!\n", para.second);
top_parameters[RTLIL::escape_id(para.first)] = sig_value.as_const();
RTLIL::Const c = sig_value.as_const();
c.tag_bare_integer_const(para.second);
top_parameters[RTLIL::escape_id(para.first)] = c;
}
}
@ -1073,7 +1075,9 @@ struct HierarchyPass : public Pass {
SigSpec sig_value;
if (!RTLIL::SigSpec::parse(sig_value, NULL, para.second))
log_cmd_error("Can't decode value '%s'!\n", para.second);
top_parameters[RTLIL::escape_id(para.first)] = sig_value.as_const();
RTLIL::Const c = sig_value.as_const();
c.tag_bare_integer_const(para.second);
top_parameters[RTLIL::escape_id(para.first)] = c;
}
top_mod = design->module(top_mod->derive(design, top_parameters));

View file

@ -343,7 +343,7 @@ struct MemMapping {
rejected_cfg_debug_msgs += "\n";
}
}
void log_reject(const Ram &ram, const PortGroup &pg, int pvi, std::string message) {
if(ys_debug(1)) {
rejected_cfg_debug_msgs += stringf("can't map to option selection [");
@ -516,7 +516,7 @@ std::pair<bool, Const> search_for_attribute(Mem mem, IdString attr) {
for (SigBit bit: port.addr)
if (bit.is_wire() && bit.wire->has_attribute(attr))
return std::make_pair(true, bit.wire->attributes.at(attr));
return std::make_pair(false, Const());
}

View file

@ -159,7 +159,7 @@ struct MuxpackWorker
if (cell->type == ID($mux))
b_sig = sigmap(cell->getPort(ID::B));
SigSpec y_sig = sigmap(cell->getPort(ID::Y));
if (sig_chain_next.count(a_sig))
for (auto a_bit : a_sig)
sigbit_with_non_chain_users.insert(a_bit);

View file

@ -73,15 +73,15 @@ struct OptBalanceTreeWorker {
// Base case: if we have only one source, return it
if (sources.size() == 1)
return sources[0];
// Base case: if we have two sources, create a single cell
if (sources.size() == 2) {
// Create a new cell of the same type
Cell* new_cell = module->addCell(NEW_ID, cell_type);
// Copy attributes from reference cell
new_cell->attributes = cell->attributes;
// Create output wire
int out_width = cell->getParam(ID::Y_WIDTH).as_int();
if (cell_type == ID($add))
@ -89,7 +89,7 @@ struct OptBalanceTreeWorker {
else if (cell_type == ID($mul))
out_width = sources[0].size() + sources[1].size();
Wire* out_wire = module->addWire(NEW_ID, out_width);
// Connect ports and fix up parameters
new_cell->setPort(ID::A, sources[0]);
new_cell->setPort(ID::B, sources[1]);
@ -97,26 +97,26 @@ struct OptBalanceTreeWorker {
new_cell->fixup_parameters();
new_cell->setParam(ID::A_SIGNED, cell->getParam(ID::A_SIGNED));
new_cell->setParam(ID::B_SIGNED, cell->getParam(ID::B_SIGNED));
// Update count and return output wire
cell_count[cell_type]++;
return out_wire;
}
// Recursive case: split sources into two groups and create subtrees
int mid = (sources.size() + 1) / 2;
vector<SigSpec> left_sources(sources.begin(), sources.begin() + mid);
vector<SigSpec> right_sources(sources.begin() + mid, sources.end());
SigSpec left_tree = create_balanced_tree(left_sources, cell_type, cell);
SigSpec right_tree = create_balanced_tree(right_sources, cell_type, cell);
// Create a cell to combine the two subtrees
Cell* new_cell = module->addCell(NEW_ID, cell_type);
// Copy attributes from reference cell
new_cell->attributes = cell->attributes;
// Create output wire
int out_width = cell->getParam(ID::Y_WIDTH).as_int();
if (cell_type == ID($add))
@ -124,7 +124,7 @@ struct OptBalanceTreeWorker {
else if (cell_type == ID($mul))
out_width = left_tree.size() + right_tree.size();
Wire* out_wire = module->addWire(NEW_ID, out_width);
// Connect ports and fix up parameters
new_cell->setPort(ID::A, left_tree);
new_cell->setPort(ID::B, right_tree);
@ -132,7 +132,7 @@ struct OptBalanceTreeWorker {
new_cell->fixup_parameters();
new_cell->setParam(ID::A_SIGNED, cell->getParam(ID::A_SIGNED));
new_cell->setParam(ID::B_SIGNED, cell->getParam(ID::B_SIGNED));
// Update count and return output wire
cell_count[cell_type]++;
return out_wire;
@ -280,7 +280,7 @@ struct OptBalanceTreeWorker {
{
// Create a tree
log_debug(" Creating tree for %s with %d sources and %d inner cells...\n", head_cell, GetSize(sources), inner_cells);
// Build a vector of all source signals
vector<SigSpec> source_signals;
vector<bool> signed_flags;
@ -295,10 +295,10 @@ struct OptBalanceTreeWorker {
if (!std::all_of(signed_flags.begin(), signed_flags.end(), [&](bool flag) { return flag == signed_flags[0]; })) {
continue;
}
// Create the balanced tree
SigSpec tree_output = create_balanced_tree(source_signals, cell_type, head_cell);
// Connect the tree output to the head cell's output
SigSpec head_output = sigmap(head_cell->getPort(ID::Y));
int connect_width = std::min(head_output.size(), tree_output.size());
@ -313,7 +313,7 @@ struct OptBalanceTreeWorker {
}
}
}
// Remove all consumed cells, which now have been replaced by trees
for (auto cell : consumed_cells)
module->remove(cell);

View file

@ -43,6 +43,91 @@ struct OptDffOptions
bool keepdc;
};
// Bit-parallel random simulation used as a cheap pre-filter for equivalence
struct BitSim {
Module *module;
SigMap &sigmap;
ModWalker &modwalker;
dict<SigBit, uint64_t> sim_vals;
uint64_t rng_state;
int max_depth;
int evals_left;
BitSim(Module *m, SigMap &sm, ModWalker &mw)
: module(m), sigmap(sm), modwalker(mw), rng_state(1337)
{
max_depth = module->design->scratchpad_get_int("opt_dff.sim_depth", 10000);
evals_left = module->design->scratchpad_get_int("opt_dff.sim_evals", 1000000);
}
uint64_t next_rand() {
uint32_t lo = mkhash_xorshift((uint32_t)rng_state);
uint32_t hi = mkhash_xorshift((uint32_t)(rng_state >> 32) ^ lo);
rng_state = ((uint64_t)hi << 32) | lo;
return rng_state;
}
uint64_t eval_bit(SigBit b, int depth = 0) {
SigBit mapped = sigmap(b);
if (mapped == State::S0) return 0ULL;
if (mapped == State::S1) return ~0ULL;
if (mapped == State::Sx || mapped == State::Sz) return 0ULL;
auto it = sim_vals.find(mapped);
if (it != sim_vals.end()) return it->second;
// Failsafe for huge designs
if (depth >= max_depth || evals_left <= 0) {
uint64_t r = next_rand();
sim_vals[mapped] = r;
return r;
}
evals_left--;
sim_vals[mapped] = 0;
uint64_t res = 0;
if (!modwalker.has_drivers(mapped)) {
res = next_rand();
} else {
auto &drivers = modwalker.signal_drivers[mapped];
if (drivers.empty()) {
res = next_rand();
} else {
auto driver = *drivers.begin();
Cell *cell = driver.cell;
if (cell->is_builtin_ff()) {
res = next_rand();
} else if (cell->type == ID($_AND_)) {
res = eval_bit(cell->getPort(ID::A)[0], depth+1) & eval_bit(cell->getPort(ID::B)[0], depth+1);
} else if (cell->type == ID($_OR_)) {
res = eval_bit(cell->getPort(ID::A)[0], depth+1) | eval_bit(cell->getPort(ID::B)[0], depth+1);
} else if (cell->type == ID($_XOR_)) {
res = eval_bit(cell->getPort(ID::A)[0], depth+1) ^ eval_bit(cell->getPort(ID::B)[0], depth+1);
} else if (cell->type == ID($_NOT_)) {
res = ~eval_bit(cell->getPort(ID::A)[0], depth+1);
} else if (cell->type == ID($_MUX_)) {
uint64_t s = eval_bit(cell->getPort(ID::S)[0], depth+1);
uint64_t a = eval_bit(cell->getPort(ID::A)[0], depth+1);
uint64_t b = eval_bit(cell->getPort(ID::B)[0], depth+1);
res = (a & ~s) | (b & s);
} else if (cell->type == ID($mux)) {
uint64_t s = eval_bit(cell->getPort(ID::S)[0], depth+1);
uint64_t a = eval_bit(cell->getPort(ID::A)[driver.offset], depth+1);
uint64_t b = eval_bit(cell->getPort(ID::B)[driver.offset], depth+1);
res = (a & ~s) | (b & s);
} else {
res = next_rand();
}
}
}
sim_vals[mapped] = res;
return res;
}
};
struct OptDffWorker
{
const OptDffOptions &opt;
@ -919,6 +1004,397 @@ struct OptDffWorker
return did_something;
}
struct EqBit {
Cell *cell;
int idx;
SigBit q;
};
// NOTE: This intentionally duplicates a subset of FfData, as flattening just the
// fields that matter for merging into a single comparable/hashable key is cheaper
struct SigKey {
enum Flag : uint16_t {
InitOne = 1u << 0,
InitX = 1u << 1,
PolClk = 1u << 2,
PolCe = 1u << 3,
PolSrst = 1u << 4,
PolArst = 1u << 5,
PolAload = 1u << 6,
PolClr = 1u << 7,
PolSet = 1u << 8,
CeOverSrst = 1u << 9,
};
SigBit clk, ce, srst, arst, aload, clr, set;
IdString cell_type; // for SR
uint16_t flags;
bool operator==(const SigKey &o) const {
return flags == o.flags && clk == o.clk && ce == o.ce && srst == o.srst && arst == o.arst
&& aload == o.aload && clr == o.clr && set == o.set && cell_type == o.cell_type;
}
Hasher hash_into(Hasher h) const {
h.eat(flags);
h.eat(clk);
h.eat(ce);
h.eat(srst);
h.eat(arst);
h.eat(aload);
h.eat(clr);
h.eat(set);
h.eat(cell_type);
return h;
}
};
bool is_def(State s) {
// Concrete constant bit (0 or 1), as opposed to x/z
return s == State::S0 || s == State::S1;
}
std::vector<std::vector<int>> gather_initial_eq_classes(std::vector<EqBit> &bits, dict<Cell *, FfData> &ff_for_cell)
{
std::vector<SigKey> keys;
// Collect FF bits eligible for merging
for (auto cell : module->selected_cells()) {
if (!cell->is_builtin_ff())
continue;
FfData ff(&initvals, cell);
if (!ff.has_clk && !ff.has_gclk)
continue;
ff_for_cell.emplace(cell, ff);
for (int i = 0; i < ff.width; i++) {
// Skip bits whose reset value is undefined (x)
if (ff.has_srst && !is_def(ff.val_srst[i])) continue;
if (ff.has_arst && !is_def(ff.val_arst[i])) continue;
// Class members are assumed equal in the current cycle and proven equal in the next, which needs
// a base case anchoring them to a common known value
bool def_init = is_def(ff.val_init[i]);
if (!def_init && !ff.has_srst && !ff.has_arst)
continue;
SigKey k = {};
// Flags
if (def_init && ff.val_init[i] == State::S1)
k.flags |= SigKey::InitOne;
else if (!def_init)
k.flags |= SigKey::InitX;
if (ff.has_clk) {
k.clk = ff.sig_clk;
if (ff.pol_clk) k.flags |= SigKey::PolClk;
}
if (ff.has_ce) {
k.ce = ff.sig_ce;
if (ff.pol_ce) k.flags |= SigKey::PolCe;
}
if (ff.has_srst) {
k.srst = ff.sig_srst;
if (ff.pol_srst) k.flags |= SigKey::PolSrst;
if (ff.ce_over_srst) k.flags |= SigKey::CeOverSrst;
}
if (ff.has_arst) {
k.arst = ff.sig_arst;
if (ff.pol_arst) k.flags |= SigKey::PolArst;
}
if (ff.has_aload) {
k.aload = ff.sig_aload;
if (ff.pol_aload) k.flags |= SigKey::PolAload;
}
if (ff.has_sr) {
k.clr = ff.sig_clr[i];
k.set = ff.sig_set[i];
k.cell_type = cell->type;
if (ff.pol_clr) k.flags |= SigKey::PolClr;
if (ff.pol_set) k.flags |= SigKey::PolSet;
}
bits.push_back({cell, i, ff.sig_q[i]});
keys.push_back(k);
}
}
dict<SigKey, std::vector<int>> buckets;
for (int i = 0; i < GetSize(bits); i++)
buckets[keys[i]].push_back(i);
std::vector<std::vector<int>> classes;
for (auto &kv : buckets)
if (GetSize(kv.second) >= 2)
classes.push_back(std::move(kv.second));
return classes;
}
std::vector<std::vector<int>> filter_classes_sim(
const std::vector<std::vector<int>> &classes,
const std::vector<EqBit> &bits,
const dict<Cell *, FfData> &ff_for_cell,
ModWalker &modwalker
) {
BitSim sim(module, sigmap, modwalker);
// Assume same class
for (auto &cls : classes) {
uint64_t class_q_val = sim.next_rand();
for (int idx : cls) {
sim.sim_vals[sigmap(bits[idx].q)] = class_q_val;
}
}
std::vector<std::vector<int>> refined_classes;
for (auto &cls : classes) {
dict<uint64_t, std::vector<int>> sim_buckets;
for (int idx : cls) {
const EqBit &eb = bits[idx];
const FfData &ff = ff_for_cell.at(eb.cell);
uint64_t n_val = sim.eval_bit(ff.sig_d[eb.idx]);
if (ff.has_aload) {
uint64_t al = sim.eval_bit(ff.sig_aload);
if (!ff.pol_aload) al = ~al;
uint64_t ad = sim.eval_bit(ff.sig_ad[eb.idx]);
n_val = (n_val & ~al) | (ad & al);
}
if (ff.has_arst) {
uint64_t ar = sim.eval_bit(ff.sig_arst);
if (!ff.pol_arst) ar = ~ar;
uint64_t ar_val = (ff.val_arst[eb.idx] == State::S1) ? ~0ULL : 0ULL;
n_val = (n_val & ~ar) | (ar_val & ar);
}
if (ff.has_sr) {
uint64_t clr = sim.eval_bit(ff.sig_clr[eb.idx]);
if (!ff.pol_clr) clr = ~clr;
uint64_t set = sim.eval_bit(ff.sig_set[eb.idx]);
if (!ff.pol_set) set = ~set;
n_val = ~clr & (set | n_val);
}
if (ff.has_srst) {
uint64_t srst = sim.eval_bit(ff.sig_srst);
if (!ff.pol_srst) srst = ~srst;
uint64_t srst_val = (ff.val_srst[eb.idx] == State::S1) ? ~0ULL : 0ULL;
n_val = (n_val & ~srst) | (srst_val & srst);
}
sim_buckets[n_val].push_back(idx);
}
for (auto &kv : sim_buckets)
if (GetSize(kv.second) >= 2)
refined_classes.push_back(std::move(kv.second));
}
return refined_classes;
}
std::vector<std::vector<int>> filter_classes_sat(
std::vector<std::vector<int>> classes,
const std::vector<EqBit> &bits,
const dict<Cell *, FfData> &ff_for_cell,
ModWalker &modwalker
) {
QuickConeSat qcsat(modwalker);
std::vector<int> q_lit(bits.size(), -1);
std::vector<int> n_lit(bits.size(), -1);
// Build the next-state function n_lit[idx] of every candidate bit by
// folding the FF's control logic on top of the D input (-> next value)
// Two bits are equivalent if their next states always agree whenever their
// current states (and those of every other candidate pair) agree
for (auto &cls : classes) {
for (int idx : cls) {
const EqBit &eb = bits[idx];
const FfData &ff = ff_for_cell.at(eb.cell);
q_lit[idx] = qcsat.importSigBit(eb.q);
int n = qcsat.importSigBit(ff.sig_d[eb.idx]);
if (ff.has_aload) {
int al = qcsat.importSigBit(ff.sig_aload);
if (!ff.pol_aload) al = qcsat.ez->NOT(al);
n = qcsat.ez->ITE(al, qcsat.importSigBit(ff.sig_ad[eb.idx]), n);
}
if (ff.has_arst) {
int ar = qcsat.importSigBit(ff.sig_arst);
if (!ff.pol_arst) ar = qcsat.ez->NOT(ar);
n = qcsat.ez->ITE(ar, qcsat.ez->value(ff.val_arst[eb.idx] == State::S1), n);
}
if (ff.has_sr) {
int clr = qcsat.importSigBit(ff.sig_clr[eb.idx]);
if (!ff.pol_clr) clr = qcsat.ez->NOT(clr);
int set = qcsat.importSigBit(ff.sig_set[eb.idx]);
if (!ff.pol_set) set = qcsat.ez->NOT(set);
n = qcsat.ez->AND(qcsat.ez->NOT(clr), qcsat.ez->OR(set, n));
}
if (ff.has_srst) {
int srst = qcsat.importSigBit(ff.sig_srst);
if (!ff.pol_srst) srst = qcsat.ez->NOT(srst);
n = qcsat.ez->ITE(srst, qcsat.ez->value(ff.val_srst[eb.idx] == State::S1), n);
}
n_lit[idx] = n;
}
}
qcsat.prepare();
// Assume the induction hypo (that every current class is internally equal in the present cycle), and try
// to prove that the members of each class therefore also agree in the next cycle
// A class survives only if no counterexample exists under that hypo, so combined with the common init/reset
// value that every class shares, this makes the equality an inductive invariant -> bits are eq and safe to merge
std::vector<int> worklist;
std::vector<bool> in_worklist(GetSize(classes), true);
for (int i = 0; i < GetSize(classes); i++)
worklist.push_back(i);
while (!worklist.empty()) {
int cls_idx = worklist.back();
worklist.pop_back();
in_worklist[cls_idx] = false;
auto &cls = classes[cls_idx];
if (GetSize(cls) < 2) continue;
// Induction hypo: assume every candidate class is equal
std::vector<int> assumptions;
for (auto &c : classes) {
if (GetSize(c) < 2) continue;
int rep = c[0];
for (int k = 1; k < GetSize(c); k++)
assumptions.push_back(qcsat.ez->IFF(q_lit[rep], q_lit[c[k]]));
}
// Scan the class members against the representative and issue a query per pair,
// stopping early at the first counterexample, which is reused to split the entire
// class at once
int rep = cls[0];
for (int i = 1; i < GetSize(cls); i++) {
if (n_lit[rep] == n_lit[cls[i]])
continue;
// Can the next state of the rep and this member ever differ?
int query = qcsat.ez->XOR(n_lit[rep], n_lit[cls[i]]);
// Capture every member's next-state value in that model so one counterexample
// partitions the whole class
std::vector<int> modelExprs;
for (int b : cls)
modelExprs.push_back(n_lit[b]);
std::vector<bool> modelVals;
assumptions.push_back(query);
if (qcsat.ez->solve(modelExprs, modelVals, assumptions)) {
// SAT -> partition entire class
std::vector<int> sub0;
std::vector<int> sub1;
for (size_t b_idx = 0; b_idx < cls.size(); b_idx++) {
if (modelVals[b_idx])
sub1.push_back(cls[b_idx]);
else
sub0.push_back(cls[b_idx]);
}
classes[cls_idx] = std::move(sub0);
classes.push_back(std::move(sub1));
in_worklist.push_back(false);
// Partition was split -> the induction hypo weakened
for (int j = 0; j < GetSize(classes); j++) {
if (GetSize(classes[j]) >= 2 && !in_worklist[j]) {
worklist.push_back(j);
in_worklist[j] = true;
}
}
break; // Process new splits
}
assumptions.pop_back(); // Remove query for the next pairwise check if UNSAT
}
}
return classes;
}
bool apply_eq_merges(const std::vector<std::vector<int>> &classes, const std::vector<EqBit> &bits, dict<Cell *, FfData> &ff_for_cell)
{
bool any_change = false;
dict<Cell *, std::set<int>> remove_bits;
// Drive every non-rep Q from its class rep, drop merged bits from their FFs
for (auto &cls : classes) {
if (GetSize(cls) < 2)
continue;
SigBit rep_q = bits[cls[0]].q;
any_change = true;
for (int k = 1; k < GetSize(cls); k++) {
const EqBit &eb = bits[cls[k]];
initvals.remove_init(eb.q);
module->connect(eb.q, rep_q);
remove_bits[eb.cell].insert(eb.idx);
}
}
for (auto &kv : remove_bits) {
Cell *cell = kv.first;
const std::set<int> &drop = kv.second;
FfData &ff = ff_for_cell.at(cell);
std::vector<int> keep;
for (int i = 0; i < ff.width; i++)
if (!drop.count(i))
keep.push_back(i);
if (keep.empty()) {
module->remove(cell);
} else {
FfData new_ff = ff.slice(keep);
new_ff.cell = cell;
new_ff.emit();
}
}
return any_change;
}
bool run_eqbits()
{
if (!opt.sat)
return false;
std::vector<EqBit> bits;
dict<Cell *, FfData> ff_for_cell;
std::vector<std::vector<int>> classes = gather_initial_eq_classes(bits, ff_for_cell);
if (classes.empty())
return false;
ModWalker modwalker(module->design, module);
// Simulation prepass
classes = filter_classes_sim(classes, bits, ff_for_cell, modwalker);
if (classes.empty())
return false;
// SAT prove
classes = filter_classes_sat(std::move(classes), bits, ff_for_cell, modwalker);
if (classes.empty())
return false;
return apply_eq_merges(classes, bits, ff_for_cell);
}
};
struct OptDffPass : public Pass {
@ -948,7 +1424,9 @@ struct OptDffPass : public Pass {
log("\n");
log(" -sat\n");
log(" additionally invoke SAT solver to detect and remove flip-flops (with\n");
log(" non-constant inputs) that can also be replaced with a constant driver\n");
log(" non-constant inputs) that can also be replaced with a constant driver,\n");
log(" or merged with equivalent flip-flops. this reasons in 2-valued logic\n");
log(" and may resolve don't-care bits, so it is incompatible with -keepdc.\n");
log("\n");
log(" -keepdc\n");
log(" some optimizations change the behavior of the circuit with respect to\n");
@ -980,6 +1458,13 @@ struct OptDffPass : public Pass {
}
extra_args(args, argidx, design);
// The SAT engine reasons in 2-valued logic (a constant x is treated as
// 0), so it can resolve don't-care bits to concrete values -- exactly
// what -keepdc promises not to do. Refuse the combination rather than
// silently ignore -keepdc.
if (opt.sat && opt.keepdc)
log_cmd_error("The -sat and -keepdc options are mutually exclusive.\n");
bool did_something = false;
for (auto mod : design->selected_modules()) {
OptDffWorker worker(opt, mod);
@ -987,6 +1472,8 @@ struct OptDffPass : public Pass {
did_something = true;
if (worker.run_constbits())
did_something = true;
if (worker.run_eqbits())
did_something = true;
}
if (did_something)

View file

@ -1970,6 +1970,56 @@ skip_identity:
}
skip_alu_split:
// replace (2^k-1)-x with ~x when x is known to be smaller than 2^k
if (do_fine && cell->type == ID($sub))
{
int y_width = GetSize(cell->getPort(ID::Y));
bool a_signed = cell->getParam(ID::A_SIGNED).as_bool();
RTLIL::SigSpec sig_a = assign_map(cell->getPort(ID::A));
sig_a.extend_u0(y_width, a_signed);
if (y_width > 0 && sig_a.is_fully_const())
{
RTLIL::Const a_val = sig_a.as_const();
int k = 0;
while (k < y_width && a_val[k] == State::S1)
k++;
bool a_is_mask = k > 0;
for (int i = k; a_is_mask && i < y_width; i++)
if (a_val[i] != State::S0)
a_is_mask = false;
if (a_is_mask)
{
bool b_signed = cell->getParam(ID::B_SIGNED).as_bool();
RTLIL::SigSpec sig_b = assign_map(cell->getPort(ID::B));
sig_b.extend_u0(y_width, b_signed);
bool b_fits = true;
for (int i = k; b_fits && i < y_width; i++)
if (sig_b[i] != State::S0)
b_fits = false;
if (b_fits)
{
RTLIL::SigSpec sig_y = module->Not(NEW_ID, sig_b.extract(0, k));
if (y_width > k)
sig_y.append(RTLIL::SigSpec(State::S0, y_width - k));
log_debug("Replacing `(2^%d-1) - B` $sub cell `%s' in module `%s' with $not.\n",
k, cell->name.c_str(), module->name.c_str());
module->connect(cell->getPort(ID::Y), sig_y);
module->remove(cell);
did_something = true;
goto next_cell;
}
}
}
}
// remove redundant pairs of bits in ==, ===, !=, and !==
// replace cell with const driver if inputs can't be equal
if (do_fine && cell->type.in(ID($eq), ID($ne), ID($eqx), ID($nex)))

View file

@ -75,7 +75,7 @@ struct ModuleIndex {
} else {
classes[pair.second[i]].append(pair.first[i]);
}
}
}
}
}
@ -217,7 +217,7 @@ struct UsageData {
for (auto port_name : module->ports) {
Wire *port = module->wire(port_name);
log_assert(port);
if (port->port_input && port->port_output) {
// ignore bidirectional: hard to come up with sound handling
continue;

View file

@ -296,7 +296,7 @@ struct OptLutWorker
luts_dlogic_inputs.erase(lut);
module->remove(lut);
eliminated_count++;
if (limit > 0)
limit--;

View file

@ -282,4 +282,3 @@ struct OptLutInsPass : public Pass {
} OptLutInsPass;
PRIVATE_NAMESPACE_END

View file

@ -347,4 +347,3 @@ struct OptMemFeedbackPass : public Pass {
} OptMemFeedbackPass;
PRIVATE_NAMESPACE_END

View file

@ -106,4 +106,3 @@ struct OptMemPriorityPass : public Pass {
} OptMemPriorityPass;
PRIVATE_NAMESPACE_END

View file

@ -68,7 +68,7 @@ struct OptMuxtreeWorker
// Is bit directly used by non-mux cells or ports?
bool seen_non_mux;
pool<int> mux_users;
pool<int> mux_drivers;
std::optional<int> mux_driver;
};
idict<SigBit> bit2num;
@ -107,7 +107,7 @@ struct OptMuxtreeWorker
// Populate bit2info[]:
// .seen_non_mux
// .mux_users
// .mux_drivers
// .mux_driver
// Populate mux2info[].ports[]:
// .ctrl_sig
// .input_sigs
@ -137,8 +137,11 @@ struct OptMuxtreeWorker
// Analyze port A
muxinfo.ports.push_back(used_port_bit(sig_a, this_mux_idx));
for (int idx : sig2bits(sig_y))
bit2info[idx].mux_drivers.insert(this_mux_idx);
for (int idx : sig2bits(sig_y)) {
if (bit2info[idx].mux_driver)
log_cmd_error("Cell %s Y port signal %s already driven by %s\n", cell->name, log_signal(sig_y), mux2info[*bit2info[idx].mux_driver].cell->name);
bit2info[idx].mux_driver = this_mux_idx;
}
for (int idx : sig2bits(sig_s))
bit2info[idx].seen_non_mux = true;
@ -170,8 +173,8 @@ struct OptMuxtreeWorker
for (int j : bit2info[i].mux_users)
for (auto &p : mux2info[j].ports) {
if (p.input_sigs.count(i))
for (int k : bit2info[i].mux_drivers)
p.input_muxes.insert(k);
if (bit2info[i].mux_driver)
p.input_muxes.insert(*bit2info[i].mux_driver);
}
}
@ -184,14 +187,14 @@ struct OptMuxtreeWorker
root_muxes.resize(GetSize(mux2info));
for (auto &bi : bit2info) {
for (int i : bi.mux_drivers)
if (bi.mux_driver)
for (int j : bi.mux_users)
mux_to_users[i].insert(j);
mux_to_users[*bi.mux_driver].insert(j);
if (!bi.seen_non_mux)
continue;
for (int mux_idx : bi.mux_drivers) {
root_muxes.at(mux_idx) = true;
root_enable_muxes.at(mux_idx) = true;
if (bi.mux_driver) {
root_muxes.at(*bi.mux_driver) = true;
root_enable_muxes.at(*bi.mux_driver) = true;
}
}

View file

@ -445,7 +445,7 @@ struct OptSharePass : public Pass {
while (mux_port_offset + op_conn_width < mux_port_size &&
op_outsig_offset + op_conn_width < op_outsig_size &&
mux_insig[mux_port_offset + op_conn_width] == op_outsig[op_outsig_offset + op_conn_width])
op_conn_width++;
op_conn_width++;
log_assert(op_conn_width >= 1);

View file

@ -55,19 +55,19 @@ code
int c_const_int = c_const.as_int(c_const_signed);
int b_const_int_shifted = b_const_int << offset;
// Helper lambdas for two's complement math
// Helper lambdas for two's complement math
auto sign2sComplement = [](auto value, int numBits) {
if (value & (1 << (numBits - 1))) {
return -1;
return -1;
} else {
return 1;
return 1;
}
};
auto twosComplement = [](auto value, int numBits) {
if (value & (1 << (numBits - 1))) {
return (~value) + 1; // invert bits before adding 1
} else {
return value;
return value;
}
};

View file

@ -104,8 +104,8 @@ code
std::string location = shift->get_src_attribute();
if(shiftadd_max_ratio>0 && offset<0 && -offset*shiftadd_max_ratio > old_a.size()) {
log_warning("at %s: candiate for shiftadd optimization (shifting '%s' by '%s - %d' bits) "
"was ignored to avoid high resource usage, see help peepopt\n",
log_warning("at %s: candiate for shiftadd optimization (shifting '%s' by '%s - %d' bits) "
"was ignored to avoid high resource usage, see help peepopt\n",
location.c_str(), log_signal(old_a), log_signal(var_signal), -offset);
reject;
}

View file

@ -650,4 +650,3 @@ struct WreducePass : public Pass {
} WreducePass;
PRIVATE_NAMESPACE_END

View file

@ -201,6 +201,77 @@ struct proc_dlatch_db_t
sig[index] = State::Sx;
cell->setPort(ID::A, sig);
}
bool sibling_undef = true;
for (int i = 0; i < (is_bwmux ? 1 : GetSize(sig_s)); i++)
if (sig_b[i*width + index] != State::Sx)
sibling_undef = false;
if (!sibling_undef) {
if (!is_bwmux) {
for (int i = 0; i < GetSize(sig_s); i++)
n = make_inner(sig_s[i], State::S0, n);
} else {
n = make_inner(sig_s[index], State::S0, n);
}
}
children.insert(n);
}
for (int i = 0; i < (is_bwmux ? 1 : GetSize(sig_s)); i++) {
n = find_mux_feedback(sig_b[i*width + index], needle, set_undef);
if (n != false_node) {
if (set_undef && sig_b[i*width + index] == needle) {
SigSpec sig = cell->getPort(ID::B);
sig[i*width + index] = State::Sx;
cell->setPort(ID::B, sig);
}
bool sibling_undef = (sig_a[index] == State::Sx);
if (!is_bwmux)
for (int j = 0; j < GetSize(sig_s); j++)
if (j != i && sig_b[j*width + index] != State::Sx)
sibling_undef = false;
if (!sibling_undef)
n = make_inner(sig_s[is_bwmux ? index : i], State::S1, n);
children.insert(n);
}
}
if (children.empty())
return false_node;
return make_inner(children);
}
int find_mux_constant(SigBit haystack, State needle, bool set_undef)
{
if (sigusers[haystack] > 1)
set_undef = false;
if (haystack == SigBit(needle))
return true_node;
auto it = mux_drivers.find(haystack);
if (it == mux_drivers.end())
return false_node;
Cell *cell = it->second.first;
int index = it->second.second;
log_assert(cell->type.in(ID($mux), ID($pmux), ID($bwmux)));
bool is_bwmux = (cell->type == ID($bwmux));
SigSpec sig_a = sigmap(cell->getPort(ID::A));
SigSpec sig_b = sigmap(cell->getPort(ID::B));
SigSpec sig_s = sigmap(cell->getPort(ID::S));
int width = GetSize(sig_a);
pool<int> children;
int n = find_mux_constant(sig_a[index], needle, set_undef);
if (n != false_node) {
if (set_undef && sig_a[index] == SigBit(needle)) {
SigSpec sig = cell->getPort(ID::A);
sig[index] = State::Sx;
cell->setPort(ID::A, sig);
}
if (!is_bwmux) {
for (int i = 0; i < GetSize(sig_s); i++)
n = make_inner(sig_s[i], State::S0, n);
@ -211,9 +282,9 @@ struct proc_dlatch_db_t
}
for (int i = 0; i < (is_bwmux ? 1 : GetSize(sig_s)); i++) {
n = find_mux_feedback(sig_b[i*width + index], needle, set_undef);
n = find_mux_constant(sig_b[i*width + index], needle, set_undef);
if (n != false_node) {
if (set_undef && sig_b[i*width + index] == needle) {
if (set_undef && sig_b[i*width + index] == SigBit(needle)) {
SigSpec sig = cell->getPort(ID::B);
sig[i*width + index] = State::Sx;
cell->setPort(ID::B, sig);
@ -355,7 +426,7 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc, LatchPolicy policy)
{
RTLIL::SigSig latches_bits, nolatches_bits;
dict<SigBit, SigBit> latches_out_in;
dict<SigBit, int> latches_hold;
dict<SigBit, int> latches_hold, latches_rst, latches_set;
std::string src = proc->get_src_attribute();
for (auto sr : proc->syncs)
@ -387,15 +458,31 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc, LatchPolicy policy)
latches_out_in.sort();
for (auto &it : latches_out_in) {
int n = db.find_mux_feedback(it.second, it.first, true);
if (n == db.false_node) {
if (db.find_mux_feedback(it.second, it.first, false) == db.false_node) {
nolatches_bits.first.append(it.first);
nolatches_bits.second.append(it.second);
} else {
latches_bits.first.append(it.first);
latches_bits.second.append(it.second);
latches_hold[it.first] = n;
continue;
}
latches_bits.first.append(it.first);
latches_bits.second.append(it.second);
int nrst = db.find_mux_constant(it.second, State::S0, false);
int nset = db.find_mux_constant(it.second, State::S1, false);
bool has_rst = (nrst != db.false_node);
bool has_set = (nset != db.false_node);
if (has_rst && !has_set)
nrst = db.find_mux_constant(it.second, State::S0, true);
else if (has_set && !has_rst)
nset = db.find_mux_constant(it.second, State::S1, true);
else
nrst = nset = db.false_node;
int n = db.find_mux_feedback(it.second, it.first, true);
log_assert(n != db.false_node);
latches_hold[it.first] = n;
latches_rst[it.first] = nrst;
latches_set[it.first] = nset;
}
int offset = 0;
@ -429,20 +516,35 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc, LatchPolicy policy)
while (offset < GetSize(latches_bits.first))
{
int width = 1;
int n = latches_hold[latches_bits.first[offset]];
Wire *w = latches_bits.first[offset].wire;
SigBit obit = latches_bits.first[offset];
int n = latches_hold[obit];
int nrst = latches_rst[obit];
int nset = latches_set[obit];
Wire *w = obit.wire;
if (w != nullptr)
{
while (offset+width < GetSize(latches_bits.first) &&
n == latches_hold[latches_bits.first[offset+width]] &&
nrst == latches_rst[latches_bits.first[offset+width]] &&
nset == latches_set[latches_bits.first[offset+width]] &&
w == latches_bits.first[offset+width].wire)
width++;
SigSpec lhs = latches_bits.first.extract(offset, width);
SigSpec rhs = latches_bits.second.extract(offset, width);
Cell *cell = db.module->addDlatch(NEW_ID, db.module->Not(NEW_ID, db.make_hold(n, src)), rhs, lhs);
SigBit en = db.module->Not(NEW_ID, db.make_hold(n, src));
bool has_rst = (nrst != db.false_node);
bool has_set = (nset != db.false_node);
Cell *cell;
if (has_rst)
cell = db.module->addAdlatch(NEW_ID, en, db.make_hold(nrst, src), rhs, lhs, RTLIL::Const(State::S0, width));
else if (has_set)
cell = db.module->addAdlatch(NEW_ID, en, db.make_hold(nset, src), rhs, lhs, RTLIL::Const(State::S1, width));
else
cell = db.module->addDlatch(NEW_ID, en, rhs, lhs);
cell->set_src_attribute(src);
db.generated_dlatches.insert(cell);

View file

@ -117,4 +117,3 @@ struct ProcMemWrPass : public Pass {
} ProcMemWrPass;
PRIVATE_NAMESPACE_END

View file

@ -116,7 +116,7 @@ struct CutpointPass : public Pass {
for (auto bit : sigmap(conn.second))
if (bit.wire)
wire_drivers.insert(bit);
for (auto wire : module->wires())
if (wire->port_input)
for (auto bit : sigmap(wire))

View file

@ -82,4 +82,3 @@ always @(posedge clk)
assign y = counter == 12;
endmodule

View file

@ -11,4 +11,3 @@ sat -show rst,counter -set-at 3 y 1'b1 -seq 4 example004
sat -prove y 1'b0 -show rst,counter,y -ignore_unknown_cells example004
sat -prove y 1'b0 -tempinduct -show rst,counter,y -set-at 1 rst 1'b1 -seq 1 example004

View file

@ -132,7 +132,7 @@ struct QbfSolutionType {
//More importantly, we want to have the ability to port hole assignments to other modules with compatible
//hole names and widths. Obviously in those cases source locations of the $anyconst cells will not match.
//
//Option 2 has the benefits previously described, but wire names can be changed automatically by
//Option 2 has the benefits previously described, but wire names can be changed automatically by
//optimization or techmapping passes, especially when (ex/im)porting from BLIF for optimization with ABC.
//
//The approach taken here is to allow both options. We write the assignment information for each bit of

View file

@ -27,6 +27,7 @@
#include "kernel/yw.h"
#include "kernel/json.h"
#include "kernel/fmt.h"
#include "kernel/drivertools.h"
#include <ctime>
@ -140,6 +141,8 @@ struct SimShared
bool serious_asserts = false;
bool fst_noinit = false;
bool initstate = true;
bool undriven_check = true;
bool undriven_warning = false;
};
void zinit(Const &v)
@ -152,7 +155,7 @@ void zinit(Const &v)
struct SimInstance
{
SimShared *shared;
std::string scope;
Module *module;
Cell *instance;
@ -180,7 +183,7 @@ struct SimInstance
State past_clk;
State past_ce;
State past_srst;
FfData data;
};
@ -452,7 +455,7 @@ struct SimInstance
{
Const value = get_state_mapped(sigmap(sig));
if (shared->debug)
log("[%s] get %s: %s\n", hiername(), log_signal(sig), log_signal(value));
log("[%s] get %s: %s\n", hiername(), log_signal(sig), log_signal(value, true));
return value;
}
@ -471,7 +474,7 @@ struct SimInstance
}
if (shared->debug)
log("[%s] set %s: %s\n", hiername(), log_signal(sig), log_signal(value));
log("[%s] set %s: %s\n", hiername(), log_signal(sig), log_signal(value, true));
return did_something;
}
@ -1047,7 +1050,7 @@ struct SimInstance
}
}
}
for (auto signal : signal_database)
{
if (shared->hdlname && signal.first->name.isPublic() && signal.first->has_attribute(ID::hdlname)) {
@ -1179,7 +1182,7 @@ struct SimInstance
{
if (cell->is_mem_cell()) {
std::string memid = cell->parameters.at(ID::MEMID).decode_string();
for (auto &data : fst_memories[memid])
for (auto &data : fst_memories[memid])
{
std::string v = shared->fst->valueOf(data.second);
set_memory_state(memid, Const(data.first), Const::from_string(v));
@ -1217,6 +1220,54 @@ struct SimInstance
child.second->addAdditionalInputs();
}
// Preconditions / assumptions:
// 1) fst_handles is populated for this instance (0 handle means not in trace).
// 2) fst_inputs is finalized (top-level inputs + addAdditionalInputs() for $anyseq).
// 3) module has no processes (sim enforces proc-lowered input before this point).
// 4) sigmap is valid for per-bit queries on this instance.
// 5) shared->fst is active, i.e. this is called from FST/VCD replay flow.
int checkUndrivenReplaySignals(bool &any_undriven_found)
{
int issue_count = 0;
bool has_replay_candidates = false;
for (auto &item : fst_handles)
if (item.second != 0 && !fst_inputs.count(item.first)) {
has_replay_candidates = true;
break;
}
if (has_replay_candidates) {
DriverMap drivermap(module->design);
drivermap.add(module);
for (auto &item : fst_handles) {
Wire *wire = item.first;
if (item.second == 0 || fst_inputs.count(wire))
continue;
SigSpec undriven;
for (auto bit : sigmap(wire))
if (bit.wire != nullptr && drivermap(DriveBit(bit)).is_none())
undriven.append(bit);
undriven.sort_and_unify();
if (undriven.empty())
continue;
issue_count++;
any_undriven_found = true;
std::string wire_name = scope + "." + RTLIL::unescape_id(wire->name);
log_warning("Input trace contains undriven signal `%s` (%s).\n", wire_name.c_str(), log_signal(undriven));
}
}
for (auto child : children)
issue_count += child.second->checkUndrivenReplaySignals(any_undriven_found);
return issue_count;
}
bool setInputs()
{
bool did_something = false;
@ -1275,7 +1326,7 @@ struct SimInstance
if (shared->sim_mode == SimulationMode::gate && !fst_val.is_fully_def()) { // FST data contains X
for(int i=0;i<fst_val.size();i++) {
if (fst_val[i]!=State::Sx && fst_val[i]!=sim_val[i]) {
log_warning("Signal '%s.%s' in file %s in simulation %s\n", scope, item.first, log_signal(fst_val), log_signal(sim_val));
log_warning("Signal '%s.%s' in file %s in simulation %s\n", scope, item.first, log_signal(fst_val, true), log_signal(sim_val, true));
retVal = true;
break;
}
@ -1283,14 +1334,14 @@ struct SimInstance
} else if (shared->sim_mode == SimulationMode::gold && !sim_val.is_fully_def()) { // sim data contains X
for(int i=0;i<sim_val.size();i++) {
if (sim_val[i]!=State::Sx && fst_val[i]!=sim_val[i]) {
log_warning("Signal '%s.%s' in file %s in simulation %s\n", scope, item.first, log_signal(fst_val), log_signal(sim_val));
log_warning("Signal '%s.%s' in file %s in simulation %s\n", scope, item.first, log_signal(fst_val, true), log_signal(sim_val, true));
retVal = true;
break;
}
}
} else {
if (fst_val!=sim_val) {
log_warning("Signal '%s.%s' in file %s in simulation '%s'\n", scope, item.first, log_signal(fst_val), log_signal(sim_val));
log_warning("Signal '%s.%s' in file %s in simulation '%s'\n", scope, item.first, log_signal(fst_val, true), log_signal(sim_val, true));
retVal = true;
}
}
@ -1348,7 +1399,7 @@ struct SimWorker : SimShared
}
for(auto& writer : outputfiles)
writer->write(use_signal);
if (writeback) {
pool<Module*> wbmods;
top->writeback(wbmods);
@ -1525,6 +1576,15 @@ struct SimWorker : SimShared
}
top->addAdditionalInputs();
if (undriven_check) {
bool any_undriven_found = false;
int issue_count = top->checkUndrivenReplaySignals(any_undriven_found);
if (any_undriven_found)
log_warning("Values for the undriven signal(s) listed above are not replayed from FST/VCD input.\n");
if (issue_count > 0 && !undriven_warning)
log_cmd_error("Found %d undriven signal%s in the replay trace. Use -undriven-warn to continue or -no-undriven-check to disable this check.\n",
issue_count, issue_count == 1 ? "" : "s");
}
uint64_t startCount = 0;
uint64_t stopCount = 0;
@ -1532,7 +1592,7 @@ struct SimWorker : SimShared
if (start_time.time < fst->getStartTime())
log_warning("Start time is before simulation file start time\n");
startCount = fst->getStartTime();
} else if (start_time.end)
} else if (start_time.end)
startCount = fst->getEndTime();
else {
startCount = start_time.time * pow10(start_time.scale - fst->getScale());
@ -1545,7 +1605,7 @@ struct SimWorker : SimShared
if (stop_time.time < fst->getStartTime())
log_warning("Stop time is before simulation file start time\n");
stopCount = fst->getStartTime();
} else if (stop_time.end)
} else if (stop_time.end)
stopCount = fst->getEndTime();
else {
stopCount = stop_time.time * pow10(stop_time.scale - fst->getScale());
@ -1561,7 +1621,7 @@ struct SimWorker : SimShared
bool initial = true;
int cycle = 0;
log("Co-simulation from %lu%s to %lu%s", (unsigned long)startCount, fst->getTimescaleString(), (unsigned long)stopCount, fst->getTimescaleString());
if (cycles_set)
if (cycles_set)
log(" for %d clock cycle(s)",numcycles);
log("\n");
bool all_samples = fst_clock.empty();
@ -1779,9 +1839,9 @@ struct SimWorker : SimShared
std::getline(f, line);
if (line.size()==0) continue;
if (line[0]=='#' || line[0]=='@' || line[0]=='.') {
if (line[0]=='#' || line[0]=='@' || line[0]=='.') {
if (line[0]!='.')
curr_cycle = atoi(line.c_str()+1);
curr_cycle = atoi(line.c_str()+1);
else
curr_cycle = -1; // force detect change
@ -1847,7 +1907,7 @@ struct SimWorker : SimShared
log_error("Cell %s not present in module %s\n",escaped_s.unescape(),topmod);
if (!c->is_mem_cell())
log_error("Cell %s is not memory cell in module %s\n",escaped_s.unescape(),topmod);
Const addr = Const::from_string(parts[1].substr(1,parts[1].size()-2));
Const data = Const::from_string(parts[2]);
top->set_memory_state(c->parameters.at(ID::MEMID).decode_string(), addr, data);
@ -2192,7 +2252,7 @@ struct SimWorker : SimShared
if (start_time.time < fst->getStartTime())
log_warning("Start time is before simulation file start time\n");
startCount = fst->getStartTime();
} else if (start_time.end)
} else if (start_time.end)
startCount = fst->getEndTime();
else {
startCount = start_time.time * pow10(start_time.scale - fst->getScale());
@ -2205,7 +2265,7 @@ struct SimWorker : SimShared
if (stop_time.time < fst->getStartTime())
log_warning("Stop time is before simulation file start time\n");
stopCount = fst->getStartTime();
} else if (stop_time.end)
} else if (stop_time.end)
stopCount = fst->getEndTime();
else {
stopCount = stop_time.time * pow10(stop_time.scale - fst->getScale());
@ -2220,7 +2280,7 @@ struct SimWorker : SimShared
int cycle = 0;
log("Generate testbench data from %lu%s to %lu%s", (unsigned long)startCount, fst->getTimescaleString(), (unsigned long)stopCount, fst->getTimescaleString());
if (cycles_set)
if (cycles_set)
log(" for %d clock cycle(s)",numcycles);
log("\n");
@ -2291,22 +2351,22 @@ struct SimWorker : SimShared
f << initstate.str();
f << stringf("\t\t$readmemb(\"%s.txt\", data);\n",tb_filename);
f << stringf("\t\t#(data[0][%d:%d]);\n", data_len-32, data_len-1);
f << stringf("\t\t{%s } = data[0][%d:%d];\n", signal_list(clocks), 0, clk_len-1);
f << stringf("\t\t#(data[0][%d:%d]);\n", data_len-32, data_len-1);
f << stringf("\t\t{%s } = data[0][%d:%d];\n", signal_list(clocks), 0, clk_len-1);
f << stringf("\t\t{%s } <= data[0][%d:%d];\n", signal_list(inputs), clk_len, clk_len+inputs_len-1);
f << stringf("\t\tfor (i = 1; i < %d; i++) begin\n",cycle);
f << stringf("\t\t\t#(data[i][%d:%d]);\n", data_len-32, data_len-1);
f << stringf("\t\t\t{%s } = data[i][%d:%d];\n", signal_list(clocks), 0, clk_len-1);
f << stringf("\t\t\t#(data[i][%d:%d]);\n", data_len-32, data_len-1);
f << stringf("\t\t\t{%s } = data[i][%d:%d];\n", signal_list(clocks), 0, clk_len-1);
f << stringf("\t\t\t{%s } <= data[i][%d:%d];\n", signal_list(inputs), clk_len, clk_len+inputs_len-1);
f << stringf("\t\t\tif ({%s } != data[i-1][%d:%d]) begin\n", signal_list(outputs), clk_len+inputs_len, clk_len+inputs_len+outputs_len-1);
f << "\t\t\t\t$error(\"Signal difference detected\\n\");\n";
f << "\t\t\tend\n";
f << "\t\tend\n";
f << "\t\t$finish;\n";
f << "\tend\n";
f << "endmodule\n";
@ -2423,7 +2483,7 @@ struct FSTWriter : public OutputWriter
fstWriterSetPackType(fstfile, FST_WR_PT_FASTLZ);
fstWriterSetRepackOnClose(fstfile, 1);
worker->top->write_output_header(
[this](IdString name) { fstWriterSetScope(fstfile, FST_ST_VCD_MODULE, stringf("%s",name.unescape()).c_str(), nullptr); },
[this]() { fstWriterSetUpscope(fstfile); },
@ -2572,7 +2632,7 @@ struct AIWWriter : public OutputWriter
aiwfile << '0';
}
aiwfile << '\n';
}
}
}
std::ofstream aiwfile;
@ -2651,8 +2711,15 @@ struct SimPass : public Pass {
log(" -r <filename>\n");
log(" read simulation or formal results file\n");
log(" File formats supported: FST, VCD, AIW, WIT and .yw\n");
log(" Yosys witness (.yw) replay is preferred when possible.\n");
log(" VCD support requires vcd2fst external tool to be present\n");
log("\n");
log(" -no-undriven-check\n");
log(" skip undriven-signal checks for FST/VCD replay (can be expensive for large designs)\n");
log("\n");
log(" -undriven-warn\n");
log(" downgrade undriven-signal replay errors to warnings\n");
log("\n");
log(" -width <integer>\n");
log(" cycle width in generated simulation output (must be divisible by 2).\n");
log("\n");
@ -2870,6 +2937,14 @@ struct SimPass : public Pass {
worker.fst_noinit = true;
continue;
}
if (args[argidx] == "-no-undriven-check") {
worker.undriven_check = false;
continue;
}
if (args[argidx] == "-undriven-warn") {
worker.undriven_warning = true;
continue;
}
if (args[argidx] == "-x") {
worker.ignore_x = true;
continue;
@ -2963,7 +3038,7 @@ struct Fst2TbPass : public Pass {
log("\n");
log(" -n <integer>\n");
log(" number of clock cycles to simulate (default: 20)\n");
log("\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override

View file

@ -159,7 +159,7 @@ void SynthPropWorker::run()
if (tracing_data[module].names.size() == 0) return;
if (!reset_name.empty()) {
int width = tracing_data[module].names.size();
int width = tracing_data[module].names.size();
SigSpec reset = module->wire(reset_name);
reset.extend_u0(width, true);

View file

@ -398,7 +398,7 @@ struct Abc9Pass : public ScriptPass
log_error("Can't handle partially selected module %s!\n", mod);
std::string tempdir_name;
if (cleanup)
if (cleanup)
tempdir_name = get_base_tmpdir() + "/";
else
tempdir_name = "_tmp_";

View file

@ -135,7 +135,7 @@ struct AbcNewPass : public ScriptPass {
void script() override
{
if (check_label("check")) {
run("abc9_ops -check");
run("abc9_ops -check");
}
if (check_label("prep_boxes")) {

View file

@ -487,4 +487,4 @@ struct ArithTreePass : public Pass {
}
} ArithTreePass;
PRIVATE_NAMESPACE_END
PRIVATE_NAMESPACE_END

View file

@ -260,7 +260,7 @@ struct BoothPassWorker {
y_sz_revised = y_sz + 1;
} else {
x_sz_revised = y_sz;
}
}
} else {
if (x_sz % 2 != 0) {
y_sz_revised = x_sz + 1;
@ -804,7 +804,7 @@ struct BoothPassWorker {
c_result = c_wire;
debug_csa_trees[column_ix].push_back(csa);
csa_ix++;
csa_ix++;
if (var_ix <= column_bits.size() - 1)
carry_bits_to_sum.append(c_wire);

View file

@ -139,7 +139,7 @@ struct DffLegalizePass : public Pass {
}
// Table of all supported cell types.
// First index in the array is one of the FF_* values, second
// First index in the array is one of the FF_* values, second
// index is the set of negative-polarity inputs (OR of NEG_*
// values), and the value is the set of supported init values
// (OR of INIT_* values).
@ -370,10 +370,16 @@ struct DffLegalizePass : public Pass {
else
fail_ff(ff, "initialized dffs with async set and reset are not supported");
} else {
if (!supported_cells[FF_DLATCHSR])
fail_ff(ff, "dlatch with async set and reset are not supported");
else
fail_ff(ff, "initialized dlatch with async set and reset are not supported");
if (!supported_dlatch) {
if (!supported_cells[FF_DLATCHSR])
fail_ff(ff, "dlatch with async set and reset are not supported");
else
fail_ff(ff, "initialized dlatch with async set and reset are not supported");
}
if (ff.cell)
log_warning("Emulating async set + reset latch with a plain D latch and logic for %s.%s\n", ff.module->name.unescape(), ff.cell->name.unescape());
emulate_dlatch(ff);
return;
}
}
@ -449,6 +455,51 @@ struct DffLegalizePass : public Pass {
legalize_ff(ff_sel);
}
void emulate_dlatch(FfData &ff) {
// emulate adlatch or dlatchsr
log_assert(!ff.has_clk);
log_assert(ff.has_aload);
log_assert(ff.width == 1);
auto active_high = [&](SigBit sig, bool pol) -> SigBit {
if (pol)
return sig;
return ff.is_fine ? ff.module->NotGate(NEW_ID, sig) : ff.module->Not(NEW_ID, sig)[0];
};
auto do_mux = [&](SigBit a, SigBit b, SigBit s) -> SigBit {
return ff.is_fine ? ff.module->MuxGate(NEW_ID, a, b, s) : ff.module->Mux(NEW_ID, a, b, s)[0];
};
auto do_or = [&](SigBit a, SigBit b) -> SigBit {
return ff.is_fine ? ff.module->OrGate(NEW_ID, a, b) : ff.module->Or(NEW_ID, a, b)[0];
};
SigBit en = active_high(ff.sig_aload, ff.pol_aload);
SigBit d = ff.sig_ad;
if (ff.has_sr) {
SigBit set = active_high(ff.sig_set[0], ff.pol_set);
SigBit clr = active_high(ff.sig_clr[0], ff.pol_clr);
// clr > set > load > hold
d = do_mux(d, State::S1, set);
d = do_mux(d, State::S0, clr);
en = do_or(en, do_or(set, clr));
ff.has_sr = false;
}
if (ff.has_arst) {
SigBit arst = active_high(ff.sig_arst[0], ff.pol_arst);
d = do_mux(d, ff.val_arst[0], arst);
en = do_or(en, arst);
ff.has_arst = false;
}
ff.sig_ad = d;
ff.sig_aload = en;
ff.pol_aload = true;
legalize_dlatch(ff);
}
void legalize_dff(FfData &ff) {
if (!try_flip(ff, supported_dff)) {
if (!supported_dff)
@ -742,8 +793,14 @@ struct DffLegalizePass : public Pass {
void legalize_adlatch(FfData &ff) {
if (!try_flip(ff, supported_adlatch)) {
if (!supported_adlatch)
fail_ff(ff, "D latches with async set or reset are not supported");
if (!supported_adlatch) {
if (!supported_dlatch)
fail_ff(ff, "D latches with async set or reset are not supported");
if (ff.cell)
log_warning("Emulating async reset latch with a plain D latch and logic for %s.%s\n", ff.module->name.unescape(), ff.cell->name.unescape());
emulate_dlatch(ff);
return;
}
if (!(supported_dlatch & (INIT_0 | INIT_1)))
fail_ff(ff, "initialized D latches are not supported");

View file

@ -326,12 +326,12 @@ int counter_tryextract(
return 24;
//Mux should have A driven by count Q, and B by muxy
//if A and B are swapped, CE polarity is inverted
if(sigmap(cemux->getPort(ID::B)) == muxy &&
if(sigmap(cemux->getPort(ID::B)) == muxy &&
sigmap(cemux->getPort(ID::A)) == sigmap(count_reg->getPort(ID::Q)))
{
extract.ce_inverted = false;
}
else if(sigmap(cemux->getPort(ID::A)) == muxy &&
else if(sigmap(cemux->getPort(ID::A)) == muxy &&
sigmap(cemux->getPort(ID::B)) == sigmap(count_reg->getPort(ID::Q)))
{
extract.ce_inverted = true;

View file

@ -1,4 +1,3 @@
#define FILTERLIB
#include "libparse.cc"

View file

@ -142,4 +142,4 @@ inline std::string convert_liberty_files_to_merged_scl(const std::vector<std::st
YOSYS_NAMESPACE_END
#endif // LIBERTY_CACHE_H
#endif // LIBERTY_CACHE_H

View file

@ -652,7 +652,7 @@ LibertyAst *LibertyParser::parse(bool top_level)
return NULL;
if (tok != 'v') {
report_unexpected_token(tok);
report_unexpected_token(tok);
}
LibertyAst *ast = new LibertyAst;
@ -662,7 +662,7 @@ LibertyAst *LibertyParser::parse(bool top_level)
{
tok = lexer(str);
// allow both ';' and new lines to
// allow both ';' and new lines to
// terminate a statement.
if ((tok == ';') || (tok == 'n'))
break;
@ -1286,4 +1286,3 @@ int main(int argc, char **argv)
}
#endif

View file

@ -33,7 +33,7 @@ int lut2mux(Cell *cell, bool word_mode)
if (GetSize(sig_a) == 1)
{
if (!word_mode)
cell->module->addMuxGate(NEW_ID, lut.extract(0)[0], lut.extract(1)[0], sig_a, sig_y);
cell->module->addMuxGate(NEW_ID, lut.extract(0)[0], lut.extract(1)[0], sig_a, sig_y);
else
cell->module->addMux(NEW_ID, lut.extract(0)[0], lut.extract(1)[0], sig_a, sig_y);
}
@ -47,11 +47,11 @@ int lut2mux(Cell *cell, bool word_mode)
Const lut1 = lut.extract(0, GetSize(lut)/2);
Const lut2 = lut.extract(GetSize(lut)/2, GetSize(lut)/2);
count += lut2mux(cell->module->addLut(NEW_ID, sig_a_lo, sig_y1, lut1), word_mode);
count += lut2mux(cell->module->addLut(NEW_ID, sig_a_lo, sig_y1, lut1), word_mode);
count += lut2mux(cell->module->addLut(NEW_ID, sig_a_lo, sig_y2, lut2), word_mode);
if (!word_mode)
cell->module->addMuxGate(NEW_ID, sig_y1, sig_y2, sig_a_hi, sig_y);
cell->module->addMuxGate(NEW_ID, sig_y1, sig_y2, sig_a_hi, sig_y);
else
cell->module->addMux(NEW_ID, sig_y1, sig_y2, sig_a_hi, sig_y);
}

View file

@ -333,7 +333,7 @@ struct TechmapWorker
RTLIL::Cell *c = module->addCell(c_name, tpl_cell);
design->select(module, c);
if (c->type == ID::_TECHMAP_PLACEHOLDER_ && tpl_cell->has_attribute(ID::techmap_chtype)) {
c->type = RTLIL::escape_id(tpl_cell->get_string_attribute(ID::techmap_chtype));
c->attributes.erase(ID::techmap_chtype);

View file

@ -389,4 +389,3 @@ struct TestAutotbBackend : public Backend {
} TestAutotbBackend;
PRIVATE_NAMESPACE_END