diff --git a/.travis.yml b/.travis.yml
index 7c6e4e43c..957735f1d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -32,6 +32,10 @@ matrix:
             - xdot
             - pkg-config
             - python
+            - python3
+            - libboost-system-dev
+            - libboost-python-dev
+            - libboost-filesystem-dev
       env:
         - MATRIX_EVAL="CONFIG=gcc && CC=gcc-4.8 && CXX=g++-4.8"
 
@@ -56,6 +60,10 @@ matrix:
             - xdot
             - pkg-config
             - python
+            - python3
+            - libboost-system-dev
+            - libboost-python-dev
+            - libboost-filesystem-dev
       env:
         - MATRIX_EVAL="CONFIG=gcc && CC=gcc-6 && CXX=g++-6"
 
@@ -80,6 +88,10 @@ matrix:
             - xdot
             - pkg-config
             - python
+            - python3
+            - libboost-system-dev
+            - libboost-python-dev
+            - libboost-filesystem-dev
       env:
         - MATRIX_EVAL="CONFIG=gcc && CC=gcc-7 && CXX=g++-7"
 
@@ -105,6 +117,10 @@ matrix:
             - xdot
             - pkg-config
             - python
+            - python3
+            - libboost-system-dev
+            - libboost-python-dev
+            - libboost-filesystem-dev
       env:
         - MATRIX_EVAL="CONFIG=clang && CC=clang-3.8 && CXX=clang++-3.8"
 
@@ -129,6 +145,10 @@ matrix:
             - xdot
             - pkg-config
             - python
+            - python3
+            - libboost-system-dev
+            - libboost-python-dev
+            - libboost-filesystem-dev
       env:
         - MATRIX_EVAL="CONFIG=clang && CC=clang-5.0 && CXX=clang++-5.0"
 
diff --git a/Makefile b/Makefile
index ac403d397..d9e01c865 100644
--- a/Makefile
+++ b/Makefile
@@ -19,6 +19,14 @@ ENABLE_COVER := 1
 ENABLE_LIBYOSYS := 0
 ENABLE_PROTOBUF := 0
 
+# python wrappers
+ENABLE_PYOSYS := 0
+PYTHON_VERSION_TESTCODE := "import sys;t='{v[0]}.{v[1]}'.format(v=list(sys.version_info[:2]));print(t)"
+PYTHON_EXECUTABLE := $(shell if python3 -c ""; then echo "python3"; else echo "python"; fi)
+PYTHON_VERSION := $(shell $(PYTHON_EXECUTABLE) -c ""$(PYTHON_VERSION_TESTCODE)"")
+PYTHON_MAJOR_VERSION := $(shell echo $(PYTHON_VERSION) | cut -f1 -d.)
+PYTHON_DESTDIR := `$(PYTHON_EXECUTABLE)-config --prefix`/lib/python$(PYTHON_VERSION)/dist-packages
+
 # other configuration flags
 ENABLE_GCOV := 0
 ENABLE_GPROF := 0
@@ -261,6 +269,34 @@ ifeq ($(ENABLE_LIBYOSYS),1)
 TARGETS += libyosys.so
 endif
 
+ifeq ($(ENABLE_PYOSYS),1)
+
+#Detect name of boost_python library. Some distros usbe boost_python-py<version>, other boost_python<version>, some only use the major version number, some a concatenation of major and minor version numbers
+BOOST_PYTHON_LIB ?= $(shell \
+	if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_EXECUTABLE)-config --libs` -lboost_python-py$(subst .,,$(PYTHON_VERSION)) - > /dev/null 2>&1;        then echo "-lboost_python-py$(subst .,,$(PYTHON_VERSION))";       else \
+	if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_EXECUTABLE)-config --libs` -lboost_python-py$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1;  then echo "-lboost_python-py$(subst .,,$(PYTHON_MAJOR_VERSION))"; else \
+	if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_EXECUTABLE)-config --libs` -lboost_python$(subst .,,$(PYTHON_VERSION)) - > /dev/null 2>&1;           then echo "-lboost_python$(subst .,,$(PYTHON_VERSION))";          else \
+	if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_EXECUTABLE)-config --libs` -lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1;     then echo "-lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION))";    else \
+                                                                                                                                                                                        echo ""; fi; fi; fi; fi;)
+
+ifeq ($(BOOST_PYTHON_LIB),)
+$(error BOOST_PYTHON_LIB could not be detected. Please define manualy)
+endif
+
+ifeq ($(PYTHON_MAJOR_VERSION),3)
+LDLIBS += `$(PYTHON_EXECUTABLE)-config --libs` $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
+CXXFLAGS += `$(PYTHON_EXECUTABLE)-config --includes` -D WITH_PYTHON
+else
+LDLIBS += `$(PYTHON_EXECUTABLE)-config --libs` $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
+CXXFLAGS += `$(PYTHON_EXECUTABLE)-config --includes` -D WITH_PYTHON
+endif
+
+PY_WRAPPER_FILE = kernel/python_wrappers
+OBJS += $(PY_WRAPPER_FILE).o
+PY_GEN_SCRIPT= py_wrap_generator
+PY_WRAP_INCLUDES := $(shell python$(PYTHON_VERSION) -c "import $(PY_GEN_SCRIPT); $(PY_GEN_SCRIPT).print_includes()")
+endif
+
 ifeq ($(ENABLE_READLINE),1)
 CXXFLAGS += -DYOSYS_ENABLE_READLINE
 ifeq ($(OS), FreeBSD)
@@ -511,6 +547,14 @@ libyosys.so: $(filter-out kernel/driver.o,$(OBJS))
 	$(Q) mkdir -p $(dir $@)
 	$(P) $(CXX) -o $@ -c $(CPPFLAGS) $(CXXFLAGS) $<
 
+%.pyh: %.h
+	$(Q) mkdir -p $(dir $@)
+	$(P) cat $< | grep -E -v "#[ ]*(include|error)" | $(LD) -x c++ -o $@ -E -P -
+
+$(PY_WRAPPER_FILE).cc: $(PY_GEN_SCRIPT).py $(PY_WRAP_INCLUDES)
+	$(Q) mkdir -p $(dir $@)
+	$(P) python$(PYTHON_VERSION) -c "import $(PY_GEN_SCRIPT); $(PY_GEN_SCRIPT).gen_wrappers(\"$(PY_WRAPPER_FILE).cc\")"
+
 %.o: %.cpp
 	$(Q) mkdir -p $(dir $@)
 	$(P) $(CXX) -o $@ -c $(CPPFLAGS) $(CXXFLAGS) $<
@@ -639,6 +683,11 @@ ifeq ($(ENABLE_LIBYOSYS),1)
 	$(INSTALL_SUDO) cp libyosys.so $(DESTDIR)$(LIBDIR)
 	$(INSTALL_SUDO) $(STRIP) -S $(DESTDIR)$(LIBDIR)/libyosys.so
 	$(INSTALL_SUDO) ldconfig
+ifeq ($(ENABLE_PYOSYS),1)
+	$(INSTALL_SUDO) mkdir -p $(PYTHON_DESTDIR)/pyosys
+	$(INSTALL_SUDO) cp libyosys.so $(PYTHON_DESTDIR)/pyosys
+	$(INSTALL_SUDO) cp __init__.py $(PYTHON_DESTDIR)/pyosys
+endif
 endif
 
 uninstall:
@@ -646,6 +695,11 @@ uninstall:
 	$(INSTALL_SUDO) rm -rvf $(DESTDIR)$(DATDIR)
 ifeq ($(ENABLE_LIBYOSYS),1)
 	$(INSTALL_SUDO) rm -vf $(DESTDIR)$(LIBDIR)/libyosys.so
+ifeq ($(ENABLE_PYOSYS),1)
+	$(INSTALL_SUDO) rm -vf $(PYTHON_DESTDIR)/pyosys/libyosys.so
+	$(INSTALL_SUDO) rm -vf $(PYTHON_DESTDIR)/pyosys/__init__.py
+	$(INSTALL_SUDO) rmdir $(PYTHON_DESTDIR)/pyosys
+endif
 endif
 
 update-manual: $(TARGETS) $(EXTRA_TARGETS)
@@ -658,8 +712,9 @@ manual: $(TARGETS) $(EXTRA_TARGETS)
 
 clean:
 	rm -rf share
