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:
commit
f5809a7c2c
675 changed files with 10003 additions and 8149 deletions
|
|
@ -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++;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -401,4 +401,3 @@ struct DesignPass : public Pass {
|
|||
} DesignPass;
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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]");
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)))
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -296,7 +296,7 @@ struct OptLutWorker
|
|||
luts_dlogic_inputs.erase(lut);
|
||||
|
||||
module->remove(lut);
|
||||
|
||||
|
||||
eliminated_count++;
|
||||
if (limit > 0)
|
||||
limit--;
|
||||
|
|
|
|||
|
|
@ -282,4 +282,3 @@ struct OptLutInsPass : public Pass {
|
|||
} OptLutInsPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
|
||||
|
|
|
|||
|
|
@ -347,4 +347,3 @@ struct OptMemFeedbackPass : public Pass {
|
|||
} OptMemFeedbackPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
|
||||
|
|
|
|||
|
|
@ -106,4 +106,3 @@ struct OptMemPriorityPass : public Pass {
|
|||
} OptMemPriorityPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -650,4 +650,3 @@ struct WreducePass : public Pass {
|
|||
} WreducePass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -117,4 +117,3 @@ struct ProcMemWrPass : public Pass {
|
|||
} ProcMemWrPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -82,4 +82,3 @@ always @(posedge clk)
|
|||
assign y = counter == 12;
|
||||
|
||||
endmodule
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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_";
|
||||
|
|
|
|||
|
|
@ -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")) {
|
||||
|
|
|
|||
|
|
@ -487,4 +487,4 @@ struct ArithTreePass : public Pass {
|
|||
}
|
||||
} ArithTreePass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
PRIVATE_NAMESPACE_END
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
#define FILTERLIB
|
||||
#include "libparse.cc"
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -389,4 +389,3 @@ struct TestAutotbBackend : public Backend {
|
|||
} TestAutotbBackend;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue