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 4f47d8abb..249c1d0ee 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, other boost_python, 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) @@ -510,6 +546,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) $< @@ -638,6 +682,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: @@ -645,6 +694,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) @@ -657,8 +711,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 5c94c34e5..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 @@ -310,12 +311,20 @@ Verilog Attributes and non-standard features that have the same ports as the real thing but do not contain information on the internal configuration. This modules are only used by the synthesis passes to identify input and output ports of cells. The Verilog backend - also does not output blackbox modules on default. + also does not output blackbox modules on default. ``read_verilog``, unless + called with ``-noblackbox`` will automatically set the blackbox attribute + on any empty module it reads. + +- The ``noblackbox`` attribute set on an empty module prevents ``read_verilog`` + from automatically setting the blackbox attribute on the module. - The ``whitebox`` attribute on modules triggers the same behavior as ``blackbox``, but is for whitebox modules, i.e. library modules that contain a behavioral model of the cell type. +- The ``lib_whitebox`` attribute overwrites ``whitebox`` when ``read_verilog`` + is run in `-lib` mode. Otherwise it's automatically removed. + - The ``dynports`` attribute is used by the Verilog front-end to mark modules that have ports with a width that depends on a parameter. 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/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/ast/ast.cc b/frontends/ast/ast.cc index 720b3f3d1..9f88b08c1 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -46,7 +46,7 @@ namespace AST { // instantiate global variables (private API) namespace AST_INTERNAL { bool flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches, flag_nomeminit; - bool flag_nomem2reg, flag_mem2reg, flag_lib, flag_wb, flag_noopt, flag_icells, flag_autowire; + bool flag_nomem2reg, flag_mem2reg, flag_noblackbox, flag_lib, flag_nowb, flag_noopt, flag_icells, flag_autowire; AstNode *current_ast, *current_ast_mod; std::map current_scope; const dict *genRTLIL_subst_ptr = NULL; @@ -942,6 +942,20 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast if (!defer) { + bool blackbox_module = flag_lib; + + if (!blackbox_module && !flag_noblackbox) { + blackbox_module = true; + for (auto child : ast->children) { + if (child->type == AST_WIRE && (child->is_input || child->is_output)) + continue; + if (child->type == AST_PARAMETER || child->type == AST_LOCALPARAM) + continue; + blackbox_module = false; + break; + } + } + while (ast->simplify(!flag_noopt, false, false, 0, -1, false, false)) { } if (flag_dump_ast2) { @@ -956,18 +970,63 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast log("--- END OF AST DUMP ---\n"); } - if (flag_wb) { - if (!ast->attributes.count("\\whitebox")) - goto blackbox_module; - AstNode *n = ast->attributes.at("\\whitebox"); - if (n->type != AST_CONSTANT) - log_file_error(ast->filename, ast->linenum, "Whitebox attribute with non-constant value!\n"); - if (!n->asBool()) - goto blackbox_module; + if (flag_nowb && ast->attributes.count("\\whitebox")) { + delete ast->attributes.at("\\whitebox"); + ast->attributes.erase("\\whitebox"); } - if (flag_lib) { - blackbox_module: + if (ast->attributes.count("\\lib_whitebox")) { + if (!flag_lib || flag_nowb) { + delete ast->attributes.at("\\lib_whitebox"); + ast->attributes.erase("\\lib_whitebox"); + } else { + if (ast->attributes.count("\\whitebox")) { + delete ast->attributes.at("\\whitebox"); + ast->attributes.erase("\\whitebox"); + } + AstNode *n = ast->attributes.at("\\lib_whitebox"); + ast->attributes["\\whitebox"] = n; + ast->attributes.erase("\\lib_whitebox"); + } + } + + if (!blackbox_module && ast->attributes.count("\\blackbox")) { + AstNode *n = ast->attributes.at("\\blackbox"); + if (n->type != AST_CONSTANT) + log_file_error(ast->filename, ast->linenum, "Got blackbox attribute with non-constant value!\n"); + blackbox_module = n->asBool(); + } + + if (blackbox_module && ast->attributes.count("\\whitebox")) { + AstNode *n = ast->attributes.at("\\whitebox"); + if (n->type != AST_CONSTANT) + log_file_error(ast->filename, ast->linenum, "Got whitebox attribute with non-constant value!\n"); + blackbox_module = !n->asBool(); + } + + if (ast->attributes.count("\\noblackbox")) { + if (blackbox_module) { + AstNode *n = ast->attributes.at("\\noblackbox"); + if (n->type != AST_CONSTANT) + log_file_error(ast->filename, ast->linenum, "Got noblackbox attribute with non-constant value!\n"); + blackbox_module = !n->asBool(); + } + delete ast->attributes.at("\\noblackbox"); + ast->attributes.erase("\\noblackbox"); + } + + if (blackbox_module) + { + if (ast->attributes.count("\\whitebox")) { + delete ast->attributes.at("\\whitebox"); + ast->attributes.erase("\\whitebox"); + } + + if (ast->attributes.count("\\lib_whitebox")) { + delete ast->attributes.at("\\lib_whitebox"); + ast->attributes.erase("\\lib_whitebox"); + } + std::vector new_children; for (auto child : ast->children) { if (child->type == AST_WIRE && (child->is_input || child->is_output)) { @@ -980,12 +1039,12 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast delete child; } } + ast->children.swap(new_children); - if (ast->attributes.count("\\whitebox")) { - delete ast->attributes.at("\\whitebox"); - ast->attributes.erase("\\whitebox"); + + if (ast->attributes.count("\\blackbox") == 0) { + ast->attributes["\\blackbox"] = AstNode::mkconst_int(1, false); } - ast->attributes["\\blackbox"] = AstNode::mkconst_int(1, false); } ignoreThisSignalsInInitial = RTLIL::SigSpec(); @@ -1024,8 +1083,9 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast current_module->nomeminit = flag_nomeminit; current_module->nomem2reg = flag_nomem2reg; current_module->mem2reg = flag_mem2reg; + current_module->noblackbox = flag_noblackbox; current_module->lib = flag_lib; - current_module->wb = flag_wb; + current_module->nowb = flag_nowb; current_module->noopt = flag_noopt; current_module->icells = flag_icells; current_module->autowire = flag_autowire; @@ -1042,7 +1102,7 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast // create AstModule instances for all modules in the AST tree and add them to 'design' void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil, - bool nolatches, bool nomeminit, bool nomem2reg, bool mem2reg, bool lib, bool wb, bool noopt, bool icells, bool nooverwrite, bool overwrite, bool defer, bool autowire) + bool nolatches, bool nomeminit, bool nomem2reg, bool mem2reg, bool noblackbox, bool lib, bool nowb, bool noopt, bool icells, bool nooverwrite, bool overwrite, bool defer, bool autowire) { current_ast = ast; flag_dump_ast1 = dump_ast1; @@ -1055,8 +1115,9 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump flag_nomeminit = nomeminit; flag_nomem2reg = nomem2reg; flag_mem2reg = mem2reg; + flag_noblackbox = noblackbox; flag_lib = lib; - flag_wb = wb; + flag_nowb = nowb; flag_noopt = noopt; flag_icells = icells; flag_autowire = autowire; @@ -1390,8 +1451,9 @@ std::string AstModule::derive_common(RTLIL::Design *design, dict parameters, bool mayfail) YS_OVERRIDE; RTLIL::IdString derive(RTLIL::Design *design, dict parameters, dict interfaces, dict modports, bool mayfail) YS_OVERRIDE; diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 63b71b800..76da5a97c 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) diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc index 4e2c5abb5..ed6ce2ecb 100644 --- a/frontends/verilog/verilog_frontend.cc +++ b/frontends/verilog/verilog_frontend.cc @@ -145,12 +145,18 @@ struct VerilogFrontend : public Frontend { log(" -nodpi\n"); log(" disable DPI-C support\n"); log("\n"); + log(" -noblackbox\n"); + log(" do not automatically add a (* blackbox *) attribute to an\n"); + log(" empty module.\n"); + log("\n"); log(" -lib\n"); log(" only create empty blackbox modules. This implies -DBLACKBOX.\n"); + log(" modules with the (* whitebox *) attribute will be preserved.\n"); + log(" (* lib_whitebox *) will be treated like (* whitebox *).\n"); log("\n"); - log(" -wb\n"); - log(" like -lib, except do not touch modules with the whitebox\n"); - log(" attribute set. This also implies -DBLACKBOX.\n"); + log(" -nowb\n"); + log(" delete (* whitebox *) and (* lib_whitebox *) attributes from\n"); + log(" all modules.\n"); log("\n"); log(" -noopt\n"); log(" don't perform basic optimizations (such as const folding) in the\n"); @@ -231,8 +237,9 @@ struct VerilogFrontend : public Frontend { formal_mode = false; norestrict_mode = false; assume_asserts_mode = false; + noblackbox_mode = false; lib_mode = false; - wb_mode = false; + nowb_mode = false; default_nettype_wire = true; log_header(design, "Executing Verilog-2005 frontend.\n"); @@ -334,14 +341,17 @@ struct VerilogFrontend : public Frontend { flag_nodpi = true; continue; } - if (arg == "-lib" && !wb_mode) { + if (arg == "-noblackbox") { + noblackbox_mode = true; + continue; + } + if (arg == "-lib") { lib_mode = true; defines_map["BLACKBOX"] = string(); continue; } - if (arg == "-wb" && !lib_mode) { - wb_mode = true; - defines_map["BLACKBOX"] = string(); + if (arg == "-nowb") { + nowb_mode = true; continue; } if (arg == "-noopt") { @@ -439,7 +449,8 @@ struct VerilogFrontend : public Frontend { if (flag_nodpi) error_on_dpi_function(current_ast); - AST::process(design, current_ast, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches, flag_nomeminit, flag_nomem2reg, flag_mem2reg, lib_mode, wb_mode, flag_noopt, flag_icells, flag_nooverwrite, flag_overwrite, flag_defer, default_nettype_wire); + AST::process(design, current_ast, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches, + flag_nomeminit, flag_nomem2reg, flag_mem2reg, noblackbox_mode, lib_mode, nowb_mode, flag_noopt, flag_icells, flag_nooverwrite, flag_overwrite, flag_defer, default_nettype_wire); if (!flag_nopp) delete lexin; diff --git a/frontends/verilog/verilog_frontend.h b/frontends/verilog/verilog_frontend.h index b5cf70c57..ca40946cb 100644 --- a/frontends/verilog/verilog_frontend.h +++ b/frontends/verilog/verilog_frontend.h @@ -69,11 +69,14 @@ namespace VERILOG_FRONTEND // running in -assert-assumes mode extern bool assert_assumes_mode; + // running in -noblackbox mode + extern bool noblackbox_mode; + // running in -lib mode extern bool lib_mode; - // running in -wb mode - extern bool wb_mode; + // running in -nowb mode + extern bool nowb_mode; // lexer input stream extern std::istream *lexin; diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index 122eb1230..40968d17a 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -59,7 +59,7 @@ namespace VERILOG_FRONTEND { std::vector case_type_stack; bool do_not_require_port_stubs; bool default_nettype_wire; - bool sv_mode, formal_mode, lib_mode, wb_mode; + bool sv_mode, formal_mode, noblackbox_mode, lib_mode, nowb_mode; bool noassert_mode, noassume_mode, norestrict_mode; bool assume_asserts_mode, assert_assumes_mode; bool current_wire_rand, current_wire_const; @@ -1906,7 +1906,7 @@ basic_expr: if ($4->substr(0, 1) != "'") frontend_verilog_yyerror("Cast operation must be applied on sized constants e.g. () , while %s is not a sized constant.", $4->c_str()); AstNode *bits = $2; - AstNode *val = const2ast(*$4, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode && !wb_mode); + AstNode *val = const2ast(*$4, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode); if (val == NULL) log_error("Value conversion failed: `%s'\n", $4->c_str()); $$ = new AstNode(AST_TO_BITS, bits, val); @@ -1917,7 +1917,7 @@ basic_expr: frontend_verilog_yyerror("Cast operation must be applied on sized constants, e.g. \'d0, while %s is not a sized constant.", $2->c_str()); AstNode *bits = new AstNode(AST_IDENTIFIER); bits->str = *$1; - AstNode *val = const2ast(*$2, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode && !wb_mode); + AstNode *val = const2ast(*$2, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode); if (val == NULL) log_error("Value conversion failed: `%s'\n", $2->c_str()); $$ = new AstNode(AST_TO_BITS, bits, val); @@ -1925,14 +1925,14 @@ basic_expr: delete $2; } | TOK_CONSTVAL TOK_CONSTVAL { - $$ = const2ast(*$1 + *$2, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode && !wb_mode); + $$ = const2ast(*$1 + *$2, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode); if ($$ == NULL || (*$2)[0] != '\'') log_error("Value conversion failed: `%s%s'\n", $1->c_str(), $2->c_str()); delete $1; delete $2; } | TOK_CONSTVAL { - $$ = const2ast(*$1, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode && !wb_mode); + $$ = const2ast(*$1, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode); if ($$ == NULL) log_error("Value conversion failed: `%s'\n", $1->c_str()); delete $1; diff --git a/kernel/cost.h b/kernel/cost.h index e795b571b..41a09eb63 100644 --- a/kernel/cost.h +++ b/kernel/cost.h @@ -26,7 +26,7 @@ YOSYS_NAMESPACE_BEGIN int get_cell_cost(RTLIL::Cell *cell, dict *mod_cost_cache = nullptr); -int get_cell_cost(RTLIL::IdString type, const dict ¶meters = dict(), +inline int get_cell_cost(RTLIL::IdString type, const dict ¶meters = dict(), RTLIL::Design *design = nullptr, dict *mod_cost_cache = nullptr) { static dict gate_cost = { @@ -76,7 +76,7 @@ int get_cell_cost(RTLIL::IdString type, const dict *mod_cost_cache) +inline int get_cell_cost(RTLIL::Cell *cell, dict *mod_cost_cache) { return get_cell_cost(cell->type, cell->parameters, cell->module->design, mod_cost_cache); } diff --git a/kernel/driver.cc b/kernel/driver.cc index a0bb7e60a..c539ba569 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) { @@ -469,6 +473,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/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 &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(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 all_designs; +std::map *RTLIL::Design::get_all_designs(void) +{ + return &all_designs; +} +#endif + RTLIL::ObjRange RTLIL::Design::modules() { return RTLIL::ObjRange(&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(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 all_modules; +std::map *RTLIL::Module::get_all_modules(void) +{ + return &all_modules; +} +#endif + void RTLIL::Module::makeblackbox() { pool 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(hashidx_, this)); +#endif } +RTLIL::Wire::~Wire() +{ +#ifdef WITH_PYTHON + RTLIL::Wire::get_all_wires()->erase(hashidx_); +#endif +} + +#ifdef WITH_PYTHON +static std::map all_wires; +std::map *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(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(hashidx_, this)); +#endif } +RTLIL::Cell::~Cell() +{ +#ifdef WITH_PYTHON + RTLIL::Cell::get_all_cells()->erase(hashidx_); +#endif +} + +#ifdef WITH_PYTHON +static std::map all_cells; +std::map *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 all_memorys; +std::map *RTLIL::Memory::get_all_memorys(void) +{ + return &all_memorys; +} +#endif YOSYS_NAMESPACE_END - diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 330a81c3b..db5c33c73 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -517,6 +517,7 @@ struct RTLIL::Const Const(RTLIL::State bit, int width = 1); Const(const std::vector &bits) : bits(bits) { flags = CONST_FLAG_NONE; } Const(const std::vector &bits); + Const(const RTLIL::Const &c); bool operator <(const RTLIL::Const &other) const; bool operator ==(const RTLIL::Const &other) const; @@ -595,6 +596,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; @@ -619,6 +621,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; @@ -940,9 +943,13 @@ struct RTLIL::Design } } + std::vector selected_modules() const; std::vector selected_whole_modules() const; std::vector selected_whole_modules_warn() const; +#ifdef WITH_PYTHON + static std::map *get_all_designs(void); +#endif }; struct RTLIL::Module : public RTLIL::AttrObject @@ -1199,6 +1206,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 *get_all_modules(void); +#endif }; struct RTLIL::Wire : public RTLIL::AttrObject @@ -1210,7 +1221,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 @@ -1221,6 +1232,10 @@ public: RTLIL::IdString name; int width, start_offset, port_id; bool port_input, port_output, upto; + +#ifdef WITH_PYTHON + static std::map *get_all_wires(void); +#endif }; struct RTLIL::Memory : public RTLIL::AttrObject @@ -1232,6 +1247,10 @@ struct RTLIL::Memory : public RTLIL::AttrObject RTLIL::IdString name; int width, start_offset, size; +#ifdef WITH_PYTHON + ~Memory(); + static std::map *get_all_memorys(void); +#endif }; struct RTLIL::Cell : public RTLIL::AttrObject @@ -1243,6 +1262,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 @@ -1283,6 +1303,10 @@ public: } template void rewrite_sigspecs(T &functor); + +#ifdef WITH_PYTHON + static std::map *get_all_cells(void); +#endif }; struct RTLIL::CaseRule @@ -1343,6 +1367,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 #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 #include @@ -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 #include +#ifdef WITH_PYTHON +#include +#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 pushed_designs; // from passes/cmds/pluginc.cc extern std::map loaded_plugins; +#ifdef WITH_PYTHON +extern std::map loaded_python_plugins; +#endif extern std::map loaded_plugin_aliases; void load_plugin(std::string filename, std::vector 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 #endif +#ifdef WITH_PYTHON +# include +# include +# include +#endif + YOSYS_NAMESPACE_BEGIN std::map loaded_plugins; +#ifdef WITH_PYTHON +std::map loaded_python_plugins; +#endif std::map loaded_plugin_aliases; #ifdef YOSYS_ENABLE_PLUGINS @@ -36,7 +45,35 @@ void load_plugin(std::string filename, std::vector 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 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/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(action)); continue; diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc index ee319b6e6..1a4318460 100644 --- a/passes/techmap/techmap.cc +++ b/passes/techmap/techmap.cc @@ -1036,7 +1036,7 @@ struct TechmapPass : public Pass { simplemap_get_mappers(worker.simplemap_mappers); std::vector map_files; - std::string verilog_frontend = "verilog -nooverwrite"; + std::string verilog_frontend = "verilog -nooverwrite -noblackbox"; int max_iter = -1; size_t argidx; 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 +# +# 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").replace("SigSig", "std::pair") + 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 + * + * 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 +#include +#include +#include +#include +#include + +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"); + 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 + * + * 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 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 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 62a28364b..00843b97c 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 e84a6714b..1449e792f 100644 --- a/techlibs/xilinx/synth_xilinx.cc +++ b/techlibs/xilinx/synth_xilinx.cc @@ -132,6 +132,7 @@ struct SynthXilinxPass : public Pass log(" techmap -map +/xilinx/lut_map.v -map +/xilinx/ff_map.v -map +/xilinx/cells_map.v"); log(" dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT -ff FDSE Q INIT \\\n"); log(" -ff FDRE_1 Q INIT -ff FDCE_1 Q INIT -ff FDPE_1 Q INIT -ff FDSE_1 Q INIT\n"); + log(" clean\n"); log("\n"); log(" check:\n"); log(" hierarchy -check\n"); @@ -309,6 +310,7 @@ struct SynthXilinxPass : public Pass Pass::call(design, "techmap -map +/xilinx/lut_map.v -map +/xilinx/ff_map.v -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"); } if (check_label(active, run_from, run_to, "check")) 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/tools/autotest.sh b/tests/tools/autotest.sh index f3dac504e..bb9c3bfb5 100755 --- a/tests/tools/autotest.sh +++ b/tests/tools/autotest.sh @@ -7,7 +7,7 @@ use_modelsim=false verbose=false keeprunning=false makejmode=false -frontend="verilog" +frontend="verilog -noblackbox" backend_opts="-noattr -noexpr -siminit" autotb_opts="" include_opts="" @@ -137,7 +137,7 @@ do egrep -v '^\s*`timescale' ../$fn > ${bn}_ref.${ext} else "$toolsdir"/../../yosys -f "$frontend $include_opts" -b "verilog" -o ${bn}_ref.v ../${fn} - frontend="verilog" + frontend="verilog -noblackbox" fi if [ ! -f ../${bn}_tb.v ]; then diff --git a/tests/various/hierarchy.sh b/tests/various/hierarchy.sh index d33a247be..9dbd1c89f 100644 --- a/tests/various/hierarchy.sh +++ b/tests/various/hierarchy.sh @@ -53,6 +53,7 @@ echo -n " no explicit top - " module noTop(a, y); input a; output [31:0] y; + assign y = a; endmodule EOV hierarchy -auto-top