+	rm -rf kernel/*.pyh
 	if test -d manual; then cd manual && sh clean.sh; fi
-	rm -f $(OBJS) $(GENFILES) $(TARGETS) $(EXTRA_TARGETS) $(EXTRA_OBJS)
+	rm -f $(OBJS) $(GENFILES) $(TARGETS) $(EXTRA_TARGETS) $(EXTRA_OBJS) $(PY_WRAP_INCLUDES) $(PY_WRAPPER_FILE).cc
 	rm -f kernel/version_*.o kernel/version_*.cc abc/abc-[0-9a-f]* abc/libabc-[0-9a-f]*.a
 	rm -f libs/*/*.d frontends/*/*.d passes/*/*.d backends/*/*.d kernel/*.d techlibs/*/*.d
 	rm -rf tests/asicworld/*.out tests/asicworld/*.log
diff --git a/README.md b/README.md
index fd45b5470..46bed4242 100644
--- a/README.md
+++ b/README.md
@@ -66,25 +66,26 @@ prerequisites for building yosys:
 
 	$ sudo apt-get install build-essential clang bison flex \
 		libreadline-dev gawk tcl-dev libffi-dev git \
-		graphviz xdot pkg-config python3
+		graphviz xdot pkg-config python3 libboost-system-dev \
+		libboost-python-dev libboost-filesystem-dev
 
 Similarily, on Mac OS X MacPorts or Homebrew can be used to install dependencies:
 
 	$ brew tap Homebrew/bundle && brew bundle
 	$ sudo port install bison flex readline gawk libffi \
-		git graphviz pkgconfig python36
+		git graphviz pkgconfig python36 boost
 
 On FreeBSD use the following command to install all prerequisites:
 
 	# pkg install bison flex readline gawk libffi\
-		git graphviz pkgconfig python3 python36 tcl-wrapper
+		git graphviz pkgconfig python3 python36 tcl-wrapper boost-libs
 
 On FreeBSD system use gmake instead of make. To run tests use:
     % MAKE=gmake CC=cc gmake test
 
 For Cygwin use the following command to install all prerequisites, or select these additional packages:
 
-	setup-x86_64.exe -q --packages=bison,flex,gcc-core,gcc-g++,git,libffi-devel,libreadline-devel,make,pkg-config,python3,tcl-devel
+	setup-x86_64.exe -q --packages=bison,flex,gcc-core,gcc-g++,git,libffi-devel,libreadline-devel,make,pkg-config,python3,tcl-devel,boost-build
 
 There are also pre-compiled Yosys binary packages for Ubuntu and Win32 as well
 as a source distribution for Visual Studio. Visit the Yosys download page for
diff --git a/__init__.py b/__init__.py
new file mode 100644
index 000000000..330fd6d86
--- /dev/null
+++ b/__init__.py
@@ -0,0 +1,5 @@
+import os
+import sys
+sys.setdlopenflags(os.RTLD_NOW | os.RTLD_GLOBAL)
+
+__all__ = ["libyosys"]
diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc
index 855409d0b..9967482d6 100644
--- a/backends/verilog/verilog_backend.cc
+++ b/backends/verilog/verilog_backend.cc
@@ -187,6 +187,10 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o
 {
 	if (width < 0)
 		width = data.bits.size() - offset;
+	if (width == 0) {
+		f << "\"\"";
+		return;
+	}
 	if (nostr)
 		goto dump_hex;
 	if ((data.flags & RTLIL::CONST_FLAG_STRING) == 0 || width != (int)data.bits.size()) {
@@ -340,6 +344,10 @@ void dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool no_decima
 
 void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig)
 {
+	if (GetSize(sig) == 0) {
+		f << "\"\"";
+		return;
+	}
 	if (sig.is_chunk()) {
 		dump_sigchunk(f, sig.as_chunk());
 	} else {
diff --git a/examples/python-api/.gitignore b/examples/python-api/.gitignore
new file mode 100644
index 000000000..758de1134
--- /dev/null
+++ b/examples/python-api/.gitignore
@@ -0,0 +1 @@
+out/**
diff --git a/examples/python-api/pass.py b/examples/python-api/pass.py
new file mode 100755
index 000000000..d67cf4a23
--- /dev/null
+++ b/examples/python-api/pass.py
@@ -0,0 +1,32 @@
+#!/usr/bin/python3
+
+from pyosys import libyosys as ys
+
+import matplotlib.pyplot as plt
+import numpy as np
+
+class CellStatsPass(ys.Pass):
+
+    def __init__(self):
+        super().__init__("cell_stats", "Shows cell stats as plot")
+
+    def py_help(self):
+        ys.log("This pass uses the matplotlib library to display cell stats\n")
+
+    def py_execute(self, args, design):
+        ys.log_header(design, "Plotting cell stats\n")
+        cell_stats = {}
+        for module in design.selected_whole_modules_warn():
+            for cell in module.selected_cells():
+                if cell.type.str() in cell_stats:
+                    cell_stats[cell.type.str()] += 1
+                else:
+                    cell_stats[cell.type.str()] = 1
+        plt.bar(range(len(cell_stats)), height = list(cell_stats.values()),align='center')
+        plt.xticks(range(len(cell_stats)), list(cell_stats.keys()))
+        plt.show()
+
+    def py_clear_flags(self):
+        ys.log("Clear Flags - CellStatsPass\n")
+
+p = CellStatsPass()
diff --git a/examples/python-api/script.py b/examples/python-api/script.py
new file mode 100755
index 000000000..f0fa5a0b8
--- /dev/null
+++ b/examples/python-api/script.py
@@ -0,0 +1,22 @@
+#!/usr/bin/python3
+
+from pyosys import libyosys as ys
+
+import matplotlib.pyplot as plt
+import numpy as np
+
+design = ys.Design()
+ys.run_pass("read_verilog ../../tests/simple/fiedler-cooley.v", design);
+ys.run_pass("prep", design)
+ys.run_pass("opt -full", design)
+
+cell_stats = {}
+for module in design.selected_whole_modules_warn():
+  for cell in module.selected_cells():
+    if cell.type.str() in cell_stats:
+      cell_stats[cell.type.str()] += 1
+    else:
+      cell_stats[cell.type.str()] = 1
+plt.bar(range(len(cell_stats)), height = list(cell_stats.values()),align='center')
+plt.xticks(range(len(cell_stats)), list(cell_stats.keys()))
+plt.show()
diff --git a/frontends/aiger/aigerparse.cc b/frontends/aiger/aigerparse.cc
index db5f9d2b9..4e3f5e7c9 100644
--- a/frontends/aiger/aigerparse.cc
+++ b/frontends/aiger/aigerparse.cc
@@ -35,9 +35,6 @@
 
 YOSYS_NAMESPACE_BEGIN
 
-//#define log_debug log
-#define log_debug(...) ;
-
 AigerReader::AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name, std::string map_filename, bool wideports)
     : design(design), f(f), clk_name(clk_name), map_filename(map_filename), wideports(wideports)
 {
diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc
index 63b71b800..3e453bd7f 100644
--- a/frontends/ast/simplify.cc
+++ b/frontends/ast/simplify.cc
@@ -1030,7 +1030,26 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
 		log_file_error(filename, linenum, "While loops are only allowed in constant functions!\n");
 
 	if (type == AST_REPEAT)
-		log_file_error(filename, linenum, "Repeat loops are only allowed in constant functions!\n");
+	{
+		AstNode *count = children[0];
+		AstNode *body = children[1];
+
+		// eval count expression
+		while (count->simplify(true, false, false, stage, 32, true, false)) { }
+
+		if (count->type != AST_CONSTANT)
+			log_file_error(filename, linenum, "Repeat loops outside must have constant repeat counts!\n");
+
+		// convert to a block with the body repeated n times
+		type = AST_BLOCK;
+		children.clear();
+		for (int i = 0; i < count->bitsAsConst().as_int(); i++)
+			children.insert(children.begin(), body->clone());
+
+		delete count;
+		delete body;
+		did_something = true;
+	}
 
 	// unroll for loops and generate-for blocks
 	if ((type == AST_GENFOR || type == AST_FOR) && children.size() != 0)
@@ -1066,7 +1085,12 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
 
 		// eval 1st expression
 		AstNode *varbuf = init_ast->children[1]->clone();
-		while (varbuf->simplify(true, false, false, stage, 32, true, false)) { }
+		{
+			int expr_width_hint = -1;
+			bool expr_sign_hint = true;
+			varbuf->detectSignWidth(expr_width_hint, expr_sign_hint);
+			while (varbuf->simplify(true, false, false, stage, 32, true, false)) { }
+		}
 
 		if (varbuf->type != AST_CONSTANT)
 			log_file_error(filename, linenum, "Right hand side of 1st expression of generate for-loop is not constant!\n");
@@ -1088,7 +1112,12 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
 		{
 			// eval 2nd expression
 			AstNode *buf = while_ast->clone();
-			while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
+			{
+				int expr_width_hint = -1;
+				bool expr_sign_hint = true;
+				buf->detectSignWidth(expr_width_hint, expr_sign_hint);
+				while (buf->simplify(true, false, false, stage, expr_width_hint, expr_sign_hint, false)) { }
+			}
 
 			if (buf->type != AST_CONSTANT)
 				log_file_error(filename, linenum, "2nd expression of generate for-loop is not constant!\n");
@@ -1129,7 +1158,12 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
 
 			// eval 3rd expression
 			buf = next_ast->children[1]->clone();
-			while (buf->simplify(true, false, false, stage, 32, true, false)) { }
+			{
+				int expr_width_hint = -1;
+				bool expr_sign_hint = true;
+				buf->detectSignWidth(expr_width_hint, expr_sign_hint);
+				while (buf->simplify(true, false, false, stage, expr_width_hint, expr_sign_hint, true)) { }
+			}
 
 			if (buf->type != AST_CONSTANT)
 				log_file_error(filename, linenum, "Right hand side of 3rd expression of generate for-loop is not constant!\n");
diff --git a/kernel/cost.h b/kernel/cost.h
index 7d7822fa0..41a09eb63 100644
--- a/kernel/cost.h
+++ b/kernel/cost.h
@@ -26,8 +26,55 @@ YOSYS_NAMESPACE_BEGIN
 
 int get_cell_cost(RTLIL::Cell *cell, dict<RTLIL::IdString, int> *mod_cost_cache = nullptr);
 
-int get_cell_cost(RTLIL::IdString type, const dict<RTLIL::IdString, RTLIL::Const> &parameters = dict<RTLIL::IdString, RTLIL::Const>(),
-		RTLIL::Design *design = nullptr, dict<RTLIL::IdString, int> *mod_cost_cache = nullptr);
+inline int get_cell_cost(RTLIL::IdString type, const dict<RTLIL::IdString, RTLIL::Const> &parameters = dict<RTLIL::IdString, RTLIL::Const>(),
+		RTLIL::Design *design = nullptr, dict<RTLIL::IdString, int> *mod_cost_cache = nullptr)
+{
+	static dict<RTLIL::IdString, int> gate_cost = {
+		{ "$_BUF_",    1 },
+		{ "$_NOT_",    2 },
+		{ "$_AND_",    4 },
+		{ "$_NAND_",   4 },
+		{ "$_OR_",     4 },
+		{ "$_NOR_",    4 },
+		{ "$_ANDNOT_", 4 },
+		{ "$_ORNOT_",  4 },
+		{ "$_XOR_",    8 },
+		{ "$_XNOR_",   8 },
+		{ "$_AOI3_",   6 },
+		{ "$_OAI3_",   6 },
+		{ "$_AOI4_",   8 },
+		{ "$_OAI4_",   8 },
+		{ "$_MUX_",    4 }
+	};
+
+	if (gate_cost.count(type))
+		return gate_cost.at(type);
+
+	if (parameters.empty() && design && design->module(type))
+	{
+		RTLIL::Module *mod = design->module(type);
+
+		if (mod->attributes.count("\\cost"))
+			return mod->attributes.at("\\cost").as_int();
+
+		dict<RTLIL::IdString, int> local_mod_cost_cache;
+		if (mod_cost_cache == nullptr)
+			mod_cost_cache = &local_mod_cost_cache;
+
+		if (mod_cost_cache->count(mod->name))
+			return mod_cost_cache->at(mod->name);
+
+		int module_cost = 1;
+		for (auto c : mod->cells())
+			module_cost += get_cell_cost(c, mod_cost_cache);
+
+		(*mod_cost_cache)[mod->name] = module_cost;
+		return module_cost;
+	}
+
+	log_warning("Can't determine cost of %s cell (%d parameters).\n", log_id(type), GetSize(parameters));
+	return 1;
+}
 
 inline int get_cell_cost(RTLIL::Cell *cell, dict<RTLIL::IdString, int> *mod_cost_cache)
 {
diff --git a/kernel/driver.cc b/kernel/driver.cc
index a0bb7e60a..1bc7a5935 100644
--- a/kernel/driver.cc
+++ b/kernel/driver.cc
@@ -110,6 +110,10 @@ int main(int argc, char **argv)
 	log_error_stderr = true;
 	yosys_banner();
 	yosys_setup();
+#ifdef WITH_PYTHON
+	PyRun_SimpleString(("sys.path.append(\""+proc_self_dirname()+"\")").c_str());
+	PyRun_SimpleString(("sys.path.append(\""+proc_share_dirname()+"plugins\")").c_str());
+#endif
 
 	if (argc == 2)
 	{
@@ -291,6 +295,9 @@ int main(int argc, char **argv)
 		printf("    -E <depsfile>\n");
 		printf("        write a Makefile dependencies file with in- and output file names\n");
 		printf("\n");
+		printf("    -g\n");
+		printf("        globally enable debug log messages\n");
+		printf("\n");
 		printf("    -V\n");
 		printf("        print version information and exit\n");
 		printf("\n");
@@ -311,7 +318,7 @@ int main(int argc, char **argv)
 	}
 
 	int opt;
-	while ((opt = getopt(argc, argv, "MXAQTVSm:f:Hh:b:o:p:l:L:qv:tds:c:W:w:e:D:P:E:")) != -1)
+	while ((opt = getopt(argc, argv, "MXAQTVSgm:f:Hh:b:o:p:l:L:qv:tds:c:W:w:e:D:P:E:")) != -1)
 	{
 		switch (opt)
 		{
@@ -336,6 +343,9 @@ int main(int argc, char **argv)
 		case 'S':
 			passes_commands.push_back("synth");
 			break;
+		case 'g':
+			log_force_debug++;
+			break;
 		case 'm':
 			plugin_filenames.push_back(optarg);
 			break;
@@ -469,6 +479,10 @@ int main(int argc, char **argv)
 #endif
 
 	yosys_setup();
+#ifdef WITH_PYTHON
+	PyRun_SimpleString(("sys.path.append(\""+proc_self_dirname()+"\")").c_str());
+	PyRun_SimpleString(("sys.path.append(\""+proc_share_dirname()+"plugins\")").c_str());
+#endif
 	log_error_atexit = yosys_atexit;
 
 	for (auto &fn : plugin_filenames)
diff --git a/kernel/log.cc b/kernel/log.cc
index 400a549dd..9a9104e26 100644
--- a/kernel/log.cc
+++ b/kernel/log.cc
@@ -56,6 +56,10 @@ int log_verbose_level;
 string log_last_error;
 void (*log_error_atexit)() = NULL;
 
+int log_make_debug = 0;
+int log_force_debug = 0;
+int log_debug_suppressed = 0;
+
 vector<int> header_count;
 pool<RTLIL::IdString> log_id_cache;
 vector<shared_str> string_buf;
@@ -92,6 +96,9 @@ void logv(const char *format, va_list ap)
 		format++;
 	}
 
+	if (log_make_debug && !ys_debug(1))
+		return;
+
 	std::string str = vstringf(format, ap);
 
 	if (str.empty())
diff --git a/kernel/log.h b/kernel/log.h
index 759939025..e6afae716 100644
--- a/kernel/log.h
+++ b/kernel/log.h
@@ -64,6 +64,10 @@ extern int log_verbose_level;
 extern string log_last_error;
 extern void (*log_error_atexit)();
 
+extern int log_make_debug;
+extern int log_force_debug;
+extern int log_debug_suppressed;
+
 void logv(const char *format, va_list ap);
 void logv_header(RTLIL::Design *design, const char *format, va_list ap);
 void logv_warning(const char *format, va_list ap);
@@ -82,6 +86,45 @@ YS_NORETURN void log_error(const char *format, ...) YS_ATTRIBUTE(format(printf,
 void log_file_error(const string &filename, int lineno, const char *format, ...) YS_ATTRIBUTE(format(printf, 3, 4), noreturn);
 YS_NORETURN void log_cmd_error(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2), noreturn);
 
+#ifndef NDEBUG
+static inline bool ys_debug(int n = 0) { if (log_force_debug) return true; log_debug_suppressed += n; return false; }
+#  define log_debug(...) do { if (ys_debug(1)) log(__VA_ARGS__); } while (0)
+#else
+static inline bool ys_debug(int n = 0) { return false; }
+#  define log_debug(_fmt, ...) do { } while (0)
+#endif
+
+static inline void log_suppressed() {
+	if (log_debug_suppressed && !log_make_debug) {
+		log("<suppressed ~%d debug messages>\n", log_debug_suppressed);
+		log_debug_suppressed = 0;
+	}
+}
+
+struct LogMakeDebugHdl {
+	bool status = false;
+	LogMakeDebugHdl(bool start_on = false) {
+		if (start_on)
+			on();
+	}
+	~LogMakeDebugHdl() {
+		off();
+	}
+	void on() {
+		if (status) return;
+		status=true;
+		log_make_debug++;
+	}
+	void off_silent() {
+		if (!status) return;
+		status=false;
+		log_make_debug--;
+	}
+	void off() {
+		off_silent();
+	}
+};
+
 void log_spacer();
 void log_push();
 void log_pop();
diff --git a/kernel/register.cc b/kernel/register.cc
index 64956401f..71eb6b187 100644
--- a/kernel/register.cc
+++ b/kernel/register.cc
@@ -87,6 +87,7 @@ Pass::pre_post_exec_state_t Pass::pre_execute()
 void Pass::post_execute(Pass::pre_post_exec_state_t state)
 {
 	IdString::checkpoint();
+	log_suppressed();
 
 	int64_t time_ns = PerformanceTimer::query() - state.begin_ns;
 	runtime_ns += time_ns;
diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc
index f6f08bb9e..7e1159cac 100644
--- a/kernel/rtlil.cc
+++ b/kernel/rtlil.cc
@@ -76,6 +76,13 @@ RTLIL::Const::Const(const std::vector<bool> &bits)
 		this->bits.push_back(b ? RTLIL::S1 : RTLIL::S0);
 }
 
+RTLIL::Const::Const(const RTLIL::Const &c)
+{
+	flags = c.flags;
+	for (auto b : c.bits)
+		this->bits.push_back(b);
+}
+
 bool RTLIL::Const::operator <(const RTLIL::Const &other) const
 {
 	if (bits.size() != other.bits.size())
@@ -363,6 +370,10 @@ RTLIL::Design::Design()
 
 	refcount_modules_ = 0;
 	selection_stack.push_back(RTLIL::Selection());
+
+#ifdef WITH_PYTHON
+	RTLIL::Design::get_all_designs()->insert(std::pair<unsigned int, RTLIL::Design*>(hashidx_, this));
+#endif
 }
 
 RTLIL::Design::~Design()
@@ -373,8 +384,19 @@ RTLIL::Design::~Design()
 		delete n;
 	for (auto n : verilog_globals)
 		delete n;
+#ifdef WITH_PYTHON
+	RTLIL::Design::get_all_designs()->erase(hashidx_);
+#endif
 }
 
+#ifdef WITH_PYTHON
+static std::map<unsigned int, RTLIL::Design*> all_designs;
+std::map<unsigned int, RTLIL::Design*> *RTLIL::Design::get_all_designs(void)
+{
+	return &all_designs;
+}
+#endif
+
 RTLIL::ObjRange<RTLIL::Module*> RTLIL::Design::modules()
 {
 	return RTLIL::ObjRange<RTLIL::Module*>(&modules_, &refcount_modules_);
@@ -630,6 +652,10 @@ RTLIL::Module::Module()
 	design = nullptr;
 	refcount_wires_ = 0;
 	refcount_cells_ = 0;
+
+#ifdef WITH_PYTHON
+	RTLIL::Module::get_all_modules()->insert(std::pair<unsigned int, RTLIL::Module*>(hashidx_, this));
+#endif
 }
 
 RTLIL::Module::~Module()
@@ -642,8 +668,19 @@ RTLIL::Module::~Module()
 		delete it->second;
 	for (auto it = processes.begin(); it != processes.end(); ++it)
 		delete it->second;
+#ifdef WITH_PYTHON
+	RTLIL::Module::get_all_modules()->erase(hashidx_);
+#endif
 }
 
+#ifdef WITH_PYTHON
+static std::map<unsigned int, RTLIL::Module*> all_modules;
+std::map<unsigned int, RTLIL::Module*> *RTLIL::Module::get_all_modules(void)
+{
+	return &all_modules;
+}
+#endif
+
 void RTLIL::Module::makeblackbox()
 {
 	pool<RTLIL::Wire*> delwires;
@@ -2229,8 +2266,27 @@ RTLIL::Wire::Wire()
 	port_input = false;
 	port_output = false;
 	upto = false;
+
+#ifdef WITH_PYTHON
+	RTLIL::Wire::get_all_wires()->insert(std::pair<unsigned int, RTLIL::Wire*>(hashidx_, this));
+#endif
 }
 
+RTLIL::Wire::~Wire()
+{
+#ifdef WITH_PYTHON
+	RTLIL::Wire::get_all_wires()->erase(hashidx_);
+#endif
+}
+
+#ifdef WITH_PYTHON
+static std::map<unsigned int, RTLIL::Wire*> all_wires;
+std::map<unsigned int, RTLIL::Wire*> *RTLIL::Wire::get_all_wires(void)
+{
+	return &all_wires;
+}
+#endif
+
 RTLIL::Memory::Memory()
 {
 	static unsigned int hashidx_count = 123456789;
@@ -2240,6 +2296,9 @@ RTLIL::Memory::Memory()
 	width = 1;
 	start_offset = 0;
 	size = 0;
+#ifdef WITH_PYTHON
+	RTLIL::Memory::get_all_memorys()->insert(std::pair<unsigned int, RTLIL::Memory*>(hashidx_, this));
+#endif
 }
 
 RTLIL::Cell::Cell() : module(nullptr)
@@ -2250,8 +2309,27 @@ RTLIL::Cell::Cell() : module(nullptr)
 
 	// log("#memtrace# %p\n", this);
 	memhasher();
+
+#ifdef WITH_PYTHON
+	RTLIL::Cell::get_all_cells()->insert(std::pair<unsigned int, RTLIL::Cell*>(hashidx_, this));
+#endif
 }
 
+RTLIL::Cell::~Cell()
+{
+#ifdef WITH_PYTHON
+	RTLIL::Cell::get_all_cells()->erase(hashidx_);
+#endif
+}
+
+#ifdef WITH_PYTHON
+static std::map<unsigned int, RTLIL::Cell*> all_cells;
+std::map<unsigned int, RTLIL::Cell*> *RTLIL::Cell::get_all_cells(void)
+{
+	return &all_cells;
+}
+#endif
+
 bool RTLIL::Cell::hasPort(RTLIL::IdString portname) const
 {
 	return connections_.count(portname) != 0;
@@ -2511,6 +2589,14 @@ RTLIL::SigChunk::SigChunk(RTLIL::SigBit bit)
 	width = 1;
 }
 
+RTLIL::SigChunk::SigChunk(const RTLIL::SigChunk &sigchunk) : data(sigchunk.data)
+{
+	wire = sigchunk.wire;
+	data = sigchunk.data;
+	width = sigchunk.width;
+	offset = sigchunk.offset;
+}
+
 RTLIL::SigChunk RTLIL::SigChunk::extract(int offset, int length) const
 {
 	RTLIL::SigChunk ret;
@@ -3895,5 +3981,15 @@ RTLIL::Process *RTLIL::Process::clone() const
 	return new_proc;
 }
 
+#ifdef WITH_PYTHON
+RTLIL::Memory::~Memory()
+{
+	RTLIL::Memory::get_all_memorys()->erase(hashidx_);
+}
+static std::map<unsigned int, RTLIL::Memory*> all_memorys;
+std::map<unsigned int, RTLIL::Memory*> *RTLIL::Memory::get_all_memorys(void)
+{
+	return &all_memorys;
+}
+#endif
 YOSYS_NAMESPACE_END
-
diff --git a/kernel/rtlil.h b/kernel/rtlil.h
index a0f1338e2..757e0dfa4 100644
--- a/kernel/rtlil.h
+++ b/kernel/rtlil.h
@@ -523,6 +523,7 @@ struct RTLIL::Const
 	Const(RTLIL::State bit, int width = 1);
 	Const(const std::vector<RTLIL::State> &bits) : bits(bits) { flags = CONST_FLAG_NONE; }
 	Const(const std::vector<bool> &bits);
+	Const(const RTLIL::Const &c);
 
 	bool operator <(const RTLIL::Const &other) const;
 	bool operator ==(const RTLIL::Const &other) const;
@@ -601,6 +602,7 @@ struct RTLIL::SigChunk
 	SigChunk(int val, int width = 32);
 	SigChunk(RTLIL::State bit, int width = 1);
 	SigChunk(RTLIL::SigBit bit);
+	SigChunk(const RTLIL::SigChunk &sigchunk);
 
 	RTLIL::SigChunk extract(int offset, int length) const;
 
@@ -625,6 +627,7 @@ struct RTLIL::SigBit
 	SigBit(const RTLIL::SigChunk &chunk);
 	SigBit(const RTLIL::SigChunk &chunk, int index);
 	SigBit(const RTLIL::SigSpec &sig);
+	SigBit(const RTLIL::SigBit &sigbit);
 
 	bool operator <(const RTLIL::SigBit &other) const;
 	bool operator ==(const RTLIL::SigBit &other) const;
@@ -946,9 +949,13 @@ struct RTLIL::Design
 		}
 	}
 
+
 	std::vector<RTLIL::Module*> selected_modules() const;
 	std::vector<RTLIL::Module*> selected_whole_modules() const;
 	std::vector<RTLIL::Module*> selected_whole_modules_warn() const;
+#ifdef WITH_PYTHON
+	static std::map<unsigned int, RTLIL::Design*> *get_all_designs(void);
+#endif
 };
 
 struct RTLIL::Module : public RTLIL::AttrObject
@@ -1205,6 +1212,10 @@ public:
 	RTLIL::SigSpec Allconst  (RTLIL::IdString name, int width = 1, const std::string &src = "");
 	RTLIL::SigSpec Allseq    (RTLIL::IdString name, int width = 1, const std::string &src = "");
 	RTLIL::SigSpec Initstate (RTLIL::IdString name, const std::string &src = "");
+
+#ifdef WITH_PYTHON
+	static std::map<unsigned int, RTLIL::Module*> *get_all_modules(void);
+#endif
 };
 
 struct RTLIL::Wire : public RTLIL::AttrObject
@@ -1216,7 +1227,7 @@ protected:
 	// use module->addWire() and module->remove() to create or destroy wires
 	friend struct RTLIL::Module;
 	Wire();
-	~Wire() { };
+	~Wire();
 
 public:
 	// do not simply copy wires
@@ -1227,6 +1238,10 @@ public:
 	RTLIL::IdString name;
 	int width, start_offset, port_id;
 	bool port_input, port_output, upto;
+
+#ifdef WITH_PYTHON
+	static std::map<unsigned int, RTLIL::Wire*> *get_all_wires(void);
+#endif
 };
 
 struct RTLIL::Memory : public RTLIL::AttrObject
@@ -1238,6 +1253,10 @@ struct RTLIL::Memory : public RTLIL::AttrObject
 
 	RTLIL::IdString name;
 	int width, start_offset, size;
+#ifdef WITH_PYTHON
+	~Memory();
+	static std::map<unsigned int, RTLIL::Memory*> *get_all_memorys(void);
+#endif
 };
 
 struct RTLIL::Cell : public RTLIL::AttrObject
@@ -1249,6 +1268,7 @@ protected:
 	// use module->addCell() and module->remove() to create or destroy cells
 	friend struct RTLIL::Module;
 	Cell();
+	~Cell();
 
 public:
 	// do not simply copy cells
@@ -1289,6 +1309,10 @@ public:
 	}
 
 	template<typename T> void rewrite_sigspecs(T &functor);
+
+#ifdef WITH_PYTHON
+	static std::map<unsigned int, RTLIL::Cell*> *get_all_cells(void);
+#endif
 };
 
 struct RTLIL::CaseRule
@@ -1349,6 +1373,7 @@ inline RTLIL::SigBit::SigBit(RTLIL::Wire *wire) : wire(wire), offset(0) { log_as
 inline RTLIL::SigBit::SigBit(RTLIL::Wire *wire, int offset) : wire(wire), offset(offset) { log_assert(wire != nullptr); }
 inline RTLIL::SigBit::SigBit(const RTLIL::SigChunk &chunk) : wire(chunk.wire) { log_assert(chunk.width == 1); if (wire) offset = chunk.offset; else data = chunk.data[0]; }
 inline RTLIL::SigBit::SigBit(const RTLIL::SigChunk &chunk, int index) : wire(chunk.wire) { if (wire) offset = chunk.offset + index; else data = chunk.data[index]; }
+inline RTLIL::SigBit::SigBit(const RTLIL::SigBit &sigbit) : wire(sigbit.wire), data(sigbit.data){if(wire) offset = sigbit.offset;}
 
 inline bool RTLIL::SigBit::operator<(const RTLIL::SigBit &other) const {
 	if (wire == other.wire)
diff --git a/kernel/yosys.cc b/kernel/yosys.cc
index 450e4e4cf..a12355f1d 100644
--- a/kernel/yosys.cc
+++ b/kernel/yosys.cc
@@ -57,6 +57,16 @@
 #  include <sys/sysctl.h>
 #endif
 
+#ifdef WITH_PYTHON
+#if PY_MAJOR_VERSION >= 3
+#   define INIT_MODULE PyInit_libyosys
+    extern "C" PyObject* INIT_MODULE();
+#else
+#   define INIT_MODULE initlibyosys
+	extern "C" void INIT_MODULE();
+#endif
+#endif
+
 #include <limits.h>
 #include <errno.h>
 
@@ -477,21 +487,42 @@ int GetSize(RTLIL::Wire *wire)
 	return wire->width;
 }
 
+bool already_setup = false;
+
 void yosys_setup()
 {
+	if(already_setup)
+		return;
+	already_setup = true;
 	// if there are already IdString objects then we have a global initialization order bug
 	IdString empty_id;
 	log_assert(empty_id.index_ == 0);
 	IdString::get_reference(empty_id.index_);
 
+	#ifdef WITH_PYTHON
+		PyImport_AppendInittab((char*)"libyosys", INIT_MODULE);
+		Py_Initialize();
+		PyRun_SimpleString("import sys");
+	#endif
+
 	Pass::init_register();
 	yosys_design = new RTLIL::Design;
 	yosys_celltypes.setup();
 	log_push();
 }
 
+bool yosys_already_setup()
+{
+	return already_setup;
+}
+
+bool already_shutdown = false;
+
 void yosys_shutdown()
 {
+	if(already_shutdown)
+		return;
+	already_shutdown = true;
 	log_pop();
 
 	delete yosys_design;
@@ -519,9 +550,16 @@ void yosys_shutdown()
 		dlclose(it.second);
 
 	loaded_plugins.clear();
+#ifdef WITH_PYTHON
+	loaded_python_plugins.clear();
+#endif
 	loaded_plugin_aliases.clear();
 #endif
 
+#ifdef WITH_PYTHON
+	Py_Finalize();
+#endif
+
 	IdString empty_id;
 	IdString::put_reference(empty_id.index_);
 }
diff --git a/kernel/yosys.h b/kernel/yosys.h
index c9f973318..2cf6188b4 100644
--- a/kernel/yosys.h
+++ b/kernel/yosys.h
@@ -66,6 +66,10 @@
 #include <stdio.h>
 #include <limits.h>
 
+#ifdef WITH_PYTHON
+#include <Python.h>
+#endif
+
 #ifndef _YOSYS_
 #  error It looks like you are trying to build Yosys without the config defines set. \
          When building Yosys with a custom make system, make sure you set all the \
@@ -115,6 +119,7 @@ extern const char *Tcl_GetStringResult(Tcl_Interp *interp);
 #  define PATH_MAX 4096
 #endif
 
+#define YOSYS_NAMESPACE          Yosys
 #define PRIVATE_NAMESPACE_BEGIN  namespace {
 #define PRIVATE_NAMESPACE_END    }
 #define YOSYS_NAMESPACE_BEGIN    namespace Yosys {
@@ -276,6 +281,11 @@ namespace hashlib {
 }
 
 void yosys_setup();
+
+#ifdef WITH_PYTHON
+bool yosys_already_setup();
+#endif
+
 void yosys_shutdown();
 
 #ifdef YOSYS_ENABLE_TCL
@@ -317,6 +327,9 @@ extern std::vector<RTLIL::Design*> pushed_designs;
 
 // from passes/cmds/pluginc.cc
 extern std::map<std::string, void*> loaded_plugins;
+#ifdef WITH_PYTHON
+extern std::map<std::string, void*> loaded_python_plugins;
+#endif
 extern std::map<std::string, std::string> loaded_plugin_aliases;
 void load_plugin(std::string filename, std::vector<std::string> aliases);
 
diff --git a/passes/cmds/plugin.cc b/passes/cmds/plugin.cc
index aa6d5b6cc..4c16b56c4 100644
--- a/passes/cmds/plugin.cc
+++ b/passes/cmds/plugin.cc
@@ -23,9 +23,18 @@
 #  include <dlfcn.h>
 #endif
 
+#ifdef WITH_PYTHON
+#  include <boost/algorithm/string/predicate.hpp>
+#  include <Python.h>
+#  include <boost/filesystem.hpp>
+#endif
+
 YOSYS_NAMESPACE_BEGIN
 
 std::map<std::string, void*> loaded_plugins;
+#ifdef WITH_PYTHON
+std::map<std::string, void*> loaded_python_plugins;
+#endif
 std::map<std::string, std::string> loaded_plugin_aliases;
 
 #ifdef YOSYS_ENABLE_PLUGINS
@@ -36,7 +45,35 @@ void load_plugin(std::string filename, std::vector<std::string> aliases)
 	if (filename.find('/') == std::string::npos)
 		filename = "./" + filename;
 
+	#ifdef WITH_PYTHON
+	if (!loaded_plugins.count(filename) && !loaded_python_plugins.count(filename)) {
+	#else
 	if (!loaded_plugins.count(filename)) {
+	#endif
+
+		#ifdef WITH_PYTHON
+
+		boost::filesystem::path full_path(filename);
+
+		if(strcmp(full_path.extension().c_str(), ".py") == 0)
+		{
+			std::string path(full_path.parent_path().c_str());
+			filename = full_path.filename().c_str();
+			filename = filename.substr(0,filename.size()-3);
+			PyRun_SimpleString(("sys.path.insert(0,\""+path+"\")").c_str());
+			PyErr_Print();
+			PyObject *module_p = PyImport_ImportModule(filename.c_str());
+			if(module_p == NULL)
+			{
+				PyErr_Print();
+				log_cmd_error("Can't load python module `%s'\n", full_path.filename().c_str());
+				return;
+			}
+			loaded_python_plugins[orig_filename] = module_p;
+			Pass::init_register();
+		} else {
+		#endif
+
 		void *hdl = dlopen(filename.c_str(), RTLD_LAZY|RTLD_LOCAL);
 		if (hdl == NULL && orig_filename.find('/') == std::string::npos)
 			hdl = dlopen((proc_share_dirname() + "plugins/" + orig_filename + ".so").c_str(), RTLD_LAZY|RTLD_LOCAL);
@@ -44,6 +81,10 @@ void load_plugin(std::string filename, std::vector<std::string> aliases)
 			log_cmd_error("Can't load module `%s': %s\n", filename.c_str(), dlerror());
 		loaded_plugins[orig_filename] = hdl;
 		Pass::init_register();
+
+		#ifdef WITH_PYTHON
+		}
+		#endif
 	}
 
 	for (auto &alias : aliases)
@@ -107,7 +148,11 @@ struct PluginPass : public Pass {
 		if (list_mode)
 		{
 			log("\n");
+#ifdef WITH_PYTHON
+			if (loaded_plugins.empty() and loaded_python_plugins.empty())
+#else
 			if (loaded_plugins.empty())
+#endif
 				log("No plugins loaded.\n");
 			else
 				log("Loaded plugins:\n");
@@ -115,6 +160,11 @@ struct PluginPass : public Pass {
 			for (auto &it : loaded_plugins)
 				log("  %s\n", it.first.c_str());
 
+#ifdef WITH_PYTHON
+			for (auto &it : loaded_python_plugins)
+				log("  %s\n", it.first.c_str());
+#endif
+
 			if (!loaded_plugin_aliases.empty()) {
 				log("\n");
 				int max_alias_len = 1;
diff --git a/passes/cmds/trace.cc b/passes/cmds/trace.cc
index f5305cde9..cf3e46ace 100644
--- a/passes/cmds/trace.cc
+++ b/passes/cmds/trace.cc
@@ -94,4 +94,38 @@ struct TracePass : public Pass {
 	}
 } TracePass;
 
+struct DebugPass : public Pass {
+	DebugPass() : Pass("debug", "run command with debug log messages enabled") { }
+	void help() YS_OVERRIDE
+	{
+		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+		log("\n");
+		log("    debug cmd\n");
+		log("\n");
+		log("Execute the specified command with debug log messages enabled\n");
+		log("\n");
+	}
+	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+	{
+		size_t argidx;
+		for (argidx = 1; argidx < args.size(); argidx++)
+		{
+			// .. parse options ..
+			break;
+		}
+
+		log_force_debug++;
+
+		try {
+			std::vector<std::string> new_args(args.begin() + argidx, args.end());
+			Pass::call(design, new_args);
+		} catch (...) {
+			log_force_debug--;
+			throw;
+		}
+
+		log_force_debug--;
+	}
+} DebugPass;
+
 PRIVATE_NAMESPACE_END
diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc
index c3e0a2a40..337fee9e4 100644
--- a/passes/opt/Makefile.inc
+++ b/passes/opt/Makefile.inc
@@ -13,5 +13,6 @@ OBJS += passes/opt/wreduce.o
 OBJS += passes/opt/opt_demorgan.o
 OBJS += passes/opt/rmports.o
 OBJS += passes/opt/opt_lut.o
+OBJS += passes/opt/pmux2shiftx.o
 endif
 
diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc
index c3b13acaf..c38e9df5e 100644
--- a/passes/opt/opt_clean.cc
+++ b/passes/opt/opt_clean.cc
@@ -137,7 +137,7 @@ void rmunused_module_cells(Module *module, bool verbose)
 
 	for (auto cell : unused) {
 		if (verbose)
-			log("  removing unused `%s' cell `%s'.\n", cell->type.c_str(), cell->name.c_str());
+			log_debug("  removing unused `%s' cell `%s'.\n", cell->type.c_str(), cell->name.c_str());
 		module->design->scratchpad_set_bool("opt.did_something", true);
 		module->remove(cell);
 		count_rm_cells++;
@@ -326,7 +326,7 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
 	for (auto wire : maybe_del_wires)
 		if (!used_signals.check_any(RTLIL::SigSpec(wire))) {
 			if (check_public_name(wire->name) && verbose) {
-				log("  removing unused non-port wire %s.\n", wire->name.c_str());
+				log_debug("  removing unused non-port wire %s.\n", wire->name.c_str());
 			}
 			del_wires.insert(wire);
 			del_wires_count++;
@@ -336,7 +336,7 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
 	count_rm_wires += del_wires.size();
 
 	if (verbose && del_wires_count > 0)
-		log("  removed %d unused temporary wires.\n", del_wires_count);
+		log_debug("  removed %d unused temporary wires.\n", del_wires_count);
 }
 
 bool rmunused_module_init(RTLIL::Module *module, bool purge_mode, bool verbose)
@@ -399,7 +399,7 @@ bool rmunused_module_init(RTLIL::Module *module, bool purge_mode, bool verbose)
 		}
 
 		if (verbose)
-			log("  removing redundant init attribute on %s.\n", log_id(wire));
+			log_debug("  removing redundant init attribute on %s.\n", log_id(wire));
 
 		wire->attributes.erase("\\init");
 		did_something = true;
@@ -426,7 +426,7 @@ void rmunused_module(RTLIL::Module *module, bool purge_mode, bool verbose, bool
 		}
 	for (auto cell : delcells) {
 		if (verbose)
-			log("  removing buffer cell `%s': %s = %s\n", cell->name.c_str(),
+			log_debug("  removing buffer cell `%s': %s = %s\n", cell->name.c_str(),
 					log_signal(cell->getPort("\\Y")), log_signal(cell->getPort("\\A")));
 		module->remove(cell);
 	}
@@ -551,6 +551,7 @@ struct CleanPass : public Pass {
 			rmunused_module(module, purge_mode, false, false);
 		}
 
+		log_suppressed();
 		if (count_rm_cells > 0 || count_rm_wires > 0)
 			log("Removed %d unused cells and %d unused wires.\n", count_rm_cells, count_rm_wires);
 
diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc
index a05db2a4f..af6d352af 100644
--- a/passes/opt/opt_expr.cc
+++ b/passes/opt/opt_expr.cc
@@ -67,7 +67,7 @@ void replace_undriven(RTLIL::Design *design, RTLIL::Module *module)
 		if (sig.size() == 0)
 			continue;
 
-		log("Setting undriven signal in %s to undef: %s\n", RTLIL::id2cstr(module->name), log_signal(c));
+		log_debug("Setting undriven signal in %s to undef: %s\n", RTLIL::id2cstr(module->name), log_signal(c));
 		module->connect(RTLIL::SigSig(c, RTLIL::SigSpec(RTLIL::State::Sx, c.width)));
 		did_something = true;
 	}
@@ -78,7 +78,7 @@ void replace_cell(SigMap &assign_map, RTLIL::Module *module, RTLIL::Cell *cell,
 	RTLIL::SigSpec Y = cell->getPort(out_port);
 	out_val.extend_u0(Y.size(), false);
 
-	log("Replacing %s cell `%s' (%s) in module `%s' with constant driver `%s = %s'.\n",
+	log_debug("Replacing %s cell `%s' (%s) in module `%s' with constant driver `%s = %s'.\n",
 			cell->type.c_str(), cell->name.c_str(), info.c_str(),
 			module->name.c_str(), log_signal(Y), log_signal(out_val));
 	// log_cell(cell);
@@ -134,7 +134,7 @@ bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutativ
 		if (GetSize(grouped_bits[i]) == GetSize(bits_y))
 			return false;
 
-	log("Replacing %s cell `%s' in module `%s' with cells using grouped bits:\n",
+	log_debug("Replacing %s cell `%s' in module `%s' with cells using grouped bits:\n",
 			log_id(cell->type), log_id(cell), log_id(module));
 
 	for (int i = 0; i < GRP_N; i++)
@@ -156,7 +156,7 @@ bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutativ
 		}
 
 		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));
+			log_debug("  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;
@@ -180,10 +180,10 @@ bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutativ
 
 		module->connect(new_conn);
 
-		log("  New cell `%s': A=%s", log_id(c), log_signal(new_a));
+		log_debug("  New cell `%s': A=%s", log_id(c), log_signal(new_a));
 		if (b_name == "\\B")
-			log(", B=%s", log_signal(new_b));
-		log("\n");
+			log_debug(", B=%s", log_signal(new_b));
+		log_debug("\n");
 	}
 
 	cover_list("opt.opt_expr.fine.group", "$not", "$pos", "$and", "$or", "$xor", "$xnor", cell->type.str());
@@ -197,7 +197,7 @@ void handle_polarity_inv(Cell *cell, IdString port, IdString param, const SigMap
 {
 	SigSpec sig = assign_map(cell->getPort(port));
 	if (invert_map.count(sig)) {
-		log("Inverting %s of %s cell `%s' in module `%s': %s -> %s\n",
+		log_debug("Inverting %s of %s cell `%s' in module `%s': %s -> %s\n",
 				log_id(port), log_id(cell->type), log_id(cell), log_id(cell->module),
 				log_signal(sig), log_signal(invert_map.at(sig)));
 		cell->setPort(port, (invert_map.at(sig)));
@@ -226,7 +226,7 @@ void handle_clkpol_celltype_swap(Cell *cell, string type1, string type2, IdStrin
 	if (cell->type.in(type1, type2)) {
 		SigSpec sig = assign_map(cell->getPort(port));
 		if (invert_map.count(sig)) {
-			log("Inverting %s of %s cell `%s' in module `%s': %s -> %s\n",
+			log_debug("Inverting %s of %s cell `%s' in module `%s': %s -> %s\n",
 					log_id(port), log_id(cell->type), log_id(cell), log_id(cell->module),
 					log_signal(sig), log_signal(invert_map.at(sig)));
 			cell->setPort(port, (invert_map.at(sig)));
@@ -455,9 +455,10 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
 		{
 			if (cell->type == "$reduce_xnor") {
 				cover("opt.opt_expr.reduce_xnor_not");
-				log("Replacing %s cell `%s' in module `%s' with $not cell.\n",
+				log_debug("Replacing %s cell `%s' in module `%s' with $not cell.\n",
 						log_id(cell->type), log_id(cell->name), log_id(module));
 				cell->type = "$not";
+				did_something = true;
 			} else {
 				cover("opt.opt_expr.unary_buffer");
 				replace_cell(assign_map, module, cell, "unary_buffer", "\\Y", cell->getPort("\\A"));
@@ -488,7 +489,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
 
 				if (GetSize(new_sig_a) < GetSize(sig_a)) {
 					cover_list("opt.opt_expr.fine.neutral_A", "$logic_not", "$logic_and", "$logic_or", "$reduce_or", "$reduce_and", "$reduce_bool", cell->type.str());
-					log("Replacing port A of %s cell `%s' in module `%s' with shorter expression: %s -> %s\n",
+					log_debug("Replacing port A of %s cell `%s' in module `%s' with shorter expression: %s -> %s\n",
 							cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_a), log_signal(new_sig_a));
 					cell->setPort("\\A", new_sig_a);
 					cell->parameters.at("\\A_WIDTH") = GetSize(new_sig_a);
@@ -511,7 +512,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
 
 				if (GetSize(new_sig_b) < GetSize(sig_b)) {
 					cover_list("opt.opt_expr.fine.neutral_B", "$logic_and", "$logic_or", cell->type.str());
-					log("Replacing port B of %s cell `%s' in module `%s' with shorter expression: %s -> %s\n",
+					log_debug("Replacing port B of %s cell `%s' in module `%s' with shorter expression: %s -> %s\n",
 							cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_b), log_signal(new_sig_b));
 					cell->setPort("\\B", new_sig_b);
 					cell->parameters.at("\\B_WIDTH") = GetSize(new_sig_b);
@@ -537,7 +538,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
 
 				if (new_a != RTLIL::State::Sm && RTLIL::SigSpec(new_a) != sig_a) {
 					cover("opt.opt_expr.fine.$reduce_and");
-					log("Replacing port A of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
+					log_debug("Replacing port A of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
 							cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_a), log_signal(new_a));
 					cell->setPort("\\A", sig_a = new_a);
 					cell->parameters.at("\\A_WIDTH") = 1;
@@ -563,7 +564,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
 
 				if (new_a != RTLIL::State::Sm && RTLIL::SigSpec(new_a) != sig_a) {
 					cover_list("opt.opt_expr.fine.A", "$logic_not", "$logic_and", "$logic_or", "$reduce_or", "$reduce_bool", cell->type.str());
-					log("Replacing port A of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
+					log_debug("Replacing port A of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
 							cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_a), log_signal(new_a));
 					cell->setPort("\\A", sig_a = new_a);
 					cell->parameters.at("\\A_WIDTH") = 1;
@@ -589,7 +590,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
 
 				if (new_b != RTLIL::State::Sm && RTLIL::SigSpec(new_b) != sig_b) {
 					cover_list("opt.opt_expr.fine.B", "$logic_and", "$logic_or", cell->type.str());
-					log("Replacing port B of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
+					log_debug("Replacing port B of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
 							cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_b), log_signal(new_b));
 					cell->setPort("\\B", sig_b = new_b);
 					cell->parameters.at("\\B_WIDTH") = 1;
@@ -640,7 +641,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
 
 		if ((cell->type == "$_MUX_" || cell->type == "$mux") && invert_map.count(assign_map(cell->getPort("\\S"))) != 0) {
 			cover_list("opt.opt_expr.invert.muxsel", "$_MUX_", "$mux", cell->type.str());
-			log("Optimizing away select inverter for %s cell `%s' in module `%s'.\n", log_id(cell->type), log_id(cell), log_id(module));
+			log_debug("Optimizing away select inverter for %s cell `%s' in module `%s'.\n", log_id(cell->type), log_id(cell), log_id(module));
 			RTLIL::SigSpec tmp = cell->getPort("\\A");
 			cell->setPort("\\A", cell->getPort("\\B"));
 			cell->setPort("\\B", tmp);
@@ -750,7 +751,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
 				ACTION_DO("\\Y", cell->getPort("\\A"));
 			if (input == State::S0 && !a.is_fully_undef()) {
 				cover("opt.opt_expr.action_" S__LINE__);
-				log("Replacing data input of %s cell `%s' in module `%s' with constant undef.\n",
+				log_debug("Replacing data input of %s cell `%s' in module `%s' with constant undef.\n",
 					cell->type.c_str(), cell->name.c_str(), module->name.c_str());
 				cell->setPort("\\A", SigSpec(State::Sx, GetSize(a)));
 				did_something = true;
@@ -822,7 +823,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
 					ACTION_DO("\\Y", cell->getPort("\\A"));
 				} else {
 					cover_list("opt.opt_expr.eqneq.isnot", "$eq", "$ne", cell->type.str());
-					log("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module));
+					log_debug("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module));
 					cell->type = "$not";
 					cell->parameters.erase("\\B_WIDTH");
 					cell->parameters.erase("\\B_SIGNED");
@@ -837,7 +838,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
 				(assign_map(cell->getPort("\\A")).is_fully_zero() || assign_map(cell->getPort("\\B")).is_fully_zero()))
 		{
 			cover_list("opt.opt_expr.eqneq.cmpzero", "$eq", "$ne", cell->type.str());
-			log("Replacing %s cell `%s' in module `%s' with %s.\n", log_id(cell->type), log_id(cell),
+			log_debug("Replacing %s cell `%s' in module `%s' with %s.\n", log_id(cell->type), log_id(cell),
 					log_id(module), "$eq" ? "$logic_not" : "$reduce_bool");
 			cell->type = cell->type == "$eq" ? "$logic_not" : "$reduce_bool";
 			if (assign_map(cell->getPort("\\A")).is_fully_zero()) {
@@ -876,7 +877,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
 
 			cover_list("opt.opt_expr.constshift", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", cell->type.str());
 
-			log("Replacing %s cell `%s' (B=%s, SHR=%d) in module `%s' with fixed wiring: %s\n",
+			log_debug("Replacing %s cell `%s' (B=%s, SHR=%d) in module `%s' with fixed wiring: %s\n",
 					log_id(cell->type), log_id(cell), log_signal(assign_map(cell->getPort("\\B"))), shift_bits, log_id(module), log_signal(sig_y));
 
 			module->connect(cell->getPort("\\Y"), sig_y);
@@ -939,7 +940,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
 				if (identity_wrt_b)
 					cover_list("opt.opt_expr.identwrt.b", "$add", "$sub", "$or", "$xor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$mul", "$div", cell->type.str());
 
-				log("Replacing %s cell `%s' in module `%s' with identity for port %c.\n",
+				log_debug("Replacing %s cell `%s' in module `%s' with identity for port %c.\n",
 					cell->type.c_str(), cell->name.c_str(), module->name.c_str(), identity_wrt_a ? 'A' : 'B');
 
 				if (!identity_wrt_a) {
@@ -969,7 +970,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
 		if (mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") &&
 				cell->getPort("\\A") == RTLIL::SigSpec(1, 1) && cell->getPort("\\B") == RTLIL::SigSpec(0, 1)) {
 			cover_list("opt.opt_expr.mux_invert", "$mux", "$_MUX_", cell->type.str());
-			log("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module));
+			log_debug("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module));
 			cell->setPort("\\A", cell->getPort("\\S"));
 			cell->unsetPort("\\B");
 			cell->unsetPort("\\S");
@@ -988,7 +989,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
 
 		if (consume_x && mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\A") == RTLIL::SigSpec(0, 1)) {
 			cover_list("opt.opt_expr.mux_and", "$mux", "$_MUX_", cell->type.str());
-			log("Replacing %s cell `%s' in module `%s' with and-gate.\n", log_id(cell->type), log_id(cell), log_id(module));
+			log_debug("Replacing %s cell `%s' in module `%s' with and-gate.\n", log_id(cell->type), log_id(cell), log_id(module));
 			cell->setPort("\\A", cell->getPort("\\S"));
 			cell->unsetPort("\\S");
 			if (cell->type == "$mux") {
@@ -1008,7 +1009,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
 
 		if (consume_x && mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\B") == RTLIL::SigSpec(1, 1)) {
 			cover_list("opt.opt_expr.mux_or", "$mux", "$_MUX_", cell->type.str());
-			log("Replacing %s cell `%s' in module `%s' with or-gate.\n", log_id(cell->type), log_id(cell), log_id(module));
+			log_debug("Replacing %s cell `%s' in module `%s' with or-gate.\n", log_id(cell->type), log_id(cell), log_id(module));
 			cell->setPort("\\B", cell->getPort("\\S"));
 			cell->unsetPort("\\S");
 			if (cell->type == "$mux") {
@@ -1061,7 +1062,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
 			}
 			if (cell->getPort("\\S").size() != new_s.size()) {
 				cover_list("opt.opt_expr.mux_reduce", "$mux", "$pmux", cell->type.str());
-				log("Optimized away %d select inputs of %s cell `%s' in module `%s'.\n",
+				log_debug("Optimized away %d select inputs of %s cell `%s' in module `%s'.\n",
 						GetSize(cell->getPort("\\S")) - GetSize(new_s), log_id(cell->type), log_id(cell), log_id(module));
 				cell->setPort("\\A", new_a);
 				cell->setPort("\\B", new_b);
@@ -1179,7 +1180,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
 				{
 					cover("opt.opt_expr.mul_shift.zero");
 
-					log("Replacing multiply-by-zero cell `%s' in module `%s' with zero-driver.\n",
+					log_debug("Replacing multiply-by-zero cell `%s' in module `%s' with zero-driver.\n",
 							cell->name.c_str(), module->name.c_str());
 
 					module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(0, sig_y.size())));
@@ -1197,7 +1198,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
 						else
 							cover("opt.opt_expr.mul_shift.unswapped");
 
-						log("Replacing multiply-by-%d cell `%s' in module `%s' with shift-by-%d.\n",
+						log_debug("Replacing multiply-by-%d cell `%s' in module `%s' with shift-by-%d.\n",
 								a_val, cell->name.c_str(), module->name.c_str(), i);
 
 						if (!swapped_ab) {
@@ -1237,7 +1238,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
 				{
 					cover("opt.opt_expr.divmod_zero");
 
-					log("Replacing divide-by-zero cell `%s' in module `%s' with undef-driver.\n",
+					log_debug("Replacing divide-by-zero cell `%s' in module `%s' with undef-driver.\n",
 							cell->name.c_str(), module->name.c_str());
 
 					module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(State::Sx, sig_y.size())));
@@ -1254,7 +1255,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
 						{
 							cover("opt.opt_expr.div_shift");
 
-							log("Replacing divide-by-%d cell `%s' in module `%s' with shift-by-%d.\n",
+							log_debug("Replacing divide-by-%d cell `%s' in module `%s' with shift-by-%d.\n",
 									b_val, cell->name.c_str(), module->name.c_str(), i);
 
 							std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(i, 6);
@@ -1272,7 +1273,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
 						{
 							cover("opt.opt_expr.mod_mask");
 
-							log("Replacing modulo-by-%d cell `%s' in module `%s' with bitmask.\n",
+							log_debug("Replacing modulo-by-%d cell `%s' in module `%s' with bitmask.\n",
 									b_val, cell->name.c_str(), module->name.c_str());
 
 							std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(State::S1, i);
@@ -1342,7 +1343,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
 				SigSpec y_sig = cell->getPort("\\Y");
 				Const y_value(cell->type.in("$eq", "$eqx") ? 0 : 1, GetSize(y_sig));
 
-				log("Replacing cell `%s' in module `%s' with constant driver %s.\n",
+				log_debug("Replacing cell `%s' in module `%s' with constant driver %s.\n",
 					log_id(cell), log_id(module), log_signal(y_value));
 
 				module->connect(y_sig, y_value);
@@ -1354,7 +1355,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
 
 			if (redundant_bits)
 			{
-				log("Removed %d redundant input bits from %s cell `%s' in module `%s'.\n",
+				log_debug("Removed %d redundant input bits from %s cell `%s' in module `%s'.\n",
 						redundant_bits, log_id(cell->type), log_id(cell), log_id(module));
 
 				cell->setPort("\\A", sig_a);
@@ -1493,7 +1494,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
 
 				if (replace || remove)
 				{
-					log("Replacing %s cell `%s' (implementing %s) with %s.\n",
+					log_debug("Replacing %s cell `%s' (implementing %s) with %s.\n",
 							log_id(cell->type), log_id(cell), condition.c_str(), replacement.c_str());
 					if (replace)
 						module->connect(cell->getPort("\\Y"), replace_sig);
@@ -1599,8 +1600,14 @@ struct OptExprPass : public Pass {
 
 		for (auto module : design->selected_modules())
 		{
-			if (undriven)
+			log("Optimizing module %s.\n", log_id(module));
+
+			if (undriven) {
+				did_something = false;
 				replace_undriven(design, module);
+				if (did_something)
+					design->scratchpad_set_bool("opt.did_something", true);
+			}
 
 			do {
 				do {
@@ -1610,7 +1617,11 @@ struct OptExprPass : public Pass {
 						design->scratchpad_set_bool("opt.did_something", true);
 				} while (did_something);
 				replace_const_cells(design, module, true, mux_undef, mux_bool, do_fine, keepdc, clkinv);
+				if (did_something)
+					design->scratchpad_set_bool("opt.did_something", true);
 			} while (did_something);
+
+			log_suppressed();
 		}
 
 		log_pop();
diff --git a/passes/opt/opt_merge.cc b/passes/opt/opt_merge.cc
index eedf88904..7567d4657 100644
--- a/passes/opt/opt_merge.cc
+++ b/passes/opt/opt_merge.cc
@@ -315,17 +315,17 @@ struct OptMergeWorker
 			{
 				if (sharemap.count(cell) > 0) {
 					did_something = true;
-					log("  Cell `%s' is identical to cell `%s'.\n", cell->name.c_str(), sharemap[cell]->name.c_str());
+					log_debug("  Cell `%s' is identical to cell `%s'.\n", cell->name.c_str(), sharemap[cell]->name.c_str());
 					for (auto &it : cell->connections()) {
 						if (cell->output(it.first)) {
 							RTLIL::SigSpec other_sig = sharemap[cell]->getPort(it.first);
-							log("    Redirecting output %s: %s = %s\n", it.first.c_str(),
+							log_debug("    Redirecting output %s: %s = %s\n", it.first.c_str(),
 									log_signal(it.second), log_signal(other_sig));
 							module->connect(RTLIL::SigSig(it.second, other_sig));
 							assign_map.add(it.second, other_sig);
 						}
 					}
-					log("    Removing %s cell `%s' from module `%s'.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str());
+					log_debug("    Removing %s cell `%s' from module `%s'.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str());
 #ifdef USE_CELL_HASH_CACHE
 					cell_hash_cache.erase(cell);
 #endif
@@ -336,6 +336,8 @@ struct OptMergeWorker
 				}
 			}
 		}
+
+		log_suppressed();
 	}
 };
 
diff --git a/passes/opt/opt_muxtree.cc b/passes/opt/opt_muxtree.cc
index 375697dc8..dbebf21e0 100644
--- a/passes/opt/opt_muxtree.cc
+++ b/passes/opt/opt_muxtree.cc
@@ -181,14 +181,14 @@ struct OptMuxtreeWorker
 
 		for (int mux_idx = 0; mux_idx < GetSize(root_muxes); mux_idx++)
 			if (root_muxes.at(mux_idx)) {
-				log("    Root of a mux tree: %s%s\n", log_id(mux2info[mux_idx].cell), root_enable_muxes.at(mux_idx) ? " (pure)" : "");
+				log_debug("    Root of a mux tree: %s%s\n", log_id(mux2info[mux_idx].cell), root_enable_muxes.at(mux_idx) ? " (pure)" : "");
 				root_mux_rerun.erase(mux_idx);
 				eval_root_mux(mux_idx);
 			}
 
 		while (!root_mux_rerun.empty()) {
 			int mux_idx = *root_mux_rerun.begin();
-			log("    Root of a mux tree: %s (rerun as non-pure)\n", log_id(mux2info[mux_idx].cell));
+			log_debug("    Root of a mux tree: %s (rerun as non-pure)\n", log_id(mux2info[mux_idx].cell));
 			log_assert(root_enable_muxes.at(mux_idx));
 			root_mux_rerun.erase(mux_idx);
 			eval_root_mux(mux_idx);
@@ -326,7 +326,7 @@ struct OptMuxtreeWorker
 				if (abort_count == 0) {
 					root_mux_rerun.insert(m);
 					root_enable_muxes.at(m) = true;
-					log("      Removing pure flag from root mux %s.\n", log_id(mux2info[m].cell));
+					log_debug("      Removing pure flag from root mux %s.\n", log_id(mux2info[m].cell));
 				} else
 					eval_mux(knowledge, m, false, do_enable_ports, abort_count - 1);
 			} else
diff --git a/passes/opt/pmux2shiftx.cc b/passes/opt/pmux2shiftx.cc
new file mode 100644
index 000000000..29870f510
--- /dev/null
+++ b/passes/opt/pmux2shiftx.cc
@@ -0,0 +1,852 @@
+/*
+ *  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 OnehotDatabase
+{
+	Module *module;
+	const SigMap &sigmap;
+	bool verbose = false;
+	bool initialized = false;
+
+	pool<SigBit> init_ones;
+	dict<SigSpec, pool<SigSpec>> sig_sources_db;
+	dict<SigSpec, bool> sig_onehot_cache;
+	pool<SigSpec> recursion_guard;
+
+	OnehotDatabase(Module *module, const SigMap &sigmap) : module(module), sigmap(sigmap)
+	{
+	}
+
+	void initialize()
+	{
+		log_assert(!initialized);
+		initialized = true;
+
+		for (auto wire : module->wires())
+		{
+			auto it = wire->attributes.find("\\init");
+			if (it == wire->attributes.end())
+				continue;
+
+			auto &val = it->second;
+			int width = std::max(GetSize(wire), GetSize(val));
+
+			for (int i = 0; i < width; i++)
+				if (val[i] == State::S1)
+					init_ones.insert(sigmap(SigBit(wire, i)));
+		}
+
+		for (auto cell : module->cells())
+		{
+			vector<SigSpec> inputs;
+			SigSpec output;
+
+			if (cell->type.in("$adff", "$dff", "$dffe", "$dlatch", "$ff"))
+			{
+				output = cell->getPort("\\Q");
+				if (cell->type == "$adff")
+					inputs.push_back(cell->getParam("\\ARST_VALUE"));
+				inputs.push_back(cell->getPort("\\D"));
+			}
+
+			if (cell->type.in("$mux", "$pmux"))
+			{
+				output = cell->getPort("\\Y");
+				inputs.push_back(cell->getPort("\\A"));
+				SigSpec B = cell->getPort("\\B");
+				for (int i = 0; i < GetSize(B); i += GetSize(output))
+					inputs.push_back(B.extract(i, GetSize(output)));
+			}
+
+			if (!output.empty())
+			{
+				output = sigmap(output);
+				auto &srcs = sig_sources_db[output];
+				for (auto src : inputs) {
+					while (!src.empty() && src[GetSize(src)-1] == State::S0)
+						src.remove(GetSize(src)-1);
+					srcs.insert(sigmap(src));
+				}
+			}
+		}
+	}
+
+	void query_worker(const SigSpec &sig, bool &retval, bool &cache, int indent)
+	{
+		if (verbose)
+			log("%*s %s\n", indent, "", log_signal(sig));
+		log_assert(retval);
+
+		if (recursion_guard.count(sig)) {
+			if (verbose)
+				log("%*s   - recursion\n", indent, "");
+			cache = false;
+			return;
+		}
+
+		auto it = sig_onehot_cache.find(sig);
+		if (it != sig_onehot_cache.end()) {
+			if (verbose)
+				log("%*s   - cached (%s)\n", indent, "", it->second ? "true" : "false");
+			if (!it->second)
+				retval = false;
+			return;
+		}
+
+		bool found_init_ones = false;
+		for (auto bit : sig) {
+			if (init_ones.count(bit)) {
+				if (found_init_ones) {
+					if (verbose)
+						log("%*s   - non-onehot init value\n", indent, "");
+					retval = false;
+					break;
+				}
+				found_init_ones = true;
+			}
+		}
+
+		if (retval)
+		{
+			if (sig.is_fully_const())
+			{
+				bool found_ones = false;
+				for (auto bit : sig) {
+					if (bit == State::S1) {
+						if (found_ones) {
+							if (verbose)
+								log("%*s   - non-onehot constant\n", indent, "");
+							retval = false;
+							break;
+						}
+						found_ones = true;
+					}
+				}
+			}
+			else
+			{
+				auto srcs = sig_sources_db.find(sig);
+				if (srcs == sig_sources_db.end()) {
+					if (verbose)
+						log("%*s   - no sources for non-const signal\n", indent, "");
+					retval = false;
+				} else {
+					for (auto &src : srcs->second) {
+						bool child_cache = true;
+						recursion_guard.insert(sig);
+						query_worker(src, retval, child_cache, indent+4);
+						recursion_guard.erase(sig);
+						if (!child_cache)
+							cache = false;
+						if (!retval)
+							break;
+					}
+				}
+			}
+		}
+
+		// it is always safe to cache a negative result
+		if (cache || !retval)
+			sig_onehot_cache[sig] = retval;
+	}
+
+	bool query(const SigSpec &sig)
+	{
+		bool retval = true;
+		bool cache = true;
+
+		if (verbose)
+			log("** ONEHOT QUERY START (%s)\n", log_signal(sig));
+
+		if (!initialized)
+			initialize();
+
+		query_worker(sig, retval, cache, 3);
+
+		if (verbose)
+			log("** ONEHOT QUERY RESULT = %s\n", retval ? "true" : "false");
+
+		// it is always safe to cache the root result of a query
+		if (!cache)
+			sig_onehot_cache[sig] = retval;
+
+		return retval;
+	}
+};
+
+struct Pmux2ShiftxPass : public Pass {
+	Pmux2ShiftxPass() : Pass("pmux2shiftx", "transform $pmux cells to $shiftx cells") { }
+	void help() YS_OVERRIDE
+	{
+		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+		log("\n");
+		log("    pmux2shiftx [options] [selection]\n");
+		log("\n");
+		log("This pass transforms $pmux cells to $shiftx cells.\n");
+		log("\n");
+		log("    -v, -vv\n");
+		log("        verbose output\n");
+		log("\n");
+		log("    -min_density <percentage>\n");
+		log("        specifies the minimum density for the shifter\n");
+		log("        default: 50\n");
+		log("\n");
+		log("    -min_choices <int>\n");
+		log("        specified the minimum number of choices for a control signal\n");
+		log("        default: 3\n");
+		log("\n");
+		log("    -onehot ignore|pmux|shiftx\n");
+		log("        select strategy for one-hot encoded control signals\n");
+		log("        default: pmux\n");
+		log("\n");
+	}
+	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+	{
+		int min_density = 50;
+		int min_choices = 3;
+		bool allow_onehot = false;
+		bool optimize_onehot = true;
+		bool verbose = false;
+		bool verbose_onehot = false;
+
+		log_header(design, "Executing PMUX2SHIFTX pass.\n");
+
+		size_t argidx;
+		for (argidx = 1; argidx < args.size(); argidx++) {
+			if (args[argidx] == "-min_density" && argidx+1 < args.size()) {
+				min_density = atoi(args[++argidx].c_str());
+				continue;
+			}
+			if (args[argidx] == "-min_choices" && argidx+1 < args.size()) {
+				min_choices = atoi(args[++argidx].c_str());
+				continue;
+			}
+			if (args[argidx] == "-onehot" && argidx+1 < args.size() && args[argidx+1] == "ignore") {
+				argidx++;
+				allow_onehot = false;
+				optimize_onehot = false;
+				continue;
+			}
+			if (args[argidx] == "-onehot" && argidx+1 < args.size() && args[argidx+1] == "pmux") {
+				argidx++;
+				allow_onehot = false;
+				optimize_onehot = true;
+				continue;
+			}
+			if (args[argidx] == "-onehot" && argidx+1 < args.size() && args[argidx+1] == "shiftx") {
+				argidx++;
+				allow_onehot = true;
+				optimize_onehot = false;
+				continue;
+			}
+			if (args[argidx] == "-v") {
+				verbose = true;
+				continue;
+			}
+			if (args[argidx] == "-vv") {
+				verbose = true;
+				verbose_onehot = true;
+				continue;
+			}
+			break;
+		}
+		extra_args(args, argidx, design);
+
+		for (auto module : design->selected_modules())
+		{
+			SigMap sigmap(module);
+			OnehotDatabase onehot_db(module, sigmap);
+			onehot_db.verbose = verbose_onehot;
+
+			dict<SigBit, pair<SigSpec, Const>> eqdb;
+
+			for (auto cell : module->cells())
+			{
+				if (cell->type == "$eq")
+				{
+					dict<SigBit, State> bits;
+
+					SigSpec A = sigmap(cell->getPort("\\A"));
+					SigSpec B = sigmap(cell->getPort("\\B"));
+
+					int a_width = cell->getParam("\\A_WIDTH").as_int();
+					int b_width = cell->getParam("\\B_WIDTH").as_int();
+
+					if (a_width < b_width) {
+						bool a_signed = cell->getParam("\\A_SIGNED").as_int();
+						A.extend_u0(b_width, a_signed);
+					}
+
+					if (b_width < a_width) {
+						bool b_signed = cell->getParam("\\B_SIGNED").as_int();
+						B.extend_u0(a_width, b_signed);
+					}
+
+					for (int i = 0; i < GetSize(A); i++) {
+						SigBit a_bit = A[i], b_bit = B[i];
+						if (b_bit.wire && !a_bit.wire) {
+							std::swap(a_bit, b_bit);
+						}
+						if (!a_bit.wire || b_bit.wire)
+							goto next_cell;
+						if (bits.count(a_bit))
+							goto next_cell;
+						bits[a_bit] = b_bit.data;
+					}
+
+					if (GetSize(bits) > 20)
+						goto next_cell;
+
+					bits.sort();
+					pair<SigSpec, Const> entry;
+
+					for (auto it : bits) {
+						entry.first.append_bit(it.first);
+						entry.second.bits.push_back(it.second);
+					}
+
+					eqdb[sigmap(cell->getPort("\\Y")[0])] = entry;
+					goto next_cell;
+				}
+
+				if (cell->type == "$logic_not")
+				{
+					dict<SigBit, State> bits;
+
+					SigSpec A = sigmap(cell->getPort("\\A"));
+
+					for (int i = 0; i < GetSize(A); i++)
+						bits[A[i]] = State::S0;
+
+					bits.sort();
+					pair<SigSpec, Const> entry;
+
+					for (auto it : bits) {
+						entry.first.append_bit(it.first);
+						entry.second.bits.push_back(it.second);
+					}
+
+					eqdb[sigmap(cell->getPort("\\Y")[0])] = entry;
+					goto next_cell;
+				}
+		next_cell:;
+			}
+
+			for (auto cell : module->selected_cells())
+			{
+				if (cell->type != "$pmux")
+					continue;
+
+				string src = cell->get_src_attribute();
+				int width = cell->getParam("\\WIDTH").as_int();
+				int width_bits = ceil_log2(width);
+				int extwidth = width;
+
+				while (extwidth & (extwidth-1))
+					extwidth++;
+
+				dict<SigSpec, pool<int>> seldb;
+
+				SigSpec A = cell->getPort("\\A");
+				SigSpec B = cell->getPort("\\B");
+				SigSpec S = sigmap(cell->getPort("\\S"));
+				for (int i = 0; i < GetSize(S); i++)
+				{
+					if (!eqdb.count(S[i]))
+						continue;
+
+					auto &entry = eqdb.at(S[i]);
+					seldb[entry.first].insert(i);
+				}
+
+				if (seldb.empty())
+					continue;
+
+				bool printed_pmux_header = false;
+
+				if (verbose) {
+					printed_pmux_header = true;
+					log("Inspecting $pmux cell %s/%s.\n", log_id(module), log_id(cell));
+					log("  data width: %d (next power-of-2 = %d, log2 = %d)\n", width, extwidth, width_bits);
+				}
+
+				SigSpec updated_S = cell->getPort("\\S");
+				SigSpec updated_B = cell->getPort("\\B");
+
+				while (!seldb.empty())
+				{
+					// pick the largest entry in seldb
+					SigSpec sig = seldb.begin()->first;
+					for (auto &it : seldb) {
+						if (GetSize(sig) < GetSize(it.first))
+							sig = it.first;
+						else if (GetSize(seldb.at(sig)) < GetSize(it.second))
+							sig = it.first;
+					}
+
+					// find the relevant choices
+					bool is_onehot = GetSize(sig) > 2;
+					dict<Const, int> choices;
+					for (int i : seldb.at(sig)) {
+						Const val = eqdb.at(S[i]).second;
+						int onebits = 0;
+						for (auto b : val.bits)
+							if (b == State::S1)
+								onebits++;
+						if (onebits > 1)
+							is_onehot = false;
+						choices[val] = i;
+					}
+
+					bool full_pmux = GetSize(choices) == GetSize(S);
+
+					// TBD: also find choices that are using signals that are subsets of the bits in "sig"
+
+					if (!verbose)
+					{
+						if (is_onehot && !allow_onehot && !optimize_onehot) {
+							seldb.erase(sig);
+							continue;
+						}
+
+						if (GetSize(choices) < min_choices) {
+							seldb.erase(sig);
+							continue;
+						}
+					}
+
+					if (!printed_pmux_header) {
+						printed_pmux_header = true;
+						log("Inspecting $pmux cell %s/%s.\n", log_id(module), log_id(cell));
+						log("  data width: %d (next power-of-2 = %d, log2 = %d)\n", width, extwidth, width_bits);
+					}
+
+					log("  checking ctrl signal %s\n", log_signal(sig));
+
+					auto print_choices = [&]() {
+						log("    table of choices:\n");
+						for (auto &it : choices)
+							log("    %3d: %s: %s\n", it.second, log_signal(it.first),
+									log_signal(B.extract(it.second*width, width)));
+					};
+
+					if (verbose)
+					{
+						if (is_onehot && !allow_onehot && !optimize_onehot) {
+							print_choices();
+							log("    ignoring one-hot encoding.\n");
+							seldb.erase(sig);
+							continue;
+						}
+
+						if (GetSize(choices) < min_choices) {
+							print_choices();
+							log("    insufficient choices.\n");
+							seldb.erase(sig);
+							continue;
+						}
+					}
+
+					if (is_onehot && optimize_onehot)
+					{
+						print_choices();
+						if (!onehot_db.query(sig))
+						{
+							log("    failed to detect onehot driver. do not optimize.\n");
+						}
+						else
+						{
+							log("    optimizing one-hot encoding.\n");
+							for (auto &it : choices)
+							{
+								const Const &val = it.first;
+								int index = -1;
+
+								for (int i = 0; i < GetSize(val); i++)
+									if (val[i] == State::S1) {
+										log_assert(index < 0);
+										index = i;
+									}
+
+								if (index < 0) {
+									log("    %3d: zero encoding.\n", it.second);
+									continue;
+								}
+
+								SigBit new_ctrl = sig[index];
+								log("    %3d: new crtl signal is %s.\n", it.second, log_signal(new_ctrl));
+								updated_S[it.second] = new_ctrl;
+							}
+						}
+						seldb.erase(sig);
+						continue;
+					}
+
+					// find the best permutation
+					vector<int> perm_new_from_old(GetSize(sig));
+					Const perm_xormask(State::S0, GetSize(sig));
+					{
+						vector<int> values(GetSize(choices));
+						vector<bool> used_src_columns(GetSize(sig));
+						vector<vector<bool>> columns(GetSize(sig), vector<bool>(GetSize(values)));
+
+						for (int i = 0; i < GetSize(choices); i++) {
+							Const val = choices.element(i)->first;
+							for (int k = 0; k < GetSize(val); k++)
+								if (val[k] == State::S1)
+									columns[k][i] = true;
+						}
+
+						for (int dst_col = GetSize(sig)-1; dst_col >= 0; dst_col--)
+						{
+							int best_src_col = -1;
+							bool best_inv = false;
+							int best_maxval = 0;
+							int best_delta = 0;
+
+							// find best src column for this dst column
+							for (int src_col = 0; src_col < GetSize(sig); src_col++)
+							{
+								if (used_src_columns[src_col])
+									continue;
+
+								int this_maxval = 0;
+								int this_minval = 1 << 30;
+
+								int this_inv_maxval = 0;
+								int this_inv_minval = 1 << 30;
+
+								for (int i = 0; i < GetSize(values); i++)
+								{
+									int val = values[i];
+									int inv_val = val;
+
+									if (columns[src_col][i])
+										val |= 1 << dst_col;
+									else
+										inv_val |= 1 << dst_col;
+
+									this_maxval = std::max(this_maxval, val);
+									this_minval = std::min(this_minval, val);
+
+									this_inv_maxval = std::max(this_inv_maxval, inv_val);
+									this_inv_minval = std::min(this_inv_minval, inv_val);
+								}
+
+								int this_delta = this_maxval - this_minval;
+								int this_inv_delta = this_maxval - this_minval;
+								bool this_inv = false;
+
+								if (this_delta != this_inv_delta)
+									this_inv = this_inv_delta < this_delta;
+								else if (this_maxval != this_inv_maxval)
+									this_inv = this_inv_maxval < this_maxval;
+
+								if (this_inv) {
+									this_delta = this_inv_delta;
+									this_maxval = this_inv_maxval;
+									this_minval = this_inv_minval;
+								}
+
+								bool this_is_better = false;
+
+								if (best_src_col < 0)
+									this_is_better = true;
+								else if (this_delta != best_delta)
+									this_is_better = this_delta < best_delta;
+								else if (this_maxval != best_maxval)
+									this_is_better = this_maxval < best_maxval;
+								else
+									this_is_better = sig[best_src_col] < sig[src_col];
+
+								if (this_is_better) {
+									best_src_col = src_col;
+									best_inv = this_inv;
+									best_maxval = this_maxval;
+									best_delta = this_delta;
+								}
+							}
+
+							used_src_columns[best_src_col] = true;
+							perm_new_from_old[dst_col] = best_src_col;
+							perm_xormask[dst_col] = best_inv ? State::S1 : State::S0;
+						}
+					}
+
+					// permutated sig
+					SigSpec perm_sig(State::S0, GetSize(sig));
+					for (int i = 0; i < GetSize(sig); i++)
+						perm_sig[i] = sig[perm_new_from_old[i]];
+
+					log("    best permutation: %s\n", log_signal(perm_sig));
+					log("    best xor mask: %s\n", log_signal(perm_xormask));
+
+					// permutated choices
+					int min_choice = 1 << 30;
+					int max_choice = -1;
+					dict<Const, int> perm_choices;
+
+					for (auto &it : choices)
+					{
+						Const &old_c = it.first;
+						Const new_c(State::S0, GetSize(old_c));
+
+						for (int i = 0; i < GetSize(old_c); i++)
+							new_c[i] = old_c[perm_new_from_old[i]];
+
+						Const new_c_before_xor = new_c;
+						new_c = const_xor(new_c, perm_xormask, false, false, GetSize(new_c));
+
+						perm_choices[new_c] = it.second;
+
+						min_choice = std::min(min_choice, new_c.as_int());
+						max_choice = std::max(max_choice, new_c.as_int());
+
+						log("    %3d: %s -> %s -> %s: %s\n", it.second, log_signal(old_c), log_signal(new_c_before_xor),
+								log_signal(new_c), log_signal(B.extract(it.second*width, width)));
+					}
+
+					int range_density = 100*GetSize(choices) / (max_choice-min_choice+1);
+					int absolute_density = 100*GetSize(choices) / (max_choice+1);
+
+					log("    choices: %d\n", GetSize(choices));
+					log("    min choice: %d\n", min_choice);
+					log("    max choice: %d\n", max_choice);
+					log("    range density: %d%%\n", range_density);
+					log("    absolute density: %d%%\n", absolute_density);
+
+					if (full_pmux) {
+						int full_density = 100*GetSize(choices) / (1 << GetSize(sig));
+						log("    full density: %d%%\n", full_density);
+						if (full_density < min_density) {
+							full_pmux = false;
+						} else {
+							min_choice = 0;
+							max_choice = (1 << GetSize(sig))-1;
+							log("    update to full case.\n");
+							log("    new min choice: %d\n", min_choice);
+							log("    new max choice: %d\n", max_choice);
+						}
+					}
+
+					bool full_case = (min_choice == 0) && (max_choice == (1 << GetSize(sig))-1) && (full_pmux || max_choice+1 == GetSize(choices));
+					log("    full case: %s\n", full_case ? "true" : "false");
+
+					// check density percentages
+					Const offset(State::S0, GetSize(sig));
+					if (absolute_density < min_density && range_density >= min_density)
+					{
+						offset = Const(min_choice, GetSize(sig));
+						log("    offset: %s\n", log_signal(offset));
+
+						min_choice -= offset.as_int();
+						max_choice -= offset.as_int();
+
+						dict<Const, int> new_perm_choices;
+						for (auto &it : perm_choices)
+							new_perm_choices[const_sub(it.first, offset, false, false, GetSize(sig))] = it.second;
+						perm_choices.swap(new_perm_choices);
+					} else
+					if (absolute_density < min_density) {
+						log("    insufficient density.\n");
+						seldb.erase(sig);
+						continue;
+					}
+
+					// creat cmp signal
+					SigSpec cmp = perm_sig;
+					if (perm_xormask.as_bool())
+						cmp = module->Xor(NEW_ID, cmp, perm_xormask, false, src);
+					if (offset.as_bool())
+						cmp = module->Sub(NEW_ID, cmp, offset, false, src);
+
+					// create enable signal
+					SigBit en = State::S1;
+					if (!full_case) {
+						Const enable_mask(State::S0, max_choice+1);
+						for (auto &it : perm_choices)
+							enable_mask[it.first.as_int()] = State::S1;
+						en = module->addWire(NEW_ID);
+						module->addShift(NEW_ID, enable_mask, cmp, en, false, src);
+					}
+
+					// create data signal
+					SigSpec data(State::Sx, (max_choice+1)*extwidth);
+					if (full_pmux) {
+						for (int i = 0; i <= max_choice; i++)
+							data.replace(i*extwidth, A);
+					}
+					for (auto &it : perm_choices) {
+						int position = it.first.as_int()*extwidth;
+						int data_index = it.second;
+						data.replace(position, B.extract(data_index*width, width));
+						updated_S[data_index] = State::S0;
+						updated_B.replace(data_index*width, SigSpec(State::Sx, width));
+					}
+
+					// create shiftx cell
+					SigSpec shifted_cmp = {cmp, SigSpec(State::S0, width_bits)};
+					SigSpec outsig = module->addWire(NEW_ID, width);
+					Cell *c = module->addShiftx(NEW_ID, data, shifted_cmp, outsig, false, src);
+					updated_S.append(en);
+					updated_B.append(outsig);
+					log("    created $shiftx cell %s.\n", log_id(c));
+
+					// remove this sig and continue with the next block
+					seldb.erase(sig);
+				}
+
+				// update $pmux cell
+				cell->setPort("\\S", updated_S);
+				cell->setPort("\\B", updated_B);
+				cell->setParam("\\S_WIDTH", GetSize(updated_S));
+			}
+		}
+	}
+} Pmux2ShiftxPass;
+
+struct OnehotPass : public Pass {
+	OnehotPass() : Pass("onehot", "optimize $eq cells for onehot signals") { }
+	void help() YS_OVERRIDE
+	{
+		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+		log("\n");
+		log("    onehot [options] [selection]\n");
+		log("\n");
+		log("This pass optimizes $eq cells that compare one-hot signals against constants\n");
+		log("\n");
+		log("    -v, -vv\n");
+		log("        verbose output\n");
+		log("\n");
+	}
+	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+	{
+		bool verbose = false;
+		bool verbose_onehot = false;
+
+		log_header(design, "Executing ONEHOT pass.\n");
+
+		size_t argidx;
+		for (argidx = 1; argidx < args.size(); argidx++) {
+			if (args[argidx] == "-v") {
+				verbose = true;
+				continue;
+			}
+			if (args[argidx] == "-vv") {
+				verbose = true;
+				verbose_onehot = true;
+				continue;
+			}
+			break;
+		}
+		extra_args(args, argidx, design);
+
+		for (auto module : design->selected_modules())
+		{
+			SigMap sigmap(module);
+			OnehotDatabase onehot_db(module, sigmap);
+			onehot_db.verbose = verbose_onehot;
+
+			for (auto cell : module->selected_cells())
+			{
+				if (cell->type != "$eq")
+					continue;
+
+				SigSpec A = sigmap(cell->getPort("\\A"));
+				SigSpec B = sigmap(cell->getPort("\\B"));
+
+				int a_width = cell->getParam("\\A_WIDTH").as_int();
+				int b_width = cell->getParam("\\B_WIDTH").as_int();
+
+				if (a_width < b_width) {
+					bool a_signed = cell->getParam("\\A_SIGNED").as_int();
+					A.extend_u0(b_width, a_signed);
+				}
+
+				if (b_width < a_width) {
+					bool b_signed = cell->getParam("\\B_SIGNED").as_int();
+					B.extend_u0(a_width, b_signed);
+				}
+
+				if (A.is_fully_const())
+					std::swap(A, B);
+
+				if (!B.is_fully_const())
+					continue;
+
+				if (verbose)
+					log("Checking $eq(%s, %s) cell %s/%s.\n", log_signal(A), log_signal(B), log_id(module), log_id(cell));
+
+				if (!onehot_db.query(A)) {
+					if (verbose)
+						log("  onehot driver test on %s failed.\n", log_signal(A));
+					continue;
+				}
+
+				int index = -1;
+				bool not_onehot = false;
+
+				for (int i = 0; i < GetSize(B); i++) {
+					if (B[i] != State::S1)
+						continue;
+					if (index >= 0)
+						not_onehot = true;
+					index = i;
+				}
+
+				if (index < 0) {
+					if (verbose)
+						log("  not optimizing the zero pattern.\n");
+					continue;
+				}
+
+				SigSpec Y = cell->getPort("\\Y");
+
+				if (not_onehot)
+				{
+					if (verbose)
+						log("  replacing with constant 0 driver.\n");
+					else
+						log("Replacing one-hot $eq(%s, %s) cell %s/%s with constant 0 driver.\n", log_signal(A), log_signal(B), log_id(module), log_id(cell));
+					module->connect(Y, SigSpec(1, GetSize(Y)));
+				}
+				else
+				{
+					SigSpec sig = A[index];
+					if (verbose)
+						log("  replacing with signal %s.\n", log_signal(sig));
+					else
+						log("Replacing one-hot $eq(%s, %s) cell %s/%s with signal %s.\n",log_signal(A), log_signal(B), log_id(module), log_id(cell), log_signal(sig));
+					sig.extend_u0(GetSize(Y));
+					module->connect(Y, sig);
+				}
+
+				module->remove(cell);
+			}
+		}
+	}
+} OnehotPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/attrmap.cc b/passes/techmap/attrmap.cc
index 0b5576b06..aa48e1125 100644
--- a/passes/techmap/attrmap.cc
+++ b/passes/techmap/attrmap.cc
@@ -111,9 +111,10 @@ struct AttrmapMap : AttrmapAction {
 };
 
 struct AttrmapRemove : AttrmapAction {
+	bool has_value;
 	string name, value;
 	bool apply(IdString &id, Const &val) YS_OVERRIDE {
-		return !(match_name(name, id) && match_value(value, val));
+		return !(match_name(name, id) && (!has_value || match_value(value, val)));
 	}
 };
 
@@ -235,6 +236,7 @@ struct AttrmapPass : public Pass {
 				}
 				auto action = new AttrmapRemove;
 				action->name = arg1;
+				action->has_value = (p != string::npos);
 				action->value = val1;
 				actions.push_back(std::unique_ptr<AttrmapAction>(action));
 				continue;
diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc
index 1a4318460..ab0bd3b54 100644
--- a/passes/techmap/techmap.cc
+++ b/passes/techmap/techmap.cc
@@ -72,6 +72,8 @@ struct TechmapWorker
 	pool<IdString> flatten_done_list;
 	pool<Cell*> flatten_keep_list;
 
+	pool<string> log_msg_cache;
+
 	struct TechmapWireData {
 		RTLIL::Wire *wire;
 		RTLIL::SigSpec value;
@@ -390,6 +392,7 @@ struct TechmapWorker
 
 		bool log_continue = false;
 		bool did_something = false;
+		LogMakeDebugHdl mkdebug;
 
 		SigMap sigmap(module);
 
@@ -547,6 +550,7 @@ struct TechmapWorker
 								if (extmapper_name == "wrap") {
 									std::string cmd_string = tpl->attributes.at("\\techmap_wrap").decode_string();
 									log("Running \"%s\" on wrapper %s.\n", cmd_string.c_str(), log_id(extmapper_module));
+									mkdebug.on();
 									Pass::call_on_module(extmapper_design, extmapper_module, cmd_string);
 									log_continue = true;
 								}
@@ -560,11 +564,21 @@ struct TechmapWorker
 								goto use_wrapper_tpl;
 							}
 
-							log("%s %s.%s (%s) to %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(cell->type), log_id(extmapper_module));
+							auto msg = stringf("Using extmapper %s for cells of type %s.", log_id(extmapper_module), log_id(cell->type));
+							if (!log_msg_cache.count(msg)) {
+								log_msg_cache.insert(msg);
+								log("%s\n", msg.c_str());
+							}
+							log_debug("%s %s.%s (%s) to %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(cell->type), log_id(extmapper_module));
 						}
 						else
 						{
-							log("%s %s.%s (%s) with %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(cell->type), extmapper_name.c_str());
+							auto msg = stringf("Using extmapper %s for cells of type %s.", extmapper_name.c_str(), log_id(cell->type));
+							if (!log_msg_cache.count(msg)) {
+								log_msg_cache.insert(msg);
+								log("%s\n", msg.c_str());
+							}
+							log_debug("%s %s.%s (%s) with %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(cell->type), extmapper_name.c_str());
 
 							if (extmapper_name == "simplemap") {
 								if (simplemap_mappers.count(cell->type) == 0)
@@ -662,6 +676,7 @@ struct TechmapWorker
 						tpl = techmap_cache[key];
 					} else {
 						if (parameters.size() != 0) {
+							mkdebug.on();
 							derived_name = tpl->derive(map, dict<RTLIL::IdString, RTLIL::Const>(parameters.begin(), parameters.end()));
 							tpl = map->module(derived_name);
 							log_continue = true;
@@ -831,6 +846,7 @@ struct TechmapWorker
 						if (log_continue) {
 							log_header(design, "Continuing TECHMAP pass.\n");
 							log_continue = false;
+							mkdebug.off();
 						}
 						while (techmap_module(map, tpl, map, handled_cells, celltypeMap, true)) { }
 					}
@@ -842,6 +858,7 @@ struct TechmapWorker
 				if (log_continue) {
 					log_header(design, "Continuing TECHMAP pass.\n");
 					log_continue = false;
+					mkdebug.off();
 				}
 
 				if (extern_mode && !in_recursion)
@@ -861,13 +878,18 @@ struct TechmapWorker
 						module_queue.insert(m);
 					}
 
-					log("%s %s.%s to imported %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(m_name));
+					log_debug("%s %s.%s to imported %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(m_name));
 					cell->type = m_name;
 					cell->parameters.clear();
 				}
 				else
 				{
-					log("%s %s.%s using %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(tpl));
+					auto msg = stringf("Using template %s for cells of type %s.", log_id(tpl), log_id(cell->type));
+					if (!log_msg_cache.count(msg)) {
+						log_msg_cache.insert(msg);
+						log("%s\n", msg.c_str());
+					}
+					log_debug("%s %s.%s (%s) using %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(cell->type), log_id(tpl));
 					techmap_module_worker(design, module, cell, tpl);
 					cell = NULL;
 				}
@@ -885,6 +907,7 @@ struct TechmapWorker
 		if (log_continue) {
 			log_header(design, "Continuing TECHMAP pass.\n");
 			log_continue = false;
+			mkdebug.off();
 		}
 
 		return did_something;
@@ -1085,7 +1108,7 @@ struct TechmapPass : public Pass {
 		if (map_files.empty()) {
 			std::istringstream f(stdcells_code);
 			Frontend::frontend_call(map, &f, "<techmap.v>", verilog_frontend);
-		} else
+		} else {
 			for (auto &fn : map_files)
 				if (fn.substr(0, 1) == "%") {
 					if (!saved_designs.count(fn.substr(1))) {
@@ -1104,6 +1127,9 @@ struct TechmapPass : public Pass {
 						log_cmd_error("Can't open map file `%s'\n", fn.c_str());
 					Frontend::frontend_call(map, &f, fn, (fn.size() > 3 && fn.substr(fn.size()-3) == ".il") ? "ilang" : verilog_frontend);
 				}
+		}
+
+		log_header(design, "Continuing TECHMAP pass.\n");
 
 		std::map<RTLIL::IdString, std::set<RTLIL::IdString, RTLIL::sort_by_id_str>> celltypeMap;
 		for (auto &it : map->modules_) {
@@ -1211,6 +1237,7 @@ struct FlattenPass : public Pass {
 			}
 		}
 
+		log_suppressed();
 		log("No more expansions possible.\n");
 
 		if (top_mod != NULL)
diff --git a/py_wrap_generator.py b/py_wrap_generator.py
new file mode 100644
index 000000000..09f934040
--- /dev/null
+++ b/py_wrap_generator.py
@@ -0,0 +1,2096 @@
+#
+#  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.
+#
+#  Author Benedikt Tutzer
+#
+
+import copy
+
+#Map c++ operator Syntax to Python functions
+wrappable_operators = {
+		"<" : "__lt__",
+		"==": "__eq__",
+		"!=": "__ne__",
+		"+" : "__add__",
+		"-" : "__sub__",
+		"*" : "__mul__",
+		"/" : "__div__",
+		"()": "__call__"
+	}
+
+#Restrict certain strings from being function names in Python
+keyword_aliases = {
+		"in" : "in_",
+		"False" : "False_",
+		"None" : "None_",
+		"True" : "True_",
+		"and" : "and_",
+		"as" : "as_",
+		"assert" : "assert_",
+		"break" : "break_",
+		"class" : "class_",
+		"continue" : "continue_",
+		"def" : "def_",
+		"del" : "del_",
+		"elif" : "elif_",
+		"else" : "else_",
+		"except" : "except_",
+		"for" : "for_",
+		"from" : "from_",
+		"global" : "global_",
+		"if" : "if_",
+		"import" : "import_",
+		"in" : "in_",
+		"is" : "is_",
+		"lambda" : "lambda_",
+		"nonlocal" : "nonlocal_",
+		"not" : "not_",
+		"or" : "or_",
+		"pass" : "pass_",
+		"raise" : "raise_",
+		"return" : "return_",
+		"try" : "try_",
+		"while" : "while_",
+		"with" : "with_",
+		"yield" : "yield_"
+	}
+
+#These can be used without any explicit conversion
+primitive_types = ["void", "bool", "int", "double", "size_t", "std::string",
+		"string", "State", "char_p"]
+
+from enum import Enum
+
+#Ways to link between Python- and C++ Objects
+class link_types(Enum):
+	global_list = 1		#Manage a global list of objects in C++, the Python
+						#object contains a key to find the corresponding C++
+						#object and a Pointer to the object to verify it is
+						#still the same, making collisions unlikely to happen
+	ref_copy = 2		#The Python object contains a copy of the C++ object.
+						#The C++ object is deleted when the Python object gets
+						#deleted
+	pointer = 3			#The Python Object contains a pointer to it's C++
+						#counterpart
+	derive = 4			#The Python-Wrapper is derived from the C++ object.
+
+class attr_types(Enum):
+	star = "*"
+	amp = "&"
+	ampamp = "&&"
+	default = ""
+
+#For source-files
+class Source:
+	name = ""
+	classes = []
+
+	def __init__(self, name, classes):
+		self.name = name
+		self.classes = classes
+
+#Splits a list by the given delimiter, without splitting strings inside
+#pointy-brackets (< and >)
+def split_list(str_def, delim):
+	str_def = str_def.strip()
+	if len(str_def) == 0:
+		return []
+	if str_def.count(delim) == 0:
+		return [str_def]
+	if str_def.count("<") == 0:
+		return str_def.split(delim)
+	if str_def.find("<") < str_def.find(" "):
+		closing = find_closing(str_def[str_def.find("<")+1:], "<", ">") + str_def.find("<")
+		comma = str_def[closing:].find(delim)
+		if comma == -1:
+			return [str_def]
+		comma = closing  + comma
+	else:
+		comma = str_def.find(delim)
+	rest = split_list(str_def[comma+1:], delim)
+	ret = [str_def[:comma]]
+	if rest != None and len(rest) != 0:
+		ret.extend(rest)
+	return ret
+
+#Represents a Type
+class WType:
+	name = ""
+	cont = None
+	attr_type = attr_types.default
+
+	def __init__(self, name = "", cont = None, attr_type = attr_types.default):
+		self.name = name
+		self.cont = cont
+		self.attr_type = attr_type
+
+	#Python type-string
+	def gen_text(self):
+		text = self.name
+		if self.name in enum_names:
+			text = enum_by_name(self.name).namespace + "::" + self.name
+		if self.cont != None:
+			return known_containers[self.name].typename
+		return text
+
+	#C++ type-string
+	def gen_text_cpp(self):
+		postfix = ""
+		if self.attr_type == attr_types.star:
+			postfix = "*"
+		if self.name in primitive_types:
+			return self.name + postfix
+		if self.name in enum_names:
+			return enum_by_name(self.name).namespace + "::" + self.name + postfix
+		if self.name in classnames:
+			return class_by_name(self.name).namespace + "::" + self.name + postfix
+		text = self.name
+		if self.cont != None:
+			text += "<"
+			for a in self.cont.args:
+				text += a.gen_text_cpp() + ", "
+			text = text[:-2]
+			text += ">"
+		return text
+
+	@staticmethod
+	def from_string(str_def, containing_file, line_number):
+		str_def = str_def.strip()
+		if len(str_def) == 0:
+			return None
+		str_def = str_def.replace("RTLIL::SigSig", "std::pair<SigSpec, SigSpec>").replace("SigSig", "std::pair<SigSpec, SigSpec>")
+		t = WType()
+		t.name = ""
+		t.cont = None
+		t.attr_type = attr_types.default
+		if str_def.find("<") != -1:# and str_def.find("<") < str_def.find(" "):
+			candidate = WContainer.from_string(str_def, containing_file, line_number)
+			if candidate == None:
+				return None
+			t.name = str_def[:str_def.find("<")]
+
+			if t.name.count("*") + t.name.count("&") > 1:
+				return None
+
+			if t.name.count("*") == 1 or str_def[0] == '*' or str_def[-1] == '*':
+				t.attr_type = attr_types.star
+				t.name = t.name.replace("*","")
+			elif t.name.count("&&") == 1:
+				t.attr_type = attr_types.ampamp
+				t.name = t.name.replace("&&","")
+			elif t.name.count("&") == 1 or str_def[0] == '&' or str_def[-1] == '&':
+				t.attr_type = attr_types.amp
+				t.name = t.name.replace("&","")
+
+			t.cont = candidate
+			if(t.name not in known_containers):
+				return None	
+			return t
+
+		prefix = ""
+
+		if str.startswith(str_def, "unsigned "):
+			prefix = "unsigned "
+			str_def = str_def[9:]
+		while str.startswith(str_def, "long "):
+			prefix= "long " + prefix
+			str_def = str_def[5:]
+		while str.startswith(str_def, "short "):
+			prefix = "short " + prefix
+			str_def = str_def[6:]
+
+		str_def = str_def.split("::")[-1]
+
+		if str_def.count("*") + str_def.count("&") >= 2:
+			return None
+
+		if str_def.count("*") == 1:
+			t.attr_type = attr_types.star
+			str_def = str_def.replace("*","")
+		elif str_def.count("&&") == 1:
+			t.attr_type = attr_types.ampamp
+			str_def = str_def.replace("&&","")
+		elif str_def.count("&") == 1:
+			t.attr_type = attr_types.amp
+			str_def = str_def.replace("&","")
+
+		if len(str_def) > 0 and str_def.split("::")[-1] not in primitive_types and str_def.split("::")[-1] not in classnames and str_def.split("::")[-1] not in enum_names:
+			return None
+
+		if str_def.count(" ") == 0:
+			t.name = (prefix + str_def).replace("char_p", "char *")
+			t.cont = None
+			return t
+		return None
+
+#Represents a container-type
+class WContainer:
+	name = ""
+	args = []
+
+	def from_string(str_def, containing_file, line_number):
+		if str_def == None or len(str_def) < 4:
+			return None
+		cont = WContainer()
+		cont.name = str_def[:str_def.find("<")]
+		str_def = str_def[str_def.find("<")+1:find_closing(str_def, "<", ">")]
+		cont.args = []
+		for arg in split_list(str_def, ","):
+			candidate = WType.from_string(arg.strip(), containing_file, line_number)
+			if candidate == None:
+				return None
+			cont.args.append(candidate)
+		return cont
+
+#Translators between Python and C++ containers
+#Base Type
+class Translator:
+	tmp_cntr = 0
+	typename = "DefaultType"
+	orig_name = "DefaultCpp"
+
+	@classmethod
+	def gen_type(c, types):
+		return "\nImplement a function that outputs the c++ type of this container here\n"
+
+	@classmethod
+	def translate(c, varname, types, prefix):
+		return "\nImplement a function translating a python container to a c++ container here\n"
+
+	@classmethod
+	def translate_cpp(c, varname, types, prefix, ref):
+		return "\nImplement a function translating a c++ container to a python container here\n"
+
+#Translates list-types (vector, pool, set), that only differ in their name and
+#the name of the insertion function
+class PythonListTranslator(Translator):
+	typename = "boost::python::list"
+	insert_name = "Default"
+
+	#generate the c++ type string
+	@classmethod
+	def gen_type(c, types):
+		text = c.orig_name + "<"
+		if types[0].name in primitive_types:
+			text += types[0].name
+		elif types[0].name in known_containers:
+			text += known_containers[types[0].name].gen_type(types[0].cont.args)
+		else:
+			text += class_by_name(types[0].name).namespace + "::" + types[0].name
+			if types[0].attr_type == attr_types.star:
+				text += "*"
+		text += ">"
+		return text
+
+	#Generate C++ code to translate from a boost::python::list
+	@classmethod
+	def translate(c, varname, types, prefix):
+		text  = prefix + c.gen_type(types) + " " + varname + "___tmp;"
+		cntr_name = "cntr_" + str(Translator.tmp_cntr)
+		Translator.tmp_cntr = Translator.tmp_cntr + 1
+		text += prefix + "for(int " + cntr_name + " = 0; " + cntr_name + " < len(" + varname + "); " + cntr_name + "++)"
+		text += prefix + "{"
+		tmp_name = "tmp_" + str(Translator.tmp_cntr)
+		Translator.tmp_cntr = Translator.tmp_cntr + 1
+		if types[0].name in known_containers:
+			text += prefix + "\t" + known_containers[types[0].name].typename + " " + tmp_name + " = boost::python::extract<" + known_containers[types[0].name].typename + ">(" + varname + "[" + cntr_name + "]);"
+			text += known_containers[types[0].name].translate(tmp_name, types[0].cont.args, prefix+"\t")
+			tmp_name = tmp_name + "___tmp"
+			text += prefix + "\t" + varname + "___tmp." + c.insert_name + "(" + tmp_name + ");"
+		elif types[0].name in classnames:
+			text += prefix + "\t" + types[0].name + "* " + tmp_name + " = boost::python::extract<" + types[0].name + "*>(" + varname + "[" + cntr_name + "]);"
+			if types[0].attr_type == attr_types.star:
+				text += prefix + "\t" + varname + "___tmp." + c.insert_name + "(" + tmp_name + "->get_cpp_obj());"
+			else:
+				text += prefix + "\t" + varname + "___tmp." + c.insert_name + "(*" + tmp_name + "->get_cpp_obj());"
+		else:
+			text += prefix + "\t" + types[0].name + " " + tmp_name + " = boost::python::extract<" + types[0].name + ">(" + varname + "[" + cntr_name + "]);"
+			text += prefix + "\t" + varname + "___tmp." + c.insert_name + "(" + tmp_name + ");"
+		text += prefix + "}"
+		return text
+
+	#Generate C++ code to translate to a boost::python::list
+	@classmethod
+	def translate_cpp(c, varname, types, prefix, ref):
+		text  = prefix + c.typename + " " + varname + "___tmp;"
+		tmp_name = "tmp_" + str(Translator.tmp_cntr)
+		Translator.tmp_cntr = Translator.tmp_cntr + 1
+		if ref:
+			text += prefix + "for(auto " + tmp_name + " : *" + varname + ")"
+		else:
+			text += prefix + "for(auto " + tmp_name + " : " + varname + ")"
+		text += prefix + "{"
+		if types[0].name in classnames:
+			if types[0].attr_type == attr_types.star:
+				text += prefix + "\t" + varname + "___tmp.append(" + types[0].name + "::get_py_obj(" + tmp_name + "));"
+			else:
+				text += prefix + "\t" + varname + "___tmp.append(*" + types[0].name + "::get_py_obj(&" + tmp_name + "));"
+		elif types[0].name in known_containers:
+			text += known_containers[types[0].name].translate_cpp(tmp_name, types[0].cont.args, prefix + "\t", types[0].attr_type == attr_types.star)
+			text += prefix + "\t" + varname + "___tmp.append(" + tmp_name + "___tmp);"
+		else:
+			text += prefix + "\t" + varname + "___tmp.append(" + tmp_name + ");"
+		text += prefix + "}"
+		return text
+
+#Sub-type for std::set
+class SetTranslator(PythonListTranslator):
+	insert_name = "insert"
+	orig_name = "std::set"
+
+#Sub-type for std::vector
+class VectorTranslator(PythonListTranslator):
+	insert_name = "push_back"
+	orig_name = "std::vector"
+
+#Sub-type for pool
+class PoolTranslator(PythonListTranslator):
+	insert_name = "insert"
+	orig_name = "pool"
+
+#Translates dict-types (dict, std::map), that only differ in their name and
+#the name of the insertion function
+class PythonDictTranslator(Translator):
+	typename = "boost::python::dict"
+	insert_name = "Default"
+
+	@classmethod
+	def gen_type(c, types):
+		text = c.orig_name + "<"
+		if types[0].name in primitive_types:
+			text += types[0].name
+		elif types[0].name in known_containers:
+			text += known_containers[types[0].name].gen_type(types[0].cont.args)
+		else:
+			text += class_by_name(types[0].name).namespace + "::" + types[0].name
+			if types[0].attr_type == attr_types.star:
+				text += "*"
+		text += ", "
+		if types[1].name in primitive_types:
+			text += types[1].name
+		elif types[1].name in known_containers:
+			text += known_containers[types[1].name].gen_type(types[1].cont.args)
+		else:
+			text += class_by_name(types[1].name).namespace + "::" + types[1].name
+			if types[1].attr_type == attr_types.star:
+				text += "*"
+		text += ">"
+		return text
+
+	#Generate c++ code to translate from a boost::python::dict
+	@classmethod
+	def translate(c, varname, types, prefix):
+		text  = prefix + c.gen_type(types) + " " + varname + "___tmp;"
+		text += prefix + "boost::python::list " + varname + "_keylist = " + varname + ".keys();"
+		cntr_name = "cntr_" + str(Translator.tmp_cntr)
+		Translator.tmp_cntr = Translator.tmp_cntr + 1
+		text += prefix + "for(int " + cntr_name + " = 0; " + cntr_name + " < len(" + varname + "_keylist); " + cntr_name + "++)"
+		text += prefix + "{"
+		key_tmp_name = "key_tmp_" + str(Translator.tmp_cntr)
+		val_tmp_name = "val_tmp_" + str(Translator.tmp_cntr)
+		Translator.tmp_cntr = Translator.tmp_cntr + 1
+
+		if types[0].name in known_containers:
+			text += prefix + "\t" + known_containers[types[0].name].typename + " " + key_tmp_name + " = boost::python::extract<" + known_containers[types[0].name].typename + ">(" + varname + "_keylist[ " + cntr_name + " ]);"
+			text += known_containers[types[0].name].translate(key_tmp_name, types[0].cont.args, prefix+"\t")
+			key_tmp_name = key_tmp_name + "___tmp"
+		elif types[0].name in classnames:
+			text += prefix + "\t" + types[0].name + "* " + key_tmp_name + " = boost::python::extract<" + types[0].name + "*>(" + varname + "_keylist[ " + cntr_name + " ]);"
+		else:
+			text += prefix + "\t" + types[0].name + " " + key_tmp_name + " = boost::python::extract<" + types[0].name + ">(" + varname + "_keylist[ " + cntr_name + " ]);"
+
+		if types[1].name in known_containers:
+			text += prefix + "\t" + known_containers[types[1].name].typename + " " + val_tmp_name + " = boost::python::extract<" + known_containers[types[1].name].typename + ">(" + varname + "[" + varname + "_keylist[ " + cntr_name + " ]]);"
+			text += known_containers[types[1].name].translate(val_tmp_name, types[1].cont.args, prefix+"\t")
+			val_tmp_name = val_tmp_name + "___tmp"
+		elif types[1].name in classnames:
+			text += prefix + "\t" + types[1].name + "* " + val_tmp_name + " = boost::python::extract<" + types[1].name + "*>(" + varname + "[" + varname + "_keylist[ " + cntr_name + " ]]);"
+		else:
+			text += prefix + "\t" + types[1].name + " " + val_tmp_name + " = boost::python::extract<" + types[1].name + ">(" + varname + "[" + varname + "_keylist[ " + cntr_name + " ]]);"
+
+		text += prefix + "\t" + varname + "___tmp." + c.insert_name + "(std::pair<" + types[0].gen_text_cpp() + ", " + types[1].gen_text_cpp() + ">("
+
+		if types[0].name not in classnames:
+			text += key_tmp_name
+		else:
+			if types[0].attr_type != attr_types.star:
+				text += "*"
+			text += key_tmp_name + "->get_cpp_obj()"
+		
+		text += ", "
+		if types[1].name not in classnames:
+			text += val_tmp_name
+		else:
+			if types[1].attr_type != attr_types.star:
+				text += "*"
+			text += val_tmp_name + "->get_cpp_obj()"
+		text += "));\n" + prefix + "}"
+		return text
+	
+	#Generate c++ code to translate to a boost::python::dict
+	@classmethod
+	def translate_cpp(c, varname, types, prefix, ref):
+		text  = prefix + c.typename + " " + varname + "___tmp;"
+		tmp_name = "tmp_" + str(Translator.tmp_cntr)
+		Translator.tmp_cntr = Translator.tmp_cntr + 1
+		if ref:
+			text += prefix + "for(auto " + tmp_name + " : *" + varname + ")"
+		else:
+			text += prefix + "for(auto " + tmp_name + " : " + varname + ")"
+		text += prefix + "{"
+		if types[1].name in known_containers:
+			text += prefix + "\tauto " + tmp_name + "_second = " + tmp_name + ".second;"
+			text += known_containers[types[1].name].translate_cpp(tmp_name + "_second", types[1].cont.args, prefix + "\t", types[1].attr_type == attr_types.star)
+
+		if types[0].name in classnames:
+			text += prefix + "\t" + varname + "___tmp[" + types[0].name + "::get_py_obj(" + tmp_name + ".first)] = "
+		elif types[0].name not in known_containers:
+			text += prefix + "\t" + varname + "___tmp[" + tmp_name + ".first] = "
+
+		if types[1].name in classnames:
+			if types[1].attr_type == attr_types.star:
+				text += types[1].name + "::get_py_obj(" + tmp_name + ".second);"
+			else:
+				text += "*" + types[1].name + "::get_py_obj(&" + tmp_name + ".second);"
+		elif types[1].name in known_containers:
+			text += tmp_name + "_second___tmp;"
+		else:
+			text += tmp_name + ".second;"
+		text += prefix + "}"
+		return text
+
+#Sub-type for dict
+class DictTranslator(PythonDictTranslator):
+	insert_name = "insert"
+	orig_name = "dict"
+
+#Sub_type for std::map
+class MapTranslator(PythonDictTranslator):
+	insert_name = "insert"
+	orig_name = "std::map"	
+
+#Translator for std::pair. Derived from PythonDictTranslator because the
+#gen_type function is the same (because both have two template parameters)
+class TupleTranslator(PythonDictTranslator):
+	typename = "boost::python::tuple"
+	orig_name = "std::pair"
+
+	#Generate c++ code to translate from a boost::python::tuple
+	@classmethod
+	def translate(c, varname, types, prefix):
+		text  = prefix + types[0].name + " " + varname + "___tmp_0 = boost::python::extract<" + types[0].name + ">(" + varname + "[0]);"
+		text += prefix + types[1].name + " " + varname + "___tmp_1 = boost::python::extract<" + types[1].name + ">(" + varname + "[1]);"
+		text += prefix + TupleTranslator.gen_type(types) + " " + varname + "___tmp("
+		if types[0].name.split(" ")[-1] in primitive_types:
+			text += varname + "___tmp_0, "
+		else:
+			text += varname + "___tmp_0.get_cpp_obj(), "
+		if types[1].name.split(" ")[-1] in primitive_types:
+			text += varname + "___tmp_1);"
+		else:
+			text += varname + "___tmp_1.get_cpp_obj());"
+		return text
+
+	#Generate c++ code to translate to a boost::python::tuple
+	@classmethod
+	def translate_cpp(c, varname, types, prefix, ref):
+		text  = prefix + TupleTranslator.typename + " " + varname + "___tmp = boost::python::make_tuple(" + varname + ".first, " + varname + ".second);"
+		return text
+		tmp_name = "tmp_" + str(Translator.tmp_cntr)
+		Translator.tmp_cntr = Translator.tmp_cntr + 1
+		if ref:
+			text += prefix + "for(auto " + tmp_name + " : *" + varname + ")"
+		else:
+			text += prefix + "for(auto " + tmp_name + " : " + varname + ")"
+		text += prefix + "{"
+		if types[0].name.split(" ")[-1] in primitive_types or types[0].name in enum_names:
+			text += prefix + "\t" + varname + "___tmp.append(" + tmp_name + ");"
+		elif types[0].name in known_containers:
+			text += known_containers[types[0].name].translate_cpp(tmp_name, types[0].cont.args, prefix + "\t", types[1].attr_type == attr_types.star)
+			text += prefix + "\t" + varname + "___tmp.append(" + types[0].name + "::get_py_obj(" + tmp_name + "___tmp);"
+		elif types[0].name in classnames:
+			text += prefix + "\t" + varname + "___tmp.append(" + types[0].name + "::get_py_obj(" + tmp_name + "));"
+		text += prefix + "}"
+		return text
+
+#Associate the Translators with their c++ type
+known_containers = {
+	"std::set"		:	SetTranslator,
+	"std::vector"	:	VectorTranslator,
+	"pool"			:	PoolTranslator,
+	"dict"			:	DictTranslator,
+	"std::pair"		:	TupleTranslator,
+	"std::map"		:	MapTranslator
+}
+
+class Attribute:
+	wtype = None
+	varname = None
+	is_const = False
+	default_value = None
+	pos = None
+	pos_counter = 0
+
+	def __init__(self, wtype, varname, is_const = False, default_value = None):
+		self.wtype = wtype
+		self.varname = varname
+		self.is_const = is_const
+		self.default_value = None
+		self.container = None
+
+	@staticmethod
+	def from_string(str_def, containing_file, line_number):
+		if len(str_def) < 3:
+			return None
+		orig = str_def
+		arg = Attribute(None, None)
+		prefix = ""
+		arg.wtype = None
+		arg.varname = None
+		arg.is_const = False
+		arg.default_value = None
+		arg.container = None
+		if str.startswith(str_def, "const "):
+			arg.is_const = True
+			str_def = str_def[6:]
+		if str.startswith(str_def, "unsigned "):
+			prefix = "unsigned "
+			str_def = str_def[9:]
+		while str.startswith(str_def, "long "):
+			prefix= "long " + prefix
+			str_def = str_def[5:]
+		while str.startswith(str_def, "short "):
+			prefix = "short " + prefix
+			str_def = str_def[6:]
+
+		if str_def.find("<") != -1 and str_def.find("<") < str_def.find(" "):
+			closing = find_closing(str_def[str_def.find("<"):], "<", ">") + str_def.find("<") + 1
+			arg.wtype = WType.from_string(str_def[:closing].strip(), containing_file, line_number)
+			str_def = str_def[closing+1:]
+		else:
+			if str_def.count(" ") > 0:
+				arg.wtype = WType.from_string(prefix + str_def[:str_def.find(" ")].strip(), containing_file, line_number)
+				str_def = str_def[str_def.find(" ")+1:]
+			else:
+				arg.wtype = WType.from_string(prefix + str_def.strip(), containing_file, line_number)
+				str_def = ""
+				arg.varname = ""
+
+		if arg.wtype == None:
+			return None
+		if str_def.count("=") == 0:
+			arg.varname = str_def.strip()
+			if arg.varname.find(" ") > 0:
+				return None
+		else:
+			arg.varname = str_def[:str_def.find("=")].strip()
+			if arg.varname.find(" ") > 0:
+				return None
+			str_def = str_def[str_def.find("=")+1:].strip()
+			arg.default_value = str_def[arg.varname.find("=")+1:].strip()
+		if len(arg.varname) == 0:
+			arg.varname = None
+			return arg
+		if arg.varname[0] == '*':
+			arg.wtype.attr_type = attr_types.star
+			arg.varname = arg.varname[1:]
+		elif arg.varname[0] == '&':
+			if arg.wtype.attr_type != attr_types.default:
+				return None
+			if arg.varname[1] == '&':
+				arg.wtype.attr_type = attr_types.ampamp
+				arg.varname = arg.varname[2:]
+			else:
+				arg.wtype.attr_type = attr_types.amp
+				arg.varname = arg.varname[1:]
+		return arg
+
+	#Generates the varname. If the attribute has no name in the header file,
+	#a name is generated
+	def gen_varname(self):
+		if self.varname != None:
+			return self.varname
+		if self.wtype.name == "void":
+			return ""
+		if self.pos == None:
+			self.pos = Attribute.pos_counter
+			Attribute.pos_counter = Attribute.pos_counter + 1
+		return "gen_varname_" + str(self.pos)
+
+	#Generates the text for the function headers with wrapper types
+	def gen_listitem(self):
+		prefix = ""
+		if self.is_const:
+			prefix = "const "
+		if self.wtype.name in classnames:
+			return prefix + self.wtype.name + "* " + self.gen_varname()
+		if self.wtype.name in known_containers:
+			return prefix + known_containers[self.wtype.name].typename + " " + self.gen_varname()
+		return prefix + self.wtype.name + " " + self.gen_varname()
+
+	#Generates the test for the function headers with c++ types
+	def gen_listitem_cpp(self):
+		prefix = ""
+		if self.is_const:
+			prefix = "const "
+		infix = ""
+		if self.wtype.attr_type == attr_types.star:
+			infix = "*"
+		elif self.wtype.attr_type == attr_types.amp:
+			infix = "&"
+		elif self.wtype.attr_type == attr_types.ampamp:
+			infix = "&&"
+		if self.wtype.name in known_containers:
+			return prefix + known_containers[self.wtype.name].gen_type(self.wtype.cont.args) + " " + infix + self.gen_varname()
+		if self.wtype.name in classnames:
+			return prefix + class_by_name(self.wtype.name).namespace + "::" + self.wtype.name + " " + infix + self.gen_varname()
+		return prefix + self.wtype.name + " " + infix + self.gen_varname()
+
+	#Generates the listitem withtout the varname, so the signature can be
+	#compared
+	def gen_listitem_hash(self):
+		prefix = ""
+		if self.is_const:
+			prefix = "const "
+		if self.wtype.name in classnames:
+			return prefix + self.wtype.name + "* "
+		if self.wtype.name in known_containers:
+			return known_containers[self.wtype.name].typename
+		return prefix + self.wtype.name
+		
+	#Generate Translation code for the attribute
+	def gen_translation(self):
+		if self.wtype.name in known_containers:
+			return known_containers[self.wtype.name].translate(self.gen_varname(), self.wtype.cont.args, "\n\t\t")
+		return ""
+
+	#Generate Translation code from c++ for the attribute
+	def gen_translation_cpp(self):
+		if self.wtype.name in known_containers:
+			return known_containers[self.wtype.name].translate_cpp(self.gen_varname(), self.wtype.cont.args, "\n\t\t", self.wtype.attr_type == attr_types.star)
+		return ""
+
+	#Generate Text for the call
+	def gen_call(self):
+		ret = self.gen_varname()
+		if self.wtype.name in known_containers:
+			if self.wtype.attr_type == attr_types.star:
+				return "&" + ret + "___tmp"
+			return ret + "___tmp"
+		if self.wtype.name in classnames:
+			if self.wtype.attr_type != attr_types.star:
+				ret = "*" + ret
+			return ret + "->get_cpp_obj()"
+		if self.wtype.name == "char *" and self.gen_varname() in ["format", "fmt"]:
+			return "\"%s\", " + self.gen_varname()
+		if self.wtype.attr_type == attr_types.star:
+			return "&" + ret
+		return ret
+
+	def gen_call_cpp(self):
+		ret = self.gen_varname()
+		if self.wtype.name.split(" ")[-1] in primitive_types or self.wtype.name in enum_names:
+			if self.wtype.attr_type == attr_types.star:
+				return "&" + ret
+			return ret
+		if self.wtype.name not in classnames:
+			if self.wtype.attr_type == attr_types.star:
+				return "&" + ret + "___tmp"
+			return ret + "___tmp"
+		if self.wtype.attr_type != attr_types.star:
+			ret = "*" + ret
+		return self.wtype.name + "::get_py_obj(" + self.gen_varname()  + ")"
+
+	#Generate cleanup code
+	def gen_cleanup(self):
+		if self.wtype.name in primitive_types or self.wtype.name in classnames or self.wtype.name in enum_names or not self.wtype.attr_type == attr_types.star or (self.wtype.name in known_containers and self.wtype.attr_type == attr_types.star):
+			return ""
+		return "\n\t\tdelete " + self.gen_varname() + "___tmp;"
+
+class WClass:
+	name = None
+	namespace = None
+	link_type = None
+	id_ = None
+	string_id = None
+	hash_id = None
+	needs_clone = False
+	found_funs = []
+	found_vars = []
+	found_constrs = []
+
+	def __init__(self, name, link_type, id_, string_id = None, hash_id = None, needs_clone = False):
+		self.name = name
+		self.namespace = None
+		self.link_type = link_type
+		self.id_ = id_
+		self.string_id = string_id
+		self.hash_id = hash_id
+		self.needs_clone = needs_clone
+		self.found_funs = []
+		self.found_vars = []
+		self.found_constrs = []
+
+	def printable_constrs(self):
+		ret = 0
+		for con in self.found_constrs:
+			if not con.protected:
+				ret += 1
+		return ret
+
+	def gen_decl(self, filename):
+		long_name = self.namespace + "::" + self.name
+
+		text = "\n\t// WRAPPED from " + filename
+		text += "\n\tstruct " + self.name
+		if self.link_type == link_types.derive:
+			text += " : public " + self.namespace + "::" + self.name
+		text += "\n\t{\n"
+
+		if self.link_type != link_types.derive:
+
+			text += "\t\t" + long_name + "* ref_obj;\n"
+
+			if self.link_type == link_types.ref_copy or self.link_type == link_types.pointer:
+				text += "\n\t\t" + long_name + "* get_cpp_obj() const\n\t\t{\n\t\t\treturn ref_obj;\n\t\t}\n"
+			elif self.link_type == link_types.global_list:
+				text += "\t\t" + self.id_.wtype.name + " " + self.id_.varname + ";\n"
+				text += "\n\t\t" + long_name + "* get_cpp_obj() const\n\t\t{"
+				text += "\n\t\t\t" + long_name + "* ret = " + long_name + "::get_all_" + self.name.lower() + "s()->at(this->" + self.id_.varname + ");"
+				text += "\n\t\t\tif(ret != NULL && ret == this->ref_obj)"
+				text += "\n\t\t\t\treturn ret;"
+				text += "\n\t\t\tthrow std::runtime_error(\"" + self.name + "'s c++ object does not exist anymore.\");"
+				text += "\n\t\t\treturn NULL;"
+				text += "\n\t\t}\n"
+
+			#if self.link_type != link_types.pointer:
+			text += "\n\t\tstatic " + self.name + "* get_py_obj(" + long_name + "* ref)\n\t\t{"
+			text += "\n\t\t\t" + self.name + "* ret = (" + self.name + "*)malloc(sizeof(" + self.name + "));"
+			if self.link_type == link_types.pointer:
+				text += "\n\t\t\tret->ref_obj = ref;"
+			if self.link_type == link_types.ref_copy:
+				if self.needs_clone:
+					text += "\n\t\t\tret->ref_obj = ref->clone();"
+				else:
+					text += "\n\t\t\tret->ref_obj = new "+long_name+"(*ref);"
+			if self.link_type == link_types.global_list:
+				text += "\n\t\t\tret->ref_obj = ref;"
+				text += "\n\t\t\tret->" + self.id_.varname + " = ret->ref_obj->" + self.id_.varname + ";"
+			text += "\n\t\t\treturn ret;"
+			text += "\n\t\t}\n"
+
+			if self.link_type == link_types.ref_copy:
+				text += "\n\t\tstatic " + self.name + "* get_py_obj(" + long_name + " ref)\n\t\t{"
+				text += "\n\t\t\t" + self.name + "* ret = (" + self.name + "*)malloc(sizeof(" + self.name + "));"
+				if self.needs_clone:
+					text += "\n\t\t\tret->ref_obj = ref.clone();"
+				else:
+					text += "\n\t\t\tret->ref_obj = new "+long_name+"(ref);"
+				text += "\n\t\t\treturn ret;"
+				text += "\n\t\t}\n"
+
+			for con in self.found_constrs:
+				text += con.gen_decl()
+			for var in self.found_vars:
+				text += var.gen_decl()
+			for fun in self.found_funs:
+				text += fun.gen_decl()
+
+
+		if self.link_type == link_types.derive:
+			duplicates = {}
+			for fun in self.found_funs:
+				if fun.name in duplicates:
+					fun.gen_alias()
+					duplicates[fun.name].gen_alias()
+				else:
+					duplicates[fun.name] = fun
+
+			text += "\n\t\t" + long_name + "* get_cpp_obj() const\n\t\t{\n\t\t\treturn (" + self.namespace + "::" + self.name +"*)this;\n\t\t}\n"
+			text += "\n\t\tstatic " + self.name + "* get_py_obj(" + long_name + "* ref)\n\t\t{"
+			text += "\n\t\t\treturn (" + self.name + "*)ref;"
+			text += "\n\t\t}\n"
+
+			for con in self.found_constrs:
+				text += con.gen_decl_derive()
+			for var in self.found_vars:
+				text += var.gen_decl()
+			for fun in self.found_funs:
+				text += fun.gen_decl_virtual()
+
+		if self.hash_id != None:
+			text += "\n\t\tunsigned int get_hash_py()"
+			text += "\n\t\t{"
+			text += "\n\t\t\treturn get_cpp_obj()->" + self.hash_id + ";"
+			text += "\n\t\t}"
+
+		text += "\n\t};\n"
+
+		if self.link_type == link_types.derive:
+			text += "\n\tstruct " + self.name + "Wrap : " + self.name + ", boost::python::wrapper<" + self.name + ">"
+			text += "\n\t{"
+
+			for con in self.found_constrs:
+				text += con.gen_decl_wrapperclass()
+			for fun in self.found_funs:
+				text += fun.gen_default_impl()
+
+			text += "\n\t};"
+
+		text += "\n\tstd::ostream &operator<<(std::ostream &ostr, const " + self.name + " &ref)"
+		text += "\n\t{"
+		text += "\n\t\tostr << \"" + self.name
+		if self.string_id != None:
+			text +=" \\\"\""
+			text += " << ref.get_cpp_obj()->" + self.string_id
+			text += " << \"\\\"\""
+		else:
+			text += " at \" << ref.get_cpp_obj()"
+		text += ";"
+		text += "\n\t\treturn ostr;"
+		text += "\n\t}"
+		text += "\n"
+
+		return text
+
+	def gen_funs(self, filename):
+		text = ""
+		if self.link_type != link_types.derive:
+			for con in self.found_constrs:
+				text += con.gen_def()
+			for var in self.found_vars:
+				text += var.gen_def()
+			for fun in self.found_funs:
+				text += fun.gen_def()
+		else:
+			for var in self.found_vars:
+				text += var.gen_def()
+			for fun in self.found_funs:
+				text += fun.gen_def_virtual()
+		return text
+
+	def gen_boost_py(self):
+		text = "\n\t\tclass_<" + self.name
+		if self.link_type == link_types.derive:
+			text += "Wrap, boost::noncopyable"
+		text += ">(\"" + self.name + "\""
+		if self.printable_constrs() == 0 or not self.contains_default_constr():
+			text += ", no_init"
+		text += ")"
+		text += "\n\t\t\t.def(boost::python::self_ns::str(boost::python::self_ns::self))"
+		text += "\n\t\t\t.def(boost::python::self_ns::repr(boost::python::self_ns::self))"
+		for con in self.found_constrs:
+			text += con.gen_boost_py()
+		for var in self.found_vars:
+			text += var.gen_boost_py()
+		static_funs = []
+		for fun in self.found_funs:
+			text += fun.gen_boost_py()
+			if fun.is_static and fun.alias not in static_funs:
+				static_funs.append(fun.alias)
+		for fun in static_funs:
+			text += "\n\t\t\t.staticmethod(\"" + fun + "\")"
+
+		if self.hash_id != None:
+			text += "\n\t\t\t.def(\"__hash__\", &" + self.name + "::get_hash_py)"
+		text += "\n\t\t\t;\n"
+		return text
+
+	def contains_default_constr(self):
+		for c in self.found_constrs:
+			if len(c.args) == 0:
+				return True
+		return False
+
+#CONFIGURE HEADER-FILES TO BE PARSED AND CLASSES EXPECTED IN THEM HERE
+
+sources = [
+	Source("kernel/celltypes",[
+		WClass("CellType", link_types.pointer, None, None, "type.hash()", True),
+		WClass("CellTypes", link_types.pointer, None, None, None, True)
+		]
+		),
+	Source("kernel/consteval",[
+		WClass("ConstEval", link_types.pointer, None, None, None, True)
+		]
+		),
+	Source("kernel/log",[]),
+	Source("kernel/register",[
+		WClass("Pass", link_types.derive, None, None, None, True),
+		]
+		),
+	Source("kernel/rtlil",[
+		WClass("IdString", link_types.ref_copy, None, "str()", "hash()"),
+		WClass("Const", link_types.ref_copy, None, "as_string()", "hash()"),
+		WClass("AttrObject", link_types.ref_copy, None, None, None),
+		WClass("Selection", link_types.ref_copy, None, None, None),
+		WClass("Monitor", link_types.derive, None, None, None),
+		WClass("CaseRule",link_types.ref_copy, None, None, None, True),
+		WClass("SwitchRule",link_types.ref_copy, None, None, None, True),
+		WClass("SyncRule", link_types.ref_copy, None, None, None, True),
+		WClass("Process",  link_types.ref_copy, None, "name.c_str()", "name.hash()"),
+		WClass("SigChunk", link_types.ref_copy, None, None, None),
+		WClass("SigBit", link_types.ref_copy, None, None, "hash()"),
+		WClass("SigSpec", link_types.ref_copy, None, None, "hash()"),
+		WClass("Cell", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"),
+		WClass("Wire", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"),
+		WClass("Memory", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"),
+		WClass("Module", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"),
+		WClass("Design", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "hashidx_", "hash()")
+		]
+		),
+	#Source("kernel/satgen",[
+	#	]
+	#	),
+	#Source("libs/ezsat/ezsat",[
+	#	]
+	#	),
+	#Source("libs/ezsat/ezminisat",[
+	#	]
+	#	),
+	Source("kernel/sigtools",[
+		WClass("SigMap", link_types.pointer, None, None, None, True)
+		]
+		),
+	Source("kernel/yosys",[
+		]
+		),
+	Source("kernel/cost",[])
+	]
+
+blacklist_methods = ["YOSYS_NAMESPACE::Pass::run_register", "YOSYS_NAMESPACE::Module::Pow", "YOSYS_NAMESPACE::Module::Bu0", "YOSYS_NAMESPACE::CaseRule::optimize"]
+
+enum_names = ["State","SyncType","ConstFlags"]
+
+enums = [] #Do not edit
+
+unowned_functions = []
+
+classnames = []
+for source in sources:
+	for wclass in source.classes:
+		classnames.append(wclass.name)
+
+def class_by_name(name):
+	for source in sources:
+		for wclass in source.classes:
+			if wclass.name == name:
+				return wclass
+	return None
+
+def enum_by_name(name):
+	for e in enums:
+		if e.name == name:
+			return e
+	return None
+
+def find_closing(text, open_tok, close_tok):
+	if text.find(open_tok) == -1 or text.find(close_tok) <= text.find(open_tok):
+		return text.find(close_tok)
+	return text.find(close_tok) + find_closing(text[text.find(close_tok)+1:], open_tok, close_tok) + 1
+
+def unpretty_string(s):
+	s = s.strip()
+	while s.find("  ") != -1:
+		s = s.replace("  "," ")
+	while s.find("\t") != -1:
+		s = s.replace("\t"," ")
+	s = s.replace(" (","(")
+	return s
+
+class WEnum:
+	name = None
+	namespace = None
+	values = []
+
+	def from_string(str_def, namespace, line_number):
+		str_def = str_def.strip()
+		if not str.startswith(str_def, "enum "):
+			return None
+		if str_def.count(";") != 1:
+			return None
+		str_def = str_def[5:]
+		enum = WEnum()
+		split = str_def.split(":")
+		if(len(split) != 2):
+			return None
+		enum.name = split[0].strip()
+		if enum.name not in enum_names:
+			return None
+		str_def = split[1]
+		if str_def.count("{") != str_def.count("}") != 1:
+			return None
+		if len(str_def) < str_def.find("}")+2 or str_def[str_def.find("}")+1] != ';':
+			return None
+		str_def = str_def.split("{")[-1].split("}")[0]
+		enum.values = []
+		for val in str_def.split(','):
+			enum.values.append(val.strip().split('=')[0].strip())
+		enum.namespace = namespace
+		return enum
+
+	def gen_boost_py(self):
+		text = "\n\t\tenum_<" + self.namespace + "::" + self.name + ">(\"" + self.name + "\")\n"
+		for value in self.values:
+			text += "\t\t\t.value(\"" + value + "\"," + self.namespace + "::" + value + ")\n"
+		text += "\t\t\t;\n"
+		return text
+
+	def __str__(self):
+		ret = "Enum " + self.namespace + "::" + self.name + "(\n"
+		for val in self.values:
+			ret = ret + "\t" + val + "\n"
+		return ret + ")"
+
+	def __repr__(self):
+		return __str__(self)
+
+class WConstructor:
+	orig_text = None
+	args = []
+	containing_file = None
+	member_of = None
+	duplicate = False
+	protected = False
+
+	def __init__(self, containing_file, class_):
+		self.orig_text = "Auto generated default constructor"
+		self.args = []
+		self.containing_file = containing_file
+		self.member_of = class_
+		self.protected = False
+
+	def from_string(str_def, containing_file, class_, line_number, protected = False):
+		if class_ == None:
+			return None
+		if str_def.count("delete;") > 0:
+			return None
+		con = WConstructor(containing_file, class_)
+		con.orig_text = str_def
+		con.args = []
+		con.duplicate = False
+		con.protected = protected
+		if not str.startswith(str_def, class_.name + "("):
+			return None
+		str_def = str_def[len(class_.name)+1:]
+		found = find_closing(str_def, "(", ")")
+		if found == -1:
+			return None
+		str_def = str_def[0:found].strip()
+		if len(str_def) == 0:
+			return con
+		for arg in split_list(str_def, ","):
+			parsed = Attribute.from_string(arg.strip(), containing_file, line_number)
+			if parsed == None:
+				return None
+			con.args.append(parsed)
+		return con
+
+	def gen_decl(self):
+		if self.duplicate or self.protected:
+			return ""
+		text =  "\n\t\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file
+		text += "\n\t\t" + self.member_of.name + "("
+		for arg in self.args:
+			text += arg.gen_listitem() + ", "
+		if len(self.args) > 0:
+			text = text[:-2]
+		text += ");\n"
+		return text
+
+	def gen_decl_derive(self):
+		if self.duplicate or self.protected:
+			return ""
+		text =  "\n\t\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file
+		text += "\n\t\t" + self.member_of.name + "("
+		for arg in self.args:
+			text += arg.gen_listitem() + ", "
+		if len(self.args) > 0:
+			text = text[:-2]
+		text += ")"
+		if len(self.args) == 0:
+			return text + "{}"
+		text += " : "
+		text += self.member_of.namespace + "::" + self.member_of.name + "("
+		for arg in self.args:
+			text += arg.gen_call() + ", "
+		if len(self.args) > 0:
+			text = text[:-2]
+		text += "){}\n"
+		return text
+
+	def gen_decl_wrapperclass(self):
+		if self.duplicate or self.protected:
+			return ""
+		text =  "\n\t\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file
+		text += "\n\t\t" + self.member_of.name + "Wrap("
+		for arg in self.args:
+			text += arg.gen_listitem() + ", "
+		if len(self.args) > 0:
+			text = text[:-2]
+		text += ")"
+		if len(self.args) == 0:
+			return text + "{}"
+		text += " : "
+		text += self.member_of.name + "("
+		for arg in self.args:
+			text += arg.gen_call() + ", "
+		if len(self.args) > 0:
+			text = text[:-2]
+		text += "){}\n"
+		return text
+
+	def gen_decl_hash_py(self):
+		text = self.member_of.name + "("
+		for arg in self.args:
+			text += arg.gen_listitem_hash() + ", "
+		if len(self.args) > 0:
+			text = text[:-2]
+		text += ");"
+		return text
+
+	def gen_def(self):
+		if self.duplicate or self.protected:
+			return ""
+		text = "\n\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file
+		text += "\n\t" + self.member_of.name + "::" + self.member_of.name + "("
+		for arg in self.args:
+			text += arg.gen_listitem() + ", "
+		if len(self.args) > 0:
+			text = text[:-2]
+		text +=")\n\t{"
+		for arg in self.args:
+			text += arg.gen_translation()
+		if self.member_of.link_type != link_types.derive:
+			text += "\n\t\tthis->ref_obj = new " + self.member_of.namespace + "::" + self.member_of.name + "("
+		for arg in self.args:
+			text += arg.gen_call() + ", "
+		if len(self.args) > 0:
+			text = text[:-2]
+		if self.member_of.link_type != link_types.derive:
+			text += ");"
+		if self.member_of.link_type == link_types.global_list:
+			text += "\n\t\tthis->" + self.member_of.id_.varname + " = this->ref_obj->" + self.member_of.id_.varname + ";"
+		for arg in self.args:
+			text += arg.gen_cleanup()
+		text += "\n\t}\n"
+		return text
+
+	def gen_boost_py(self):
+		if self.duplicate or self.protected or len(self.args) == 0:
+			return ""
+		text  = "\n\t\t\t.def(init"
+		text += "<"
+		for a in self.args:
+			text += a.gen_listitem_hash() + ", "
+		text = text[0:-2] + ">())"
+		return text
+
+class WFunction:
+	orig_text = None
+	is_static = False
+	is_inline = False
+	is_virtual = False
+	ret_attr_type = attr_types.default
+	is_operator = False
+	ret_type = None
+	name = None
+	alias = None
+	args = []
+	containing_file = None
+	member_of = None
+	duplicate = False
+	namespace = ""
+
+	def from_string(str_def, containing_file, class_, line_number, namespace):
+		if str_def.count("delete;") > 0:
+			return None
+		func = WFunction()
+		func.is_static = False
+		func.is_inline = False
+		func.is_virtual = False
+		func.ret_attr_type = attr_types.default
+		func.is_operator = False
+		func.member_of = None
+		func.orig_text = str_def
+		func.args = []
+		func.containing_file = containing_file
+		func.member_of = class_
+		func.duplicate = False
+		func.namespace = namespace
+		str_def = str_def.replace("operator ","operator")
+		if str.startswith(str_def, "static "):
+			func.is_static = True
+			str_def = str_def[7:]
+		else:
+			func.is_static = False
+		if str.startswith(str_def, "inline "):
+			func.is_inline = True
+			str_def = str_def[7:]
+		else:
+			func.is_inline = False
+		if str.startswith(str_def, "virtual "):
+			func.is_virtual = True
+			str_def = str_def[8:]
+		else:
+			func.is_virtual = False
+
+		if str_def.count(" ") == 0:
+			return None
+
+		parts = split_list(str_def.strip(), " ")
+
+		prefix = ""
+		i = 0
+		for part in parts:
+			if part in ["unsigned", "long", "short"]:
+				prefix += part + " "
+				i += 1
+			else:
+				break
+		parts = parts[i:]
+
+		if len(parts) <= 1:
+			return None
+
+		func.ret_type = WType.from_string(prefix + parts[0], containing_file, line_number)
+
+		if func.ret_type == None:
+			return None
+
+		str_def = parts[1]
+		for part in parts[2:]:
+			str_def = str_def + " " + part
+
+		found = str_def.find("(")
+		if found == -1 or (str_def.find(" ") != -1 and found > str_def.find(" ")):
+			return None
+		func.name = str_def[:found]
+		str_def = str_def[found:]
+		if func.name.find("operator") != -1 and str.startswith(str_def, "()("):
+				func.name += "()"
+				str_def = str_def[2:]
+		str_def = str_def[1:]
+		if func.name.find("operator") != -1:
+			func.is_operator = True
+		if func.name.find("*") == 0:
+			func.name = func.name.replace("*", "")
+			func.ret_type.attr_type = attr_types.star
+		if func.name.find("&&") == 0:
+			func.name = func.name.replace("&&", "")
+			func.ret_type.attr_type = attr_types.ampamp
+		if func.name.find("&") == 0:
+			func.name = func.name.replace("&", "")
+			func.ret_type.attr_type = attr_types.amp
+
+		found = find_closing(str_def, "(", ")")
+		if found == -1:
+			return None
+		str_def = str_def[0:found]
+		if func.name in blacklist_methods:
+			return None
+		if func.namespace != None and func.namespace != "":
+			if (func.namespace + "::" + func.name) in blacklist_methods:
+				return None
+			if func.member_of != None:
+				if (func.namespace + "::" + func.member_of.name + "::" + func.name) in blacklist_methods:
+					return None
+		if func.is_operator and func.name.replace(" ","").replace("operator","").split("::")[-1] not in wrappable_operators:
+			return None
+
+		testname = func.name
+		if func.is_operator:
+			testname = testname[:testname.find("operator")]
+		if testname.count(")") != 0 or testname.count("(") != 0 or testname.count("~") != 0 or testname.count(";") != 0 or testname.count(">") != 0 or testname.count("<") != 0 or testname.count("throw") != 0:
+			return None
+
+		func.alias = func.name
+		if func.name in keyword_aliases:
+			func.alias = keyword_aliases[func.name]
+		str_def = str_def[:found].strip()
+		if(len(str_def) == 0):
+			return func
+		for arg in split_list(str_def, ","):
+			if arg.strip() == "...":
+				continue
+			parsed = Attribute.from_string(arg.strip(), containing_file, line_number)
+			if parsed == None:
+				return None
+			func.args.append(parsed)
+		return func
+
+	def gen_alias(self):
+		self.alias = self.name
+		for arg in self.args:
+			self.alias += "__" + arg.wtype.gen_text_cpp().replace("::", "_").replace("<","_").replace(">","_").replace(" ","").replace("*","").replace(",","")
+
+	def gen_decl(self):
+		if self.duplicate:
+			return ""
+		text =  "\n\t\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file
+		text += "\n\t\t"
+		if self.is_static:
+			text += "static "
+		text += self.ret_type.gen_text() + " " + self.alias + "("
+		for arg in self.args:
+			text += arg.gen_listitem()
+			text += ", "
+		if len(self.args) > 0:
+			text = text[:-2]
+		text += ");\n"
+		return text
+
+	def gen_decl_virtual(self):
+		if self.duplicate:
+			return ""
+		if not self.is_virtual:
+			return self.gen_decl()
+		text =  "\n\t\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file
+		text += "\n\t\tvirtual "
+		if self.is_static:
+			text += "static "
+		text += self.ret_type.gen_text() + " py_" + self.alias + "("
+		for arg in self.args:
+			text += arg.gen_listitem()
+			text += ", "
+		if len(self.args) > 0:
+			text = text[:-2]
+		text += ")"
+		if len(self.args) == 0:
+			text += "{}"
+		else:
+			text += "\n\t\t{"
+			for arg in self.args:
+				text += "\n\t\t\t(void)" + arg.gen_varname() + ";"
+			text += "\n\t\t}\n"
+		text += "\n\t\tvirtual "
+		if self.is_static:
+			text += "static "
+		text += self.ret_type.gen_text() + " " + self.name + "("
+		for arg in self.args:
+			text += arg.gen_listitem_cpp()
+			text += ", "
+		if len(self.args) > 0:
+			text = text[:-2]
+		text += ") YS_OVERRIDE;\n"
+		return text
+
+	def gen_decl_hash_py(self):
+		text = self.ret_type.gen_text() + " " + self.alias + "("
+		for arg in self.args:
+			text += arg.gen_listitem_hash() + ", "
+		if len(self.args) > 0:
+			text = text[:-2]
+		text += ");"
+		return text
+
+	def gen_def(self):
+		if self.duplicate:
+			return ""
+		text  = "\n\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file
+		text += "\n\t" + self.ret_type.gen_text() + " "
+		if self.member_of != None:
+			text += self.member_of.name + "::"
+		text += self.alias + "("
+		for arg in self.args:
+			text += arg.gen_listitem()
+			text += ", "
+		if len(self.args) > 0:
+			text = text[:-2]
+		text +=")\n\t{"
+		for arg in self.args:
+			text += arg.gen_translation()
+		text += "\n\t\t"
+		if self.ret_type.name != "void":
+			if self.ret_type.name in known_containers:
+				text += self.ret_type.gen_text_cpp()
+			else:
+				text += self.ret_type.gen_text()
+			if self.ret_type.name in classnames or (self.ret_type.name in known_containers and self.ret_type.attr_type == attr_types.star):
+				text += "*"
+			text += " ret_ = "
+			if self.ret_type.name in classnames:
+				text += self.ret_type.name + "::get_py_obj("
+		if self.member_of == None:
+			text += "::" + self.namespace + "::" + self.alias + "("
+		elif self.is_static:
+			text += self.member_of.namespace + "::" + self.member_of.name + "::" + self.name + "("
+		else:
+			text += "this->get_cpp_obj()->" + self.name + "("
+		for arg in self.args:
+			text += arg.gen_call() + ", "
+		if len(self.args) > 0:
+			text = text[:-2]
+		if self.ret_type.name in classnames:
+			text += ")"
+		text += ");"
+		for arg in self.args:
+			text += arg.gen_cleanup()
+		if self.ret_type.name != "void":
+			if self.ret_type.name in classnames:
+				text += "\n\t\treturn *ret_;"
+			elif self.ret_type.name in known_containers:
+				text += known_containers[self.ret_type.name].translate_cpp("ret_", self.ret_type.cont.args, "\n\t\t", self.ret_type.attr_type == attr_types.star)
+				text += "\n\t\treturn ret____tmp;"
+			else:
+				text += "\n\t\treturn ret_;"
+		text += "\n\t}\n"
+		return text
+
+	def gen_def_virtual(self):
+		if self.duplicate:
+			return ""
+		if not self.is_virtual:
+			return self.gen_def()
+		text =  "\n\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file
+		text += "\n\t"
+		if self.is_static:
+			text += "static "
+		text += self.ret_type.gen_text() + " " + self.member_of.name + "::" + self.name + "("
+		for arg in self.args:
+			text += arg.gen_listitem_cpp()
+			text += ", "
+		if len(self.args) > 0:
+			text = text[:-2]
+		text += ")\n\t{"
+		for arg in self.args:
+			text += arg.gen_translation_cpp()
+		text += "\n\t\t"
+		if self.member_of == None:
+			text += "::" + self.namespace + "::" + self.alias + "("
+		elif self.is_static:
+			text += self.member_of.namespace + "::" + self.member_of.name + "::" + self.name + "("
+		else:
+			text += "py_" + self.alias + "("
+		for arg in self.args:
+			text += arg.gen_call_cpp() + ", "
+		if len(self.args) > 0:
+			text = text[:-2]
+		if self.ret_type.name in classnames:
+			text += ")"
+		text += ");"
+		for arg in self.args:
+			text += arg.gen_cleanup()
+		text += "\n\t}\n"
+		return text
+
+	def gen_default_impl(self):
+		if self.duplicate:
+			return ""
+		if not self.is_virtual:
+			return ""
+		text = "\n\n\t\t" + self.ret_type.gen_text() + " py_" + self.alias + "("
+		for arg in self.args:
+			text += arg.gen_listitem() + ", "
+		if len(self.args) > 0:
+			text = text[:-2]
+
+		call_string = "py_" + self.alias + "("
+		for arg in self.args:
+			call_string += arg.gen_varname() + ", "
+		if len(self.args) > 0:
+			call_string = call_string[0:-2]
+		call_string += ");"
+
+		text += ")\n\t\t{"
+		text += "\n\t\t\tif(boost::python::override py_" + self.alias + " = this->get_override(\"py_" + self.alias + "\"))"
+		text += "\n\t\t\t\t" + call_string
+		text += "\n\t\t\telse"
+		text += "\n\t\t\t\t" + self.member_of.name + "::" + call_string
+		text += "\n\t\t}"
+
+		text += "\n\n\t\t" + self.ret_type.gen_text() + " default_py_" + self.alias + "("
+		for arg in self.args:
+			text += arg.gen_listitem() + ", "
+		if len(self.args) > 0:
+			text = text[:-2]
+		text += ")\n\t\t{"
+		text += "\n\t\t\tthis->" + self.member_of.name + "::" + call_string
+		text += "\n\t\t}"
+		return text
+
+
+	def gen_boost_py(self):
+		if self.duplicate:
+			return ""
+		if self.member_of == None:
+			text = "\n\t\tdef"
+		else:
+			text = "\n\t\t\t.def"
+		if len(self.args) > -1:
+			if self.ret_type.name in known_containers:
+				text += "<" + known_containers[self.ret_type.name].typename + " "
+			else:
+				text += "<" + self.ret_type.name + " "
+			if self.member_of == None or self.is_static:
+				text += "(*)("
+			else:
+				text += "(" + self.member_of.name + "::*)("
+			for a in self.args:
+				text += a.gen_listitem_hash() + ", "
+			if len(self.args) > 0:
+				text = text[0:-2] + ")>"
+			else:
+				text += "void)>"
+
+		if self.is_operator:
+			text += "(\"" + wrappable_operators[self.name.replace("operator","")] + "\""
+		else:
+			if self.member_of != None and self.member_of.link_type == link_types.derive and self.is_virtual:
+				text += "(\"py_" + self.alias + "\""
+			else:
+				text += "(\"" + self.alias + "\""
+		if self.member_of != None:
+			text += ", &" + self.member_of.name + "::"
+			if self.member_of.link_type == link_types.derive and self.is_virtual:
+				text += "py_" + self.alias
+				text += ", &" + self.member_of.name + "Wrap::default_py_" + self.alias
+			else:
+				text += self.alias
+
+			text += ")"
+		else:
+			text += ", " + "YOSYS_PYTHON::" + self.alias + ");"
+		return text
+
+class WMember:
+	orig_text = None
+	wtype = attr_types.default
+	name = None
+	containing_file = None
+	member_of = None
+	namespace = ""
+	is_const = False
+
+	def from_string(str_def, containing_file, class_, line_number, namespace):
+		member = WMember()
+		member.orig_text = str_def
+		member.wtype = None
+		member.name = ""
+		member.containing_file = containing_file
+		member.member_of = class_
+		member.namespace = namespace
+		member.is_const = False
+
+		if str.startswith(str_def, "const "):
+			member.is_const = True
+			str_def = str_def[6:]
+
+		if str_def.count(" ") == 0:
+			return None
+
+		parts = split_list(str_def.strip(), " ")
+
+		prefix = ""
+		i = 0
+		for part in parts:
+			if part in ["unsigned", "long", "short"]:
+				prefix += part + " "
+				i += 1
+			else:
+				break
+		parts = parts[i:]
+
+		if len(parts) <= 1:
+			return None
+
+		member.wtype = WType.from_string(prefix + parts[0], containing_file, line_number)
+
+		if member.wtype == None:
+			return None
+
+		str_def = parts[1]
+		for part in parts[2:]:
+			str_def = str_def + " " + part
+
+		if str_def.find("(") != -1 or str_def.find(")") != -1 or str_def.find("{") != -1 or str_def.find("}") != -1:
+			return None
+
+		found = str_def.find(";")
+		if found == -1:
+			return None
+
+		found_eq = str_def.find("=")
+		if found_eq != -1:
+			found = found_eq
+
+		member.name = str_def[:found]
+		str_def = str_def[found+1:]
+		if member.name.find("*") == 0:
+			member.name = member.name.replace("*", "")
+			member.wtype.attr_type = attr_types.star
+		if member.name.find("&&") == 0:
+			member.name = member.name.replace("&&", "")
+			member.wtype.attr_type = attr_types.ampamp
+		if member.name.find("&") == 0:
+			member.name = member.name.replace("&", "")
+			member.wtype.attr_type = attr_types.amp
+
+		if(len(str_def.strip()) != 0):
+			return None
+
+		if len(member.name.split(",")) > 1:
+			member_list = []
+			for name in member.name.split(","):
+				name = name.strip();
+				member_list.append(WMember())
+				member_list[-1].orig_text = member.orig_text
+				member_list[-1].wtype = member.wtype
+				member_list[-1].name = name
+				member_list[-1].containing_file = member.containing_file
+				member_list[-1].member_of = member.member_of
+				member_list[-1].namespace = member.namespace
+				member_list[-1].is_const = member.is_const
+			return member_list
+
+		return member
+
+	def gen_decl(self):
+		text = "\n\t\t" + self.wtype.gen_text() + " get_var_py_" + self.name + "();\n"
+		if self.is_const:
+			return text
+		if self.wtype.name in classnames:
+			text += "\n\t\tvoid set_var_py_" + self.name + "(" + self.wtype.gen_text() + " *rhs);\n"
+		else:
+			text += "\n\t\tvoid set_var_py_" + self.name + "(" + self.wtype.gen_text() + " rhs);\n"
+		return text
+
+	def gen_def(self):
+		text = "\n\t" + self.wtype.gen_text() + " " + self.member_of.name +"::get_var_py_" + self.name + "()"
+		text += "\n\t{\n\t\t"
+		if self.wtype.attr_type == attr_types.star:
+			text += "if(this->get_cpp_obj()->" + self.name + " == NULL)\n\t\t\t"
+			text += "throw std::runtime_error(\"Member \\\"" + self.name + "\\\" is NULL\");\n\t\t"
+		if self.wtype.name in known_containers:
+			text += self.wtype.gen_text_cpp()
+		else:
+			text += self.wtype.gen_text()
+
+		if self.wtype.name in classnames or (self.wtype.name in known_containers and self.wtype.attr_type == attr_types.star):
+			text += "*"
+		text += " ret_ = "
+		if self.wtype.name in classnames:
+			text += self.wtype.name + "::get_py_obj("
+			if self.wtype.attr_type != attr_types.star:
+				text += "&"
+		text += "this->get_cpp_obj()->" + self.name
+		if self.wtype.name in classnames:
+			text += ")"
+		text += ";"
+		
+		if self.wtype.name in classnames:
+			text += "\n\t\treturn *ret_;"
+		elif self.wtype.name in known_containers:
+			text += known_containers[self.wtype.name].translate_cpp("ret_", self.wtype.cont.args, "\n\t\t", self.wtype.attr_type == attr_types.star)
+			text += "\n\t\treturn ret____tmp;"
+		else:
+			text += "\n\t\treturn ret_;"
+		text += "\n\t}\n"
+
+		if self.is_const:
+			return text
+
+		ret = Attribute(self.wtype, "rhs");
+
+		if self.wtype.name in classnames:
+			text += "\n\tvoid " + self.member_of.name+ "::set_var_py_" + self.name + "(" + self.wtype.gen_text() + " *rhs)"
+		else:
+			text += "\n\tvoid " + self.member_of.name+ "::set_var_py_" + self.name + "(" + self.wtype.gen_text() + " rhs)"
+		text += "\n\t{"
+		text += ret.gen_translation()
+		text += "\n\t\tthis->get_cpp_obj()->" + self.name + " = " + ret.gen_call() + ";"
+		text += "\n\t}\n"		
+
+		return text;
+
+	def gen_boost_py(self):
+		text = "\n\t\t\t.add_property(\"" + self.name + "\", &" + self.member_of.name + "::get_var_py_" + self.name 
+		if not self.is_const:
+			text += ", &" + self.member_of.name + "::set_var_py_" + self.name
+		text += ")"
+		return text
+
+def concat_namespace(tuple_list):
+	if len(tuple_list) == 0:
+		return ""
+	ret = ""
+	for namespace in tuple_list:
+		ret += "::" + namespace[0]
+	return ret[2:]
+
+def calc_ident(text):
+	if len(text) == 0 or text[0] != ' ':
+		return 0
+	return calc_ident(text[1:]) + 1
+
+def assure_length(text, length, left = False):
+	if len(text) > length:
+		return text[:length]
+	if left:
+		return text + " "*(length - len(text))
+	return " "*(length - len(text)) + text
+	
+def parse_header(source):
+	debug("Parsing " + source.name + ".pyh",1)
+	source_file = open(source.name + ".pyh", "r")
+
+	source_text = []
+	in_line = source_file.readline()
+
+	namespaces = []
+
+	while(in_line):
+		if(len(in_line)>1):
+			source_text.append(in_line.replace("char *", "char_p ").replace("char* ", "char_p "))
+		in_line = source_file.readline()
+
+	i = 0
+
+	namespaces = []
+	class_ = None
+	private_segment = False
+
+	while i < len(source_text):
+		line = source_text[i].replace("YOSYS_NAMESPACE_BEGIN", "                    namespace YOSYS_NAMESPACE{").replace("YOSYS_NAMESPACE_END","                    }")
+		ugly_line = unpretty_string(line)
+
+		if str.startswith(ugly_line, "namespace "):# and ugly_line.find("std") == -1 and ugly_line.find("__") == -1:
+			namespace_name = ugly_line[10:].replace("{","").strip()
+			namespaces.append((namespace_name, ugly_line.count("{")))
+			debug("-----NAMESPACE " + concat_namespace(namespaces) + "-----",3)
+			i += 1
+			continue
+
+		if len(namespaces) != 0:
+			namespaces[-1] = (namespaces[-1][0], namespaces[-1][1] + ugly_line.count("{") - ugly_line.count("}"))
+			if namespaces[-1][1] == 0:
+				debug("-----END NAMESPACE " + concat_namespace(namespaces) + "-----",3)
+				del namespaces[-1]
+				i += 1
+				continue
+
+		if class_ == None and (str.startswith(ugly_line, "struct ") or str.startswith(ugly_line, "class")) and ugly_line.count(";") == 0:
+
+			struct_name = ugly_line.split(" ")[1].split("::")[-1]
+			impl_namespaces = ugly_line.split(" ")[1].split("::")[:-1]
+			complete_namespace = concat_namespace(namespaces)
+			for namespace in impl_namespaces:
+				complete_namespace += "::" + namespace
+			debug("\tFound " + struct_name + " in " + complete_namespace,2)
+			class_ = (class_by_name(struct_name), ugly_line.count("{"))#calc_ident(line))
+			if struct_name in classnames:
+				class_[0].namespace = complete_namespace
+			i += 1
+			continue
+
+		if class_ != None:
+			class_ = (class_[0], class_[1] + ugly_line.count("{") - ugly_line.count("}"))
+			if class_[1] == 0:
+				if class_[0] == None:
+					debug("\tExiting unknown class", 3)
+				else:
+					debug("\tExiting class " + class_[0].name, 3)
+				class_ = None
+				private_segment = False
+				i += 1
+				continue
+
+		if class_ != None and (line.find("private:") != -1 or line.find("protected:") != -1):
+			private_segment = True
+			i += 1
+			continue
+		if class_ != None and line.find("public:") != -1:
+			private_segment = False
+			i += 1
+			continue
+
+		candidate = None
+
+		if private_segment and class_ != None and class_[0] != None:
+			candidate = WConstructor.from_string(ugly_line, source.name, class_[0], i, True)
+			if candidate != None:
+				debug("\t\tFound constructor of class \"" + class_[0].name + "\" in namespace " + concat_namespace(namespaces),2)
+				class_[0].found_constrs.append(candidate)
+				i += 1
+				continue
+
+		if not private_segment and (class_ == None or class_[0] != None):
+			if class_ != None:
+				candidate = WFunction.from_string(ugly_line, source.name, class_[0], i, concat_namespace(namespaces))
+			else:
+				candidate = WFunction.from_string(ugly_line, source.name, None, i, concat_namespace(namespaces))
+			if candidate != None and candidate.name.find("::") == -1:
+				if class_ == None:
+					debug("\tFound unowned function \"" + candidate.name + "\" in namespace " + concat_namespace(namespaces),2)
+					unowned_functions.append(candidate)
+				else:
+					debug("\t\tFound function \"" + candidate.name + "\" of class \"" + class_[0].name + "\" in namespace " + concat_namespace(namespaces),2)
+					class_[0].found_funs.append(candidate)
+			else:
+				candidate = WEnum.from_string(ugly_line, concat_namespace(namespaces), i)
+				if candidate != None:
+					enums.append(candidate)
+					debug("\tFound enum \"" + candidate.name + "\" in namespace " + concat_namespace(namespaces),2)
+				elif class_ != None and class_[1] == 1:
+					candidate = WConstructor.from_string(ugly_line, source.name, class_[0], i)
+					if candidate != None:
+						debug("\t\tFound constructor of class \"" + class_[0].name + "\" in namespace " + concat_namespace(namespaces),2)
+						class_[0].found_constrs.append(candidate)
+					else:
+						candidate = WMember.from_string(ugly_line, source.name, class_[0], i, concat_namespace(namespaces))
+						if candidate != None:
+							if type(candidate) == list:
+								for c in candidate:
+									debug("\t\tFound member \"" + c.name + "\" of class \"" + class_[0].name + "\" of type \"" + c.wtype.name + "\"", 2)
+								class_[0].found_vars.extend(candidate)
+							else:
+								debug("\t\tFound member \"" + candidate.name + "\" of class \"" + class_[0].name + "\" of type \"" + candidate.wtype.name + "\"", 2)
+								class_[0].found_vars.append(candidate)
+
+			j = i
+			line = unpretty_string(line)
+			while candidate == None and j+1 < len(source_text) and  line.count(';') <= 1 and line.count("(") >= line.count(")"):
+				j += 1
+				line = line + "\n" + unpretty_string(source_text[j])
+				if class_ != None:
+					candidate = WFunction.from_string(ugly_line, source.name, class_[0], i, concat_namespace(namespaces))
+				else:
+					candidate = WFunction.from_string(ugly_line, source.name, None, i, concat_namespace(namespaces))
+				if candidate != None and candidate.name.find("::") == -1:
+					if class_ == None:
+						debug("\tFound unowned function \"" + candidate.name + "\" in namespace " + concat_namespace(namespaces),2)
+						unowned_functions.append(candidate)
+					else:
+						debug("\t\tFound function \"" + candidate.name + "\" of class \"" + class_[0].name + "\" in namespace " + concat_namespace(namespaces),2)
+						class_[0].found_funs.append(candidate)
+					continue
+				candidate = WEnum.from_string(line, concat_namespace(namespaces), i)
+				if candidate != None:
+					enums.append(candidate)
+					debug("\tFound enum \"" + candidate.name + "\" in namespace " + concat_namespace(namespaces),2)
+					continue
+				if class_ != None:
+					candidate = WConstructor.from_string(line, source.name, class_[0], i)
+					if candidate != None:
+						debug("\t\tFound constructor of class \"" + class_[0].name + "\" in namespace " + concat_namespace(namespaces),2)
+						class_[0].found_constrs.append(candidate)
+						continue
+		if candidate != None:
+			while i < j:
+				i += 1
+				line = source_text[i].replace("YOSYS_NAMESPACE_BEGIN", "                    namespace YOSYS_NAMESPACE{").replace("YOSYS_NAMESPACE_END","                    }")
+				ugly_line = unpretty_string(line)
+				if len(namespaces) != 0:
+					namespaces[-1] = (namespaces[-1][0], namespaces[-1][1] + ugly_line.count("{") - ugly_line.count("}"))
+					if namespaces[-1][1] == 0:
+						debug("-----END NAMESPACE " + concat_namespace(namespaces) + "-----",3)
+						del namespaces[-1]
+				if class_ != None:
+					class_ = (class_[0] , class_[1] + ugly_line.count("{") - ugly_line.count("}"))
+					if class_[1] == 0:
+						if class_[0] == None:
+							debug("\tExiting unknown class", 3)
+						else:
+							debug("\tExiting class " + class_[0].name, 3)
+						class_ = None
+						private_segment = False
+			i += 1
+		else:
+			i += 1
+
+def debug(message, level):
+	if level <= debug.debug_level:
+		print(message)
+
+def expand_function(f):
+	fun_list = []
+	arg_list = []
+	for arg in f.args:
+		if arg.default_value != None and (arg.wtype.name.split(" ")[-1] in primitive_types or arg.wtype.name in enum_names or (arg.wtype.name in classnames and arg.default_value == "nullptr")):
+			fi = copy.deepcopy(f)
+			fi.args = copy.deepcopy(arg_list)
+			fun_list.append(fi)
+		arg_list.append(arg)
+	fun_list.append(f)
+	return fun_list
+
+def expand_functions():
+	global unowned_functions
+	new_funs = []
+	for fun in unowned_functions:
+		new_funs.extend(expand_function(fun))
+	unowned_functions = new_funs
+	for source in sources:
+		for class_ in source.classes:
+			new_funs = []
+			for fun in class_.found_funs:
+				new_funs.extend(expand_function(fun))
+			class_.found_funs = new_funs
+
+def clean_duplicates():
+	for source in sources:
+		for class_ in source.classes:
+			known_decls = {}
+			for fun in class_.found_funs:
+				if fun.gen_decl_hash_py() in known_decls:
+					debug("Multiple declarations of " + fun.gen_decl_hash_py(),3)
+					other = known_decls[fun.gen_decl_hash_py()]
+					other.gen_alias()
+					fun.gen_alias()
+					if fun.gen_decl_hash_py() == other.gen_decl_hash_py():
+						fun.duplicate = True
+						debug("Disabled \"" + fun.gen_decl_hash_py() + "\"", 3)
+				else:
+					known_decls[fun.gen_decl_hash_py()] = fun
+			known_decls = []
+			for con in class_.found_constrs:
+				if con.gen_decl_hash_py() in known_decls:
+					debug("Multiple declarations of " + con.gen_decl_hash_py(),3)
+					con.duplicate = True
+				else:
+					known_decls.append(con.gen_decl_hash_py())
+	known_decls = []
+	for fun in unowned_functions:
+		if fun.gen_decl_hash_py() in known_decls:
+			debug("Multiple declarations of " + fun.gen_decl_hash_py(),3)
+			fun.duplicate = True
+		else:
+			known_decls.append(fun.gen_decl_hash_py())
+
+def gen_wrappers(filename, debug_level_ = 0):
+	debug.debug_level = debug_level_
+	for source in sources:
+		parse_header(source)
+
+	expand_functions()
+	clean_duplicates()
+
+	import shutil
+	import math
+	col = shutil.get_terminal_size((80,20)).columns
+	debug("-"*col, 1)
+	debug("-"*math.floor((col-7)/2)+"SUMMARY"+"-"*math.ceil((col-7)/2), 1)
+	debug("-"*col, 1)
+	for source in sources:
+		for class_ in source.classes:
+			debug("Class " + assure_length(class_.name, len(max(classnames, key=len)), True) + " contains " + assure_length(str(len(class_.found_vars)), 3, False) + " member variables, "+ assure_length(str(len(class_.found_funs)), 3, False) + " methods and " + assure_length(str(len(class_.found_constrs)), 2, False) + " constructors", 1)
+			if len(class_.found_constrs) == 0:
+				class_.found_constrs.append(WConstructor(source.name, class_))
+	debug(str(len(unowned_functions)) + " functions are unowned", 1)
+	for enum in enums:
+		debug("Enum " + assure_length(enum.name, len(max(enum_names, key=len)), True) + " contains " + assure_length(str(len(enum.values)), 2, False) + " values", 1)
+	debug("-"*col, 1)
+	wrapper_file = open(filename, "w+")
+	wrapper_file.write(
+"""/*
+ *  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.
+ *
+ *  This is a generated file and can be overwritten by make
+ */
+
+#ifdef WITH_PYTHON
+""")
+	for source in sources:
+		wrapper_file.write("#include \""+source.name+".h\"\n")
+	wrapper_file.write("""
+#include <boost/python/module.hpp>
+#include <boost/python/class.hpp>
+#include <boost/python/wrapper.hpp>
+#include <boost/python/call.hpp>
+#include <boost/python.hpp>
+#include <boost/log/exceptions.hpp>
+
+USING_YOSYS_NAMESPACE
+
+namespace YOSYS_PYTHON {
+""")
+
+	for source in sources:
+		for wclass in source.classes:
+			wrapper_file.write("\n\tstruct " + wclass.name + ";")
+
+	wrapper_file.write("\n")
+
+	for source in sources:
+		for wclass in source.classes:
+			wrapper_file.write(wclass.gen_decl(source.name))
+
+	wrapper_file.write("\n")
+
+	for source in sources:
+		for wclass in source.classes:
+			wrapper_file.write(wclass.gen_funs(source.name))
+
+	for fun in unowned_functions:
+		wrapper_file.write(fun.gen_def())
+
+	wrapper_file.write("""	struct Initializer
+	{
+		Initializer() {
+			if(!Yosys::yosys_already_setup())
+			{
+				Yosys::log_streams.push_back(&std::cout);
+				Yosys::log_error_stderr = true;
+				Yosys::yosys_setup();
+				Yosys::yosys_banner();
+			}
+		}
+
+		Initializer(Initializer const &) {}
+
+		~Initializer() {
+			Yosys::yosys_shutdown();
+		}
+	};
+
+	BOOST_PYTHON_MODULE(libyosys)
+	{
+		using namespace boost::python;
+
+		class_<Initializer>("Initializer");
+		scope().attr("_hidden") = new Initializer();
+""")
+
+	for enum in enums:
+		wrapper_file.write(enum.gen_boost_py())
+
+	for source in sources:
+		for wclass in source.classes:
+			wrapper_file.write(wclass.gen_boost_py())
+
+	for fun in unowned_functions:
+		wrapper_file.write(fun.gen_boost_py())
+
+	wrapper_file.write("\n\t}\n}\n#endif")
+
+def print_includes():
+	for source in sources:
+		print(source.name + ".pyh")
diff --git a/techlibs/gowin/Makefile.inc b/techlibs/gowin/Makefile.inc
index 2f82def7d..6f2159349 100644
--- a/techlibs/gowin/Makefile.inc
+++ b/techlibs/gowin/Makefile.inc
@@ -1,7 +1,17 @@
 
 OBJS += techlibs/gowin/synth_gowin.o
+OBJS += techlibs/gowin/determine_init.o
+
 
 $(eval $(call add_share_file,share/gowin,techlibs/gowin/cells_map.v))
 $(eval $(call add_share_file,share/gowin,techlibs/gowin/cells_sim.v))
 $(eval $(call add_share_file,share/gowin,techlibs/gowin/arith_map.v))
+$(eval $(call add_share_file,share/gowin,techlibs/gowin/brams_map.v))
+$(eval $(call add_share_file,share/gowin,techlibs/gowin/bram.txt))
+$(eval $(call add_share_file,share/gowin,techlibs/gowin/drams_map.v))
+$(eval $(call add_share_file,share/gowin,techlibs/gowin/dram.txt))
+
+
+
+$(eval $(call add_share_file,share/gowin,techlibs/gowin/brams_init3.vh))
 
diff --git a/techlibs/gowin/bram.txt b/techlibs/gowin/bram.txt
new file mode 100644
index 000000000..b5f9a981c
--- /dev/null
+++ b/techlibs/gowin/bram.txt
@@ -0,0 +1,29 @@
+bram $__GW1NR_SDP
+# uncomment when done
+#  init 1
+  abits 10 @a10d18
+  dbits 16 @a10d18
+  abits 11 @a11d9
+  dbits 8  @a11d9
+  abits 12 @a12d4
+  dbits 4  @a12d4
+  abits 13 @a13d2
+  dbits 2  @a13d2
+  abits 14 @a14d1
+  dbits 1  @a14d1
+  groups 2
+  ports  1 1
+  wrmode 1 0
+  enable 1 1 @a10d18
+  enable 1 1 @a11d9 @a12d4 @a13d2 @a14d1
+  transp 0 0
+  clocks 2 3
+  clkpol 2 3
+endbram
+
+match $__GW1NR_SDP
+  min bits 2048
+  min efficiency 5
+  shuffle_enable B
+  make_transp
+endmatch
diff --git a/techlibs/gowin/brams_init3.vh b/techlibs/gowin/brams_init3.vh
new file mode 100644
index 000000000..84397fa24
--- /dev/null
+++ b/techlibs/gowin/brams_init3.vh
@@ -0,0 +1,12 @@
+localparam [15:0] INIT_0 = {
+  INIT[  60], INIT[  56], INIT[  52], INIT[  48], INIT[  44], INIT[  40], INIT[  36], INIT[  32], INIT[  28], INIT[  24], INIT[  20], INIT[  16], INIT[  12], INIT[   8], INIT[   4], INIT[   0]
+};
+localparam [15:0] INIT_1 = {
+  INIT[  61], INIT[  57], INIT[  53], INIT[  49], INIT[  45], INIT[  41], INIT[  37], INIT[  33], INIT[  29], INIT[  25], INIT[  21], INIT[  17], INIT[  13], INIT[   9], INIT[   5], INIT[   1]
+};
+localparam [15:0] INIT_2 = {
+  INIT[  62], INIT[  58], INIT[  54], INIT[  50], INIT[  46], INIT[  42], INIT[  38], INIT[  34], INIT[  30], INIT[  26], INIT[  22], INIT[  18], INIT[  14], INIT[  10], INIT[   6], INIT[   2]
+};
+localparam [15:0] INIT_3 = {
+  INIT[  63], INIT[  59], INIT[  55], INIT[  51], INIT[  47], INIT[  43], INIT[  39], INIT[  35], INIT[  31], INIT[  27], INIT[  23], INIT[  19], INIT[  15], INIT[  11], INIT[   7], INIT[   3]
+};
diff --git a/techlibs/gowin/brams_map.v b/techlibs/gowin/brams_map.v
new file mode 100644
index 000000000..e963cfa88
--- /dev/null
+++ b/techlibs/gowin/brams_map.v
@@ -0,0 +1,103 @@
+/* Semi Dual Port (SDP) memory have the following configurations:
+ * Memory Config    RAM(BIT)   Port Mode   Memory Depth   Data Depth
+ * ----------------|---------| ----------|--------------|------------|
+ * B-SRAM_16K_SD1      16K      16Kx1       16,384           1
+ * B-SRAM_8K_SD2       16K       8Kx2        8,192           2
+ * B-SRAM_4K_SD4       16K       4Kx2        4,096           4
+ */
+module \$__GW1NR_SDP (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN);
+	parameter CFG_ABITS = 10;
+	parameter CFG_DBITS = 16;
+	parameter CFG_ENABLE_A = 3;
+
+        parameter [16383:0] INIT = 16384'hx;
+        parameter CLKPOL2 = 1;
+        parameter CLKPOL3 = 1;
+
+	input CLK2;
+	input CLK3;
+
+	input [CFG_ABITS-1:0] A1ADDR;
+	input [CFG_DBITS-1:0] A1DATA;   
+        input [CFG_ENABLE_A-1:0] A1EN;
+
+	input [CFG_ABITS-1:0] B1ADDR;
+	output [CFG_DBITS-1:0] B1DATA;
+	input B1EN;
+
+	
+	generate if (CFG_DBITS == 1) begin
+		SDP   #(
+			.READ_MODE(0),
+			.BIT_WIDTH_0(1),
+			.BIT_WIDTH_1(1),
+			.BLK_SEL(3'b000),
+			.RESET_MODE("SYNC")
+		) _TECHMAP_REPLACE_ (
+			.CLKA(CLK2),   .CLKB(CLK3),
+			.WREA(A1EN),   .OCE(1'b0), .CEA(1'b1),
+			.WREB(1'b0),   .CEB(B1EN),
+			.RESETA(1'b0), .RESETB(1'b0), .BLKSEL(3'b000),
+			.DI(A1DATA), .DO(B1DATA), .ADA(A1ADDR), .ADB(B1ADDR)
+		);
+	end else if (CFG_DBITS == 2) begin
+		SDP    #(
+			.READ_MODE(0),
+			.BIT_WIDTH_0(2),
+			.BIT_WIDTH_1(2),
+			.BLK_SEL(3'b000),
+			.RESET_MODE("SYNC")
+		) _TECHMAP_REPLACE_ (
+			.CLKA(CLK2),   .CLKB(CLK3),
+			.WREA(A1EN),   .OCE(1'b0), .CEA(1'b1),
+			.WREB(1'b0),   .CEB(B1EN),
+			.RESETA(1'b0), .RESETB(1'b0), .BLKSEL(3'b000),
+                        .DI(A1DATA), .DO(B1DATA), .ADA(A1ADDR), .ADB(B1ADDR)
+		);
+	end else if (CFG_DBITS <= 4) begin
+		SDP    #(
+			.READ_MODE(0),
+			.BIT_WIDTH_0(4),
+			.BIT_WIDTH_1(4),
+			.BLK_SEL(3'b000),
+			.RESET_MODE("SYNC")
+		) _TECHMAP_REPLACE_ (
+			.CLKA(CLK2),   .CLKB(CLK3),
+			.WREA(A1EN),   .OCE(1'b0),
+			.WREB(1'b0),   .CEB(B1EN), .CEA(1'b1),
+			.RESETA(1'b0), .RESETB(1'b0), .BLKSEL(3'b000),
+                        .DI(A1DATA), .DO(B1DATA), .ADA(A1ADDR), .ADB(B1ADDR)
+		);
+	end else if (CFG_DBITS <= 8) begin
+		SDP    #(
+			.READ_MODE(0),
+			.BIT_WIDTH_0(8),
+			.BIT_WIDTH_1(8),
+			.BLK_SEL(3'b000),
+			.RESET_MODE("SYNC")
+		) _TECHMAP_REPLACE_ (
+			.CLKA(CLK2),   .CLKB(CLK3),
+			.WREA(A1EN),   .OCE(1'b0), .CEA(1'b1),
+			.WREB(1'b0),   .CEB(B1EN),
+			.RESETA(1'b0), .RESETB(1'b0), .BLKSEL(3'b000),
+                        .DI(A1DATA), .DO(B1DATA), .ADA(A1ADDR), .ADB(B1ADDR)
+		);
+	end else if (CFG_DBITS <= 16) begin
+		SDP    #(
+			.READ_MODE(0),
+			.BIT_WIDTH_0(16),
+			.BIT_WIDTH_1(16),
+			.BLK_SEL(3'b000),
+			.RESET_MODE("SYNC")
+		) _TECHMAP_REPLACE_ (
+			.CLKA(CLK2),   .CLKB(CLK3),
+			.WREA(A1EN),   .OCE(1'b0),
+			.WREB(1'b0),   .CEB(B1EN), .CEA(1'b1),
+			.RESETA(1'b0), .RESETB(1'b0), .BLKSEL(3'b000),
+                        .DI(A1DATA), .DO(B1DATA), .ADA(A1ADDR), .ADB(B1ADDR)
+		);
+	end else begin
+		wire TECHMAP_FAIL = 1'b1;
+	end endgenerate
+	
+endmodule
diff --git a/techlibs/gowin/cells_map.v b/techlibs/gowin/cells_map.v
index e1f85effa..ebdc88a0a 100644
--- a/techlibs/gowin/cells_map.v
+++ b/techlibs/gowin/cells_map.v
@@ -1,5 +1,9 @@
 module  \$_DFF_N_ (input D, C, output Q); DFFN _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C)); endmodule
-module  \$_DFF_P_ (input D, C, output Q); DFF  _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C)); endmodule
+module  \$_DFF_P_ #(parameter INIT = 1'b0) (input D, C, output Q); DFF  #(.INIT(INIT)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C)); endmodule
+
+module  \$__DFFS_PN0_ (input D, C, R, output Q); DFFR _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(!R)); endmodule
+module  \$__DFFS_PP0_ (input D, C, R, output Q); DFFR _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(R)); endmodule
+module  \$__DFFS_PP1_ (input D, C, R, output Q); DFFR  _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(R)); endmodule
 
 module \$lut (A, Y);
   parameter WIDTH = 0;
diff --git a/techlibs/gowin/cells_sim.v b/techlibs/gowin/cells_sim.v
index 14441c2fc..ebb238bad 100644
--- a/techlibs/gowin/cells_sim.v
+++ b/techlibs/gowin/cells_sim.v
@@ -38,6 +38,17 @@ module DFFN (output reg Q, input CLK, D);
 		Q <= D;
 endmodule
 
+module DFFR (output reg Q, input D, CLK, RESET);
+	parameter [0:0] INIT = 1'b0;
+	initial Q = INIT;
+	always @(posedge CLK) begin
+        if (RESET)
+                Q <= 1'b0;
+        else
+                Q <= D;
+	end
+endmodule // DFFR (positive clock edge; synchronous reset)
+
 module VCC(output V);
 	assign V = 1;
 endmodule
@@ -63,3 +74,126 @@ module ALU (input I0, input I1, input I3, input CIN, output COUT, output SUM);
    assign  {COUT, SUM} = CIN + I1 + I0;
 endmodule // alu
 
+module RAM16S4 (DO, DI, AD, WRE, CLK);
+   parameter WIDTH  = 4;
+   parameter INIT_0 = 16'h0000;
+   parameter INIT_1 = 16'h0000;
+   parameter INIT_2 = 16'h0000;
+   parameter INIT_3 = 16'h0000;
+   
+   input  [WIDTH-1:0] AD;
+   input  [WIDTH-1:0] DI;
+   output [WIDTH-1:0] DO;
+   input 	      CLK;
+   input 	      WRE;
+
+   reg [15:0] 	    mem0, mem1, mem2, mem3;
+   
+   initial begin
+      mem0 = INIT_0;
+      mem1 = INIT_1;
+      mem2 = INIT_2;
+      mem3 = INIT_3;	
+   end
+   
+   assign	DO[0] = mem0[AD];
+   assign	DO[1] = mem1[AD];
+   assign	DO[2] = mem2[AD];
+   assign	DO[3] = mem3[AD];
+   
+   always @(posedge CLK) begin
+      if (WRE) begin
+	 mem0[AD] <= DI[0];
+	 mem1[AD] <= DI[1];
+	 mem2[AD] <= DI[2];
+	 mem3[AD] <= DI[3];
+      end
+   end
+   
+endmodule // RAM16S4
+
+
+(* blackbox *)
+module SDP (DO, DI, BLKSEL, ADA, ADB, WREA, WREB, CLKA, CLKB, CEA, CEB, OCE, RESETA, RESETB);
+//1'b0: Bypass mode; 1'b1 Pipeline mode
+parameter READ_MODE = 1'b0;
+parameter BIT_WIDTH_0 = 32; // 1, 2, 4, 8, 16, 32
+parameter BIT_WIDTH_1 = 32; // 1, 2, 4, 8, 16, 32
+parameter BLK_SEL = 3'b000;
+parameter RESET_MODE = "SYNC";
+parameter INIT_RAM_00 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_01 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_02 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_03 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_04 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_05 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_06 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_07 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_08 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_09 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_0A = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_0B = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_0C = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_0D = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_0E = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_0F = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_10 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_11 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_12 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_13 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_14 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_15 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_16 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_17 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_18 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_19 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_1A = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_1B = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_1C = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_1D = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_1E = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_1F = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_20 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_21 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_22 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_23 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_24 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_25 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_26 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_27 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_28 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_29 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_2A = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_2B = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_2C = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_2D = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_2E = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_2F = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_30 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_31 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_32 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_33 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_34 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_35 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_36 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_37 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_38 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_39 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_3A = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_3B = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_3C = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_3D = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_3E = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_3F = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+
+input CLKA, CEA, CLKB, CEB;
+input OCE; // clock enable of memory output register
+input RESETA, RESETB; // resets output registers, not memory contents
+input WREA, WREB; // 1'b0: read enabled; 1'b1: write enabled
+input [13:0] ADA, ADB;
+input [31:0] DI;
+input [2:0] BLKSEL;
+output [31:0] DO;
+
+endmodule
+
diff --git a/techlibs/gowin/determine_init.cc b/techlibs/gowin/determine_init.cc
new file mode 100644
index 000000000..991e5245a
--- /dev/null
+++ b/techlibs/gowin/determine_init.cc
@@ -0,0 +1,72 @@
+/*
+ *  yosys -- Yosys Open SYnthesis Suite
+ *
+ *  Copyright (C) 2018 Icenowy Zheng <icenowy@aosc.io>
+ *
+ *  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 DetermineInitPass : public Pass {
+	DetermineInitPass() : Pass("determine_init", "Determine the init value of cells") { }
+	void help() YS_OVERRIDE
+	{
+		log("\n");
+		log("    determine_init [selection]\n");
+		log("\n");
+		log("Determine the init value of cells that doesn't allow unknown init value.\n");
+		log("\n");
+	}
+
+	Const determine_init(Const init)
+	{
+		for (int i = 0; i < GetSize(init); i++) {
+			if (init[i] != State::S0 && init[i] != State::S1)
+				init[i] = State::S0;
+		}
+
+		return init;
+	}
+
+	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+	{
+		log_header(design, "Executing DETERMINE_INIT pass (determine init value for cells).\n");
+
+		extra_args(args, args.size(), design);
+
+		size_t cnt = 0;
+		for (auto module : design->selected_modules())
+		{
+			for (auto cell : module->selected_cells())
+			{
+				if (cell->type == "\\RAM16S4")
+				{
+					cell->setParam("\\INIT_0", determine_init(cell->getParam("\\INIT_0")));
+					cell->setParam("\\INIT_1", determine_init(cell->getParam("\\INIT_1")));
+					cell->setParam("\\INIT_2", determine_init(cell->getParam("\\INIT_2")));
+					cell->setParam("\\INIT_3", determine_init(cell->getParam("\\INIT_3")));
+					cnt++;
+				}
+			}
+		}
+		log_header(design, "Updated %lu cells with determined init value.\n", cnt);
+	}
+} DetermineInitPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/techlibs/gowin/dram.txt b/techlibs/gowin/dram.txt
new file mode 100644
index 000000000..9db530251
--- /dev/null
+++ b/techlibs/gowin/dram.txt
@@ -0,0 +1,17 @@
+bram $__GW1NR_RAM16S4
+  init 1
+  abits 4
+  dbits 4
+  groups 2
+  ports  1 1
+  wrmode 0 1
+  enable 0 1
+  transp 0 1
+  clocks 0 1
+  clkpol 0 1
+endbram
+
+match $__GW1NR_RAM16S4
+  make_outreg
+  min wports 1
+endmatch
diff --git a/techlibs/gowin/drams_map.v b/techlibs/gowin/drams_map.v
new file mode 100644
index 000000000..a50ab365a
--- /dev/null
+++ b/techlibs/gowin/drams_map.v
@@ -0,0 +1,31 @@
+module \$__GW1NR_RAM16S4 (CLK1, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN);
+	parameter CFG_ABITS = 4;
+	parameter CFG_DBITS = 4;
+
+        parameter [63:0] INIT = 64'bx;
+	input CLK1;
+
+	input  [CFG_ABITS-1:0] A1ADDR;
+	output [CFG_DBITS-1:0] A1DATA;   
+        input                  A1EN;
+
+	input  [CFG_ABITS-1:0] B1ADDR;
+	input  [CFG_DBITS-1:0] B1DATA;
+	input  B1EN;
+
+        `include "brams_init3.vh"
+
+  RAM16S4
+   #(.INIT_0(INIT_0),
+     .INIT_1(INIT_1),
+     .INIT_2(INIT_2),
+     .INIT_3(INIT_3))
+   _TECHMAP_REPLACE_
+     (.AD(B1ADDR),
+      .DI(B1DATA),
+      .DO(A1DATA),
+      .CLK(CLK1),
+      .WRE(B1EN));
+
+	
+endmodule
diff --git a/techlibs/gowin/synth_gowin.cc b/techlibs/gowin/synth_gowin.cc
index 9a3fcdbb6..ac3dbfb29 100644
--- a/techlibs/gowin/synth_gowin.cc
+++ b/techlibs/gowin/synth_gowin.cc
@@ -49,9 +49,15 @@ struct SynthGowinPass : public ScriptPass
 		log("        from label is synonymous to 'begin', and empty to label is\n");
 		log("        synonymous to the end of the command list.\n");
 		log("\n");
+		log("    -nodffe\n");
+		log("        do not use flipflops with CE in output netlist\n");
+		log("\n");
 		log("    -nobram\n");
 		log("        do not use BRAM cells in output netlist\n");
 		log("\n");
+		log("    -nodram\n");
+		log("        do not use distributed RAM cells in output netlist\n");
+		log("\n");
 		log("    -noflatten\n");
 		log("        do not flatten design before synthesis\n");
 		log("\n");
@@ -65,7 +71,7 @@ struct SynthGowinPass : public ScriptPass
 	}
 
 	string top_opt, vout_file;
-	bool retime, flatten, nobram;
+	bool retime, nobram, nodram, flatten, nodffe;
 
 	void clear_flags() YS_OVERRIDE
 	{
@@ -73,7 +79,9 @@ struct SynthGowinPass : public ScriptPass
 		vout_file = "";
 		retime = false;
 		flatten = true;
-		nobram = true;
+		nobram = false;
+		nodffe = false;
+		nodram = false;
 	}
 
 	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
@@ -108,6 +116,14 @@ struct SynthGowinPass : public ScriptPass
 				nobram = true;
 				continue;
 			}
+			if (args[argidx] == "-nodram") {
+				nodram = true;
+				continue;
+			}
+			if (args[argidx] == "-nodffe") {
+				nodffe = true;
+				continue;
+			}
 			if (args[argidx] == "-noflatten") {
 				flatten = false;
 				continue;
@@ -147,25 +163,43 @@ struct SynthGowinPass : public ScriptPass
 		{
 			run("synth -run coarse");
 		}
-		if (!nobram && check_label("bram", "(skip if -nobram)"))
+		
+                if (!nobram && check_label("bram", "(skip if -nobram)"))
 		{
 			run("memory_bram -rules +/gowin/bram.txt");
-			run("techmap -map +/gowin/brams_map.v");
+			run("techmap -map +/gowin/brams_map.v -map +/gowin/cells_sim.v");
 		}
+
+		if (!nodram && check_label("dram", "(skip if -nodram)"))
+		{
+			run("memory_bram -rules +/gowin/dram.txt");
+			run("techmap -map +/gowin/drams_map.v");
+			run("determine_init");
+		}
+
 		if (check_label("fine"))
 		{
 			run("opt -fast -mux_undef -undriven -fine");
 			run("memory_map");
 			run("opt -undriven -fine");
 			run("techmap -map +/techmap.v -map +/gowin/arith_map.v");
-			run("opt -fine");
-			run("clean -purge");
-			run("splitnets -ports");
-			run("setundef -undriven -zero");
+			run("techmap -map +/techmap.v");
 			if (retime || help_mode)
 				run("abc -dff", "(only if -retime)");
 		}
 
+		if (check_label("map_ffs"))
+		{
+			run("dffsr2dff");
+			run("dff2dffs");
+			run("opt_clean");
+			if (!nodffe)
+				run("dff2dffe -direct-match $_DFF_* -direct-match $__DFFS_*");
+			run("techmap -map +/gowin/cells_map.v");
+			run("opt_expr -mux_undef");
+			run("simplemap");
+		}
+
 		if (check_label("map_luts"))
 		{
 			run("abc -lut 4");
@@ -176,8 +210,10 @@ struct SynthGowinPass : public ScriptPass
 		{
 			run("techmap -map +/gowin/cells_map.v");
 			run("hilomap -hicell VCC V -locell GND G");
-			run("iopadmap -inpad IBUF O:I -outpad OBUF I:O");
-			run("clean -purge");
+			run("iopadmap -bits -inpad IBUF O:I -outpad OBUF I:O", "(unless -noiopads)");
+			run("dffinit  -ff DFF Q INIT");
+			run("clean");
+
 		}
 
 		if (check_label("check"))
diff --git a/techlibs/ice40/cells_sim.v b/techlibs/ice40/cells_sim.v
index 93d970762..40e54f9f0 100644
--- a/techlibs/ice40/cells_sim.v
+++ b/techlibs/ice40/cells_sim.v
@@ -27,18 +27,27 @@ module SB_IO (
 	reg dout_q_0, dout_q_1;
 	reg outena_q;
 
+	// IO tile generates a constant 1'b1 internally if global_cen is not connected
+	wire clken_pulled = CLOCK_ENABLE || CLOCK_ENABLE === 1'bz;
+	reg  clken_pulled_ri;
+	reg  clken_pulled_ro;
+
 	generate if (!NEG_TRIGGER) begin
-		always @(posedge INPUT_CLK)  if (CLOCK_ENABLE) din_q_0  <= PACKAGE_PIN;
-		always @(negedge INPUT_CLK)  if (CLOCK_ENABLE) din_q_1  <= PACKAGE_PIN;
-		always @(posedge OUTPUT_CLK) if (CLOCK_ENABLE) dout_q_0 <= D_OUT_0;
-		always @(negedge OUTPUT_CLK) if (CLOCK_ENABLE) dout_q_1 <= D_OUT_1;
-		always @(posedge OUTPUT_CLK) if (CLOCK_ENABLE) outena_q <= OUTPUT_ENABLE;
+		always @(posedge INPUT_CLK)                       clken_pulled_ri <= clken_pulled;
+		always @(posedge INPUT_CLK)  if (clken_pulled)    din_q_0         <= PACKAGE_PIN;
+		always @(negedge INPUT_CLK)  if (clken_pulled_ri) din_q_1         <= PACKAGE_PIN;
+		always @(posedge OUTPUT_CLK)                      clken_pulled_ro <= clken_pulled;
+		always @(posedge OUTPUT_CLK) if (clken_pulled)    dout_q_0        <= D_OUT_0;
+		always @(negedge OUTPUT_CLK) if (clken_pulled_ro) dout_q_1        <= D_OUT_1;
+		always @(posedge OUTPUT_CLK) if (clken_pulled)    outena_q        <= OUTPUT_ENABLE;
 	end else begin
-		always @(negedge INPUT_CLK)  if (CLOCK_ENABLE) din_q_0  <= PACKAGE_PIN;
-		always @(posedge INPUT_CLK)  if (CLOCK_ENABLE) din_q_1  <= PACKAGE_PIN;
-		always @(negedge OUTPUT_CLK) if (CLOCK_ENABLE) dout_q_0 <= D_OUT_0;
-		always @(posedge OUTPUT_CLK) if (CLOCK_ENABLE) dout_q_1 <= D_OUT_1;
-		always @(negedge OUTPUT_CLK) if (CLOCK_ENABLE) outena_q <= OUTPUT_ENABLE;
+		always @(negedge INPUT_CLK)                       clken_pulled_ri <= clken_pulled;
+		always @(negedge INPUT_CLK)  if (clken_pulled)    din_q_0         <= PACKAGE_PIN;
+		always @(posedge INPUT_CLK)  if (clken_pulled_ri) din_q_1         <= PACKAGE_PIN;
+		always @(negedge OUTPUT_CLK)                      clken_pulled_ro <= clken_pulled;
+		always @(negedge OUTPUT_CLK) if (clken_pulled)    dout_q_0        <= D_OUT_0;
+		always @(posedge OUTPUT_CLK) if (clken_pulled_ro) dout_q_1        <= D_OUT_1;
+		always @(negedge OUTPUT_CLK) if (clken_pulled)    outena_q        <= OUTPUT_ENABLE;
 	end endgenerate
 
 	always @* begin
diff --git a/techlibs/xilinx/synth_xilinx.cc b/techlibs/xilinx/synth_xilinx.cc
index 8a531c497..08d74cd3b 100644
--- a/techlibs/xilinx/synth_xilinx.cc
+++ b/techlibs/xilinx/synth_xilinx.cc
@@ -116,14 +116,15 @@ struct SynthXilinxPass : public Pass
 		log("        techmap -map +/techmap.v -map +/xilinx/arith_map.v\n");
 		log("        opt -fast\n");
 		log("\n");
+		log("    map_cells:\n");
+		log("        techmap -map +/xilinx/cells_map.v\n");
+		log("        clean\n");
+		log("\n");
 		log("    map_luts:\n");
 		log("        techmap -map +/techmap.v -map +/xilinx/ff_map.v t:$_DFF_?N?\n");
 		log("        abc -luts 2:2,3,6:5,10,20 [-dff]\n");
 		log("        clean\n");
 		log("        techmap -map +/xilinx/lut_map.v -map +/xilinx/ff_map.v");
-		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("                -ff FDRE_1 Q INIT -ff FDCE_1 Q INIT -ff FDPE_1 Q INIT -ff FDSE_1 Q INIT\n");
 		log("        clean\n");
@@ -274,17 +275,18 @@ struct SynthXilinxPass : public Pass
 			Pass::call(design, "opt -fast");
 		}
 
+		if (check_label(active, run_from, run_to, "map_cells"))
+		{
+			Pass::call(design, "techmap -map +/xilinx/cells_map.v");
+			Pass::call(design, "clean");
+		}
+
 		if (check_label(active, run_from, run_to, "map_luts"))
 		{
 			Pass::call(design, "techmap -map +/techmap.v -map +/xilinx/ff_map.v t:$_DFF_?N?");
 			Pass::call(design, abc + " -luts 2:2,3,6:5,10,20" + string(retime ? " -dff" : ""));
 			Pass::call(design, "clean");
 			Pass::call(design, "techmap -map +/xilinx/lut_map.v -map +/xilinx/ff_map.v");
-		}
-
-		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 "
 					"-ff FDRE_1 Q INIT -ff FDCE_1 Q INIT -ff FDPE_1 Q INIT -ff FDSE_1 Q INIT");
 			Pass::call(design, "clean");
diff --git a/tests/sat/counters-repeat.v b/tests/sat/counters-repeat.v
new file mode 100644
index 000000000..2ea45499a
--- /dev/null
+++ b/tests/sat/counters-repeat.v
@@ -0,0 +1,38 @@
+// coverage for repeat loops outside of constant functions
+
+module counter1(clk, rst, ping);
+	input clk, rst;
+	output ping;
+	reg [31:0] count;
+
+	always @(posedge clk) begin
+		if (rst)
+			count <= 0;
+		else
+			count <= count + 1;
+	end
+
+	assign ping = &count;
+endmodule
+
+module counter2(clk, rst, ping);
+	input clk, rst;
+	output ping;
+	reg [31:0] count;
+
+	integer i;
+	reg carry;
+
+	always @(posedge clk) begin
+		carry = 1;
+		i = 0;
+		repeat (32) begin
+			count[i] <= !rst & (count[i] ^ carry);
+			carry = count[i] & carry;
+			i = i+1;
+		end
+	end
+
+	assign ping = &count;
+endmodule
+
diff --git a/tests/sat/counters-repeat.ys b/tests/sat/counters-repeat.ys
new file mode 100644
index 000000000..b3dcfe08a
--- /dev/null
+++ b/tests/sat/counters-repeat.ys
@@ -0,0 +1,10 @@
+
+read_verilog counters-repeat.v
+proc; opt
+
+expose -shared counter1 counter2
+miter -equiv -make_assert -make_outputs counter1 counter2 miter
+
+cd miter; flatten; opt
+sat -verify -prove-asserts -tempinduct -set-at 1 in_rst 1 -seq 1 -show-inputs -show-outputs
+
diff --git a/tests/various/pmux2shiftx.v b/tests/various/pmux2shiftx.v
new file mode 100644
index 000000000..fec84187b
--- /dev/null
+++ b/tests/various/pmux2shiftx.v
@@ -0,0 +1,34 @@
+module pmux2shiftx_test (
+	input [2:0] S1,
+	input [5:0] S2,
+	input [1:0] S3,
+	input [9:0] A, B, C, D, D, E, F, G, H,
+	input [9:0] I, J, K, L, M, N, O, P, Q,
+	output reg [9:0] X
+);
+	always @* begin
+		case (S1)
+			3'd 0: X = A;
+			3'd 1: X = B;
+			3'd 2: X = C;
+			3'd 3: X = D;
+			3'd 4: X = E;
+			3'd 5: X = F;
+			3'd 6: X = G;
+			3'd 7: X = H;
+		endcase
+		case (S2)
+			6'd 45: X = I;
+			6'd 47: X = J;
+			6'd 49: X = K;
+			6'd 55: X = L;
+			6'd 57: X = M;
+			6'd 59: X = N;
+		endcase
+		case (S3)
+			2'd 1: X = O;
+			2'd 2: X = P;
+			2'd 3: X = Q;
+		endcase
+	end
+endmodule
diff --git a/tests/various/pmux2shiftx.ys b/tests/various/pmux2shiftx.ys
new file mode 100644
index 000000000..deb134083
--- /dev/null
+++ b/tests/various/pmux2shiftx.ys
@@ -0,0 +1,28 @@
+read_verilog pmux2shiftx.v
+prep
+design -save gold
+
+pmux2shiftx -min_density 70
+
+opt
+
+stat
+# show -width
+select -assert-count 1 t:$sub
+select -assert-count 1 t:$mux
+select -assert-count 1 t:$shift
+select -assert-count 3 t:$shiftx
+
+design -stash gate
+
+design -import gold -as gold
+design -import gate -as gate
+
+miter -equiv -flatten -make_assert -make_outputs gold gate miter
+sat -verify -prove-asserts -show-ports miter
+
+design -load gold
+stat
+
+design -load gate
+stat