mirror of
https://github.com/YosysHQ/yosys
synced 2025-08-07 11:41:23 +00:00
Merge branch 'master' into xaig
This commit is contained in:
commit
bca3cf6843
115 changed files with 5852 additions and 720 deletions
|
@ -24,7 +24,7 @@
|
|||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
static void rename_in_module(RTLIL::Module *module, std::string from_name, std::string to_name)
|
||||
static void rename_in_module(RTLIL::Module *module, std::string from_name, std::string to_name, bool flag_output)
|
||||
{
|
||||
from_name = RTLIL::escape_id(from_name);
|
||||
to_name = RTLIL::escape_id(to_name);
|
||||
|
@ -37,13 +37,18 @@ static void rename_in_module(RTLIL::Module *module, std::string from_name, std::
|
|||
Wire *w = it.second;
|
||||
log("Renaming wire %s to %s in module %s.\n", log_id(w), log_id(to_name), log_id(module));
|
||||
module->rename(w, to_name);
|
||||
if (w->port_id)
|
||||
if (w->port_id || flag_output) {
|
||||
if (flag_output)
|
||||
w->port_output = true;
|
||||
module->fixup_ports();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto &it : module->cells_)
|
||||
if (it.first == from_name) {
|
||||
if (flag_output)
|
||||
log_cmd_error("Called with -output but the specified object is a cell.\n");
|
||||
log("Renaming cell %s to %s in module %s.\n", log_id(it.second), log_id(to_name), log_id(module));
|
||||
module->rename(it.second, to_name);
|
||||
return;
|
||||
|
@ -108,15 +113,26 @@ struct RenamePass : public Pass {
|
|||
log("Rename the specified object. Note that selection patterns are not supported\n");
|
||||
log("by this command.\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log(" rename -output old_name new_name\n");
|
||||
log("\n");
|
||||
log("Like above, but also make the wire an output. This will fail if the object is\n");
|
||||
log("not a wire.\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log(" rename -src [selection]\n");
|
||||
log("\n");
|
||||
log("Assign names auto-generated from the src attribute to all selected wires and\n");
|
||||
log("cells with private names.\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log(" rename -wire [selection]\n");
|
||||
log("\n");
|
||||
log("Assign auto-generated names based on the wires they drive to all selected\n");
|
||||
log("cells with private names. Ignores cells driving privatly named wires.\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log(" rename -enumerate [-pattern <pattern>] [selection]\n");
|
||||
log("\n");
|
||||
log("Assign short auto-generated names to all selected wires and cells with private\n");
|
||||
|
@ -124,11 +140,13 @@ struct RenamePass : public Pass {
|
|||
log("The character %% in the pattern is replaced with a integer number. The default\n");
|
||||
log("pattern is '_%%_'.\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log(" rename -hide [selection]\n");
|
||||
log("\n");
|
||||
log("Assign private names (the ones with $-prefix) to all selected wires and cells\n");
|
||||
log("with public names. This ignores all selected ports.\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log(" rename -top new_name\n");
|
||||
log("\n");
|
||||
log("Rename top module.\n");
|
||||
|
@ -142,6 +160,7 @@ struct RenamePass : public Pass {
|
|||
bool flag_enumerate = false;
|
||||
bool flag_hide = false;
|
||||
bool flag_top = false;
|
||||
bool flag_output = false;
|
||||
bool got_mode = false;
|
||||
|
||||
size_t argidx;
|
||||
|
@ -153,6 +172,11 @@ struct RenamePass : public Pass {
|
|||
got_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-output" && !got_mode) {
|
||||
flag_output = true;
|
||||
got_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-wire" && !got_mode) {
|
||||
flag_wire = true;
|
||||
got_mode = true;
|
||||
|
@ -322,10 +346,12 @@ struct RenamePass : public Pass {
|
|||
if (!design->selected_active_module.empty())
|
||||
{
|
||||
if (design->modules_.count(design->selected_active_module) > 0)
|
||||
rename_in_module(design->modules_.at(design->selected_active_module), from_name, to_name);
|
||||
rename_in_module(design->modules_.at(design->selected_active_module), from_name, to_name, flag_output);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (flag_output)
|
||||
log_cmd_error("Mode -output requires that there is an active module selected.\n");
|
||||
for (auto &mod : design->modules_) {
|
||||
if (mod.first == from_name || RTLIL::unescape_id(mod.first) == from_name) {
|
||||
to_name = RTLIL::escape_id(to_name);
|
||||
|
|
|
@ -910,7 +910,7 @@ struct HierarchyPass : public Pass {
|
|||
if (m == nullptr)
|
||||
continue;
|
||||
|
||||
if (m->get_bool_attribute("\\blackbox") && !cell->parameters.empty()) {
|
||||
if (m->get_bool_attribute("\\blackbox") && !cell->parameters.empty() && m->get_bool_attribute("\\dynports")) {
|
||||
IdString new_m_name = m->derive(design, cell->parameters, true);
|
||||
if (new_m_name.empty())
|
||||
continue;
|
||||
|
|
|
@ -87,6 +87,8 @@ struct UniquifyPass : public Pass {
|
|||
smod->name = newname;
|
||||
cell->type = newname;
|
||||
smod->set_bool_attribute("\\unique");
|
||||
if (smod->attributes.count("\\hdlname") == 0)
|
||||
smod->attributes["\\hdlname"] = string(log_id(tmod->name));
|
||||
design->add(smod);
|
||||
|
||||
did_something = true;
|
||||
|
|
|
@ -542,7 +542,7 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
|
|||
}
|
||||
|
||||
// assign write ports
|
||||
|
||||
pair<SigBit, bool> wr_clkdom;
|
||||
for (int cell_port_i = 0, bram_port_i = 0; cell_port_i < wr_ports; cell_port_i++)
|
||||
{
|
||||
bool clken = wr_clken[cell_port_i] == State::S1;
|
||||
|
@ -552,7 +552,7 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
|
|||
pair<SigBit, bool> clkdom(clksig, clkpol);
|
||||
if (!clken)
|
||||
clkdom = pair<SigBit, bool>(State::S1, false);
|
||||
|
||||
wr_clkdom = clkdom;
|
||||
log(" Write port #%d is in clock domain %s%s.\n",
|
||||
cell_port_i, clkdom.second ? "" : "!",
|
||||
clken ? log_signal(clkdom.first) : "~async~");
|
||||
|
@ -641,6 +641,7 @@ grow_read_ports:;
|
|||
pi.sig_data = SigSpec();
|
||||
pi.sig_en = SigSpec();
|
||||
pi.make_outreg = false;
|
||||
pi.make_transp = false;
|
||||
}
|
||||
new_portinfos.push_back(pi);
|
||||
if (pi.dupidx == dup_count-1) {
|
||||
|
@ -718,7 +719,13 @@ grow_read_ports:;
|
|||
if (read_transp.count(pi.transp) && read_transp.at(pi.transp) != transp) {
|
||||
if (match.make_transp && wr_ports <= 1) {
|
||||
pi.make_transp = true;
|
||||
enable_make_transp = true;
|
||||
if (pi.clocks != 0) {
|
||||
if (wr_ports == 1 && wr_clkdom != clkdom) {
|
||||
log(" Bram port %c%d.%d cannot have soft transparency logic added as read and write clock domains differ.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
|
||||
goto skip_bram_rport;
|
||||
}
|
||||
enable_make_transp = true;
|
||||
}
|
||||
} else {
|
||||
log(" Bram port %c%d.%d has incompatible read transparency.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
|
||||
goto skip_bram_rport;
|
||||
|
@ -737,7 +744,8 @@ grow_read_ports:;
|
|||
if (clken) {
|
||||
clock_domains[pi.clocks] = clkdom;
|
||||
clock_polarities[pi.clkpol] = clkdom.second;
|
||||
read_transp[pi.transp] = transp;
|
||||
if (!pi.make_transp)
|
||||
read_transp[pi.transp] = transp;
|
||||
pi.sig_clock = clkdom.first;
|
||||
pi.sig_en = rd_en[cell_port_i];
|
||||
pi.effective_clkpol = clkdom.second;
|
||||
|
@ -913,17 +921,18 @@ grow_read_ports:;
|
|||
} else {
|
||||
SigSpec bram_dout = module->addWire(NEW_ID, bram.dbits);
|
||||
c->setPort(stringf("\\%sDATA", pf), bram_dout);
|
||||
|
||||
if (pi.make_outreg) {
|
||||
if (pi.make_outreg && pi.make_transp) {
|
||||
log(" Moving output register to address for transparent port %c%d.%d.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
|
||||
SigSpec sig_addr_q = module->addWire(NEW_ID, bram.abits);
|
||||
module->addDff(NEW_ID, pi.sig_clock, sig_addr, sig_addr_q, pi.effective_clkpol);
|
||||
c->setPort(stringf("\\%sADDR", pf), sig_addr_q);
|
||||
} else if (pi.make_outreg) {
|
||||
SigSpec bram_dout_q = module->addWire(NEW_ID, bram.dbits);
|
||||
if (!pi.sig_en.empty())
|
||||
bram_dout = module->Mux(NEW_ID, bram_dout_q, bram_dout, pi.sig_en);
|
||||
module->addDff(NEW_ID, pi.sig_clock, bram_dout, bram_dout_q, pi.effective_clkpol);
|
||||
bram_dout = bram_dout_q;
|
||||
}
|
||||
|
||||
if (pi.make_transp)
|
||||
{
|
||||
} else if (pi.make_transp) {
|
||||
log(" Adding extra logic for transparent port %c%d.%d.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
|
||||
|
||||
SigSpec transp_en_d = module->Mux(NEW_ID, SigSpec(0, make_transp_enbits),
|
||||
|
@ -949,6 +958,8 @@ grow_read_ports:;
|
|||
SigSpec addr_ok_q = addr_ok;
|
||||
if ((pi.clocks || pi.make_outreg) && !addr_ok.empty()) {
|
||||
addr_ok_q = module->addWire(NEW_ID);
|
||||
if (!pi.sig_en.empty())
|
||||
addr_ok = module->Mux(NEW_ID, addr_ok_q, addr_ok, pi.sig_en);
|
||||
module->addDff(NEW_ID, pi.sig_clock, addr_ok, addr_ok_q, pi.effective_clkpol);
|
||||
}
|
||||
|
||||
|
|
|
@ -155,6 +155,13 @@ bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutativ
|
|||
new_b.append_bit(it.first.second);
|
||||
}
|
||||
|
||||
if (cell->type.in("$and", "$or") && i == GRP_CONST_A) {
|
||||
log(" Direct Connection: %s (%s with %s)\n", log_signal(new_b), log_id(cell->type), log_signal(new_a));
|
||||
module->connect(new_y, new_b);
|
||||
module->connect(new_conn);
|
||||
continue;
|
||||
}
|
||||
|
||||
RTLIL::Cell *c = module->addCell(NEW_ID, cell->type);
|
||||
|
||||
c->setPort("\\A", new_a);
|
||||
|
|
|
@ -53,6 +53,8 @@ struct WreduceWorker
|
|||
std::set<Cell*, IdString::compare_ptr_by_name<Cell>> work_queue_cells;
|
||||
std::set<SigBit> work_queue_bits;
|
||||
pool<SigBit> keep_bits;
|
||||
dict<SigBit, State> init_bits;
|
||||
pool<SigBit> remove_init_bits;
|
||||
|
||||
WreduceWorker(WreduceConfig *config, Module *module) :
|
||||
config(config), module(module), mi(module) { }
|
||||
|
@ -141,6 +143,7 @@ struct WreduceWorker
|
|||
|
||||
SigSpec sig_d = mi.sigmap(cell->getPort("\\D"));
|
||||
SigSpec sig_q = mi.sigmap(cell->getPort("\\Q"));
|
||||
Const initval;
|
||||
|
||||
int width_before = GetSize(sig_q);
|
||||
|
||||
|
@ -150,24 +153,35 @@ struct WreduceWorker
|
|||
bool zero_ext = sig_d[GetSize(sig_d)-1] == State::S0;
|
||||
bool sign_ext = !zero_ext;
|
||||
|
||||
for (int i = 0; i < GetSize(sig_q); i++) {
|
||||
SigBit bit = sig_q[i];
|
||||
if (init_bits.count(bit))
|
||||
initval.bits.push_back(init_bits.at(bit));
|
||||
else
|
||||
initval.bits.push_back(State::Sx);
|
||||
}
|
||||
|
||||
for (int i = GetSize(sig_q)-1; i >= 0; i--)
|
||||
{
|
||||
if (zero_ext && sig_d[i] == State::S0) {
|
||||
if (zero_ext && sig_d[i] == State::S0 && (initval[i] == State::S0 || initval[i] == State::Sx)) {
|
||||
module->connect(sig_q[i], State::S0);
|
||||
remove_init_bits.insert(sig_q[i]);
|
||||
sig_d.remove(i);
|
||||
sig_q.remove(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sign_ext && i > 0 && sig_d[i] == sig_d[i-1]) {
|
||||
if (sign_ext && i > 0 && sig_d[i] == sig_d[i-1] && initval[i] == initval[i-1]) {
|
||||
module->connect(sig_q[i], sig_q[i-1]);
|
||||
remove_init_bits.insert(sig_q[i]);
|
||||
sig_d.remove(i);
|
||||
sig_q.remove(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto info = mi.query(sig_q[i]);
|
||||
if (!info->is_output && GetSize(info->ports) <= 1 && !keep_bits.count(mi.sigmap(sig_q[i]))) {
|
||||
if (!info->is_output && GetSize(info->ports) == 1 && !keep_bits.count(mi.sigmap(sig_q[i]))) {
|
||||
remove_init_bits.insert(sig_q[i]);
|
||||
sig_d.remove(i);
|
||||
sig_q.remove(i);
|
||||
zero_ext = false;
|
||||
|
@ -183,10 +197,11 @@ struct WreduceWorker
|
|||
|
||||
if (GetSize(sig_q) == 0) {
|
||||
log("Removed cell %s.%s (%s).\n", log_id(module), log_id(cell), log_id(cell->type));
|
||||
module->remove(cell);
|
||||
return;
|
||||
}
|
||||
|
||||
log("Removed top %d bits (of %d) from mux cell %s.%s (%s).\n", width_before - GetSize(sig_q), width_before,
|
||||
log("Removed top %d bits (of %d) from FF cell %s.%s (%s).\n", width_before - GetSize(sig_q), width_before,
|
||||
log_id(module), log_id(cell), log_id(cell->type));
|
||||
|
||||
for (auto bit : sig_d)
|
||||
|
@ -376,10 +391,21 @@ struct WreduceWorker
|
|||
|
||||
void run()
|
||||
{
|
||||
for (auto w : module->wires())
|
||||
// create a copy as mi.sigmap will be updated as we process the module
|
||||
SigMap init_attr_sigmap = mi.sigmap;
|
||||
|
||||
for (auto w : module->wires()) {
|
||||
if (w->get_bool_attribute("\\keep"))
|
||||
for (auto bit : mi.sigmap(w))
|
||||
keep_bits.insert(bit);
|
||||
if (w->attributes.count("\\init")) {
|
||||
Const initval = w->attributes.at("\\init");
|
||||
SigSpec initsig = init_attr_sigmap(w);
|
||||
int width = std::min(GetSize(initval), GetSize(initsig));
|
||||
for (int i = 0; i < width; i++)
|
||||
init_bits[initsig[i]] = initval[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (auto c : module->selected_cells())
|
||||
work_queue_cells.insert(c);
|
||||
|
@ -427,6 +453,24 @@ struct WreduceWorker
|
|||
module->connect(nw, SigSpec(w).extract(0, GetSize(nw)));
|
||||
module->swap_names(w, nw);
|
||||
}
|
||||
|
||||
if (!remove_init_bits.empty()) {
|
||||
for (auto w : module->wires()) {
|
||||
if (w->attributes.count("\\init")) {
|
||||
Const initval = w->attributes.at("\\init");
|
||||
Const new_initval(State::Sx, GetSize(w));
|
||||
SigSpec initsig = init_attr_sigmap(w);
|
||||
int width = std::min(GetSize(initval), GetSize(initsig));
|
||||
for (int i = 0; i < width; i++) {
|
||||
log_dump(initsig[i], remove_init_bits.count(initsig[i]));
|
||||
if (!remove_init_bits.count(initsig[i]))
|
||||
new_initval[i] = initval[i];
|
||||
}
|
||||
w->attributes.at("\\init") = new_initval;
|
||||
log_dump(w->name, initval, new_initval);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -4,5 +4,5 @@ passes/pmgen/ice40_dsp.o: passes/pmgen/ice40_dsp_pm.h
|
|||
EXTRA_OBJS += passes/pmgen/ice40_dsp_pm.h
|
||||
.SECONDARY: passes/pmgen/ice40_dsp_pm.h
|
||||
|
||||
passes/pmgen/ice40_dsp_pm.h: passes/pmgen/ice40_dsp.pmg passes/pmgen/pmgen.py
|
||||
$(P) cd passes/pmgen && python3 pmgen.py ice40_dsp
|
||||
passes/pmgen/ice40_dsp_pm.h: passes/pmgen/pmgen.py passes/pmgen/ice40_dsp.pmg
|
||||
$(P) mkdir -p passes/pmgen && python3 $^ $@
|
||||
|
|
|
@ -16,7 +16,7 @@ API of Generated Matcher
|
|||
========================
|
||||
|
||||
When `pmgen.py` reads a `foobar.pmg` file, it writes `foobar_pm.h` containing
|
||||
a class `foobar_pm`. That class is instanciated with an RTLIL module and a
|
||||
a class `foobar_pm`. That class is instantiated with an RTLIL module and a
|
||||
list of cells from that module:
|
||||
|
||||
foobar_pm pm(module, module->selected_cells());
|
||||
|
@ -142,7 +142,7 @@ The `select` lines are evaluated once for each cell when the matcher is
|
|||
initialized. A `match` block will only consider cells for which all `select`
|
||||
expressions evaluated to `true`. Note that the state variable corresponding to
|
||||
the match (in the example `mul`) is the only state variable that may be used
|
||||
`select` lines.
|
||||
in `select` lines.
|
||||
|
||||
Index lines are using the `index <type> expr1 === expr2` syntax. `expr1` is
|
||||
evaluated during matcher initialization and the same restrictions apply as for
|
||||
|
|
|
@ -6,7 +6,11 @@ import pprint
|
|||
|
||||
pp = pprint.PrettyPrinter(indent=4)
|
||||
|
||||
prefix = sys.argv[1]
|
||||
pmgfile = sys.argv[1]
|
||||
assert pmgfile.endswith(".pmg")
|
||||
prefix = pmgfile[0:-4]
|
||||
prefix = prefix.split('/')[-1]
|
||||
outfile = sys.argv[2]
|
||||
|
||||
state_types = dict()
|
||||
udata_types = dict()
|
||||
|
@ -73,7 +77,7 @@ def rewrite_cpp(s):
|
|||
|
||||
return "".join(t)
|
||||
|
||||
with open("%s.pmg" % prefix, "r") as f:
|
||||
with open(pmgfile, "r") as f:
|
||||
while True:
|
||||
line = f.readline()
|
||||
if line == "": break
|
||||
|
@ -82,7 +86,7 @@ with open("%s.pmg" % prefix, "r") as f:
|
|||
cmd = line.split()
|
||||
if len(cmd) == 0 or cmd[0].startswith("//"): continue
|
||||
cmd = cmd[0]
|
||||
|
||||
|
||||
if cmd == "state":
|
||||
m = re.match(r"^state\s+<(.*?)>\s+(([A-Za-z_][A-Za-z_0-9]*\s+)*[A-Za-z_][A-Za-z_0-9]*)\s*$", line)
|
||||
assert m
|
||||
|
@ -176,7 +180,7 @@ with open("%s.pmg" % prefix, "r") as f:
|
|||
|
||||
blocks.append(block)
|
||||
|
||||
with open("%s_pm.h" % prefix, "w") as f:
|
||||
with open(outfile, "w") as f:
|
||||
print("// Generated by pmgen.py from {}.pgm".format(prefix), file=f)
|
||||
print("", file=f)
|
||||
|
||||
|
|
|
@ -340,6 +340,7 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, d
|
|||
// evaluate in reverse order to give the first entry the top priority
|
||||
RTLIL::SigSpec initial_val = result;
|
||||
RTLIL::Cell *last_mux_cell = NULL;
|
||||
bool shiftx = initial_val.is_fully_undef();
|
||||
for (size_t i = 0; i < sw->cases.size(); i++) {
|
||||
int case_idx = sw->cases.size() - i - 1;
|
||||
RTLIL::CaseRule *cs2 = sw->cases[case_idx];
|
||||
|
@ -348,6 +349,33 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, d
|
|||
append_pmux(mod, sw->signal, cs2->compare, value, last_mux_cell, sw, ifxmode);
|
||||
else
|
||||
result = gen_mux(mod, sw->signal, cs2->compare, value, result, last_mux_cell, sw, ifxmode);
|
||||
|
||||
// Ignore output values which are entirely don't care
|
||||
if (shiftx && !value.is_fully_undef()) {
|
||||
// Keep checking if case condition is the same as the current case index
|
||||
if (cs2->compare.size() == 1 && cs2->compare.front().is_fully_const())
|
||||
shiftx = (cs2->compare.front().as_int() == case_idx);
|
||||
else
|
||||
shiftx = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Transform into a $shiftx where possible
|
||||
if (shiftx && last_mux_cell && last_mux_cell->type == "$pmux") {
|
||||
// Create bit-blasted $shiftx-es that shifts by the address line used in the case statement
|
||||
auto pmux_b_port = last_mux_cell->getPort("\\B");
|
||||
auto pmux_y_port = last_mux_cell->getPort("\\Y");
|
||||
int width = last_mux_cell->getParam("\\WIDTH").as_int();
|
||||
for (int i = 0; i < width; ++i) {
|
||||
RTLIL::SigSpec a_port;
|
||||
// Because we went in reverse order above, un-reverse $pmux's B port here
|
||||
for (int j = pmux_b_port.size()/width-1; j >= 0; --j)
|
||||
a_port.append(pmux_b_port.extract(j*width+i, 1));
|
||||
// Create a $shiftx that shifts by the address line used in the case statement
|
||||
mod->addShiftx(NEW_ID, a_port, sw->signal, pmux_y_port.extract(i, 1));
|
||||
}
|
||||
// Disconnect $pmux by replacing its output port with a floating wire
|
||||
last_mux_cell->setPort("\\Y", mod->addWire(NEW_ID, width));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,4 +8,8 @@ OBJS += passes/sat/expose.o
|
|||
OBJS += passes/sat/assertpmux.o
|
||||
OBJS += passes/sat/clk2fflogic.o
|
||||
OBJS += passes/sat/async2sync.o
|
||||
OBJS += passes/sat/supercover.o
|
||||
OBJS += passes/sat/fmcombine.o
|
||||
OBJS += passes/sat/mutate.o
|
||||
OBJS += passes/sat/cutpoint.o
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ struct Async2syncPass : public Pass {
|
|||
log("reset value in the next cycle regardless of the data-in value at the time of\n");
|
||||
log("the clock edge.\n");
|
||||
log("\n");
|
||||
log("Currently only $adff cells are supported by this pass.\n");
|
||||
log("Currently only $adff and $dffsr cells are supported by this pass.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
|
@ -84,7 +84,7 @@ struct Async2syncPass : public Pass {
|
|||
bool arst_pol = cell->parameters["\\ARST_POLARITY"].as_bool();
|
||||
Const arst_val = cell->parameters["\\ARST_VALUE"];
|
||||
|
||||
SigSpec sig_clk = cell->getPort("\\CLK");
|
||||
// SigSpec sig_clk = cell->getPort("\\CLK");
|
||||
SigSpec sig_arst = cell->getPort("\\ARST");
|
||||
SigSpec sig_d = cell->getPort("\\D");
|
||||
SigSpec sig_q = cell->getPort("\\Q");
|
||||
|
@ -120,6 +120,55 @@ struct Async2syncPass : public Pass {
|
|||
cell->type = "$dff";
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type.in("$dffsr"))
|
||||
{
|
||||
// bool clk_pol = cell->parameters["\\CLK_POLARITY"].as_bool();
|
||||
bool set_pol = cell->parameters["\\SET_POLARITY"].as_bool();
|
||||
bool clr_pol = cell->parameters["\\CLR_POLARITY"].as_bool();
|
||||
|
||||
// SigSpec sig_clk = cell->getPort("\\CLK");
|
||||
SigSpec sig_set = cell->getPort("\\SET");
|
||||
SigSpec sig_clr = cell->getPort("\\CLR");
|
||||
SigSpec sig_d = cell->getPort("\\D");
|
||||
SigSpec sig_q = cell->getPort("\\Q");
|
||||
|
||||
log("Replacing %s.%s (%s): SET=%s, CLR=%s, D=%s, Q=%s\n",
|
||||
log_id(module), log_id(cell), log_id(cell->type),
|
||||
log_signal(sig_set), log_signal(sig_clr), log_signal(sig_d), log_signal(sig_q));
|
||||
|
||||
Const init_val;
|
||||
for (int i = 0; i < GetSize(sig_q); i++) {
|
||||
SigBit bit = sigmap(sig_q[i]);
|
||||
init_val.bits.push_back(initbits.count(bit) ? initbits.at(bit) : State::Sx);
|
||||
del_initbits.insert(bit);
|
||||
}
|
||||
|
||||
Wire *new_d = module->addWire(NEW_ID, GetSize(sig_d));
|
||||
Wire *new_q = module->addWire(NEW_ID, GetSize(sig_q));
|
||||
new_q->attributes["\\init"] = init_val;
|
||||
|
||||
if (!set_pol)
|
||||
sig_set = module->Not(NEW_ID, sig_set);
|
||||
|
||||
if (clr_pol)
|
||||
sig_clr = module->Not(NEW_ID, sig_clr);
|
||||
|
||||
SigSpec tmp = module->Or(NEW_ID, sig_d, sig_set);
|
||||
module->addAnd(NEW_ID, tmp, sig_clr, new_d);
|
||||
|
||||
tmp = module->Or(NEW_ID, new_q, sig_set);
|
||||
module->addAnd(NEW_ID, tmp, sig_clr, sig_q);
|
||||
|
||||
cell->setPort("\\D", new_d);
|
||||
cell->setPort("\\Q", new_q);
|
||||
cell->unsetPort("\\SET");
|
||||
cell->unsetPort("\\CLR");
|
||||
cell->unsetParam("\\SET_POLARITY");
|
||||
cell->unsetParam("\\CLR_POLARITY");
|
||||
cell->type = "$dff";
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto wire : module->wires())
|
||||
|
|
168
passes/sat/cutpoint.cc
Normal file
168
passes/sat/cutpoint.cc
Normal file
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/sigtools.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct CutpointPass : public Pass {
|
||||
CutpointPass() : Pass("cutpoint", "add hi/lo cover cells for each wire bit") { }
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" cutpoint [options] [selection]\n");
|
||||
log("\n");
|
||||
log("This command adds formal cut points to the design.\n");
|
||||
log("\n");
|
||||
log(" -undef\n");
|
||||
log(" set cupoint nets to undef (x). the default behavior is to create a\n");
|
||||
log(" $anyseq cell and drive the cutpoint net from that\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
bool flag_undef = false;
|
||||
|
||||
log_header(design, "Executing CUTPOINT pass.\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
if (args[argidx] == "-undef") {
|
||||
flag_undef = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
for (auto module : design->selected_modules())
|
||||
{
|
||||
if (design->selected_whole_module(module->name)) {
|
||||
log("Making all outputs of module %s cut points, removing module contents.\n", log_id(module));
|
||||
module->new_connections(std::vector<RTLIL::SigSig>());
|
||||
for (auto cell : vector<Cell*>(module->cells()))
|
||||
module->remove(cell);
|
||||
vector<Wire*> output_wires;
|
||||
for (auto wire : module->wires())
|
||||
if (wire->port_output)
|
||||
output_wires.push_back(wire);
|
||||
for (auto wire : output_wires)
|
||||
module->connect(wire, flag_undef ? Const(State::Sx, GetSize(wire)) : module->Anyseq(NEW_ID, GetSize(wire)));
|
||||
continue;
|
||||
}
|
||||
|
||||
SigMap sigmap(module);
|
||||
pool<SigBit> cutpoint_bits;
|
||||
|
||||
for (auto cell : module->selected_cells()) {
|
||||
if (cell->type == "$anyseq")
|
||||
continue;
|
||||
log("Removing cell %s.%s, making all cell outputs cutpoints.\n", log_id(module), log_id(cell));
|
||||
for (auto &conn : cell->connections()) {
|
||||
if (cell->output(conn.first))
|
||||
module->connect(conn.second, flag_undef ? Const(State::Sx, GetSize(conn.second)) : module->Anyseq(NEW_ID, GetSize(conn.second)));
|
||||
}
|
||||
module->remove(cell);
|
||||
}
|
||||
|
||||
for (auto wire : module->selected_wires()) {
|
||||
if (wire->port_output) {
|
||||
log("Making output wire %s.%s a cutpoint.\n", log_id(module), log_id(wire));
|
||||
Wire *new_wire = module->addWire(NEW_ID, wire);
|
||||
module->swap_names(wire, new_wire);
|
||||
module->connect(new_wire, flag_undef ? Const(State::Sx, GetSize(new_wire)) : module->Anyseq(NEW_ID, GetSize(new_wire)));
|
||||
wire->port_id = 0;
|
||||
wire->port_input = false;
|
||||
wire->port_output = false;
|
||||
continue;
|
||||
}
|
||||
log("Making wire %s.%s a cutpoint.\n", log_id(module), log_id(wire));
|
||||
for (auto bit : sigmap(wire))
|
||||
cutpoint_bits.insert(bit);
|
||||
}
|
||||
|
||||
if (!cutpoint_bits.empty())
|
||||
{
|
||||
for (auto cell : module->cells()) {
|
||||
for (auto &conn : cell->connections()) {
|
||||
if (!cell->output(conn.first))
|
||||
continue;
|
||||
SigSpec sig = sigmap(conn.second);
|
||||
int bit_count = 0;
|
||||
for (auto &bit : sig) {
|
||||
if (cutpoint_bits.count(bit))
|
||||
bit_count++;
|
||||
}
|
||||
if (bit_count == 0)
|
||||
continue;
|
||||
SigSpec dummy = module->addWire(NEW_ID, bit_count);
|
||||
bit_count = 0;
|
||||
for (auto &bit : sig) {
|
||||
if (cutpoint_bits.count(bit))
|
||||
bit = dummy[bit_count++];
|
||||
}
|
||||
cell->setPort(conn.first, sig);
|
||||
}
|
||||
}
|
||||
|
||||
vector<Wire*> rewrite_wires;
|
||||
for (auto wire : module->wires()) {
|
||||
if (!wire->port_input)
|
||||
continue;
|
||||
int bit_count = 0;
|
||||
for (auto &bit : sigmap(wire))
|
||||
if (cutpoint_bits.count(bit))
|
||||
bit_count++;
|
||||
if (bit_count)
|
||||
rewrite_wires.push_back(wire);
|
||||
}
|
||||
|
||||
for (auto wire : rewrite_wires) {
|
||||
Wire *new_wire = module->addWire(NEW_ID, wire);
|
||||
SigSpec lhs, rhs, sig = sigmap(wire);
|
||||
for (int i = 0; i < GetSize(sig); i++)
|
||||
if (!cutpoint_bits.count(sig[i])) {
|
||||
lhs.append(SigBit(wire, i));
|
||||
rhs.append(SigBit(new_wire, i));
|
||||
}
|
||||
if (GetSize(lhs))
|
||||
module->connect(lhs, rhs);
|
||||
module->swap_names(wire, new_wire);
|
||||
wire->port_id = 0;
|
||||
wire->port_input = false;
|
||||
wire->port_output = false;
|
||||
}
|
||||
|
||||
SigSpec sig(cutpoint_bits);
|
||||
sig.sort_and_unify();
|
||||
|
||||
for (auto chunk : sig.chunks()) {
|
||||
SigSpec s(chunk);
|
||||
module->connect(s, flag_undef ? Const(State::Sx, GetSize(s)) : module->Anyseq(NEW_ID, GetSize(s)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} CutpointPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
341
passes/sat/fmcombine.cc
Normal file
341
passes/sat/fmcombine.cc
Normal file
|
@ -0,0 +1,341 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/celltypes.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct opts_t
|
||||
{
|
||||
bool fwd = false;
|
||||
bool bwd = false;
|
||||
bool nop = false;
|
||||
};
|
||||
|
||||
struct FmcombineWorker
|
||||
{
|
||||
const opts_t &opts;
|
||||
Design *design;
|
||||
Module *original = nullptr;
|
||||
Module *module = nullptr;
|
||||
IdString orig_type, combined_type;
|
||||
|
||||
FmcombineWorker(Design *design, IdString orig_type, const opts_t &opts) :
|
||||
opts(opts), design(design), original(design->module(orig_type)),
|
||||
orig_type(orig_type), combined_type("$fmcombine" + orig_type.str())
|
||||
{
|
||||
}
|
||||
|
||||
SigSpec import_sig(SigSpec sig, const string &suffix)
|
||||
{
|
||||
SigSpec newsig;
|
||||
for (auto chunk : sig.chunks()) {
|
||||
if (chunk.wire != nullptr)
|
||||
chunk.wire = module->wire(chunk.wire->name.str() + suffix);
|
||||
newsig.append(chunk);
|
||||
}
|
||||
return newsig;
|
||||
}
|
||||
|
||||
void import_prim_cell(Cell *cell, const string &suffix)
|
||||
{
|
||||
Cell *c = module->addCell(cell->name.str() + suffix, cell->type);
|
||||
c->parameters = cell->parameters;
|
||||
c->attributes = cell->attributes;
|
||||
|
||||
for (auto &conn : cell->connections())
|
||||
c->setPort(conn.first, import_sig(conn.second, suffix));
|
||||
}
|
||||
|
||||
void import_hier_cell(Cell *cell)
|
||||
{
|
||||
if (!cell->parameters.empty())
|
||||
log_cmd_error("Cell %s.%s has unresolved instance parameters.\n", log_id(original), log_id(cell));
|
||||
|
||||
FmcombineWorker sub_worker(design, cell->type, opts);
|
||||
sub_worker.generate();
|
||||
|
||||
Cell *c = module->addCell(cell->name.str() + "_combined", sub_worker.combined_type);
|
||||
// c->parameters = cell->parameters;
|
||||
c->attributes = cell->attributes;
|
||||
|
||||
for (auto &conn : cell->connections()) {
|
||||
c->setPort(conn.first.str() + "_gold", import_sig(conn.second, "_gold"));
|
||||
c->setPort(conn.first.str() + "_gate", import_sig(conn.second, "_gate"));
|
||||
}
|
||||
}
|
||||
|
||||
void generate()
|
||||
{
|
||||
if (design->module(combined_type)) {
|
||||
// log("Combined module %s already exists.\n", log_id(combined_type));
|
||||
return;
|
||||
}
|
||||
|
||||
log("Generating combined module %s from module %s.\n", log_id(combined_type), log_id(orig_type));
|
||||
module = design->addModule(combined_type);
|
||||
|
||||
for (auto wire : original->wires()) {
|
||||
module->addWire(wire->name.str() + "_gold", wire);
|
||||
module->addWire(wire->name.str() + "_gate", wire);
|
||||
}
|
||||
module->fixup_ports();
|
||||
|
||||
for (auto cell : original->cells()) {
|
||||
if (design->module(cell->type) == nullptr) {
|
||||
import_prim_cell(cell, "_gold");
|
||||
import_prim_cell(cell, "_gate");
|
||||
} else {
|
||||
import_hier_cell(cell);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &conn : original->connections()) {
|
||||
module->connect(import_sig(conn.first, "_gold"), import_sig(conn.second, "_gold"));
|
||||
module->connect(import_sig(conn.first, "_gate"), import_sig(conn.second, "_gate"));
|
||||
}
|
||||
|
||||
if (opts.nop)
|
||||
return;
|
||||
|
||||
CellTypes ct;
|
||||
ct.setup_internals_eval();
|
||||
ct.setup_stdcells_eval();
|
||||
|
||||
SigMap sigmap(module);
|
||||
|
||||
dict<SigBit, SigBit> data_bit_to_eq_net;
|
||||
dict<Cell*, SigSpec> cell_to_eq_nets;
|
||||
dict<SigSpec, SigSpec> reduce_db;
|
||||
dict<SigSpec, SigSpec> invert_db;
|
||||
|
||||
for (auto cell : original->cells())
|
||||
{
|
||||
if (!ct.cell_known(cell->type))
|
||||
continue;
|
||||
|
||||
for (auto &conn : cell->connections())
|
||||
{
|
||||
if (!cell->output(conn.first))
|
||||
continue;
|
||||
|
||||
SigSpec A = import_sig(conn.second, "_gold");
|
||||
SigSpec B = import_sig(conn.second, "_gate");
|
||||
SigBit EQ = module->Eq(NEW_ID, A, B);
|
||||
|
||||
for (auto bit : sigmap({A, B}))
|
||||
data_bit_to_eq_net[bit] = EQ;
|
||||
|
||||
cell_to_eq_nets[cell].append(EQ);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto cell : original->cells())
|
||||
{
|
||||
if (!ct.cell_known(cell->type))
|
||||
continue;
|
||||
|
||||
bool skip_cell = !cell_to_eq_nets.count(cell);
|
||||
pool<SigBit> src_eq_bits;
|
||||
|
||||
for (auto &conn : cell->connections())
|
||||
{
|
||||
if (skip_cell)
|
||||
break;
|
||||
|
||||
if (cell->output(conn.first))
|
||||
continue;
|
||||
|
||||
SigSpec A = import_sig(conn.second, "_gold");
|
||||
SigSpec B = import_sig(conn.second, "_gate");
|
||||
|
||||
for (auto bit : sigmap({A, B})) {
|
||||
if (data_bit_to_eq_net.count(bit))
|
||||
src_eq_bits.insert(data_bit_to_eq_net.at(bit));
|
||||
else
|
||||
skip_cell = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!skip_cell) {
|
||||
SigSpec antecedent = SigSpec(src_eq_bits);
|
||||
antecedent.sort_and_unify();
|
||||
|
||||
if (GetSize(antecedent) > 1) {
|
||||
if (reduce_db.count(antecedent) == 0)
|
||||
reduce_db[antecedent] = module->ReduceAnd(NEW_ID, antecedent);
|
||||
antecedent = reduce_db.at(antecedent);
|
||||
}
|
||||
|
||||
SigSpec consequent = cell_to_eq_nets.at(cell);
|
||||
consequent.sort_and_unify();
|
||||
|
||||
if (GetSize(consequent) > 1) {
|
||||
if (reduce_db.count(consequent) == 0)
|
||||
reduce_db[consequent] = module->ReduceAnd(NEW_ID, consequent);
|
||||
consequent = reduce_db.at(consequent);
|
||||
}
|
||||
|
||||
if (opts.fwd)
|
||||
module->addAssume(NEW_ID, consequent, antecedent);
|
||||
|
||||
if (opts.bwd)
|
||||
{
|
||||
if (invert_db.count(antecedent) == 0)
|
||||
invert_db[antecedent] = module->Not(NEW_ID, antecedent);
|
||||
|
||||
if (invert_db.count(consequent) == 0)
|
||||
invert_db[consequent] = module->Not(NEW_ID, consequent);
|
||||
|
||||
module->addAssume(NEW_ID, invert_db.at(antecedent), invert_db.at(consequent));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct FmcombinePass : public Pass {
|
||||
FmcombinePass() : Pass("fmcombine", "combine two instances of a cell into one") { }
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" fmcombine [options] module_name gold_cell gate_cell\n");
|
||||
// log(" fmcombine [options] @gold_cell @gate_cell\n");
|
||||
log("\n");
|
||||
log("This pass takes two cells, which are instances of the same module, and replaces\n");
|
||||
log("them with one instance of a special 'combined' module, that effectively\n");
|
||||
log("contains two copies of the original module, plus some formal properties.\n");
|
||||
log("\n");
|
||||
log("This is useful for formal test benches that check what differences in behavior\n");
|
||||
log("a slight difference in input causes in a module.\n");
|
||||
log("\n");
|
||||
log(" -fwd\n");
|
||||
log(" Insert forward hint assumptions into the combined module.\n");
|
||||
log("\n");
|
||||
log(" -bwd\n");
|
||||
log(" Insert backward hint assumptions into the combined module.\n");
|
||||
log(" (Backward hints are logically equivalend to fordward hits, but\n");
|
||||
log(" some solvers are faster with bwd hints, or even both -bwd and -fwd.)\n");
|
||||
log("\n");
|
||||
log(" -nop\n");
|
||||
log(" Don't insert hint assumptions into the combined module.\n");
|
||||
log(" (This should not provide any speedup over the original design, but\n");
|
||||
log(" strangely sometimes it does.)\n");
|
||||
log("\n");
|
||||
log("If none of -fwd, -bwd, and -nop is given, then -fwd is used as default.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
opts_t opts;
|
||||
Module *module = nullptr;
|
||||
Cell *gold_cell = nullptr;
|
||||
Cell *gate_cell = nullptr;
|
||||
|
||||
log_header(design, "Executing FMCOMBINE pass.\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
// if (args[argidx] == "-o" && argidx+1 < args.size()) {
|
||||
// filename = args[++argidx];
|
||||
// continue;
|
||||
// }
|
||||
if (args[argidx] == "-fwd") {
|
||||
opts.fwd = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-bwd") {
|
||||
opts.bwd = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-nop") {
|
||||
opts.nop = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (argidx+2 == args.size())
|
||||
{
|
||||
string gold_name = args[argidx++];
|
||||
string gate_name = args[argidx++];
|
||||
log_cmd_error("fmcombine @gold_cell @gate_cell call style is not implemented yet.");
|
||||
}
|
||||
else if (argidx+3 == args.size())
|
||||
{
|
||||
IdString module_name = RTLIL::escape_id(args[argidx++]);
|
||||
IdString gold_name = RTLIL::escape_id(args[argidx++]);
|
||||
IdString gate_name = RTLIL::escape_id(args[argidx++]);
|
||||
|
||||
module = design->module(module_name);
|
||||
if (module == nullptr)
|
||||
log_cmd_error("Module %s not found.\n", log_id(module_name));
|
||||
|
||||
gold_cell = module->cell(gold_name);
|
||||
if (gold_cell == nullptr)
|
||||
log_cmd_error("Gold cell %s not found in module %s.\n", log_id(gold_name), log_id(module));
|
||||
|
||||
gate_cell = module->cell(gate_name);
|
||||
if (gate_cell == nullptr)
|
||||
log_cmd_error("Gold cell %s not found in module %s.\n", log_id(gate_name), log_id(module));
|
||||
}
|
||||
else
|
||||
{
|
||||
log_cmd_error("Invalid number of arguments.\n");
|
||||
}
|
||||
// extra_args(args, argidx, design);
|
||||
|
||||
if (opts.nop && (opts.fwd || opts.bwd))
|
||||
log_cmd_error("Option -nop can not be combined with -fwd and/or -bwd.\n");
|
||||
|
||||
if (!opts.nop && !opts.fwd && !opts.bwd)
|
||||
opts.fwd = true;
|
||||
|
||||
if (gold_cell->type != gate_cell->type)
|
||||
log_cmd_error("Types of gold and gate cells do not match.\n");
|
||||
if (!gold_cell->parameters.empty())
|
||||
log_cmd_error("Gold cell has unresolved instance parameters.\n");
|
||||
if (!gate_cell->parameters.empty())
|
||||
log_cmd_error("Gold cell has unresolved instance parameters.\n");
|
||||
|
||||
FmcombineWorker worker(design, gold_cell->type, opts);
|
||||
worker.generate();
|
||||
IdString combined_cell_name = module->uniquify(stringf("\\%s_%s", log_id(gold_cell), log_id(gate_cell)));
|
||||
|
||||
Cell *cell = module->addCell(combined_cell_name, worker.combined_type);
|
||||
cell->attributes = gold_cell->attributes;
|
||||
cell->add_strpool_attribute("\\src", gate_cell->get_strpool_attribute("\\src"));
|
||||
|
||||
log("Combining cells %s and %s in module %s into new cell %s.\n", log_id(gold_cell), log_id(gate_cell), log_id(module), log_id(cell));
|
||||
|
||||
for (auto &conn : gold_cell->connections())
|
||||
cell->setPort(conn.first.str() + "_gold", conn.second);
|
||||
module->remove(gold_cell);
|
||||
|
||||
for (auto &conn : gate_cell->connections())
|
||||
cell->setPort(conn.first.str() + "_gate", conn.second);
|
||||
module->remove(gate_cell);
|
||||
}
|
||||
} FmcombinePass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
988
passes/sat/mutate.cc
Normal file
988
passes/sat/mutate.cc
Normal file
|
@ -0,0 +1,988 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/sigtools.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct mutate_t {
|
||||
string mode;
|
||||
pool<string> src;
|
||||
IdString module, cell;
|
||||
IdString port, wire;
|
||||
int portbit = -1;
|
||||
int ctrlbit = -1;
|
||||
int wirebit = -1;
|
||||
bool used = false;
|
||||
};
|
||||
|
||||
struct mutate_opts_t {
|
||||
int seed = 0;
|
||||
std::string mode;
|
||||
pool<string> src;
|
||||
IdString module, cell, port, wire;
|
||||
int portbit = -1;
|
||||
int ctrlbit = -1;
|
||||
int wirebit = -1;
|
||||
|
||||
IdString ctrl_name;
|
||||
int ctrl_width = -1, ctrl_value = -1;
|
||||
|
||||
bool none = false;
|
||||
|
||||
int pick_cover_prcnt = 80;
|
||||
|
||||
int weight_cover = 500;
|
||||
|
||||
int weight_pq_w = 100;
|
||||
int weight_pq_b = 100;
|
||||
int weight_pq_c = 100;
|
||||
int weight_pq_s = 100;
|
||||
|
||||
int weight_pq_mw = 100;
|
||||
int weight_pq_mb = 100;
|
||||
int weight_pq_mc = 100;
|
||||
int weight_pq_ms = 100;
|
||||
};
|
||||
|
||||
void database_add(std::vector<mutate_t> &database, const mutate_opts_t &opts, const mutate_t &entry)
|
||||
{
|
||||
if (!opts.mode.empty() && opts.mode != entry.mode)
|
||||
return;
|
||||
|
||||
if (!opts.src.empty()) {
|
||||
bool found_match = false;
|
||||
for (auto &s : opts.src) {
|
||||
if (entry.src.count(s))
|
||||
found_match = true;
|
||||
}
|
||||
if (!found_match)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!opts.module.empty() && opts.module != entry.module)
|
||||
return;
|
||||
|
||||
if (!opts.cell.empty() && opts.cell != entry.cell)
|
||||
return;
|
||||
|
||||
if (!opts.port.empty() && opts.port != entry.port)
|
||||
return;
|
||||
|
||||
if (opts.portbit >= 0 && opts.portbit != entry.portbit)
|
||||
return;
|
||||
|
||||
if (opts.ctrlbit >= 0 && opts.ctrlbit != entry.ctrlbit)
|
||||
return;
|
||||
|
||||
if (!opts.wire.empty() && opts.wire != entry.wire)
|
||||
return;
|
||||
|
||||
if (opts.wirebit >= 0 && opts.wirebit != entry.wirebit)
|
||||
return;
|
||||
|
||||
database.push_back(entry);
|
||||
}
|
||||
|
||||
struct xs128_t
|
||||
{
|
||||
uint32_t x = 123456789;
|
||||
uint32_t y = 0, z = 0, w = 0;
|
||||
|
||||
xs128_t(int seed = 0) : w(seed) {
|
||||
next();
|
||||
next();
|
||||
next();
|
||||
}
|
||||
|
||||
void next() {
|
||||
uint32_t t = x ^ (x << 11);
|
||||
x = y, y = z, z = w;
|
||||
w ^= (w >> 19) ^ t ^ (t >> 8);
|
||||
}
|
||||
|
||||
int operator()() {
|
||||
next();
|
||||
return w & 0x3fffffff;
|
||||
}
|
||||
|
||||
int operator()(int n) {
|
||||
if (n < 2)
|
||||
return 0;
|
||||
while (1) {
|
||||
int k = (*this)(), p = k % n;
|
||||
if ((k - p + n) <= 0x40000000)
|
||||
return p;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct coverdb_t
|
||||
{
|
||||
dict<string, int> src_db;
|
||||
dict<tuple<IdString, IdString>, int> wire_db;
|
||||
dict<tuple<IdString, IdString, int>, int> wirebit_db;
|
||||
|
||||
void insert(const mutate_t &m) {
|
||||
if (!m.wire.empty()) {
|
||||
wire_db[tuple<IdString, IdString>(m.module, m.wire)] = 0;
|
||||
wirebit_db[tuple<IdString, IdString, int>(m.module, m.wire, m.wirebit)] = 0;
|
||||
}
|
||||
for (auto &s : m.src) {
|
||||
src_db[s] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void update(const mutate_t &m) {
|
||||
if (!m.wire.empty()) {
|
||||
wire_db.at(tuple<IdString, IdString>(m.module, m.wire))++;
|
||||
wirebit_db.at(tuple<IdString, IdString, int>(m.module, m.wire, m.wirebit))++;
|
||||
}
|
||||
for (auto &s : m.src) {
|
||||
src_db.at(s)++;
|
||||
}
|
||||
}
|
||||
|
||||
int score(const mutate_t &m) {
|
||||
int this_score = m.src.empty() ? 0 : 1;
|
||||
if (!m.wire.empty()) {
|
||||
this_score += wire_db.at(tuple<IdString, IdString>(m.module, m.wire)) ? 0 : 5;
|
||||
this_score += wirebit_db.at(tuple<IdString, IdString, int>(m.module, m.wire, m.wirebit)) ? 0 : 1;
|
||||
}
|
||||
for (auto &s : m.src) {
|
||||
this_score += src_db.at(s) ? 0 : 5;
|
||||
}
|
||||
return this_score;
|
||||
}
|
||||
};
|
||||
|
||||
struct mutate_queue_t
|
||||
{
|
||||
pool<mutate_t*, hash_ptr_ops> db;
|
||||
|
||||
mutate_t *pick(xs128_t &rng, coverdb_t &coverdb, const mutate_opts_t &opts) {
|
||||
mutate_t *m = nullptr;
|
||||
if (rng(100) < opts.pick_cover_prcnt) {
|
||||
vector<mutate_t*> candidates, rmqueue;
|
||||
int best_score = -1;
|
||||
for (auto p : db) {
|
||||
if (p->used) {
|
||||
rmqueue.push_back(p);
|
||||
continue;
|
||||
}
|
||||
int this_score = coverdb.score(*p);
|
||||
if (this_score > best_score) {
|
||||
best_score = this_score;
|
||||
candidates.clear();
|
||||
}
|
||||
if (best_score == this_score)
|
||||
candidates.push_back(p);
|
||||
}
|
||||
for (auto p : rmqueue)
|
||||
db.erase(p);
|
||||
if (!candidates.empty())
|
||||
m = candidates[rng(GetSize(candidates))];
|
||||
}
|
||||
if (m == nullptr) {
|
||||
while (!db.empty()) {
|
||||
int i = rng(GetSize(db));
|
||||
auto it = db.element(i);
|
||||
mutate_t *p = *it;
|
||||
db.erase(it);
|
||||
if (p->used == false) {
|
||||
m = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
void add(mutate_t *m) {
|
||||
db.insert(m);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename K, typename T>
|
||||
struct mutate_chain_queue_t
|
||||
{
|
||||
dict<K, T> db;
|
||||
|
||||
mutate_t *pick(xs128_t &rng, coverdb_t &coverdb, const mutate_opts_t &opts) {
|
||||
while (!db.empty()) {
|
||||
int i = rng(GetSize(db));
|
||||
auto it = db.element(i);
|
||||
mutate_t *m = it->second.pick(rng, coverdb, opts);
|
||||
if (m != nullptr)
|
||||
return m;
|
||||
db.erase(it);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void add(mutate_t *m, K key, Args... args) {
|
||||
db[key].add(m, args...);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename K, typename T>
|
||||
struct mutate_once_queue_t
|
||||
{
|
||||
dict<K, T> db;
|
||||
|
||||
mutate_t *pick(xs128_t &rng, coverdb_t &coverdb, const mutate_opts_t &opts) {
|
||||
while (!db.empty()) {
|
||||
int i = rng(GetSize(db));
|
||||
auto it = db.element(i);
|
||||
mutate_t *m = it->second.pick(rng, coverdb, opts);
|
||||
db.erase(it);
|
||||
if (m != nullptr)
|
||||
return m;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void add(mutate_t *m, K key, Args... args) {
|
||||
db[key].add(m, args...);
|
||||
}
|
||||
};
|
||||
|
||||
void database_reduce(std::vector<mutate_t> &database, const mutate_opts_t &opts, int N, xs128_t &rng)
|
||||
{
|
||||
std::vector<mutate_t> new_database;
|
||||
coverdb_t coverdb;
|
||||
|
||||
int total_weight = opts.weight_cover + opts.weight_pq_w + opts.weight_pq_b + opts.weight_pq_c + opts.weight_pq_s;
|
||||
total_weight += opts.weight_pq_mw + opts.weight_pq_mb + opts.weight_pq_mc + opts.weight_pq_ms;
|
||||
|
||||
if (N >= GetSize(database))
|
||||
return;
|
||||
|
||||
mutate_once_queue_t<tuple<IdString, IdString>, mutate_queue_t> primary_queue_wire;
|
||||
mutate_once_queue_t<tuple<IdString, IdString, int>, mutate_queue_t> primary_queue_bit;
|
||||
mutate_once_queue_t<tuple<IdString, IdString>, mutate_queue_t> primary_queue_cell;
|
||||
mutate_once_queue_t<string, mutate_queue_t> primary_queue_src;
|
||||
|
||||
mutate_chain_queue_t<IdString, mutate_once_queue_t<IdString, mutate_queue_t>> primary_queue_module_wire;
|
||||
mutate_chain_queue_t<IdString, mutate_once_queue_t<pair<IdString, int>, mutate_queue_t>> primary_queue_module_bit;
|
||||
mutate_chain_queue_t<IdString, mutate_once_queue_t<IdString, mutate_queue_t>> primary_queue_module_cell;
|
||||
mutate_chain_queue_t<IdString, mutate_once_queue_t<string, mutate_queue_t>> primary_queue_module_src;
|
||||
|
||||
for (auto &m : database)
|
||||
{
|
||||
coverdb.insert(m);
|
||||
|
||||
if (!m.wire.empty()) {
|
||||
primary_queue_wire.add(&m, tuple<IdString, IdString>(m.module, m.wire));
|
||||
primary_queue_bit.add(&m, tuple<IdString, IdString, int>(m.module, m.wire, m.wirebit));
|
||||
primary_queue_module_wire.add(&m, m.module, m.wire);
|
||||
primary_queue_module_bit.add(&m, m.module, pair<IdString, int>(m.wire, m.wirebit));
|
||||
}
|
||||
|
||||
primary_queue_cell.add(&m, tuple<IdString, IdString>(m.module, m.cell));
|
||||
primary_queue_module_cell.add(&m, m.module, m.cell);
|
||||
|
||||
for (auto &s : m.src) {
|
||||
primary_queue_src.add(&m, s);
|
||||
primary_queue_module_src.add(&m, m.module, s);
|
||||
}
|
||||
}
|
||||
|
||||
vector<mutate_t*> cover_candidates;
|
||||
int best_cover_score = -1;
|
||||
bool skip_cover = false;
|
||||
|
||||
while (GetSize(new_database) < N)
|
||||
{
|
||||
int k = rng(total_weight);
|
||||
|
||||
k -= opts.weight_cover;
|
||||
if (k < 0) {
|
||||
while (!skip_cover) {
|
||||
if (cover_candidates.empty()) {
|
||||
best_cover_score = -1;
|
||||
for (auto &m : database) {
|
||||
if (m.used || m.src.empty())
|
||||
continue;
|
||||
int this_score = -1;
|
||||
for (auto &s : m.src) {
|
||||
if (this_score == -1 || this_score > coverdb.src_db.at(s))
|
||||
this_score = coverdb.src_db.at(s);
|
||||
}
|
||||
log_assert(this_score != -1);
|
||||
if (best_cover_score == -1 || this_score < best_cover_score) {
|
||||
cover_candidates.clear();
|
||||
best_cover_score = this_score;
|
||||
}
|
||||
if (best_cover_score == this_score)
|
||||
cover_candidates.push_back(&m);
|
||||
}
|
||||
if (best_cover_score == -1) {
|
||||
skip_cover = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutate_t *m = nullptr;
|
||||
while (!cover_candidates.empty())
|
||||
{
|
||||
int idx = rng(GetSize(cover_candidates));
|
||||
mutate_t *p = cover_candidates[idx];
|
||||
cover_candidates[idx] = cover_candidates.back();
|
||||
cover_candidates.pop_back();
|
||||
|
||||
if (p->used)
|
||||
continue;
|
||||
|
||||
int this_score = -1;
|
||||
for (auto &s : p->src) {
|
||||
if (this_score == -1 || this_score > coverdb.src_db.at(s))
|
||||
this_score = coverdb.src_db.at(s);
|
||||
}
|
||||
|
||||
if (this_score != best_cover_score)
|
||||
continue;
|
||||
|
||||
m = p;
|
||||
break;
|
||||
}
|
||||
|
||||
if (m != nullptr) {
|
||||
m->used = true;
|
||||
coverdb.update(*m);
|
||||
new_database.push_back(*m);
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
#define X(__wght, __queue) \
|
||||
k -= __wght; \
|
||||
if (k < 0) { \
|
||||
mutate_t *m = __queue.pick(rng, coverdb, opts); \
|
||||
if (m != nullptr) { \
|
||||
m->used = true; \
|
||||
coverdb.update(*m); \
|
||||
new_database.push_back(*m); \
|
||||
}; \
|
||||
continue; \
|
||||
}
|
||||
|
||||
X(opts.weight_pq_w, primary_queue_wire)
|
||||
X(opts.weight_pq_b, primary_queue_bit)
|
||||
X(opts.weight_pq_c, primary_queue_cell)
|
||||
X(opts.weight_pq_s, primary_queue_src)
|
||||
|
||||
X(opts.weight_pq_mw, primary_queue_module_wire)
|
||||
X(opts.weight_pq_mb, primary_queue_module_bit)
|
||||
X(opts.weight_pq_mc, primary_queue_module_cell)
|
||||
X(opts.weight_pq_ms, primary_queue_module_src)
|
||||
#undef X
|
||||
}
|
||||
|
||||
std::swap(new_database, database);
|
||||
|
||||
int covered_src_cnt = 0;
|
||||
int covered_wire_cnt = 0;
|
||||
int covered_wirebit_cnt = 0;
|
||||
|
||||
for (auto &it : coverdb.src_db)
|
||||
if (it.second)
|
||||
covered_src_cnt++;
|
||||
|
||||
for (auto &it : coverdb.wire_db)
|
||||
if (it.second)
|
||||
covered_wire_cnt++;
|
||||
|
||||
for (auto &it : coverdb.wirebit_db)
|
||||
if (it.second)
|
||||
covered_wirebit_cnt++;
|
||||
|
||||
log("Covered %d/%d src attributes (%.2f%%).\n", covered_src_cnt, GetSize(coverdb.src_db), 100.0 * covered_src_cnt / GetSize(coverdb.src_db));
|
||||
log("Covered %d/%d wires (%.2f%%).\n", covered_wire_cnt, GetSize(coverdb.wire_db), 100.0 * covered_wire_cnt / GetSize(coverdb.wire_db));
|
||||
log("Covered %d/%d wire bits (%.2f%%).\n", covered_wirebit_cnt, GetSize(coverdb.wirebit_db), 100.0 * covered_wirebit_cnt / GetSize(coverdb.wirebit_db));
|
||||
}
|
||||
|
||||
void mutate_list(Design *design, const mutate_opts_t &opts, const string &filename, const string &srcsfile, int N)
|
||||
{
|
||||
pool<string> sources;
|
||||
std::vector<mutate_t> database;
|
||||
xs128_t rng(opts.seed);
|
||||
|
||||
for (auto module : design->selected_modules())
|
||||
{
|
||||
if (!opts.module.empty() && module->name != opts.module)
|
||||
continue;
|
||||
|
||||
SigMap sigmap(module);
|
||||
dict<SigBit, int> bit_user_cnt;
|
||||
|
||||
for (auto wire : module->wires()) {
|
||||
if (wire->name[0] == '\\' && wire->attributes.count("\\src"))
|
||||
sigmap.add(wire);
|
||||
}
|
||||
|
||||
for (auto cell : module->cells()) {
|
||||
for (auto &conn : cell->connections()) {
|
||||
if (cell->output(conn.first))
|
||||
continue;
|
||||
for (auto bit : sigmap(conn.second))
|
||||
bit_user_cnt[bit]++;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto wire : module->selected_wires())
|
||||
{
|
||||
for (SigBit bit : SigSpec(wire))
|
||||
{
|
||||
SigBit sigbit = sigmap(bit);
|
||||
|
||||
if (bit.wire == nullptr || sigbit.wire == nullptr)
|
||||
continue;
|
||||
|
||||
if (!bit.wire->port_id != !sigbit.wire->port_id) {
|
||||
if (bit.wire->port_id)
|
||||
sigmap.add(bit);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!bit.wire->name[0] != !sigbit.wire->name[0]) {
|
||||
if (bit.wire->name[0] == '\\')
|
||||
sigmap.add(bit);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto cell : module->selected_cells())
|
||||
{
|
||||
if (!opts.cell.empty() && cell->name != opts.cell)
|
||||
continue;
|
||||
|
||||
for (auto &conn : cell->connections())
|
||||
{
|
||||
for (int i = 0; i < GetSize(conn.second); i++) {
|
||||
mutate_t entry;
|
||||
entry.module = module->name;
|
||||
entry.cell = cell->name;
|
||||
entry.port = conn.first;
|
||||
entry.portbit = i;
|
||||
|
||||
for (auto &s : cell->get_strpool_attribute("\\src"))
|
||||
entry.src.insert(s);
|
||||
|
||||
SigBit bit = sigmap(conn.second[i]);
|
||||
if (bit.wire && bit.wire->name[0] == '\\' && (cell->output(conn.first) || bit_user_cnt[bit] == 1)) {
|
||||
for (auto &s : bit.wire->get_strpool_attribute("\\src"))
|
||||
entry.src.insert(s);
|
||||
entry.wire = bit.wire->name;
|
||||
entry.wirebit = bit.offset;
|
||||
}
|
||||
|
||||
if (!srcsfile.empty())
|
||||
sources.insert(entry.src.begin(), entry.src.end());
|
||||
|
||||
entry.mode = "inv";
|
||||
database_add(database, opts, entry);
|
||||
|
||||
entry.mode = "const0";
|
||||
database_add(database, opts, entry);
|
||||
|
||||
entry.mode = "const1";
|
||||
database_add(database, opts, entry);
|
||||
|
||||
entry.mode = "cnot0";
|
||||
entry.ctrlbit = rng(GetSize(conn.second));
|
||||
if (entry.ctrlbit != entry.portbit && conn.second[entry.ctrlbit].wire)
|
||||
database_add(database, opts, entry);
|
||||
|
||||
entry.mode = "cnot1";
|
||||
entry.ctrlbit = rng(GetSize(conn.second));
|
||||
if (entry.ctrlbit != entry.portbit && conn.second[entry.ctrlbit].wire)
|
||||
database_add(database, opts, entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log("Raw database size: %d\n", GetSize(database));
|
||||
if (N != 0) {
|
||||
database_reduce(database, opts, opts.none ? N-1 : N, rng);
|
||||
log("Reduced database size: %d\n", GetSize(database));
|
||||
}
|
||||
|
||||
if (!srcsfile.empty()) {
|
||||
std::ofstream sout;
|
||||
sout.open(srcsfile, std::ios::out | std::ios::trunc);
|
||||
if (!sout.is_open())
|
||||
log_error("Could not open file \"%s\" with write access.\n", srcsfile.c_str());
|
||||
sources.sort();
|
||||
for (auto &s : sources)
|
||||
sout << s << std::endl;
|
||||
}
|
||||
|
||||
std::ofstream fout;
|
||||
|
||||
if (!filename.empty()) {
|
||||
fout.open(filename, std::ios::out | std::ios::trunc);
|
||||
if (!fout.is_open())
|
||||
log_error("Could not open file \"%s\" with write access.\n", filename.c_str());
|
||||
}
|
||||
|
||||
int ctrl_value = opts.ctrl_value;
|
||||
|
||||
if (opts.none) {
|
||||
string str = "mutate";
|
||||
if (!opts.ctrl_name.empty())
|
||||
str += stringf(" -ctrl %s %d %d", log_id(opts.ctrl_name), opts.ctrl_width, ctrl_value++);
|
||||
str += " -mode none";
|
||||
if (filename.empty())
|
||||
log("%s\n", str.c_str());
|
||||
else
|
||||
fout << str << std::endl;
|
||||
}
|
||||
|
||||
for (auto &entry : database) {
|
||||
string str = "mutate";
|
||||
if (!opts.ctrl_name.empty())
|
||||
str += stringf(" -ctrl %s %d %d", log_id(opts.ctrl_name), opts.ctrl_width, ctrl_value++);
|
||||
str += stringf(" -mode %s", entry.mode.c_str());
|
||||
if (!entry.module.empty())
|
||||
str += stringf(" -module %s", log_id(entry.module));
|
||||
if (!entry.cell.empty())
|
||||
str += stringf(" -cell %s", log_id(entry.cell));
|
||||
if (!entry.port.empty())
|
||||
str += stringf(" -port %s", log_id(entry.port));
|
||||
if (entry.portbit >= 0)
|
||||
str += stringf(" -portbit %d", entry.portbit);
|
||||
if (entry.ctrlbit >= 0)
|
||||
str += stringf(" -ctrlbit %d", entry.ctrlbit);
|
||||
if (!entry.wire.empty())
|
||||
str += stringf(" -wire %s", log_id(entry.wire));
|
||||
if (entry.wirebit >= 0)
|
||||
str += stringf(" -wirebit %d", entry.wirebit);
|
||||
for (auto &s : entry.src)
|
||||
str += stringf(" -src %s", s.c_str());
|
||||
if (filename.empty())
|
||||
log("%s\n", str.c_str());
|
||||
else
|
||||
fout << str << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
SigSpec mutate_ctrl_sig(Module *module, IdString name, int width)
|
||||
{
|
||||
Wire *ctrl_wire = module->wire(name);
|
||||
|
||||
if (ctrl_wire == nullptr)
|
||||
{
|
||||
log("Adding ctrl port %s to module %s.\n", log_id(name), log_id(module));
|
||||
|
||||
ctrl_wire = module->addWire(name, width);
|
||||
ctrl_wire->port_input = true;
|
||||
module->fixup_ports();
|
||||
|
||||
for (auto mod : module->design->modules())
|
||||
for (auto cell : mod->cells())
|
||||
{
|
||||
if (cell->type != module->name)
|
||||
continue;
|
||||
|
||||
SigSpec ctrl = mutate_ctrl_sig(mod, name, width);
|
||||
|
||||
log("Connecting ctrl port to cell %s in module %s.\n", log_id(cell), log_id(mod));
|
||||
cell->setPort(name, ctrl);
|
||||
}
|
||||
}
|
||||
|
||||
log_assert(GetSize(ctrl_wire) == width);
|
||||
return ctrl_wire;
|
||||
}
|
||||
|
||||
SigBit mutate_ctrl(Module *module, const mutate_opts_t &opts)
|
||||
{
|
||||
if (opts.ctrl_name.empty())
|
||||
return State::S1;
|
||||
|
||||
SigSpec sig = mutate_ctrl_sig(module, opts.ctrl_name, opts.ctrl_width);
|
||||
return module->Eq(NEW_ID, sig, Const(opts.ctrl_value, GetSize(sig)));
|
||||
}
|
||||
|
||||
SigSpec mutate_ctrl_mux(Module *module, const mutate_opts_t &opts, SigSpec unchanged_sig, SigSpec changed_sig)
|
||||
{
|
||||
SigBit ctrl_bit = mutate_ctrl(module, opts);
|
||||
if (ctrl_bit == State::S0)
|
||||
return unchanged_sig;
|
||||
if (ctrl_bit == State::S1)
|
||||
return changed_sig;
|
||||
return module->Mux(NEW_ID, unchanged_sig, changed_sig, ctrl_bit);
|
||||
}
|
||||
|
||||
void mutate_inv(Design *design, const mutate_opts_t &opts)
|
||||
{
|
||||
Module *module = design->module(opts.module);
|
||||
Cell *cell = module->cell(opts.cell);
|
||||
|
||||
SigBit bit = cell->getPort(opts.port)[opts.portbit];
|
||||
SigBit inbit, outbit;
|
||||
|
||||
if (cell->input(opts.port))
|
||||
{
|
||||
log("Add input inverter at %s.%s.%s[%d].\n", log_id(module), log_id(cell), log_id(opts.port), opts.portbit);
|
||||
SigBit outbit = module->Not(NEW_ID, bit);
|
||||
bit = mutate_ctrl_mux(module, opts, bit, outbit);
|
||||
}
|
||||
else
|
||||
{
|
||||
log("Add output inverter at %s.%s.%s[%d].\n", log_id(module), log_id(cell), log_id(opts.port), opts.portbit);
|
||||
SigBit inbit = module->addWire(NEW_ID);
|
||||
SigBit outbit = module->Not(NEW_ID, inbit);
|
||||
module->connect(bit, mutate_ctrl_mux(module, opts, inbit, outbit));
|
||||
bit = inbit;
|
||||
}
|
||||
|
||||
SigSpec s = cell->getPort(opts.port);
|
||||
s[opts.portbit] = bit;
|
||||
cell->setPort(opts.port, s);
|
||||
}
|
||||
|
||||
void mutate_const(Design *design, const mutate_opts_t &opts, bool one)
|
||||
{
|
||||
Module *module = design->module(opts.module);
|
||||
Cell *cell = module->cell(opts.cell);
|
||||
|
||||
SigBit bit = cell->getPort(opts.port)[opts.portbit];
|
||||
SigBit inbit, outbit;
|
||||
|
||||
if (cell->input(opts.port))
|
||||
{
|
||||
log("Add input constant %d at %s.%s.%s[%d].\n", one ? 1 : 0, log_id(module), log_id(cell), log_id(opts.port), opts.portbit);
|
||||
SigBit outbit = one ? State::S1 : State::S0;
|
||||
bit = mutate_ctrl_mux(module, opts, bit, outbit);
|
||||
}
|
||||
else
|
||||
{
|
||||
log("Add output constant %d at %s.%s.%s[%d].\n", one ? 1 : 0, log_id(module), log_id(cell), log_id(opts.port), opts.portbit);
|
||||
SigBit inbit = module->addWire(NEW_ID);
|
||||
SigBit outbit = one ? State::S1 : State::S0;
|
||||
module->connect(bit, mutate_ctrl_mux(module, opts, inbit, outbit));
|
||||
bit = inbit;
|
||||
}
|
||||
|
||||
SigSpec s = cell->getPort(opts.port);
|
||||
s[opts.portbit] = bit;
|
||||
cell->setPort(opts.port, s);
|
||||
}
|
||||
|
||||
void mutate_cnot(Design *design, const mutate_opts_t &opts, bool one)
|
||||
{
|
||||
Module *module = design->module(opts.module);
|
||||
Cell *cell = module->cell(opts.cell);
|
||||
|
||||
SigBit bit = cell->getPort(opts.port)[opts.portbit];
|
||||
SigBit ctrl = cell->getPort(opts.port)[opts.ctrlbit];
|
||||
SigBit inbit, outbit;
|
||||
|
||||
if (cell->input(opts.port))
|
||||
{
|
||||
log("Add input cnot%d at %s.%s.%s[%d,%d].\n", one ? 1 : 0, log_id(module), log_id(cell), log_id(opts.port), opts.portbit, opts.ctrlbit);
|
||||
SigBit outbit = one ? module->Xor(NEW_ID, bit, ctrl) : module->Xnor(NEW_ID, bit, ctrl);
|
||||
bit = mutate_ctrl_mux(module, opts, bit, outbit);
|
||||
}
|
||||
else
|
||||
{
|
||||
log("Add output cnot%d at %s.%s.%s[%d,%d].\n", one ? 1 : 0, log_id(module), log_id(cell), log_id(opts.port), opts.portbit, opts.ctrlbit);
|
||||
SigBit inbit = module->addWire(NEW_ID);
|
||||
SigBit outbit = one ? module->Xor(NEW_ID, inbit, ctrl) : module->Xnor(NEW_ID, inbit, ctrl);
|
||||
module->connect(bit, mutate_ctrl_mux(module, opts, inbit, outbit));
|
||||
bit = inbit;
|
||||
}
|
||||
|
||||
SigSpec s = cell->getPort(opts.port);
|
||||
s[opts.portbit] = bit;
|
||||
cell->setPort(opts.port, s);
|
||||
}
|
||||
|
||||
struct MutatePass : public Pass {
|
||||
MutatePass() : Pass("mutate", "generate or apply design mutations") { }
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" mutate -list N [options] [selection]\n");
|
||||
log("\n");
|
||||
log("Create a list of N mutations using an even sampling.\n");
|
||||
log("\n");
|
||||
log(" -o filename\n");
|
||||
log(" Write list to this file instead of console output\n");
|
||||
log("\n");
|
||||
log(" -s filename\n");
|
||||
log(" Write a list of all src tags found in the design to the specified file\n");
|
||||
log("\n");
|
||||
log(" -seed N\n");
|
||||
log(" RNG seed for selecting mutations\n");
|
||||
log("\n");
|
||||
log(" -none\n");
|
||||
log(" Include a \"none\" mutation in the output\n");
|
||||
log("\n");
|
||||
log(" -ctrl name width value\n");
|
||||
log(" Add -ctrl options to the output. Use 'value' for first mutation, then\n");
|
||||
log(" simply count up from there.\n");
|
||||
log("\n");
|
||||
log(" -mode name\n");
|
||||
log(" -module name\n");
|
||||
log(" -cell name\n");
|
||||
log(" -port name\n");
|
||||
log(" -portbit int\n");
|
||||
log(" -ctrlbit int\n");
|
||||
log(" -wire name\n");
|
||||
log(" -wirebit int\n");
|
||||
log(" -src string\n");
|
||||
log(" Filter list of mutation candidates to those matching\n");
|
||||
log(" the given parameters.\n");
|
||||
log("\n");
|
||||
log(" -cfg option int\n");
|
||||
log(" Set a configuration option. Options available:\n");
|
||||
log(" weight_pq_w weight_pq_b weight_pq_c weight_pq_s\n");
|
||||
log(" weight_pq_mw weight_pq_mb weight_pq_mc weight_pq_ms\n");
|
||||
log(" weight_cover pick_cover_prcnt\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log(" mutate -mode MODE [options]\n");
|
||||
log("\n");
|
||||
log("Apply the given mutation.\n");
|
||||
log("\n");
|
||||
log(" -ctrl name width value\n");
|
||||
log(" Add a control signal with the given name and width. The mutation is\n");
|
||||
log(" activated if the control signal equals the given value.\n");
|
||||
log("\n");
|
||||
log(" -module name\n");
|
||||
log(" -cell name\n");
|
||||
log(" -port name\n");
|
||||
log(" -portbit int\n");
|
||||
log(" -ctrlbit int\n");
|
||||
log(" Mutation parameters, as generated by 'mutate -list N'.\n");
|
||||
log("\n");
|
||||
log(" -wire name\n");
|
||||
log(" -wirebit int\n");
|
||||
log(" -src string\n");
|
||||
log(" Ignored. (They are generated by -list for documentation purposes.)\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
mutate_opts_t opts;
|
||||
string filename;
|
||||
string srcsfile;
|
||||
int N = -1;
|
||||
|
||||
log_header(design, "Executing MUTATE pass.\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
if (args[argidx] == "-list" && argidx+1 < args.size()) {
|
||||
N = atoi(args[++argidx].c_str());
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-o" && argidx+1 < args.size()) {
|
||||
filename = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-s" && argidx+1 < args.size()) {
|
||||
srcsfile = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-seed" && argidx+1 < args.size()) {
|
||||
opts.seed = atoi(args[++argidx].c_str());
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-none") {
|
||||
opts.none = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-mode" && argidx+1 < args.size()) {
|
||||
opts.mode = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-ctrl" && argidx+3 < args.size()) {
|
||||
opts.ctrl_name = RTLIL::escape_id(args[++argidx]);
|
||||
opts.ctrl_width = atoi(args[++argidx].c_str());
|
||||
opts.ctrl_value = atoi(args[++argidx].c_str());
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-module" && argidx+1 < args.size()) {
|
||||
opts.module = RTLIL::escape_id(args[++argidx]);
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-cell" && argidx+1 < args.size()) {
|
||||
opts.cell = RTLIL::escape_id(args[++argidx]);
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-port" && argidx+1 < args.size()) {
|
||||
opts.port = RTLIL::escape_id(args[++argidx]);
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-portbit" && argidx+1 < args.size()) {
|
||||
opts.portbit = atoi(args[++argidx].c_str());
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-ctrlbit" && argidx+1 < args.size()) {
|
||||
opts.ctrlbit = atoi(args[++argidx].c_str());
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-wire" && argidx+1 < args.size()) {
|
||||
opts.wire = RTLIL::escape_id(args[++argidx]);
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-wirebit" && argidx+1 < args.size()) {
|
||||
opts.wirebit = atoi(args[++argidx].c_str());
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-src" && argidx+1 < args.size()) {
|
||||
opts.src.insert(args[++argidx]);
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-cfg" && argidx+2 < args.size()) {
|
||||
if (args[argidx+1] == "pick_cover_prcnt") {
|
||||
opts.pick_cover_prcnt = atoi(args[argidx+2].c_str());
|
||||
argidx += 2;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx+1] == "weight_cover") {
|
||||
opts.weight_cover = atoi(args[argidx+2].c_str());
|
||||
argidx += 2;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx+1] == "weight_pq_w") {
|
||||
opts.weight_pq_w = atoi(args[argidx+2].c_str());
|
||||
argidx += 2;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx+1] == "weight_pq_b") {
|
||||
opts.weight_pq_b = atoi(args[argidx+2].c_str());
|
||||
argidx += 2;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx+1] == "weight_pq_c") {
|
||||
opts.weight_pq_c = atoi(args[argidx+2].c_str());
|
||||
argidx += 2;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx+1] == "weight_pq_s") {
|
||||
opts.weight_pq_s = atoi(args[argidx+2].c_str());
|
||||
argidx += 2;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx+1] == "weight_pq_mw") {
|
||||
opts.weight_pq_mw = atoi(args[argidx+2].c_str());
|
||||
argidx += 2;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx+1] == "weight_pq_mb") {
|
||||
opts.weight_pq_mb = atoi(args[argidx+2].c_str());
|
||||
argidx += 2;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx+1] == "weight_pq_mc") {
|
||||
opts.weight_pq_mc = atoi(args[argidx+2].c_str());
|
||||
argidx += 2;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx+1] == "weight_pq_ms") {
|
||||
opts.weight_pq_ms = atoi(args[argidx+2].c_str());
|
||||
argidx += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
if (N >= 0) {
|
||||
mutate_list(design, opts, filename, srcsfile, N);
|
||||
return;
|
||||
}
|
||||
|
||||
if (opts.mode == "none") {
|
||||
if (!opts.ctrl_name.empty()) {
|
||||
Module *topmod = opts.module.empty() ? design->top_module() : design->module(opts.module);
|
||||
if (topmod)
|
||||
mutate_ctrl_sig(topmod, opts.ctrl_name, opts.ctrl_width);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (opts.module.empty())
|
||||
log_cmd_error("Missing -module argument.\n");
|
||||
|
||||
Module *module = design->module(opts.module);
|
||||
if (module == nullptr)
|
||||
log_cmd_error("Module %s not found.\n", log_id(opts.module));
|
||||
|
||||
if (opts.cell.empty())
|
||||
log_cmd_error("Missing -cell argument.\n");
|
||||
|
||||
Cell *cell = module->cell(opts.cell);
|
||||
if (cell == nullptr)
|
||||
log_cmd_error("Cell %s not found in module %s.\n", log_id(opts.cell), log_id(opts.module));
|
||||
|
||||
if (opts.port.empty())
|
||||
log_cmd_error("Missing -port argument.\n");
|
||||
|
||||
if (!cell->hasPort(opts.port))
|
||||
log_cmd_error("Port %s not found on cell %s.%s.\n", log_id(opts.port), log_id(opts.module), log_id(opts.cell));
|
||||
|
||||
if (opts.portbit < 0)
|
||||
log_cmd_error("Missing -portbit argument.\n");
|
||||
|
||||
if (GetSize(cell->getPort(opts.port)) <= opts.portbit)
|
||||
log_cmd_error("Out-of-range -portbit argument for port %s on cell %s.%s.\n", log_id(opts.port), log_id(opts.module), log_id(opts.cell));
|
||||
|
||||
if (opts.mode == "inv") {
|
||||
mutate_inv(design, opts);
|
||||
return;
|
||||
}
|
||||
|
||||
if (opts.mode == "const0" || opts.mode == "const1") {
|
||||
mutate_const(design, opts, opts.mode == "const1");
|
||||
return;
|
||||
}
|
||||
|
||||
if (opts.ctrlbit < 0)
|
||||
log_cmd_error("Missing -ctrlbit argument.\n");
|
||||
|
||||
if (GetSize(cell->getPort(opts.port)) <= opts.ctrlbit)
|
||||
log_cmd_error("Out-of-range -ctrlbit argument for port %s on cell %s.%s.\n", log_id(opts.port), log_id(opts.module), log_id(opts.cell));
|
||||
|
||||
if (opts.mode == "cnot0" || opts.mode == "cnot1") {
|
||||
mutate_cnot(design, opts, opts.mode == "cnot1");
|
||||
return;
|
||||
}
|
||||
|
||||
log_cmd_error("Invalid mode: %s\n", opts.mode.c_str());
|
||||
}
|
||||
} MutatePass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
92
passes/sat/supercover.cc
Normal file
92
passes/sat/supercover.cc
Normal file
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/sigtools.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct SupercoverPass : public Pass {
|
||||
SupercoverPass() : Pass("supercover", "add hi/lo cover cells for each wire bit") { }
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" supercover [options] [selection]\n");
|
||||
log("\n");
|
||||
log("This command adds two cover cells for each bit of each selected wire, one\n");
|
||||
log("checking for a hi signal level and one checking for lo level.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
// bool flag_noinit = false;
|
||||
|
||||
log_header(design, "Executing SUPERCOVER pass.\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
// if (args[argidx] == "-noinit") {
|
||||
// flag_noinit = true;
|
||||
// continue;
|
||||
// }
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
for (auto module : design->selected_modules())
|
||||
{
|
||||
SigMap sigmap(module);
|
||||
pool<SigBit> handled_bits;
|
||||
|
||||
int cnt_wire = 0, cnt_bits = 0;
|
||||
log("Adding cover cells to module %s.\n", log_id(module));
|
||||
for (auto wire : module->selected_wires())
|
||||
{
|
||||
bool counted_wire = false;
|
||||
std::string src = wire->get_src_attribute();
|
||||
|
||||
for (auto bit : sigmap(SigSpec(wire)))
|
||||
{
|
||||
if (bit.wire == nullptr)
|
||||
continue;
|
||||
|
||||
if (handled_bits.count(bit))
|
||||
continue;
|
||||
|
||||
SigSpec inv = module->Not(NEW_ID, bit);
|
||||
module->addCover(NEW_ID, bit, State::S1, src);
|
||||
module->addCover(NEW_ID, inv, State::S1, src);
|
||||
|
||||
handled_bits.insert(bit);
|
||||
if (!counted_wire) {
|
||||
counted_wire = false;
|
||||
cnt_wire++;
|
||||
}
|
||||
cnt_bits++;
|
||||
}
|
||||
}
|
||||
log(" added cover cells to %d wires, %d bits.\n", cnt_wire, cnt_bits);
|
||||
}
|
||||
}
|
||||
} SupercoverPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -24,6 +24,7 @@
|
|||
#include <istream>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#ifndef FILTERLIB
|
||||
#include "kernel/log.h"
|
||||
|
@ -86,15 +87,17 @@ int LibertyParser::lexer(std::string &str)
|
|||
{
|
||||
int c;
|
||||
|
||||
// eat whitespace
|
||||
do {
|
||||
c = f.get();
|
||||
} while (c == ' ' || c == '\t' || c == '\r');
|
||||
|
||||
if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_' || c == '-' || c == '+' || c == '.' || c == '[' || c == ']') {
|
||||
// search for identifiers, numbers, plus or minus.
|
||||
if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_' || c == '-' || c == '+' || c == '.') {
|
||||
str = c;
|
||||
while (1) {
|
||||
c = f.get();
|
||||
if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_' || c == '-' || c == '+' || c == '.' || c == '[' || c == ']')
|
||||
if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_' || c == '-' || c == '+' || c == '.')
|
||||
str += c;
|
||||
else
|
||||
break;
|
||||
|
@ -111,6 +114,8 @@ int LibertyParser::lexer(std::string &str)
|
|||
}
|
||||
}
|
||||
|
||||
// if it wasn't an identifer, number of array range,
|
||||
// maybe it's a string?
|
||||
if (c == '"') {
|
||||
str = "";
|
||||
while (1) {
|
||||
|
@ -125,9 +130,10 @@ int LibertyParser::lexer(std::string &str)
|
|||
return 'v';
|
||||
}
|
||||
|
||||
// if it wasn't a string, perhaps it's a comment or a forward slash?
|
||||
if (c == '/') {
|
||||
c = f.get();
|
||||
if (c == '*') {
|
||||
if (c == '*') { // start of '/*' block comment
|
||||
int last_c = 0;
|
||||
while (c > 0 && (last_c != '*' || c != '/')) {
|
||||
last_c = c;
|
||||
|
@ -136,7 +142,7 @@ int LibertyParser::lexer(std::string &str)
|
|||
line++;
|
||||
}
|
||||
return lexer(str);
|
||||
} else if (c == '/') {
|
||||
} else if (c == '/') { // start of '//' line comment
|
||||
while (c > 0 && c != '\n')
|
||||
c = f.get();
|
||||
line++;
|
||||
|
@ -144,24 +150,31 @@ int LibertyParser::lexer(std::string &str)
|
|||
}
|
||||
f.unget();
|
||||
// fprintf(stderr, "LEX: char >>/<<\n");
|
||||
return '/';
|
||||
return '/'; // a single '/' charater.
|
||||
}
|
||||
|
||||
// check for a backslash
|
||||
if (c == '\\') {
|
||||
c = f.get();
|
||||
c = f.get();
|
||||
if (c == '\r')
|
||||
c = f.get();
|
||||
if (c == '\n')
|
||||
if (c == '\n') {
|
||||
line++;
|
||||
return lexer(str);
|
||||
}
|
||||
f.unget();
|
||||
return '\\';
|
||||
}
|
||||
|
||||
// check for a new line
|
||||
if (c == '\n') {
|
||||
line++;
|
||||
return 'n';
|
||||
}
|
||||
|
||||
// anything else, such as ';' will get passed
|
||||
// through as literal items.
|
||||
|
||||
// if (c >= 32 && c < 255)
|
||||
// fprintf(stderr, "LEX: char >>%c<<\n", c);
|
||||
// else
|
||||
|
@ -175,14 +188,39 @@ LibertyAst *LibertyParser::parse()
|
|||
|
||||
int tok = lexer(str);
|
||||
|
||||
while (tok == 'n')
|
||||
// there are liberty files in the wild that
|
||||
// have superfluous ';' at the end of
|
||||
// a { ... }. We simply ignore a ';' here.
|
||||
// and get to the next statement.
|
||||
|
||||
while ((tok == 'n') || (tok == ';'))
|
||||
tok = lexer(str);
|
||||
|
||||
if (tok == '}' || tok < 0)
|
||||
return NULL;
|
||||
|
||||
if (tok != 'v')
|
||||
error();
|
||||
if (tok != 'v') {
|
||||
std::string eReport;
|
||||
switch(tok)
|
||||
{
|
||||
case 'n':
|
||||
error("Unexpected newline.");
|
||||
break;
|
||||
case '[':
|
||||
case ']':
|
||||
case '}':
|
||||
case '{':
|
||||
case '\"':
|
||||
case ':':
|
||||
eReport = "Unexpected '";
|
||||
eReport += static_cast<char>(tok);
|
||||
eReport += "'.";
|
||||
error(eReport);
|
||||
break;
|
||||
default:
|
||||
error();
|
||||
}
|
||||
}
|
||||
|
||||
LibertyAst *ast = new LibertyAst;
|
||||
ast->id = str;
|
||||
|
@ -191,12 +229,11 @@ LibertyAst *LibertyParser::parse()
|
|||
{
|
||||
tok = lexer(str);
|
||||
|
||||
if (tok == ';')
|
||||
// allow both ';' and new lines to
|
||||
// terminate a statement.
|
||||
if ((tok == ';') || (tok == 'n'))
|
||||
break;
|
||||
|
||||
if (tok == 'n')
|
||||
continue;
|
||||
|
||||
if (tok == ':' && ast->value.empty()) {
|
||||
tok = lexer(ast->value);
|
||||
if (tok != 'v')
|
||||
|
@ -210,7 +247,12 @@ LibertyAst *LibertyParser::parse()
|
|||
ast->value += str;
|
||||
tok = lexer(str);
|
||||
}
|
||||
if (tok == ';')
|
||||
|
||||
// In a liberty file, all key : value pairs should end in ';'
|
||||
// However, there are some liberty files in the wild that
|
||||
// just have a newline. We'll be kind and accept a newline
|
||||
// instead of the ';' too..
|
||||
if ((tok == ';') || (tok == 'n'))
|
||||
break;
|
||||
else
|
||||
error();
|
||||
|
@ -225,8 +267,70 @@ LibertyAst *LibertyParser::parse()
|
|||
continue;
|
||||
if (tok == ')')
|
||||
break;
|
||||
if (tok != 'v')
|
||||
error();
|
||||
|
||||
// FIXME: the AST needs to be extended to store
|
||||
// these vector ranges.
|
||||
if (tok == '[')
|
||||
{
|
||||
// parse vector range [A] or [A:B]
|
||||
std::string arg;
|
||||
tok = lexer(arg);
|
||||
if (tok != 'v')
|
||||
{
|
||||
// expected a vector array index
|
||||
error("Expected a number.");
|
||||
}
|
||||
else
|
||||
{
|
||||
// fixme: check for number A
|
||||
}
|
||||
tok = lexer(arg);
|
||||
// optionally check for : in case of [A:B]
|
||||
// if it isn't we just expect ']'
|
||||
// as we have [A]
|
||||
if (tok == ':')
|
||||
{
|
||||
tok = lexer(arg);
|
||||
if (tok != 'v')
|
||||
{
|
||||
// expected a vector array index
|
||||
error("Expected a number.");
|
||||
}
|
||||
else
|
||||
{
|
||||
// fixme: check for number B
|
||||
tok = lexer(arg);
|
||||
}
|
||||
}
|
||||
// expect a closing bracket of array range
|
||||
if (tok != ']')
|
||||
{
|
||||
error("Expected ']' on array range.");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (tok != 'v') {
|
||||
std::string eReport;
|
||||
switch(tok)
|
||||
{
|
||||
case 'n':
|
||||
error("Unexpected newline.");
|
||||
break;
|
||||
case '[':
|
||||
case ']':
|
||||
case '}':
|
||||
case '{':
|
||||
case '\"':
|
||||
case ':':
|
||||
eReport = "Unexpected '";
|
||||
eReport += static_cast<char>(tok);
|
||||
eReport += "'.";
|
||||
error(eReport);
|
||||
break;
|
||||
default:
|
||||
error();
|
||||
}
|
||||
}
|
||||
ast->args.push_back(arg);
|
||||
}
|
||||
continue;
|
||||
|
@ -255,6 +359,14 @@ void LibertyParser::error()
|
|||
log_error("Syntax error in liberty file on line %d.\n", line);
|
||||
}
|
||||
|
||||
void LibertyParser::error(const std::string &str)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Syntax error in liberty file on line " << line << ".\n";
|
||||
ss << " " << str << "\n";
|
||||
log_error("%s", ss.str().c_str());
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void LibertyParser::error()
|
||||
|
@ -263,25 +375,34 @@ void LibertyParser::error()
|
|||
exit(1);
|
||||
}
|
||||
|
||||
void LibertyParser::error(const std::string &str)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Syntax error in liberty file on line " << line << ".\n";
|
||||
ss << " " << str << "\n";
|
||||
printf("%s", ss.str().c_str());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**** BEGIN: http://svn.clifford.at/tools/trunk/examples/check.h ****/
|
||||
|
||||
#define CHECK_NV(result, check) \
|
||||
do { \
|
||||
auto _R = (result); \
|
||||
if (!(_R check)) { \
|
||||
fprintf(stderr, "Error from '%s' (%ld %s) in %s:%d.\n", \
|
||||
#result, (long int)_R, #check, __FILE__, __LINE__); \
|
||||
abort(); \
|
||||
} \
|
||||
auto _R = (result); \
|
||||
if (!(_R check)) { \
|
||||
fprintf(stderr, "Error from '%s' (%ld %s) in %s:%d.\n", \
|
||||
#result, (long int)_R, #check, __FILE__, __LINE__); \
|
||||
abort(); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define CHECK_COND(result) \
|
||||
do { \
|
||||
if (!(result)) { \
|
||||
fprintf(stderr, "Error from '%s' in %s:%d.\n", \
|
||||
#result, __FILE__, __LINE__); \
|
||||
abort(); \
|
||||
} \
|
||||
if (!(result)) { \
|
||||
fprintf(stderr, "Error from '%s' in %s:%d.\n", \
|
||||
#result, __FILE__, __LINE__); \
|
||||
abort(); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
/**** END: http://svn.clifford.at/tools/trunk/examples/check.h ****/
|
||||
|
|
|
@ -46,9 +46,17 @@ namespace Yosys
|
|||
LibertyAst *ast;
|
||||
LibertyParser(std::istream &f) : f(f), line(1), ast(parse()) {}
|
||||
~LibertyParser() { if (ast) delete ast; }
|
||||
|
||||
/* lexer return values:
|
||||
'v': identifier, string, array range [...] -> str holds the token string
|
||||
'n': newline
|
||||
anything else is a single character.
|
||||
*/
|
||||
int lexer(std::string &str);
|
||||
LibertyAst *parse();
|
||||
|
||||
LibertyAst *parse();
|
||||
void error();
|
||||
void error(const std::string &str);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue