Merge remote-tracking branch 'origin/master' into xc7srl

This commit is contained in:
Eddie Hung 2019-03-19 13:11:30 -07:00
commit 24553326dd
53 changed files with 2399 additions and 39 deletions

View File

@ -4,6 +4,17 @@
all necessary source files. (You can simply drag&drop a .zip file into
the issue editor.)*
Also, make sure that the issue is actually reproducable in current git
master of Yosys.
See https://stackoverflow.com/help/mcve for some information on how to
create a Minimal, Complete, and Verifiable example (MCVE).
Please do not waste our time with issues that lack sufficient information
to reproduce the issue easily. We will simply close those issues.
Contact https://www.symbioticeda.com/ if you need commercial support for Yosys.
## Expected behavior
*Please describe the behavior you would have expected from the tool.*
@ -11,6 +22,3 @@ the issue editor.)*
## Actual behavior
*Please describe how the behavior you see differs from the expected behavior.*
**Important Note:** Nobody will be able to help you and/or fix the issue if you
do not provide sufficient information for reproducing the problem.

2
.gitignore vendored
View File

@ -24,6 +24,8 @@
/yosys-abc.exe
/yosys-config
/yosys-smtbmc
/yosys-smtbmc.exe
/yosys-smtbmc-script.py
/yosys-filterlib
/yosys-filterlib.exe
/kernel/version_*.cc

View File

@ -575,7 +575,7 @@ test: $(TARGETS) $(EXTRA_TARGETS)
+cd tests/simple && bash run-test.sh $(SEEDOPT)
+cd tests/hana && bash run-test.sh $(SEEDOPT)
+cd tests/asicworld && bash run-test.sh $(SEEDOPT)
+cd tests/realmath && bash run-test.sh $(SEEDOPT)
# +cd tests/realmath && bash run-test.sh $(SEEDOPT)
+cd tests/share && bash run-test.sh $(SEEDOPT)
+cd tests/fsm && bash run-test.sh $(SEEDOPT)
+cd tests/techmap && bash run-test.sh
@ -585,6 +585,7 @@ test: $(TARGETS) $(EXTRA_TARGETS)
+cd tests/sat && bash run-test.sh
+cd tests/svinterfaces && bash run-test.sh $(SEEDOPT)
+cd tests/opt && bash run-test.sh
+cd tests/aiger && bash run-test.sh
@echo ""
@echo " Passed \"make test\"."
@echo ""

View File

@ -105,12 +105,15 @@ Makefile.
To build Yosys simply type 'make' in this directory.
$ make
$ make test
$ sudo make install
Note that this also downloads, builds and installs ABC (using yosys-abc
as executable name).
Tests are located in the tests subdirectory and can be executed using the test target. Note that you need gawk as well as a recent version of iverilog (i.e. build from git). Then, execute tests via:
$ make test
Getting Started
===============

View File

@ -204,7 +204,7 @@ void ILANG_BACKEND::dump_proc_switch(std::ostream &f, std::string indent, const
f << stringf("%s case ", indent.c_str());
for (size_t i = 0; i < (*it)->compare.size(); i++) {
if (i > 0)
f << stringf(", ");
f << stringf(" , ");
dump_sigspec(f, (*it)->compare[i]);
}
f << stringf("\n");

View File

@ -3,14 +3,30 @@ OBJS += backends/smt2/smt2.o
ifneq ($(CONFIG),mxe)
ifneq ($(CONFIG),emcc)
# MSYS targets support yosys-smtbmc, but require a launcher script
ifeq ($(CONFIG),$(filter $(CONFIG),msys2 msys2-64))
TARGETS += yosys-smtbmc.exe yosys-smtbmc-script.py
# Needed to find the Python interpreter for yosys-smtbmc scripts.
# Override if necessary, it is only used for msys2 targets.
PYTHON := $(shell cygpath -w -m $(PREFIX)/bin/python3)
yosys-smtbmc-script.py: backends/smt2/smtbmc.py
$(P) sed -e 's|##yosys-sys-path##|sys.path += [os.path.dirname(os.path.realpath(__file__)) + p for p in ["/share/python3", "/../share/yosys/python3"]]|;' \
-e "s|#!/usr/bin/env python3|#!$(PYTHON)|" < $< > $@
yosys-smtbmc.exe: misc/launcher.c yosys-smtbmc-script.py
$(P) gcc -DGUI=0 -O -s -o $@ $<
# Other targets
else
TARGETS += yosys-smtbmc
yosys-smtbmc: backends/smt2/smtbmc.py
$(P) sed 's|##yosys-sys-path##|sys.path += [os.path.dirname(os.path.realpath(__file__)) + p for p in ["/share/python3", "/../share/yosys/python3"]]|;' < $< > $@.new
$(Q) chmod +x $@.new
$(Q) mv $@.new $@
endif
$(eval $(call add_share_file,share/python3,backends/smt2/smtio.py))
endif
endif

View File

@ -1484,11 +1484,11 @@ else: # not tempind, covermode
smt_assert_antecedent("(|%s_h| s%d)" % (topmod, i))
smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, i-1, i))
smt_assert_consequent(get_constr_expr(constr_assumes, i))
print_msg("Re-solving with appended steps..")
if smt_check_sat() == "unsat":
print("%s Cannot appended steps without violating assumptions!" % smt.timestamp())
retstatus = False
break
print_msg("Re-solving with appended steps..")
if smt_check_sat() == "unsat":
print("%s Cannot appended steps without violating assumptions!" % smt.timestamp())
retstatus = False
break
print_anyconsts(step)
for i in range(step, last_check_step+1):
print_failed_asserts(i)

View File

@ -0,0 +1,3 @@
OBJS += frontends/aiger/aigerparse.o

View File

@ -0,0 +1,414 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Eddie Hung <eddie@fpgeh.com>
*
* 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.
*
*/
// [[CITE]] The AIGER And-Inverter Graph (AIG) Format Version 20071012
// Armin Biere. The AIGER And-Inverter Graph (AIG) Format Version 20071012. Technical Report 07/1, October 2011, FMV Reports Series, Institute for Formal Models and Verification, Johannes Kepler University, Altenbergerstr. 69, 4040 Linz, Austria.
// http://fmv.jku.at/papers/Biere-FMV-TR-07-1.pdf
#ifndef _WIN32
#include <libgen.h>
#endif
#include <array>
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
#include "aigerparse.h"
YOSYS_NAMESPACE_BEGIN
#define log_debug log
AigerReader::AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name)
: design(design), f(f), clk_name(clk_name)
{
module = new RTLIL::Module;
module->name = module_name;
if (design->module(module->name))
log_error("Duplicate definition of module %s!\n", log_id(module->name));
}
void AigerReader::parse_aiger()
{
std::string header;
f >> header;
if (header != "aag" && header != "aig")
log_error("Unsupported AIGER file!\n");
// Parse rest of header
if (!(f >> M >> I >> L >> O >> A))
log_error("Invalid AIGER header\n");
// Optional values
B = C = J = F = 0;
for (auto &i : std::array<std::reference_wrapper<unsigned>,4>{B, C, J, F}) {
if (f.peek() != ' ') break;
if (!(f >> i))
log_error("Invalid AIGER header\n");
}
std::string line;
std::getline(f, line); // Ignore up to start of next line, as standard
// says anything that follows could be used for
// optional sections
log_debug("M=%u I=%u L=%u O=%u A=%u B=%u C=%u J=%u F=%u\n", M, I, L, O, A, B, C, J, F);
line_count = 1;
if (header == "aag")
parse_aiger_ascii();
else if (header == "aig")
parse_aiger_binary();
else
log_abort();
// Parse footer (symbol table, comments, etc.)
unsigned l1;
std::string s;
for (int c = f.peek(); c != EOF; c = f.peek(), ++line_count) {
if (c == 'i' || c == 'l' || c == 'o') {
f.ignore(1);
if (!(f >> l1 >> s))
log_error("Line %u cannot be interpreted as a symbol entry!\n", line_count);
if ((c == 'i' && l1 > inputs.size()) || (c == 'l' && l1 > latches.size()) || (c == 'o' && l1 > outputs.size()))
log_error("Line %u has invalid symbol position!\n", line_count);
RTLIL::Wire* wire;
if (c == 'i') wire = inputs[l1];
else if (c == 'l') wire = latches[l1];
else if (c == 'o') wire = outputs[l1];
else log_abort();
module->rename(wire, stringf("\\%s", s.c_str()));
}
else if (c == 'b' || c == 'j' || c == 'f') {
// TODO
}
else if (c == 'c') {
f.ignore(1);
if (f.peek() == '\n')
break;
// Else constraint (TODO)
}
else
log_error("Line %u: cannot interpret first character '%c'!\n", line_count, c);
std::getline(f, line); // Ignore up to start of next line
}
module->fixup_ports();
design->add(module);
}
static RTLIL::Wire* createWireIfNotExists(RTLIL::Module *module, unsigned literal)
{
const unsigned variable = literal >> 1;
const bool invert = literal & 1;
RTLIL::IdString wire_name(stringf("\\n%d%s", variable, invert ? "_inv" : "")); // FIXME: is "_inv" the right suffix?
RTLIL::Wire *wire = module->wire(wire_name);
if (wire) return wire;
log_debug("Creating %s\n", wire_name.c_str());
wire = module->addWire(wire_name);
if (!invert) return wire;
RTLIL::IdString wire_inv_name(stringf("\\n%d", variable));
RTLIL::Wire *wire_inv = module->wire(wire_inv_name);
if (wire_inv) {
if (module->cell(wire_inv_name)) return wire;
}
else {
log_debug("Creating %s\n", wire_inv_name.c_str());
wire_inv = module->addWire(wire_inv_name);
}
log_debug("Creating %s = ~%s\n", wire_name.c_str(), wire_inv_name.c_str());
module->addNotGate(stringf("\\n%d_not", variable), wire_inv, wire); // FIXME: is "_not" the right suffix?
return wire;
}
void AigerReader::parse_aiger_ascii()
{
std::string line;
std::stringstream ss;
unsigned l1, l2, l3;
// Parse inputs
for (unsigned i = 0; i < I; ++i, ++line_count) {
if (!(f >> l1))
log_error("Line %u cannot be interpreted as an input!\n", line_count);
log_debug("%d is an input\n", l1);
log_assert(!(l1 & 1)); // TODO: Inputs can't be inverted?
RTLIL::Wire *wire = createWireIfNotExists(module, l1);
wire->port_input = true;
inputs.push_back(wire);
}
// Parse latches
RTLIL::Wire *clk_wire = nullptr;
if (L > 0) {
clk_wire = module->wire(clk_name);
log_assert(!clk_wire);
log_debug("Creating %s\n", clk_name.c_str());
clk_wire = module->addWire(clk_name);
clk_wire->port_input = true;
}
for (unsigned i = 0; i < L; ++i, ++line_count) {
if (!(f >> l1 >> l2))
log_error("Line %u cannot be interpreted as a latch!\n", line_count);
log_debug("%d %d is a latch\n", l1, l2);
log_assert(!(l1 & 1)); // TODO: Latch outputs can't be inverted?
RTLIL::Wire *q_wire = createWireIfNotExists(module, l1);
RTLIL::Wire *d_wire = createWireIfNotExists(module, l2);
module->addDffGate(NEW_ID, clk_wire, d_wire, q_wire);
// Reset logic is optional in AIGER 1.9
if (f.peek() == ' ') {
if (!(f >> l3))
log_error("Line %u cannot be interpreted as a latch!\n", line_count);
if (l3 == 0 || l3 == 1)
q_wire->attributes["\\init"] = RTLIL::Const(l3);
else if (l3 == l1) {
//q_wire->attributes["\\init"] = RTLIL::Const(RTLIL::State::Sx);
}
else
log_error("Line %u has invalid reset literal for latch!\n", line_count);
}
else {
// AIGER latches are assumed to be initialized to zero
q_wire->attributes["\\init"] = RTLIL::Const(0);
}
latches.push_back(q_wire);
}
// Parse outputs
for (unsigned i = 0; i < O; ++i, ++line_count) {
if (!(f >> l1))
log_error("Line %u cannot be interpreted as an output!\n", line_count);
log_debug("%d is an output\n", l1);
RTLIL::Wire *wire = createWireIfNotExists(module, l1);
wire->port_output = true;
outputs.push_back(wire);
}
std::getline(f, line); // Ignore up to start of next line
// TODO: Parse bad state properties
for (unsigned i = 0; i < B; ++i, ++line_count)
std::getline(f, line); // Ignore up to start of next line
// TODO: Parse invariant constraints
for (unsigned i = 0; i < C; ++i, ++line_count)
std::getline(f, line); // Ignore up to start of next line
// TODO: Parse justice properties
for (unsigned i = 0; i < J; ++i, ++line_count)
std::getline(f, line); // Ignore up to start of next line
// TODO: Parse fairness constraints
for (unsigned i = 0; i < F; ++i, ++line_count)
std::getline(f, line); // Ignore up to start of next line
// Parse AND
for (unsigned i = 0; i < A; ++i) {
if (!(f >> l1 >> l2 >> l3))
log_error("Line %u cannot be interpreted as an AND!\n", line_count);
log_debug("%d %d %d is an AND\n", l1, l2, l3);
log_assert(!(l1 & 1)); // TODO: Output of ANDs can't be inverted?
RTLIL::Wire *o_wire = createWireIfNotExists(module, l1);
RTLIL::Wire *i1_wire = createWireIfNotExists(module, l2);
RTLIL::Wire *i2_wire = createWireIfNotExists(module, l3);
module->addAndGate(NEW_ID, i1_wire, i2_wire, o_wire);
}
std::getline(f, line); // Ignore up to start of next line
}
static unsigned parse_next_delta_literal(std::istream &f, unsigned ref)
{
unsigned x = 0, i = 0;
unsigned char ch;
while ((ch = f.get()) & 0x80)
x |= (ch & 0x7f) << (7 * i++);
return ref - (x | (ch << (7 * i)));
}
void AigerReader::parse_aiger_binary()
{
unsigned l1, l2, l3;
std::string line;
// Parse inputs
for (unsigned i = 1; i <= I; ++i) {
RTLIL::Wire *wire = createWireIfNotExists(module, i << 1);
wire->port_input = true;
inputs.push_back(wire);
}
// Parse latches
RTLIL::Wire *clk_wire = nullptr;
if (L > 0) {
clk_wire = module->wire(clk_name);
log_assert(!clk_wire);
log_debug("Creating %s\n", clk_name.c_str());
clk_wire = module->addWire(clk_name);
clk_wire->port_input = true;
}
l1 = (I+1) * 2;
for (unsigned i = 0; i < L; ++i, ++line_count, l1 += 2) {
if (!(f >> l2))
log_error("Line %u cannot be interpreted as a latch!\n", line_count);
log_debug("%d %d is a latch\n", l1, l2);
RTLIL::Wire *q_wire = createWireIfNotExists(module, l1);
RTLIL::Wire *d_wire = createWireIfNotExists(module, l2);
module->addDff(NEW_ID, clk_wire, d_wire, q_wire);
// Reset logic is optional in AIGER 1.9
if (f.peek() == ' ') {
if (!(f >> l3))
log_error("Line %u cannot be interpreted as a latch!\n", line_count);
if (l3 == 0 || l3 == 1)
q_wire->attributes["\\init"] = RTLIL::Const(l3);
else if (l3 == l1) {
//q_wire->attributes["\\init"] = RTLIL::Const(RTLIL::State::Sx);
}
else
log_error("Line %u has invalid reset literal for latch!\n", line_count);
}
else {
// AIGER latches are assumed to be initialized to zero
q_wire->attributes["\\init"] = RTLIL::Const(0);
}
latches.push_back(q_wire);
}
// Parse outputs
for (unsigned i = 0; i < O; ++i, ++line_count) {
if (!(f >> l1))
log_error("Line %u cannot be interpreted as an output!\n", line_count);
log_debug("%d is an output\n", l1);
RTLIL::Wire *wire = createWireIfNotExists(module, l1);
wire->port_output = true;
outputs.push_back(wire);
}
std::getline(f, line); // Ignore up to start of next line
// TODO: Parse bad state properties
for (unsigned i = 0; i < B; ++i, ++line_count)
std::getline(f, line); // Ignore up to start of next line
// TODO: Parse invariant constraints
for (unsigned i = 0; i < C; ++i, ++line_count)
std::getline(f, line); // Ignore up to start of next line
// TODO: Parse justice properties
for (unsigned i = 0; i < J; ++i, ++line_count)
std::getline(f, line); // Ignore up to start of next line
// TODO: Parse fairness constraints
for (unsigned i = 0; i < F; ++i, ++line_count)
std::getline(f, line); // Ignore up to start of next line
// Parse AND
l1 = (I+L+1) << 1;
for (unsigned i = 0; i < A; ++i, ++line_count, l1 += 2) {
l2 = parse_next_delta_literal(f, l1);
l3 = parse_next_delta_literal(f, l2);
log_debug("%d %d %d is an AND\n", l1, l2, l3);
log_assert(!(l1 & 1)); // TODO: Output of ANDs can't be inverted?
RTLIL::Wire *o_wire = createWireIfNotExists(module, l1);
RTLIL::Wire *i1_wire = createWireIfNotExists(module, l2);
RTLIL::Wire *i2_wire = createWireIfNotExists(module, l3);
RTLIL::Cell *and_cell = module->addCell(NEW_ID, "$_AND_");
and_cell->setPort("\\A", i1_wire);
and_cell->setPort("\\B", i2_wire);
and_cell->setPort("\\Y", o_wire);
}
}
struct AigerFrontend : public Frontend {
AigerFrontend() : Frontend("aiger", "read AIGER file") { }
void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" read_aiger [options] [filename]\n");
log("\n");
log("Load module from an AIGER file into the current design.\n");
log("\n");
log(" -module_name <module_name>\n");
log(" Name of module to be created (default: <filename>)"
#ifdef _WIN32
"top" // FIXME
#else
"<filename>"
#endif
")\n");
log("\n");
log(" -clk_name <wire_name>\n");
log(" AIGER latches to be transformed into posedge DFFs clocked by wire of");
log(" this name (default: clk)\n");
log("\n");
}
void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing AIGER frontend.\n");
RTLIL::IdString clk_name = "\\clk";
RTLIL::IdString module_name;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
std::string arg = args[argidx];
if (arg == "-module_name" && argidx+1 < args.size()) {
module_name = RTLIL::escape_id(args[++argidx]);
continue;
}
if (arg == "-clk_name" && argidx+1 < args.size()) {
clk_name = RTLIL::escape_id(args[++argidx]);
continue;
}
break;
}
extra_args(f, filename, args, argidx);
if (module_name.empty()) {
#ifdef _WIN32
module_name = "top"; // FIXME: basename equivalent on Win32?
#else
char* bn = strdup(filename.c_str());
module_name = RTLIL::escape_id(bn);
free(bn);
#endif
}
AigerReader reader(design, *f, module_name, clk_name);
reader.parse_aiger();
}
} AigerFrontend;
YOSYS_NAMESPACE_END

View File

@ -0,0 +1,51 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Eddie Hung <eddie@fpgeh.com>
*
* 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.
*
*/
#ifndef ABC_AIGERPARSE
#define ABC_AIGERPARSE
#include "kernel/yosys.h"
YOSYS_NAMESPACE_BEGIN
struct AigerReader
{
RTLIL::Design *design;
std::istream &f;
RTLIL::IdString clk_name;
RTLIL::Module *module;
unsigned M, I, L, O, A;
unsigned B, C, J, F; // Optional in AIGER 1.9
unsigned line_count;
std::vector<RTLIL::Wire*> inputs;
std::vector<RTLIL::Wire*> latches;
std::vector<RTLIL::Wire*> outputs;
AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name);
void parse_aiger();
void parse_aiger_ascii();
void parse_aiger_binary();
};
YOSYS_NAMESPACE_END
#endif

View File

@ -525,7 +525,16 @@ struct AST_INTERNAL::ProcessGenerator
}
if (last_generated_case != NULL && ast->get_bool_attribute("\\full_case") && default_case == NULL) {
#if 0
// this is a valid transformation, but as optimization it is premature.
// better: add a default case that assigns 'x' to everything, and let later
// optimizations take care of the rest
last_generated_case->compare.clear();
#else
default_case = new RTLIL::CaseRule;
addChunkActions(default_case->actions, this_case_eq_ltemp, SigSpec(State::Sx, GetSize(this_case_eq_rvalue)));
sw->cases.push_back(default_case);
#endif
} else {
if (default_case == NULL) {
default_case = new RTLIL::CaseRule;

View File

@ -2863,7 +2863,11 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma
for (size_t i = 0; i < children.size(); i++) {
AstNode *child = children[i];
if (child->type != AST_FUNCTION && child->type != AST_TASK && child->type != AST_PREFIX)
// AST_PREFIX member names should not be prefixed; a nested AST_PREFIX
// still needs to recursed-into
if (type == AST_PREFIX && i == 1 && child->type == AST_IDENTIFIER)
continue;
if (child->type != AST_FUNCTION && child->type != AST_TASK)
child->expand_genblock(index_var, prefix, name_map);
}

View File

@ -584,7 +584,7 @@ struct BlifFrontend : public Frontend {
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" read_blif [filename]\n");
log(" read_blif [options] [filename]\n");
log("\n");
log("Load modules from a BLIF file into the current design.\n");
log("\n");

View File

@ -81,6 +81,27 @@ struct CellTypes
}
void setup_internals()
{
setup_internals_eval();
IdString A = "\\A", B = "\\B", EN = "\\EN", Y = "\\Y";
setup_type("$tribuf", {A, EN}, {Y}, true);
setup_type("$assert", {A, EN}, pool<RTLIL::IdString>(), true);
setup_type("$assume", {A, EN}, pool<RTLIL::IdString>(), true);
setup_type("$live", {A, EN}, pool<RTLIL::IdString>(), true);
setup_type("$fair", {A, EN}, pool<RTLIL::IdString>(), true);
setup_type("$cover", {A, EN}, pool<RTLIL::IdString>(), true);
setup_type("$initstate", pool<RTLIL::IdString>(), {Y}, true);
setup_type("$anyconst", pool<RTLIL::IdString>(), {Y}, true);
setup_type("$anyseq", pool<RTLIL::IdString>(), {Y}, true);
setup_type("$allconst", pool<RTLIL::IdString>(), {Y}, true);
setup_type("$allseq", pool<RTLIL::IdString>(), {Y}, true);
setup_type("$equiv", {A, B}, {Y}, true);
}
void setup_internals_eval()
{
std::vector<RTLIL::IdString> unary_ops = {
"$not", "$pos", "$neg",
@ -111,20 +132,6 @@ struct CellTypes
setup_type("$lcu", {P, G, CI}, {CO}, true);
setup_type("$alu", {A, B, CI, BI}, {X, Y, CO}, true);
setup_type("$fa", {A, B, C}, {X, Y}, true);
setup_type("$tribuf", {A, EN}, {Y}, true);
setup_type("$assert", {A, EN}, pool<RTLIL::IdString>(), true);
setup_type("$assume", {A, EN}, pool<RTLIL::IdString>(), true);
setup_type("$live", {A, EN}, pool<RTLIL::IdString>(), true);
setup_type("$fair", {A, EN}, pool<RTLIL::IdString>(), true);
setup_type("$cover", {A, EN}, pool<RTLIL::IdString>(), true);
setup_type("$initstate", pool<RTLIL::IdString>(), {Y}, true);
setup_type("$anyconst", pool<RTLIL::IdString>(), {Y}, true);
setup_type("$anyseq", pool<RTLIL::IdString>(), {Y}, true);
setup_type("$allconst", pool<RTLIL::IdString>(), {Y}, true);
setup_type("$allseq", pool<RTLIL::IdString>(), {Y}, true);
setup_type("$equiv", {A, B}, {Y}, true);
}
void setup_internals_mem()
@ -153,6 +160,15 @@ struct CellTypes
}
void setup_stdcells()
{
setup_stdcells_eval();
IdString A = "\\A", E = "\\E", Y = "\\Y";
setup_type("$_TBUF_", {A, E}, {Y}, true);
}
void setup_stdcells_eval()
{
IdString A = "\\A", B = "\\B", C = "\\C", D = "\\D";
IdString E = "\\E", F = "\\F", G = "\\G", H = "\\H";
@ -179,7 +195,6 @@ struct CellTypes
setup_type("$_OAI3_", {A, B, C}, {Y}, true);
setup_type("$_AOI4_", {A, B, C, D}, {Y}, true);
setup_type("$_OAI4_", {A, B, C, D}, {Y}, true);
setup_type("$_TBUF_", {A, E}, {Y}, true);
}
void setup_stdcells_mem()

View File

@ -557,9 +557,11 @@ public:
void clear() { hashtable.clear(); entries.clear(); }
iterator begin() { return iterator(this, int(entries.size())-1); }
iterator element(int n) { return iterator(this, int(entries.size())-1-n); }
iterator end() { return iterator(nullptr, -1); }
const_iterator begin() const { return const_iterator(this, int(entries.size())-1); }
const_iterator element(int n) const { return const_iterator(this, int(entries.size())-1-n); }
const_iterator end() const { return const_iterator(nullptr, -1); }
};
@ -881,9 +883,11 @@ public:
void clear() { hashtable.clear(); entries.clear(); }
iterator begin() { return iterator(this, int(entries.size())-1); }
iterator element(int n) { return iterator(this, int(entries.size())-1-n); }
iterator end() { return iterator(nullptr, -1); }
const_iterator begin() const { return const_iterator(this, int(entries.size())-1); }
const_iterator element(int n) const { return const_iterator(this, int(entries.size())-1-n); }
const_iterator end() const { return const_iterator(nullptr, -1); }
};
@ -952,6 +956,7 @@ public:
void clear() { database.clear(); }
const_iterator begin() const { return database.begin(); }
const_iterator element(int n) const { return database.element(n); }
const_iterator end() const { return database.end(); }
};
@ -1051,6 +1056,7 @@ public:
void clear() { database.clear(); parents.clear(); }
const_iterator begin() const { return database.begin(); }
const_iterator element(int n) const { return database.element(n); }
const_iterator end() const { return database.end(); }
};

View File

@ -760,7 +760,7 @@ namespace {
void check()
{
if (cell->type.substr(0, 1) != "$" || cell->type.substr(0, 3) == "$__" || cell->type.substr(0, 8) == "$paramod" ||
if (cell->type.substr(0, 1) != "$" || cell->type.substr(0, 3) == "$__" || cell->type.substr(0, 8) == "$paramod" || cell->type.substr(0,10) == "$fmcombine" ||
cell->type.substr(0, 9) == "$verific$" || cell->type.substr(0, 7) == "$array:" || cell->type.substr(0, 8) == "$extern:")
return;
@ -2360,7 +2360,7 @@ void RTLIL::Cell::check()
void RTLIL::Cell::fixup_parameters(bool set_a_signed, bool set_b_signed)
{
if (type.substr(0, 1) != "$" || type.substr(0, 2) == "$_" || type.substr(0, 8) == "$paramod" ||
if (type.substr(0, 1) != "$" || type.substr(0, 2) == "$_" || type.substr(0, 8) == "$paramod" || type.substr(0,10) == "$fmcombine" ||
type.substr(0, 9) == "$verific$" || type.substr(0, 7) == "$array:" || type.substr(0, 8) == "$extern:")
return;

358
misc/launcher.c Normal file
View File

@ -0,0 +1,358 @@
/* This file comes from the PyPA Setuptools repository, commit 16e452a:
https://github.com/pypa/setuptools
Modifications include this comment and inline inclusion of the LICENSE text. */
/* Copyright (C) 2016 Jason R Coombs <jaraco@jaraco.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. */
/* Setuptools Script Launcher for Windows
This is a stub executable for Windows that functions somewhat like
Effbot's "exemaker", in that it runs a script with the same name but
a .py extension, using information from a #! line. It differs in that
it spawns the actual Python executable, rather than attempting to
hook into the Python DLL. This means that the script will run with
sys.executable set to the Python executable, where exemaker ends up with
sys.executable pointing to itself. (Which means it won't work if you try
to run another Python process using sys.executable.)
To build/rebuild with mingw32, do this in the setuptools project directory:
gcc -DGUI=0 -mno-cygwin -O -s -o setuptools/cli.exe launcher.c
gcc -DGUI=1 -mwindows -mno-cygwin -O -s -o setuptools/gui.exe launcher.c
To build for Windows RT, install both Visual Studio Express for Windows 8
and for Windows Desktop (both freeware), create "win32" application using
"Windows Desktop" version, create new "ARM" target via
"Configuration Manager" menu and modify ".vcxproj" file by adding
"<WindowsSDKDesktopARMSupport>true</WindowsSDKDesktopARMSupport>" tag
as child of "PropertyGroup" tags that has "Debug|ARM" and "Release|ARM"
properties.
It links to msvcrt.dll, but this shouldn't be a problem since it doesn't
actually run Python in the same process. Note that using 'exec' instead
of 'spawn' doesn't work, because on Windows this leads to the Python
executable running in the *background*, attached to the same console
window, meaning you get a command prompt back *before* Python even finishes
starting. So, we have to use spawnv() and wait for Python to exit before
continuing. :(
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <tchar.h>
#include <fcntl.h>
int child_pid=0;
int fail(char *format, char *data) {
/* Print error message to stderr and return 2 */
fprintf(stderr, format, data);
return 2;
}
char *quoted(char *data) {
int i, ln = strlen(data), nb;
/* We allocate twice as much space as needed to deal with worse-case
of having to escape everything. */
char *result = calloc(ln*2+3, sizeof(char));
char *presult = result;
*presult++ = '"';
for (nb=0, i=0; i < ln; i++)
{
if (data[i] == '\\')
nb += 1;
else if (data[i] == '"')
{
for (; nb > 0; nb--)
*presult++ = '\\';
*presult++ = '\\';
}
else
nb = 0;
*presult++ = data[i];
}
for (; nb > 0; nb--) /* Deal w trailing slashes */
*presult++ = '\\';
*presult++ = '"';
*presult++ = 0;
return result;
}
char *loadable_exe(char *exename) {
/* HINSTANCE hPython; DLL handle for python executable */
char *result;
/* hPython = LoadLibraryEx(exename, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
if (!hPython) return NULL; */
/* Return the absolute filename for spawnv */
result = calloc(MAX_PATH, sizeof(char));
strncpy(result, exename, MAX_PATH);
/*if (result) GetModuleFileNameA(hPython, result, MAX_PATH);
FreeLibrary(hPython); */
return result;
}
char *find_exe(char *exename, char *script) {
char drive[_MAX_DRIVE], dir[_MAX_DIR], fname[_MAX_FNAME], ext[_MAX_EXT];
char path[_MAX_PATH], c, *result;
/* convert slashes to backslashes for uniform search below */
result = exename;
while (c = *result++) if (c=='/') result[-1] = '\\';
_splitpath(exename, drive, dir, fname, ext);
if (drive[0] || dir[0]=='\\') {
return loadable_exe(exename); /* absolute path, use directly */
}
/* Use the script's parent directory, which should be the Python home
(This should only be used for bdist_wininst-installed scripts, because
easy_install-ed scripts use the absolute path to python[w].exe
*/
_splitpath(script, drive, dir, fname, ext);
result = dir + strlen(dir) -1;
if (*result == '\\') result--;
while (*result != '\\' && result>=dir) *result-- = 0;
_makepath(path, drive, dir, exename, NULL);
return loadable_exe(path);
}
char **parse_argv(char *cmdline, int *argc)
{
/* Parse a command line in-place using MS C rules */
char **result = calloc(strlen(cmdline), sizeof(char *));
char *output = cmdline;
char c;
int nb = 0;
int iq = 0;
*argc = 0;
result[0] = output;
while (isspace(*cmdline)) cmdline++; /* skip leading spaces */
do {
c = *cmdline++;
if (!c || (isspace(c) && !iq)) {
while (nb) {*output++ = '\\'; nb--; }
*output++ = 0;
result[++*argc] = output;
if (!c) return result;
while (isspace(*cmdline)) cmdline++; /* skip leading spaces */
if (!*cmdline) return result; /* avoid empty arg if trailing ws */
continue;
}
if (c == '\\')
++nb; /* count \'s */
else {
if (c == '"') {
if (!(nb & 1)) { iq = !iq; c = 0; } /* skip " unless odd # of \ */
nb = nb >> 1; /* cut \'s in half */
}
while (nb) {*output++ = '\\'; nb--; }
if (c) *output++ = c;
}
} while (1);
}
void pass_control_to_child(DWORD control_type) {
/*
* distribute-issue207
* passes the control event to child process (Python)
*/
if (!child_pid) {
return;
}
GenerateConsoleCtrlEvent(child_pid,0);
}
BOOL control_handler(DWORD control_type) {
/*
* distribute-issue207
* control event handler callback function
*/
switch (control_type) {
case CTRL_C_EVENT:
pass_control_to_child(0);
break;
}
return TRUE;
}
int create_and_wait_for_subprocess(char* command) {
/*
* distribute-issue207
* launches child process (Python)
*/
DWORD return_value = 0;
LPSTR commandline = command;
STARTUPINFOA s_info;
PROCESS_INFORMATION p_info;
ZeroMemory(&p_info, sizeof(p_info));
ZeroMemory(&s_info, sizeof(s_info));
s_info.cb = sizeof(STARTUPINFO);
// set-up control handler callback funciotn
SetConsoleCtrlHandler((PHANDLER_ROUTINE) control_handler, TRUE);
if (!CreateProcessA(NULL, commandline, NULL, NULL, TRUE, 0, NULL, NULL, &s_info, &p_info)) {
fprintf(stderr, "failed to create process.\n");
return 0;
}
child_pid = p_info.dwProcessId;
// wait for Python to exit
WaitForSingleObject(p_info.hProcess, INFINITE);
if (!GetExitCodeProcess(p_info.hProcess, &return_value)) {
fprintf(stderr, "failed to get exit code from process.\n");
return 0;
}
return return_value;
}
char* join_executable_and_args(char *executable, char **args, int argc)
{
/*
* distribute-issue207
* CreateProcess needs a long string of the executable and command-line arguments,
* so we need to convert it from the args that was built
*/
int len,counter;
char* cmdline;
len=strlen(executable)+2;
for (counter=1; counter<argc; counter++) {
len+=strlen(args[counter])+1;
}
cmdline = (char*)calloc(len, sizeof(char));
sprintf(cmdline, "%s", executable);
len=strlen(executable);
for (counter=1; counter<argc; counter++) {
sprintf(cmdline+len, " %s", args[counter]);
len+=strlen(args[counter])+1;
}
return cmdline;
}
int run(int argc, char **argv, int is_gui) {
char python[256]; /* python executable's filename*/
char *pyopt; /* Python option */
char script[256]; /* the script's filename */
int scriptf; /* file descriptor for script file */
char **newargs, **newargsp, **parsedargs; /* argument array for exec */
char *ptr, *end; /* working pointers for string manipulation */
char *cmdline;
int i, parsedargc; /* loop counter */
/* compute script name from our .exe name*/
GetModuleFileNameA(NULL, script, sizeof(script));
end = script + strlen(script);
while( end>script && *end != '.')
*end-- = '\0';
*end-- = '\0';
strcat(script, (GUI ? "-script.pyw" : "-script.py"));
/* figure out the target python executable */
scriptf = open(script, O_RDONLY);
if (scriptf == -1) {
return fail("Cannot open %s\n", script);
}
end = python + read(scriptf, python, sizeof(python));
close(scriptf);
ptr = python-1;
while(++ptr < end && *ptr && *ptr!='\n' && *ptr!='\r') {;}
*ptr-- = '\0';
if (strncmp(python, "#!", 2)) {
/* default to python.exe if no #! header */
strcpy(python, "#!python.exe");
}
parsedargs = parse_argv(python+2, &parsedargc);
/* Using spawnv() can fail strangely if you e.g. find the Cygwin
Python, so we'll make sure Windows can find and load it */
ptr = find_exe(parsedargs[0], script);
if (!ptr) {
return fail("Cannot find Python executable %s\n", parsedargs[0]);
}
/* printf("Python executable: %s\n", ptr); */
/* Argument array needs to be
parsedargc + argc, plus 1 for null sentinel */
newargs = (char **)calloc(parsedargc + argc + 1, sizeof(char *));
newargsp = newargs;
*newargsp++ = quoted(ptr);
for (i = 1; i<parsedargc; i++) *newargsp++ = quoted(parsedargs[i]);
*newargsp++ = quoted(script);
for (i = 1; i < argc; i++) *newargsp++ = quoted(argv[i]);
*newargsp++ = NULL;
/* printf("args 0: %s\nargs 1: %s\n", newargs[0], newargs[1]); */
if (is_gui) {
/* Use exec, we don't need to wait for the GUI to finish */
execv(ptr, (const char * const *)(newargs));
return fail("Could not exec %s", ptr); /* shouldn't get here! */
}
/*
* distribute-issue207: using CreateProcessA instead of spawnv
*/
cmdline = join_executable_and_args(ptr, newargs, parsedargc + argc);
return create_and_wait_for_subprocess(cmdline);
}
int WINAPI WinMain(HINSTANCE hI, HINSTANCE hP, LPSTR lpCmd, int nShow) {
return run(__argc, __argv, GUI);
}
int main(int argc, char** argv) {
return run(argc, argv, GUI);
}

View File

@ -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);

View File

@ -9,4 +9,6 @@ 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

341
passes/sat/fmcombine.cc Normal file
View 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

905
passes/sat/mutate.cc Normal file
View File

@ -0,0 +1,905 @@
/*
* 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;
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, int N)
{
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;
}
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, N, rng);
log("Reduced database size: %d\n", GetSize(database));
}
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;
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(" -seed N\n");
log(" RNG seed for selecting mutations\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;
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] == "-seed" && argidx+1 < args.size()) {
opts.seed = atoi(args[++argidx].c_str());
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, N);
return;
}
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.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

View File

@ -122,7 +122,8 @@ struct SynthXilinxPass : public Pass
log("\n");
log(" map_cells:\n");
log(" techmap -map +/xilinx/cells_map.v\n");
log(" dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT -ff FDSE Q INIT\n");
log(" dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT -ff FDSE Q INIT \\\n");
log(" -ff FDRE_1 Q INIT -ff FDCE_1 Q INIT -ff FDPE_1 Q INIT -ff FDSE_1 Q INIT\n");
log(" clean\n");
log("\n");
log(" check:\n");
@ -278,7 +279,8 @@ struct SynthXilinxPass : public Pass
if (check_label(active, run_from, run_to, "map_cells"))
{
Pass::call(design, "techmap -map +/xilinx/cells_map.v");
Pass::call(design, "dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT -ff FDSE Q INIT");
Pass::call(design, "dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT -ff FDSE Q INIT "
"-ff FDRE_1 Q INIT -ff FDCE_1 Q INIT -ff FDPE_1 Q INIT -ff FDSE_1 Q INIT");
Pass::call(design, "clean");
}

5
tests/aiger/and.aag Normal file
View File

@ -0,0 +1,5 @@
aag 3 2 0 1 1
2
4
6
6 2 4

3
tests/aiger/and.aig Normal file
View File

@ -0,0 +1,3 @@
aig 3 2 0 1 1
6


3
tests/aiger/buffer.aag Normal file
View File

@ -0,0 +1,3 @@
aag 1 1 0 1 0
2
2

2
tests/aiger/buffer.aig Normal file
View File

@ -0,0 +1,2 @@
aig 1 1 0 1 0
2

3
tests/aiger/cnt1.aag Normal file
View File

@ -0,0 +1,3 @@
aag 1 0 1 0 0 1
2 3
2

3
tests/aiger/cnt1.aig Normal file
View File

@ -0,0 +1,3 @@
aig 1 0 1 0 0 1
3
2

8
tests/aiger/cnt1e.aag Normal file
View File

@ -0,0 +1,8 @@
aag 5 1 1 0 3 1
2
4 10
4
6 5 3
8 4 2
10 9 7
b0 AIGER_NEVER

4
tests/aiger/cnt1e.aig Normal file
View File

@ -0,0 +1,4 @@
aig 5 1 1 0 3 1
10
4
b0 AIGER_NEVER

1
tests/aiger/empty.aag Normal file
View File

@ -0,0 +1 @@
aag 0 0 0 0 0

1
tests/aiger/empty.aig Normal file
View File

@ -0,0 +1 @@
aig 0 0 0 0 0

2
tests/aiger/false.aag Normal file
View File

@ -0,0 +1,2 @@
aag 0 0 0 1 0
0

2
tests/aiger/false.aig Normal file
View File

@ -0,0 +1,2 @@
aig 0 0 0 1 0
0

14
tests/aiger/halfadder.aag Normal file
View File

@ -0,0 +1,14 @@
aag 7 2 0 2 3
2
4
6
12
6 13 15
12 2 4
14 3 5
i0 x
i1 y
o0 s
o1 c
c
half adder

View File

@ -0,0 +1,9 @@
aig 5 2 0 2 3
10
6
i0 x
i1 y
o0 s
o1 c
c
half adder

3
tests/aiger/inverter.aag Normal file
View File

@ -0,0 +1,3 @@
aag 1 1 0 1 0
2
3

2
tests/aiger/inverter.aig Normal file
View File

@ -0,0 +1,2 @@
aig 1 1 0 1 0
3

4
tests/aiger/notcnt1.aag Normal file
View File

@ -0,0 +1,4 @@
aag 1 0 1 0 0 1
2 3
3
b0 AIGER_NEVER

4
tests/aiger/notcnt1.aig Normal file
View File

@ -0,0 +1,4 @@
aig 1 0 1 0 0 1
3
3
b0 AIGER_NEVER

8
tests/aiger/notcnt1e.aag Normal file
View File

@ -0,0 +1,8 @@
aag 5 1 1 0 3 1
2
4 10
5
6 5 3
8 4 2
10 9 7
b0 AIGER_NEVER

4
tests/aiger/notcnt1e.aig Normal file
View File

@ -0,0 +1,4 @@
aig 5 1 1 0 3 1
10
5
b0 AIGER_NEVER

5
tests/aiger/or.aag Normal file
View File

@ -0,0 +1,5 @@
aag 3 2 0 1 1
2
4
7
6 3 5

3
tests/aiger/or.aig Normal file
View File

@ -0,0 +1,3 @@
aig 3 2 0 1 1
7


24
tests/aiger/run-test.sh Executable file
View File

@ -0,0 +1,24 @@
#!/bin/bash
OPTIND=1
seed="" # default to no seed specified
while getopts "S:" opt
do
case "$opt" in
S) arg="${OPTARG#"${OPTARG%%[![:space:]]*}"}" # remove leading space
seed="SEED=$arg" ;;
esac
done
shift "$((OPTIND-1))"
# check for Icarus Verilog
if ! which iverilog > /dev/null ; then
echo "$0: Error: Icarus Verilog 'iverilog' not found."
exit 1
fi
echo "===== AAG ======"
${MAKE:-make} -f ../tools/autotest.mk $seed *.aag EXTRA_FLAGS="-f aiger"
echo "===== AIG ======"
exec ${MAKE:-make} -f ../tools/autotest.mk $seed *.aig EXTRA_FLAGS="-f aiger"

14
tests/aiger/toggle-re.aag Normal file
View File

@ -0,0 +1,14 @@
aag 7 2 1 2 4
2
4
6 8
6
7
8 4 10
10 13 15
12 2 6
14 3 7
i0 enable
i1 reset
o0 Q
o1 !Q

View File

@ -0,0 +1,8 @@
aig 7 2 1 2 4
14
6
7
i0 enable
i1 reset
o0 Q
o1 !Q

4
tests/aiger/toggle.aag Normal file
View File

@ -0,0 +1,4 @@
aag 1 0 1 2 0
2 3
2
3

4
tests/aiger/toggle.aig Normal file
View File

@ -0,0 +1,4 @@
aig 1 0 1 2 0
3
2
3

2
tests/aiger/true.aag Normal file
View File

@ -0,0 +1,2 @@
aag 0 0 0 1 0
1

2
tests/aiger/true.aig Normal file
View File

@ -0,0 +1,2 @@
aig 0 0 0 1 0
1

View File

@ -90,5 +90,61 @@ generate
endcase
end
endgenerate
endmodule
// ------------------------------------------
module gen_test4(a, b);
input [3:0] a;
output [3:0] b;
genvar i;
generate
for (i=0; i < 3; i=i+1) begin : foo
localparam PREV = i - 1;
wire temp;
if (i == 0)
assign temp = a[0];
else
assign temp = foo[PREV].temp & a[i];
assign b[i] = temp;
end
endgenerate
endmodule
// ------------------------------------------
module gen_test5(input_bits, out);
parameter WIDTH = 256;
parameter CHUNK = 4;
input [WIDTH-1:0] input_bits;
output out;
genvar step, i, j;
generate
for (step = 1; step <= WIDTH; step = step * CHUNK) begin : steps
localparam PREV = step / CHUNK;
localparam DIM = WIDTH / step;
for (i = 0; i < DIM; i = i + 1) begin : outer
localparam LAST_START = i * CHUNK;
for (j = 0; j < CHUNK; j = j + 1) begin : inner
wire temp;
if (step == 1)
assign temp = input_bits[i];
else if (j == 0)
assign temp = steps[PREV].outer[LAST_START].val;
else
assign temp
= steps[step].outer[i].inner[j-1].temp
& steps[PREV].outer[LAST_START + j].val;
end
wire val;
assign val = steps[step].outer[i].inner[CHUNK - 1].temp;
end
end
endgenerate
assign out = steps[WIDTH].outer[0].val;
endmodule

View File

@ -108,8 +108,9 @@ shift $((OPTIND - 1))
for fn
do
bn=${fn%.v}
if [ "$bn" == "$fn" ]; then
bn=${fn%.*}
ext=${fn##*.}
if [[ "$ext" != "v" ]] && [[ "$ext" != "aag" ]] && [[ "$ext" != "aig" ]]; then
echo "Invalid argument: $fn" >&2
exit 1
fi
@ -132,8 +133,12 @@ do
bn=$(basename $bn)
rm -f ${bn}_ref.fir
egrep -v '^\s*`timescale' ../$fn > ${bn}_ref.v
if [[ "$ext" == "v" ]]; then
egrep -v '^\s*`timescale' ../$fn > ${bn}_ref.${ext}
else
"$toolsdir"/../../yosys -f "$frontend $include_opts" -b "verilog" -o ${bn}_ref.v ../${fn}
frontend="verilog"
fi
if [ ! -f ../${bn}_tb.v ]; then
"$toolsdir"/../../yosys -f "$frontend $include_opts" -b "test_autotb $autotb_opts" -o ${bn}_tb.v ${bn}_ref.v
@ -141,7 +146,8 @@ do
cp ../${bn}_tb.v ${bn}_tb.v
fi
if $genvcd; then sed -i 's,// \$dump,$dump,g' ${bn}_tb.v; fi
compile_and_run ${bn}_tb_ref ${bn}_out_ref ${bn}_tb.v ${bn}_ref.v $libs
compile_and_run ${bn}_tb_ref ${bn}_out_ref ${bn}_tb.v ${bn}_ref.v $libs \
"$toolsdir"/../../techlibs/common/simlib.v
if $genvcd; then mv testbench.vcd ${bn}_ref.vcd; fi
test_count=0