diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index d81e340aa..41d9183fc 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -4,7 +4,7 @@ name: Build Wheels for PyPI on: workflow_dispatch: schedule: - - cron: '0 10 * * 0' + - cron: "0 10 * * 0" jobs: build_wheels: @@ -54,9 +54,6 @@ jobs: fetch-depth: 0 submodules: true persist-credentials: false - - if: ${{ matrix.os.family == 'linux' }} - name: "[Linux] Set up QEMU" - uses: docker/setup-qemu-action@v3 - uses: actions/setup-python@v5 - name: Get Boost Source shell: bash @@ -68,14 +65,19 @@ jobs: run: | mkdir -p ffi curl -L https://github.com/libffi/libffi/releases/download/v3.4.6/libffi-3.4.6.tar.gz | tar --strip-components=1 -xzC ffi + - if: ${{ matrix.os.family == 'linux' }} + name: "[Linux] Bison 3.8.2" + shell: bash + run: | + mkdir -p bison + curl -L https://ftpmirror.gnu.org/gnu/bison/bison-3.8.2.tar.gz | tar --strip-components=1 -xzC bison ## Software installed by default in GitHub Action Runner VMs: ## https://github.com/actions/runner-images - if: ${{ matrix.os.family == 'macos' }} name: "[macOS] Flex/Bison" run: | brew install flex bison - echo "PATH=$(brew --prefix flex)/bin:$PATH" >> $GITHUB_ENV - echo "PATH=$(brew --prefix bison)/bin:$PATH" >> $GITHUB_ENV + echo "PATH=$(brew --prefix flex)/bin:$(brew --prefix bison)/bin:$PATH" >> $GITHUB_ENV - if: ${{ matrix.os.family == 'windows' }} name: "[Windows] Flex/Bison" run: | @@ -100,16 +102,20 @@ jobs: CIBW_MANYLINUX_AARCH64_IMAGE: manylinux_2_28 CIBW_BEFORE_ALL: bash ./.github/workflows/wheels/cibw_before_all.sh CIBW_ENVIRONMENT: > + OPTFLAGS=-O3 CXXFLAGS=-I./boost/pfx/include LINKFLAGS=-L./boost/pfx/lib PKG_CONFIG_PATH=./ffi/pfx/lib/pkgconfig makeFlags='BOOST_PYTHON_LIB=./boost/pfx/lib/libboost_python*.a' + PATH="$PWD/bison/src:$PATH" CIBW_ENVIRONMENT_MACOS: > + OPTFLAGS=-O3 CXXFLAGS=-I./boost/pfx/include LINKFLAGS=-L./boost/pfx/lib PKG_CONFIG_PATH=./ffi/pfx/lib/pkgconfig MACOSX_DEPLOYMENT_TARGET=11 makeFlags='BOOST_PYTHON_LIB=./boost/pfx/lib/libboost_python*.a CONFIG=clang' + PATH="$PWD/bison/src:$PATH" CIBW_BEFORE_BUILD: bash ./.github/workflows/wheels/cibw_before_build.sh CIBW_TEST_COMMAND: python3 {project}/tests/arch/ecp5/add_sub.py - uses: actions/upload-artifact@v4 diff --git a/.github/workflows/wheels/_run_cibw_linux.py b/.github/workflows/wheels/_run_cibw_linux.py index 894470a5a..fb4e0b839 100644 --- a/.github/workflows/wheels/_run_cibw_linux.py +++ b/.github/workflows/wheels/_run_cibw_linux.py @@ -20,11 +20,19 @@ import os import yaml import platform import subprocess +from pathlib import Path -__dir__ = os.path.dirname(os.path.abspath(__file__)) +__yosys_root__ = Path(__file__).absolute().parents[3] +for source in ["boost", "ffi", "bison"]: + if not (__yosys_root__ / source).is_dir(): + print( + "You need to download boost, ffi and bison in a similar manner to wheels.yml first." + ) + exit(-1) -workflow = yaml.safe_load(open(os.path.join(os.path.dirname(__dir__), "wheels.yml"))) +with open(__yosys_root__ / ".github" / "workflows" / "wheels.yml") as f: + workflow = yaml.safe_load(f) env = os.environ.copy() @@ -40,5 +48,5 @@ for key, value in cibw_step["env"].items(): continue env[key] = value -env["CIBW_ARCHS"] = os.getenv("CIBW_ARCHS") or platform.machine() +env["CIBW_ARCHS"] = os.getenv("CIBW_ARCHS", platform.machine()) subprocess.check_call(["cibuildwheel"], env=env) diff --git a/.github/workflows/wheels/cibw_before_all.sh b/.github/workflows/wheels/cibw_before_all.sh index fbb8dcad8..1aef650d7 100644 --- a/.github/workflows/wheels/cibw_before_all.sh +++ b/.github/workflows/wheels/cibw_before_all.sh @@ -1,23 +1,37 @@ -set -e -set -x +#!/bin/bash +set -e -x # Build-time dependencies ## Linux Docker Images if command -v yum &> /dev/null; then - yum install -y flex bison + yum install -y flex # manylinux's bison versions are hopelessly out of date fi if command -v apk &> /dev/null; then apk add flex bison fi +if ! printf '%s\n' '%require "3.8"' '%%' 'start: ;' | bison -o /dev/null /dev/stdin ; then + ( + set -e -x + cd bison + ./configure + make clean + make install -j$(getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n hw.ncpu) + ) +fi + ## macOS/Windows -- installed in GitHub Action itself, not container -# Build Static FFI (platform-dependent but not Python version dependent) -cd ffi -## Ultimate libyosys.so will be shared, so we need fPIC for the static libraries -CFLAGS=-fPIC CXXFLAGS=-fPIC ./configure --prefix=$PWD/pfx -## Without this, SHELL has a space in its path which breaks the makefile -make install -j$(getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n hw.ncpu) -## Forces static library to be used in all situations -sed -i.bak 's@-L${toolexeclibdir} -lffi@${toolexeclibdir}/libffi.a@' ./pfx/lib/pkgconfig/libffi.pc +# Runtime Dependencies +## Build Static FFI (platform-dependent but not Python version dependent) +( + set -e -x + cd ffi + ## Ultimate libyosys.so will be shared, so we need fPIC for the static libraries + CFLAGS=-fPIC CXXFLAGS=-fPIC ./configure --prefix=$PWD/pfx + make clean + make install -j$(getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n hw.ncpu) + ## Forces static library to be used in all situations + sed -i.bak 's@-L${toolexeclibdir} -lffi@${toolexeclibdir}/libffi.a@' ./pfx/lib/pkgconfig/libffi.pc +) diff --git a/.gitignore b/.gitignore index 0fafa39af..60340146e 100644 --- a/.gitignore +++ b/.gitignore @@ -73,6 +73,7 @@ __pycache__ /boost /ffi /doxygen +/bison /venv /*.whl /*.egg-info diff --git a/Makefile b/Makefile index ded12df44..dfb5cfb81 100644 --- a/Makefile +++ b/Makefile @@ -175,7 +175,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.56+105 +YOSYS_VER := 0.56+165 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'.' -f3) @@ -376,7 +376,7 @@ BOOST_PYTHON_LIB ?= $(shell \ # Inside CentOS 7 ifeq (${PLATFORM},centos7) -BOOST_PYTHON_LIB = -L/opt/boost/lib -lboost_python38 -lboost_system -lboost_filesystem +BOOST_PYTHON_LIB = -L/opt/boost/lib -lboost_python38 CXXFLAGS += -I/opt/boost/include endif @@ -384,7 +384,7 @@ ifeq ($(BOOST_PYTHON_LIB),) $(error BOOST_PYTHON_LIB could not be detected. Please define manually) endif -LIBS += $(BOOST_PYTHON_LIB) +LIBS += $(BOOST_PYTHON_LIB) -lboost_filesystem PY_WRAPPER_FILE = kernel/python_wrappers OBJS += $(PY_WRAPPER_FILE).o PY_GEN_SCRIPT= py_wrap_generator diff --git a/backends/btor/btor.cc b/backends/btor/btor.cc index bfd293557..1daf68c9d 100644 --- a/backends/btor/btor.cc +++ b/backends/btor/btor.cc @@ -129,7 +129,7 @@ struct BtorWorker std::replace(src.begin(), src.end(), ' ', '_'); if (srcsymbols.count(src) || module->count_id("\\" + src)) { for (int i = 1;; i++) { - string s = stringf("%s-%d", src.c_str(), i); + string s = stringf("%s-%d", src, i); if (!srcsymbols.count(s) && !module->count_id("\\" + s)) { src = s; break; @@ -192,7 +192,7 @@ struct BtorWorker void btorf_push(const string &id) { if (verbose) { - f << indent << stringf(" ; begin %s\n", id.c_str()); + f << indent << stringf(" ; begin %s\n", id); indent += " "; } } @@ -201,7 +201,7 @@ struct BtorWorker { if (verbose) { indent = indent.substr(4); - f << indent << stringf(" ; end %s\n", id.c_str()); + f << indent << stringf(" ; end %s\n", id); } } diff --git a/backends/firrtl/firrtl.cc b/backends/firrtl/firrtl.cc index e4254f85a..7c9feebb1 100644 --- a/backends/firrtl/firrtl.cc +++ b/backends/firrtl/firrtl.cc @@ -253,7 +253,7 @@ void emit_extmodule(RTLIL::Cell *cell, RTLIL::Module *mod_instance, std::ostream const std::string extmoduleFileinfo = getFileinfo(cell); // Emit extmodule header. - f << stringf(" extmodule %s: %s\n", exported_name.c_str(), extmoduleFileinfo.c_str()); + f << stringf(" extmodule %s: %s\n", exported_name, extmoduleFileinfo); // Emit extmodule ports. for (auto wire : mod_instance->wires()) @@ -280,7 +280,7 @@ void emit_extmodule(RTLIL::Cell *cell, RTLIL::Module *mod_instance, std::ostream // Emit extmodule "defname" field. This is the name of the verilog blackbox // that is used when verilog is emitted, so we use the name of mod_instance // here. - f << stringf("%sdefname = %s\n", indent.c_str(), blackbox_name.c_str()); + f << stringf("%sdefname = %s\n", indent, blackbox_name); // Emit extmodule generic parameters. for (const auto &p : cell->parameters) @@ -301,7 +301,7 @@ void emit_extmodule(RTLIL::Cell *cell, RTLIL::Module *mod_instance, std::ostream param_name.end() ); - f << stringf("%sparameter %s = %s\n", indent.c_str(), param_name.c_str(), param_value.c_str()); + f << stringf("%sparameter %s = %s\n", indent, param_name, param_value); } f << "\n"; @@ -417,7 +417,7 @@ struct FirrtlWorker else { string wire_id = make_id(chunk.wire->name); - new_expr = stringf("bits(%s, %d, %d)", wire_id.c_str(), chunk.offset + chunk.width - 1, chunk.offset); + new_expr = stringf("bits(%s, %d, %d)", wire_id, chunk.offset + chunk.width - 1, chunk.offset); } if (expr.empty()) @@ -477,7 +477,7 @@ struct FirrtlWorker instanceOf; std::string cellFileinfo = getFileinfo(cell); - wire_exprs.push_back(stringf("%s" "inst %s%s of %s %s", indent.c_str(), cell_name.c_str(), cell_name_comment.c_str(), instanceName.c_str(), cellFileinfo.c_str())); + wire_exprs.push_back(stringf("%s" "inst %s%s of %s %s", indent, cell_name, cell_name_comment, instanceName, cellFileinfo)); for (auto it = cell->connections().begin(); it != cell->connections().end(); ++it) { if (it->second.size() > 0) { @@ -518,7 +518,7 @@ struct FirrtlWorker // as part of the coalesced subfield assignments for this wire. register_reverse_wire_map(sourceExpr, *sinkSig); } else { - wire_exprs.push_back(stringf("\n%s%s <= %s %s", indent.c_str(), sinkExpr.c_str(), sourceExpr.c_str(), cellFileinfo.c_str())); + wire_exprs.push_back(stringf("\n%s%s <= %s %s", indent, sinkExpr, sourceExpr, cellFileinfo)); } } } @@ -535,7 +535,7 @@ struct FirrtlWorker int max_shift_width_bits = FIRRTL_MAX_DSH_WIDTH_ERROR - 1; string max_shift_string = stringf("UInt<%d>(%d)", max_shift_width_bits, (1<name), moduleFileinfo.c_str()); + f << stringf(" module %s: %s\n", make_id(module->name), moduleFileinfo); vector port_decls, wire_decls, mem_exprs, cell_exprs, wire_exprs; std::vector memories = Mem::get_all_memories(module); @@ -602,7 +602,7 @@ struct FirrtlWorker if (cell->type.in(ID($not), ID($logic_not), ID($_NOT_), ID($neg), ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_bool), ID($reduce_xnor))) { string a_expr = make_expr(cell->getPort(ID::A)); - wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent.c_str(), y_id.c_str(), y_width, cellFileinfo.c_str())); + wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent, y_id, y_width, cellFileinfo)); if (a_signed) { a_expr = "asSInt(" + a_expr + ")"; @@ -610,7 +610,7 @@ struct FirrtlWorker // Don't use the results of logical operations (a single bit) to control padding if (!(cell->type.in(ID($eq), ID($eqx), ID($gt), ID($ge), ID($lt), ID($le), ID($ne), ID($nex), ID($reduce_bool), ID($logic_not)) && y_width == 1) ) { - a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width); + a_expr = stringf("pad(%s, %d)", a_expr, y_width); } // Assume the FIRRTL width is a single bit. @@ -622,27 +622,27 @@ struct FirrtlWorker firrtl_width = a_width; } else if (cell->type == ID($logic_not)) { primop = "eq"; - a_expr = stringf("%s, UInt(0)", a_expr.c_str()); + a_expr = stringf("%s, UInt(0)", a_expr); } else if (cell->type == ID($reduce_and)) primop = "andr"; else if (cell->type == ID($reduce_or)) primop = "orr"; else if (cell->type == ID($reduce_xor)) primop = "xorr"; else if (cell->type == ID($reduce_xnor)) { primop = "not"; - a_expr = stringf("xorr(%s)", a_expr.c_str()); + a_expr = stringf("xorr(%s)", a_expr); } else if (cell->type == ID($reduce_bool)) { primop = "neq"; // Use the sign of the a_expr and its width as the type (UInt/SInt) and width of the comparand. - a_expr = stringf("%s, %cInt<%d>(0)", a_expr.c_str(), a_signed ? 'S' : 'U', a_width); + a_expr = stringf("%s, %cInt<%d>(0)", a_expr, a_signed ? 'S' : 'U', a_width); } - string expr = stringf("%s(%s)", primop.c_str(), a_expr.c_str()); + string expr = stringf("%s(%s)", primop, a_expr); if ((firrtl_is_signed && !always_uint)) - expr = stringf("asUInt(%s)", expr.c_str()); + expr = stringf("asUInt(%s)", expr); - cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); + cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent, y_id, expr, cellFileinfo)); register_reverse_wire_map(y_id, cell->getPort(ID::Y)); continue; @@ -654,13 +654,13 @@ struct FirrtlWorker string a_expr = make_expr(cell->getPort(ID::A)); string b_expr = make_expr(cell->getPort(ID::B)); std::string cellFileinfo = getFileinfo(cell); - wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent.c_str(), y_id.c_str(), y_width, cellFileinfo.c_str())); + wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent, y_id, y_width, cellFileinfo)); if (a_signed) { a_expr = "asSInt(" + a_expr + ")"; // Expand the "A" operand to the result width if (a_width < y_width) { - a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width); + a_expr = stringf("pad(%s, %d)", a_expr, y_width); a_width = y_width; } } @@ -670,7 +670,7 @@ struct FirrtlWorker b_expr = "asSInt(" + b_expr + ")"; // Expand the "B" operand to the result width if (b_width < y_width) { - b_expr = stringf("pad(%s, %d)", b_expr.c_str(), y_width); + b_expr = stringf("pad(%s, %d)", b_expr, y_width); b_width = y_width; } } @@ -680,11 +680,11 @@ struct FirrtlWorker if (cell->type.in(ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($xor), ID($_XOR_), ID($xnor), ID($and), ID($_AND_), ID($or), ID($_OR_))) { if (a_width < y_width) { - a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width); + a_expr = stringf("pad(%s, %d)", a_expr, y_width); a_width = y_width; } if (b_width < y_width) { - b_expr = stringf("pad(%s, %d)", b_expr.c_str(), y_width); + b_expr = stringf("pad(%s, %d)", b_expr, y_width); b_width = y_width; } } @@ -856,23 +856,23 @@ struct FirrtlWorker string expr; // Deal with $xnor == ~^ (not xor) if (primop == "xnor") { - expr = stringf("not(xor(%s, %s))", a_expr.c_str(), b_expr.c_str()); + expr = stringf("not(xor(%s, %s))", a_expr, b_expr); } else { - expr = stringf("%s(%s, %s)", primop.c_str(), a_expr.c_str(), b_expr.c_str()); + expr = stringf("%s(%s, %s)", primop, a_expr, b_expr); } // Deal with FIRRTL's "shift widens" semantics, or the need to widen the FIRRTL result. // If the operation is signed, the FIRRTL width will be 1 one bit larger. if (extract_y_bits) { - expr = stringf("bits(%s, %d, 0)", expr.c_str(), y_width - 1); + expr = stringf("bits(%s, %d, 0)", expr, y_width - 1); } else if (firrtl_is_signed && (firrtl_width + 1) < y_width) { - expr = stringf("pad(%s, %d)", expr.c_str(), y_width); + expr = stringf("pad(%s, %d)", expr, y_width); } if ((firrtl_is_signed && !always_uint)) - expr = stringf("asUInt(%s)", expr.c_str()); + expr = stringf("asUInt(%s)", expr); - cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); + cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent, y_id, expr, cellFileinfo)); register_reverse_wire_map(y_id, cell->getPort(ID::Y)); continue; @@ -887,9 +887,9 @@ struct FirrtlWorker string s_expr = make_expr(cell->getPort(ID::S)); wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent.c_str(), y_id.c_str(), width, cellFileinfo.c_str())); - string expr = stringf("mux(%s, %s, %s)", s_expr.c_str(), b_expr.c_str(), a_expr.c_str()); + string expr = stringf("mux(%s, %s, %s)", s_expr, b_expr, a_expr); - cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); + cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent, y_id, expr, cellFileinfo)); register_reverse_wire_map(y_id, cell->getPort(ID::Y)); continue; @@ -911,9 +911,9 @@ struct FirrtlWorker string expr = make_expr(cell->getPort(ID::D)); string clk_expr = "asClock(" + make_expr(cell->getPort(ID::CLK)) + ")"; - wire_decls.push_back(stringf("%sreg %s: UInt<%d>, %s %s\n", indent.c_str(), y_id.c_str(), width, clk_expr.c_str(), cellFileinfo.c_str())); + wire_decls.push_back(stringf("%sreg %s: UInt<%d>, %s %s\n", indent, y_id, width, clk_expr, cellFileinfo)); - cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); + cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent, y_id, expr, cellFileinfo)); register_reverse_wire_map(y_id, cell->getPort(ID::Q)); continue; @@ -934,7 +934,7 @@ struct FirrtlWorker int b_sign = cell->parameters.at(ID::B_WIDTH).as_int() - 1; b_expr = stringf("validif(not(bits(%s, %d, %d)), %s)", b_string, b_sign, b_sign, b_string); } - string expr = stringf("dshr(%s, %s)", a_expr.c_str(), b_expr.c_str()); + string expr = stringf("dshr(%s, %s)", a_expr, b_expr); cell_exprs.push_back(stringf("%s%s <= %s\n", indent.c_str(), y_id.c_str(), expr.c_str())); register_reverse_wire_map(y_id, cell->getPort(ID::Y)); @@ -973,7 +973,7 @@ struct FirrtlWorker // Verilog appears to treat the result as signed, so if the result is wider than "A", // we need to pad. if (a_width < y_width) { - a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width); + a_expr = stringf("pad(%s, %d)", a_expr, y_width); } wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent.c_str(), y_id.c_str(), y_width)); cell_exprs.push_back(stringf("%s%s <= %s\n", indent.c_str(), y_id.c_str(), a_expr.c_str())); diff --git a/backends/intersynth/intersynth.cc b/backends/intersynth/intersynth.cc index dcf107de3..4c098e019 100644 --- a/backends/intersynth/intersynth.cc +++ b/backends/intersynth/intersynth.cc @@ -172,7 +172,7 @@ struct IntersynthBackend : public Backend { if (sig.size() != 0) { conntypes_code.insert(stringf("conntype b%d %d 2 %d\n", sig.size(), sig.size(), sig.size())); celltype_code += stringf(" b%d %s%s", sig.size(), ct.cell_output(cell->type, port.first) ? "*" : "", log_id(port.first)); - node_code += stringf(" %s %s", log_id(port.first), netname(conntypes_code, celltypes_code, constcells_code, sig).c_str()); + node_code += stringf(" %s %s", log_id(port.first), netname(conntypes_code, celltypes_code, constcells_code, sig)); } } for (auto ¶m : cell->parameters) { @@ -199,13 +199,13 @@ struct IntersynthBackend : public Backend { if (!flag_notypes) { *f << stringf("### Connection Types\n"); for (auto code : conntypes_code) - *f << stringf("%s", code.c_str()); + *f << stringf("%s", code); *f << stringf("\n### Cell Types\n"); for (auto code : celltypes_code) - *f << stringf("%s", code.c_str()); + *f << stringf("%s", code); } *f << stringf("\n### Netlists\n"); - *f << stringf("%s", netlists_code.c_str()); + *f << stringf("%s", netlists_code); for (auto lib : libs) delete lib; diff --git a/backends/simplec/simplec.cc b/backends/simplec/simplec.cc index e70c62a71..1ab586e43 100644 --- a/backends/simplec/simplec.cc +++ b/backends/simplec/simplec.cc @@ -218,8 +218,8 @@ struct SimplecWorker s[i] -= 'a' - 'A'; util_declarations.push_back(""); - util_declarations.push_back(stringf("#ifndef %s", s.c_str())); - util_declarations.push_back(stringf("#define %s", s.c_str())); + util_declarations.push_back(stringf("#ifndef %s", s)); + util_declarations.push_back(stringf("#define %s", s)); } string util_get_bit(const string &signame, int n, int idx) @@ -232,33 +232,33 @@ struct SimplecWorker if (generated_utils.count(util_name) == 0) { util_ifdef_guard(util_name); - util_declarations.push_back(stringf("static inline bool %s(const %s *sig)", util_name.c_str(), sigtype(n).c_str())); + util_declarations.push_back(stringf("static inline bool %s(const %s *sig)", util_name, sigtype(n))); util_declarations.push_back(stringf("{")); int word_idx = idx / max_uintsize, word_offset = idx % max_uintsize; string value_name = stringf("value_%d_%d", std::min(n-1, (word_idx+1)*max_uintsize-1), word_idx*max_uintsize); - util_declarations.push_back(stringf(" return (sig->%s >> %d) & 1;", value_name.c_str(), word_offset)); + util_declarations.push_back(stringf(" return (sig->%s >> %d) & 1;", value_name, word_offset)); util_declarations.push_back(stringf("}")); util_declarations.push_back(stringf("#endif")); generated_utils.insert(util_name); } - return stringf("%s(&%s)", util_name.c_str(), signame.c_str()); + return stringf("%s(&%s)", util_name, signame); } string util_set_bit(const string &signame, int n, int idx, const string &expr) { if (n == 1 && idx == 0) - return stringf(" %s.value_0_0 = %s;", signame.c_str(), expr.c_str()); + return stringf(" %s.value_0_0 = %s;", signame, expr); string util_name = stringf("yosys_simplec_set_bit_%d_of_%d", idx, n); if (generated_utils.count(util_name) == 0) { util_ifdef_guard(util_name); - util_declarations.push_back(stringf("static inline void %s(%s *sig, bool value)", util_name.c_str(), sigtype(n).c_str())); + util_declarations.push_back(stringf("static inline void %s(%s *sig, bool value)", util_name, sigtype(n))); util_declarations.push_back(stringf("{")); int word_idx = idx / max_uintsize, word_offset = idx % max_uintsize; @@ -266,9 +266,9 @@ struct SimplecWorker #if 0 util_declarations.push_back(stringf(" if (value)")); - util_declarations.push_back(stringf(" sig->%s |= 1UL << %d;", value_name.c_str(), word_offset)); + util_declarations.push_back(stringf(" sig->%s |= 1UL << %d;", value_name, word_offset)); util_declarations.push_back(stringf(" else")); - util_declarations.push_back(stringf(" sig->%s &= ~(1UL << %d);", value_name.c_str(), word_offset)); + util_declarations.push_back(stringf(" sig->%s &= ~(1UL << %d);", value_name, word_offset)); #else util_declarations.push_back(stringf(" sig->%s = (sig->%s & ~((uint%d_t)1 << %d)) | ((uint%d_t)value << %d);", value_name.c_str(), value_name.c_str(), max_uintsize, word_offset, max_uintsize, word_offset)); @@ -279,7 +279,7 @@ struct SimplecWorker generated_utils.insert(util_name); } - return stringf(" %s(&%s, %s);", util_name.c_str(), signame.c_str(), expr.c_str()); + return stringf(" %s(&%s, %s);", util_name, signame, expr); } void create_module_struct(Module *mod) @@ -339,38 +339,38 @@ struct SimplecWorker for (int i = 0; i < GetSize(topo.sorted); i++) topoidx[mod->cell(topo.sorted[i])] = i; - string ifdef_name = stringf("yosys_simplec_%s_state_t", cid(mod->name).c_str()); + string ifdef_name = stringf("yosys_simplec_%s_state_t", cid(mod->name)); for (int i = 0; i < GetSize(ifdef_name); i++) if ('a' <= ifdef_name[i] && ifdef_name[i] <= 'z') ifdef_name[i] -= 'a' - 'A'; struct_declarations.push_back(""); - struct_declarations.push_back(stringf("#ifndef %s", ifdef_name.c_str())); - struct_declarations.push_back(stringf("#define %s", ifdef_name.c_str())); - struct_declarations.push_back(stringf("struct %s_state_t", cid(mod->name).c_str())); + struct_declarations.push_back(stringf("#ifndef %s", ifdef_name)); + struct_declarations.push_back(stringf("#define %s", ifdef_name)); + struct_declarations.push_back(stringf("struct %s_state_t", cid(mod->name))); struct_declarations.push_back("{"); struct_declarations.push_back(" // Input Ports"); for (Wire *w : mod->wires()) if (w->port_input) - struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width).c_str(), cid(w->name).c_str(), log_id(w))); + struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width), cid(w->name), log_id(w))); struct_declarations.push_back(""); struct_declarations.push_back(" // Output Ports"); for (Wire *w : mod->wires()) if (!w->port_input && w->port_output) - struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width).c_str(), cid(w->name).c_str(), log_id(w))); + struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width), cid(w->name), log_id(w))); struct_declarations.push_back(""); struct_declarations.push_back(" // Internal Wires"); for (Wire *w : mod->wires()) if (!w->port_input && !w->port_output) - struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width).c_str(), cid(w->name).c_str(), log_id(w))); + struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width), cid(w->name), log_id(w))); for (Cell *c : mod->cells()) if (design->module(c->type)) - struct_declarations.push_back(stringf(" struct %s_state_t %s; // %s", cid(c->type).c_str(), cid(c->name).c_str(), log_id(c))); + struct_declarations.push_back(stringf(" struct %s_state_t %s; // %s", cid(c->type), cid(c->name), log_id(c))); struct_declarations.push_back(stringf("};")); struct_declarations.push_back("#endif"); @@ -407,14 +407,14 @@ struct SimplecWorker string b_expr = b.wire ? util_get_bit(work->prefix + cid(b.wire->name), b.wire->width, b.offset) : b.data ? "1" : "0"; string expr; - if (cell->type == ID($_AND_)) expr = stringf("%s & %s", a_expr.c_str(), b_expr.c_str()); - if (cell->type == ID($_NAND_)) expr = stringf("!(%s & %s)", a_expr.c_str(), b_expr.c_str()); - if (cell->type == ID($_OR_)) expr = stringf("%s | %s", a_expr.c_str(), b_expr.c_str()); - if (cell->type == ID($_NOR_)) expr = stringf("!(%s | %s)", a_expr.c_str(), b_expr.c_str()); - if (cell->type == ID($_XOR_)) expr = stringf("%s ^ %s", a_expr.c_str(), b_expr.c_str()); - if (cell->type == ID($_XNOR_)) expr = stringf("!(%s ^ %s)", a_expr.c_str(), b_expr.c_str()); - if (cell->type == ID($_ANDNOT_)) expr = stringf("%s & (!%s)", a_expr.c_str(), b_expr.c_str()); - if (cell->type == ID($_ORNOT_)) expr = stringf("%s | (!%s)", a_expr.c_str(), b_expr.c_str()); + if (cell->type == ID($_AND_)) expr = stringf("%s & %s", a_expr, b_expr); + if (cell->type == ID($_NAND_)) expr = stringf("!(%s & %s)", a_expr, b_expr); + if (cell->type == ID($_OR_)) expr = stringf("%s | %s", a_expr, b_expr); + if (cell->type == ID($_NOR_)) expr = stringf("!(%s | %s)", a_expr, b_expr); + if (cell->type == ID($_XOR_)) expr = stringf("%s ^ %s", a_expr, b_expr); + if (cell->type == ID($_XNOR_)) expr = stringf("!(%s ^ %s)", a_expr, b_expr); + if (cell->type == ID($_ANDNOT_)) expr = stringf("%s & (!%s)", a_expr, b_expr); + if (cell->type == ID($_ORNOT_)) expr = stringf("%s | (!%s)", a_expr, b_expr); log_assert(y.wire); funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) + @@ -436,8 +436,8 @@ struct SimplecWorker string c_expr = c.wire ? util_get_bit(work->prefix + cid(c.wire->name), c.wire->width, c.offset) : c.data ? "1" : "0"; string expr; - if (cell->type == ID($_AOI3_)) expr = stringf("!((%s & %s) | %s)", a_expr.c_str(), b_expr.c_str(), c_expr.c_str()); - if (cell->type == ID($_OAI3_)) expr = stringf("!((%s | %s) & %s)", a_expr.c_str(), b_expr.c_str(), c_expr.c_str()); + if (cell->type == ID($_AOI3_)) expr = stringf("!((%s & %s) | %s)", a_expr, b_expr, c_expr); + if (cell->type == ID($_OAI3_)) expr = stringf("!((%s | %s) & %s)", a_expr, b_expr, c_expr); log_assert(y.wire); funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) + @@ -461,8 +461,8 @@ struct SimplecWorker string d_expr = d.wire ? util_get_bit(work->prefix + cid(d.wire->name), d.wire->width, d.offset) : d.data ? "1" : "0"; string expr; - if (cell->type == ID($_AOI4_)) expr = stringf("!((%s & %s) | (%s & %s))", a_expr.c_str(), b_expr.c_str(), c_expr.c_str(), d_expr.c_str()); - if (cell->type == ID($_OAI4_)) expr = stringf("!((%s | %s) & (%s | %s))", a_expr.c_str(), b_expr.c_str(), c_expr.c_str(), d_expr.c_str()); + if (cell->type == ID($_AOI4_)) expr = stringf("!((%s & %s) | (%s & %s))", a_expr, b_expr, c_expr, d_expr); + if (cell->type == ID($_OAI4_)) expr = stringf("!((%s | %s) & (%s | %s))", a_expr, b_expr, c_expr, d_expr); log_assert(y.wire); funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) + @@ -484,9 +484,9 @@ struct SimplecWorker string s_expr = s.wire ? util_get_bit(work->prefix + cid(s.wire->name), s.wire->width, s.offset) : s.data ? "1" : "0"; // casts to bool are a workaround for CBMC bug (https://github.com/diffblue/cbmc/issues/933) - string expr = stringf("%s ? %s(bool)%s : %s(bool)%s", s_expr.c_str(), - cell->type == ID($_NMUX_) ? "!" : "", b_expr.c_str(), - cell->type == ID($_NMUX_) ? "!" : "", a_expr.c_str()); + string expr = stringf("%s ? %s(bool)%s : %s(bool)%s", s_expr, + cell->type == ID($_NMUX_) ? "!" : "", b_expr, + cell->type == ID($_NMUX_) ? "!" : "", a_expr); log_assert(y.wire); funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) + @@ -518,7 +518,7 @@ struct SimplecWorker continue; if (verbose) log(" Propagating %s.%s[%d:%d].\n", work->log_prefix.c_str(), log_id(chunk.wire), chunk.offset+chunk.width-1, chunk.offset); - funct_declarations.push_back(stringf(" // Updated signal in %s: %s", work->log_prefix.c_str(), log_signal(chunk))); + funct_declarations.push_back(stringf(" // Updated signal in %s: %s", work->log_prefix, log_signal(chunk))); } for (SigBit bit : dirtysig) @@ -636,7 +636,7 @@ struct SimplecWorker reactivated_cells.clear(); funct_declarations.push_back(""); - funct_declarations.push_back(stringf("static void %s(struct %s_state_t *state)", func_name.c_str(), cid(work->module->name).c_str())); + funct_declarations.push_back(stringf("static void %s(struct %s_state_t *state)", func_name, cid(work->module->name))); funct_declarations.push_back("{"); for (auto &line : preamble) funct_declarations.push_back(line); diff --git a/backends/spice/spice.cc b/backends/spice/spice.cc index e55db95e1..573093ff7 100644 --- a/backends/spice/spice.cc +++ b/backends/spice/spice.cc @@ -51,16 +51,16 @@ static void print_spice_net(std::ostream &f, RTLIL::SigBit s, std::string &neg, if (s.wire->port_id) use_inames = true; if (s.wire->width > 1) - f << stringf(" %s.%d", spice_id2str(s.wire->name, use_inames, inums).c_str(), s.offset); + f << stringf(" %s.%d", spice_id2str(s.wire->name, use_inames, inums), s.offset); else - f << stringf(" %s", spice_id2str(s.wire->name, use_inames, inums).c_str()); + f << stringf(" %s", spice_id2str(s.wire->name, use_inames, inums)); } else { if (s == RTLIL::State::S0) - f << stringf(" %s", neg.c_str()); + f << stringf(" %s", neg); else if (s == RTLIL::State::S1) - f << stringf(" %s", pos.c_str()); + f << stringf(" %s", pos); else - f << stringf(" %s%d", ncpf.c_str(), nc_counter++); + f << stringf(" %s%d", ncpf, nc_counter++); } } @@ -119,7 +119,7 @@ static void print_spice_module(std::ostream &f, RTLIL::Module *module, RTLIL::De } } - f << stringf(" %s\n", spice_id2str(cell->type).c_str()); + f << stringf(" %s\n", spice_id2str(cell->type)); } for (auto &conn : module->connections()) @@ -127,7 +127,7 @@ static void print_spice_module(std::ostream &f, RTLIL::Module *module, RTLIL::De f << (buf == "DC" ? stringf("V%d", conn_counter++) : stringf("X%d", cell_counter++)); print_spice_net(f, conn.second.extract(i, 1), neg, pos, ncpf, nc_counter, use_inames, inums); print_spice_net(f, conn.first.extract(i, 1), neg, pos, ncpf, nc_counter, use_inames, inums); - f << (buf == "DC" ? " DC 0\n" : stringf(" %s\n", buf.c_str())); + f << (buf == "DC" ? " DC 0\n" : stringf(" %s\n", buf)); } } @@ -242,18 +242,18 @@ struct SpiceBackend : public Backend { ports.at(wire->port_id-1) = wire; } - *f << stringf(".SUBCKT %s", spice_id2str(module->name).c_str()); + *f << stringf(".SUBCKT %s", spice_id2str(module->name)); for (RTLIL::Wire *wire : ports) { log_assert(wire != NULL); if (wire->width > 1) { for (int i = 0; i < wire->width; i++) - *f << stringf(" %s.%d", spice_id2str(wire->name).c_str(), big_endian ? wire->width - 1 - i : i); + *f << stringf(" %s.%d", spice_id2str(wire->name), big_endian ? wire->width - 1 - i : i); } else - *f << stringf(" %s", spice_id2str(wire->name).c_str()); + *f << stringf(" %s", spice_id2str(wire->name)); } *f << stringf("\n"); print_spice_module(*f, module, design, neg, pos, buf, ncpf, big_endian, use_inames); - *f << stringf(".ENDS %s\n\n", spice_id2str(module->name).c_str()); + *f << stringf(".ENDS %s\n\n", spice_id2str(module->name)); } if (!top_module_name.empty()) { diff --git a/frontends/verilog/verilog_location.h b/frontends/verilog/verilog_location.h index 9b530fbeb..8ffe0552d 100644 --- a/frontends/verilog/verilog_location.h +++ b/frontends/verilog/verilog_location.h @@ -5,6 +5,7 @@ #include #include #include +#include /** * Provide frontend-wide location tracking like what bison generates diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index e299d3148..392d8921a 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -33,7 +33,7 @@ * */ -%require "3.8" +%require "3.6" %language "c++" %define api.value.type variant %define api.prefix {frontend_verilog_yy} diff --git a/kernel/ffinit.h b/kernel/ffinit.h index 6d4c97ac3..66c13b68f 100644 --- a/kernel/ffinit.h +++ b/kernel/ffinit.h @@ -27,10 +27,10 @@ YOSYS_NAMESPACE_BEGIN struct FfInitVals { - const SigMap *sigmap; + const SigMapView *sigmap; dict> initbits; - void set(const SigMap *sigmap_, RTLIL::Module *module) + void set(const SigMapView *sigmap_, RTLIL::Module *module) { sigmap = sigmap_; initbits.clear(); @@ -126,7 +126,7 @@ struct FfInitVals initbits.clear(); } - FfInitVals (const SigMap *sigmap, RTLIL::Module *module) + FfInitVals (const SigMapView *sigmap, RTLIL::Module *module) { set(sigmap, module); } diff --git a/kernel/ffmerge.h b/kernel/ffmerge.h index 5428da324..028987503 100644 --- a/kernel/ffmerge.h +++ b/kernel/ffmerge.h @@ -58,7 +58,7 @@ YOSYS_NAMESPACE_BEGIN struct FfMergeHelper { - const SigMap *sigmap; + const SigMapView *sigmap; RTLIL::Module *module; FfInitVals *initvals; diff --git a/kernel/hashlib.h b/kernel/hashlib.h index be7716a79..086e77197 100644 --- a/kernel/hashlib.h +++ b/kernel/hashlib.h @@ -12,6 +12,7 @@ #ifndef HASHLIB_H #define HASHLIB_H +#include #include #include #include @@ -101,7 +102,7 @@ private: uint32_t hash = ((a << 5) + a) ^ b; return hash; } - public: +public: void hash32(uint32_t i) { state = djb2_xor(i, state); state = mkhash_xorshift(fudge ^ state); @@ -128,6 +129,7 @@ private: *this = hash_ops::hash_into(t, *this); } + [[deprecated]] void commutative_eat(hash_t t) { state ^= t; } @@ -357,6 +359,29 @@ template> class idict; template> class pool; template> class mfp; +// Computes the hash value of an unordered set of elements. +// See https://www.preprints.org/manuscript/201710.0192/v1/download. +// This is the Sum(4) algorithm from that paper, which has good collision resistance, +// much better than Sum(1) or Xor(1) (and somewhat better than Xor(4)). +class commutative_hash { +public: + commutative_hash() { + buckets.fill(0); + } + void eat(Hasher h) { + Hasher::hash_t v = h.yield(); + size_t index = v & (buckets.size() - 1); + buckets[index] += v; + } + [[nodiscard]] Hasher hash_into(Hasher h) const { + for (auto b : buckets) + h.eat(b); + return h; + } +private: + std::array buckets; +}; + template class dict { struct entry_t @@ -802,14 +827,14 @@ public: } [[nodiscard]] Hasher hash_into(Hasher h) const { + commutative_hash comm; for (auto &it : entries) { Hasher entry_hash; entry_hash.eat(it.udata.first); entry_hash.eat(it.udata.second); - h.commutative_eat(entry_hash.yield()); + comm.eat(entry_hash); } - h.eat(entries.size()); - return h; + return comm.hash_into(h); } void reserve(size_t n) { entries.reserve(n); } @@ -1190,11 +1215,11 @@ public: } [[nodiscard]] Hasher hash_into(Hasher h) const { + commutative_hash comm; for (auto &it : entries) { - h.commutative_eat(ops.hash(it.udata).yield()); + comm.eat(ops.hash(it.udata)); } - h.eat(entries.size()); - return h; + return comm.hash_into(h); } void reserve(size_t n) { entries.reserve(n); } @@ -1359,7 +1384,8 @@ public: return p; } - // Merge sets if the given indices belong to different sets + // Merge sets if the given indices belong to different sets. + // Makes ifind(j) the root of the merged set. void imerge(int i, int j) { i = ifind(i); diff --git a/kernel/io.cc b/kernel/io.cc index 9811919ba..ef73a3b3d 100644 --- a/kernel/io.cc +++ b/kernel/io.cc @@ -412,6 +412,70 @@ static std::string string_view_stringf(std::string_view spec, ...) return result; } +static int spec_parameter_size(std::string_view spec) +{ + // Every valid spec starts with '%' which means the code below + // won't look before the spec start. + switch (spec[spec.size() - 1]) { + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + switch (spec[spec.size() - 2]) { + case 'h': + if (spec[spec.size() - 3] == 'h') + return sizeof(char); + return sizeof(short); + case 'l': + if (spec[spec.size() - 3] == 'l') + return sizeof(long long); + return sizeof(long); + case 'L': + case 'q': + return sizeof(long long); + case 'j': + return sizeof(intmax_t); + case 'z': + case 'Z': + return sizeof(size_t); + case 't': + return sizeof(ptrdiff_t); + } + return sizeof(int); + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + case 'a': + case 'A': + if (spec[spec.size() - 2] == 'L') + return sizeof(long double); + if (spec[spec.size() - 2] == 'l' && spec[spec.size() - 3] == 'l') + return sizeof(long double); + return sizeof(double); + case 'c': + if (spec[spec.size() - 2] == 'l') { + return sizeof(wchar_t); + } + return sizeof(unsigned char); + case 'C': + return sizeof(wchar_t); + case 's': + case 'p': + case 'S': + case 'n': + return sizeof(void *); + case 'm': + return sizeof(int); + default: + return -1; + } +} + template static void format_emit_stringf(std::string &result, std::string_view spec, int *dynamic_ints, DynamicIntCount num_dynamic_ints, Arg arg) @@ -439,7 +503,13 @@ void format_emit_long_long(std::string &result, std::string_view spec, int *dyna result += std::to_string(static_cast(arg)); return; } - format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg); + if (spec_parameter_size(spec) <= 4) { + // On some platforms (Wasm) we must ensure that the arg is properly aligned + // after the dynamic `int` parameters. + format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, (int)arg); + } else { + format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg); + } } void format_emit_unsigned_long_long(std::string &result, std::string_view spec, int *dynamic_ints, @@ -454,7 +524,13 @@ void format_emit_unsigned_long_long(std::string &result, std::string_view spec, result += static_cast(arg); return; } - format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg); + if (spec_parameter_size(spec) <= 4) { + // On some platforms (Wasm) we must ensure that the arg is properly aligned + // after the dynamic `int` parameters. + format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, (unsigned int)arg); + } else { + format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg); + } } void format_emit_double(std::string &result, std::string_view spec, int *dynamic_ints, diff --git a/kernel/sigtools.h b/kernel/sigtools.h index 3b0d7b40d..7962eaa70 100644 --- a/kernel/sigtools.h +++ b/kernel/sigtools.h @@ -237,6 +237,42 @@ using sort_by_name_id_guard = typename std::enable_if class SigSet> : public SigSet::type>> {}; +struct SigMapView +{ + mfp database; + + // Modify bit to its representative + void apply(RTLIL::SigBit &bit) const + { + bit = database.find(bit); + } + + void apply(RTLIL::SigSpec &sig) const + { + for (auto &bit : sig) + apply(bit); + } + + RTLIL::SigBit operator()(RTLIL::SigBit bit) const + { + apply(bit); + return bit; + } + + RTLIL::SigSpec operator()(RTLIL::SigSpec sig) const + { + apply(sig); + return sig; + } + + RTLIL::SigSpec operator()(RTLIL::Wire *wire) const + { + SigSpec sig(wire); + apply(sig); + return sig; + } +}; + /** * SigMap wraps a union-find "database" * to map SigBits of a module to canonical representative SigBits. @@ -244,10 +280,8 @@ class SigSet> : public SigSet database; - SigMap(RTLIL::Module *module = NULL) { if (module != NULL) @@ -320,37 +354,6 @@ struct SigMap inline void add(Wire *wire) { return add(RTLIL::SigSpec(wire)); } - // Modify bit to its representative - void apply(RTLIL::SigBit &bit) const - { - bit = database.find(bit); - } - - void apply(RTLIL::SigSpec &sig) const - { - for (auto &bit : sig) - apply(bit); - } - - RTLIL::SigBit operator()(RTLIL::SigBit bit) const - { - apply(bit); - return bit; - } - - RTLIL::SigSpec operator()(RTLIL::SigSpec sig) const - { - apply(sig); - return sig; - } - - RTLIL::SigSpec operator()(RTLIL::Wire *wire) const - { - SigSpec sig(wire); - apply(sig); - return sig; - } - // All non-const bits RTLIL::SigSpec allbits() const { @@ -362,6 +365,107 @@ struct SigMap } }; +/** + * SiValgMap wraps a union-find "database" to map SigBits of a module to + * canonical representative SigBits plus some optional Val value associated with the bits. + * Val has a commutative, associative, idempotent operator|=, a default constructor + * which constructs an identity element, and a copy constructor. + * SigBits that are connected share a set in the underlying database; + * the associated value is the "sum" of all the values associated with the contributing bits. + * If any of the SigBits in a set are a constant, the canonical SigBit is a constant. + */ +template +struct SigValMap final : public SigMapView +{ + dict values; + + void swap(SigValMap &other) + { + database.swap(other.database); + values.swap(other.values); + } + + void clear() + { + database.clear(); + values.clear(); + } + + // Rebuild SigMap for all connections in module + void set(RTLIL::Module *module) + { + int bitcount = 0; + for (auto &it : module->connections()) + bitcount += it.first.size(); + + database.clear(); + values.clear(); + database.reserve(bitcount); + + for (auto &it : module->connections()) + add(it.first, it.second); + } + + // Add connections from "from" to "to", bit-by-bit. + void add(const RTLIL::SigSpec& from, const RTLIL::SigSpec& to) + { + log_assert(GetSize(from) == GetSize(to)); + + for (int i = 0; i < GetSize(from); i++) + { + int bfi = database.lookup(from[i]); + int bti = database.lookup(to[i]); + if (bfi == bti) { + continue; + } + + const RTLIL::SigBit &bf = database[bfi]; + const RTLIL::SigBit &bt = database[bti]; + if (bf.wire == nullptr) { + // bf is constant so make it the canonical representative. + database.imerge(bti, bfi); + merge_value(bt, bf); + } else { + // Make bt the canonical representative. + database.imerge(bfi, bti); + merge_value(bf, bt); + } + } + } + + void addVal(const RTLIL::SigBit &bit, const Val &val) + { + values[database.find(bit)] |= val; + } + + void addVal(const RTLIL::SigSpec &sig, const Val &val) + { + for (const auto &bit : sig) + addVal(bit, val); + } + + Val apply_and_get_value(RTLIL::SigBit &bit) const + { + bit = database.find(bit); + auto it = values.find(bit); + return it == values.end() ? Val() : it->second; + } + +private: + void merge_value(const RTLIL::SigBit &from, const RTLIL::SigBit &to) + { + auto it = values.find(from); + if (it == values.end()) { + return; + } + // values[to] could resize the underlying `entries` so + // finish using `it` first. + Val v = it->second; + values.erase(it); + values[to] |= v; + } +}; + YOSYS_NAMESPACE_END #endif /* SIGTOOLS_H */ diff --git a/kernel/yosys_common.h b/kernel/yosys_common.h index 195f14e4c..2de48d242 100644 --- a/kernel/yosys_common.h +++ b/kernel/yosys_common.h @@ -20,6 +20,7 @@ #ifndef YOSYS_COMMON_H #define YOSYS_COMMON_H +#include #include #include #include diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index d4eb22ca5..907a16fe0 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -373,6 +373,12 @@ struct AbstractPass : public Pass { log(" abstractions performed by either mode. This option is not supported in\n"); log(" the -init mode.\n"); log("\n"); + log(" -initstates \n"); + log(" Perform conditional abstraction for the first time steps. See the\n"); + log(" description of the -state and -value modes for details on how the\n"); + log(" condition affects the abstractions performed by either mode. This option\n"); + log(" is not supported in the -init mode.\n"); + log("\n"); log(" -slice :\n"); log(" -slice \n"); log(" -rtlilslice :\n"); @@ -402,8 +408,10 @@ struct AbstractPass : public Pass { Always = -1, ActiveLow = false, // ensuring we can use bool(enable) ActiveHigh = true, + Initstates = 2, }; Enable enable = Enable::Always; + int initstates = 0; std::string enable_name; std::vector slices; for (argidx = 1; argidx < args.size(); argidx++) @@ -435,6 +443,13 @@ struct AbstractPass : public Pass { enable = Enable::ActiveLow; continue; } + if (arg == "-initstates" && argidx + 1 < args.size()) { + if (enable != Enable::Always) + log_cmd_error("Multiple enable condition are not supported\n"); + initstates = atoi(args[++argidx].c_str()); + enable = Enable::Initstates; + continue; + } if (arg == "-slice" && argidx + 1 < args.size()) { slices.emplace_back(SliceIndices::HdlSlice, args[++argidx]); continue; @@ -451,22 +466,50 @@ struct AbstractPass : public Pass { if (mode == Mode::Initial) log_cmd_error("Conditional initial value abstraction is not supported\n"); - if (enable_name.empty()) - log_cmd_error("Unspecified enable wire\n"); + switch (enable) { + case Enable::Always: + log_assert(false); + case Enable::ActiveLow: + case Enable::ActiveHigh: { + if (enable_name.empty()) + log_cmd_error("Unspecified enable wire\n"); + } break; + case Enable::Initstates: { + if (initstates <= 0) + log_cmd_error("Number of initial time steps must be positive\n"); + } break; + } } unsigned int changed = 0; if ((mode == State) || (mode == Value)) { for (auto mod : design->selected_modules()) { - EnableLogic enable_logic = { State::S1, true }; - if (enable != Enable::Always) { - Wire *enable_wire = mod->wire("\\" + enable_name); - if (!enable_wire) - log_cmd_error("Enable wire %s not found in module %s\n", enable_name.c_str(), mod->name.c_str()); - if (GetSize(enable_wire) != 1) - log_cmd_error("Enable wire %s must have width 1 but has width %d in module %s\n", - enable_name.c_str(), GetSize(enable_wire), mod->name.c_str()); - enable_logic = { enable_wire, enable == Enable::ActiveHigh }; + EnableLogic enable_logic; + + switch (enable) { + case Enable::Always: { + enable_logic = { State::S1, true }; + } break; + case Enable::ActiveLow: + case Enable::ActiveHigh: { + Wire *enable_wire = mod->wire("\\" + enable_name); + if (!enable_wire) + log_cmd_error("Enable wire %s not found in module %s\n", enable_name.c_str(), mod->name.c_str()); + if (GetSize(enable_wire) != 1) + log_cmd_error("Enable wire %s must have width 1 but has width %d in module %s\n", + enable_name.c_str(), GetSize(enable_wire), mod->name.c_str()); + enable_logic = { enable_wire, enable == Enable::ActiveHigh }; + } break; + case Enable::Initstates: { + SigBit in_init_states = mod->Initstate(NEW_ID); + for (int i = 1; i < initstates; i++) { + Wire *in_init_states_q = mod->addWire(NEW_ID); + mod->addFf(NEW_ID, in_init_states, in_init_states_q); + in_init_states_q->attributes[ID::init] = State::S1; + in_init_states = in_init_states_q; + } + enable_logic = { in_init_states, true }; + } break; } if (mode == State) changed += abstract_state(mod, enable_logic, slices); diff --git a/passes/cmds/check.cc b/passes/cmds/check.cc index 8bbcb8da0..3017630a6 100644 --- a/passes/cmds/check.cc +++ b/passes/cmds/check.cc @@ -195,16 +195,23 @@ struct CheckPass : public Pass { // in port widths are those for us to check. if (!cell->type.in( ID($add), ID($sub), - ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx))) + ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx), + ID($pmux), ID($bmux))) return false; int in_widths = 0, out_widths = 0; - for (auto &conn : cell->connections()) { - if (cell->input(conn.first)) - in_widths += conn.second.size(); - if (cell->output(conn.first)) - out_widths += conn.second.size(); + if (cell->type.in(ID($pmux), ID($bmux))) { + // We're skipping inputs A and B, since each of their bits contributes only one edge + in_widths = GetSize(cell->getPort(ID::S)); + out_widths = GetSize(cell->getPort(ID::Y)); + } else { + for (auto &conn : cell->connections()) { + if (cell->input(conn.first)) + in_widths += conn.second.size(); + if (cell->output(conn.first)) + out_widths += conn.second.size(); + } } const int threshold = 1024; diff --git a/passes/cmds/rename.cc b/passes/cmds/rename.cc index 7e3f87147..167997dc0 100644 --- a/passes/cmds/rename.cc +++ b/passes/cmds/rename.cc @@ -598,6 +598,8 @@ struct RenamePass : public Pass { for (auto &it : new_cell_names) module->rename(it.first, it.second); + + module->fixup_ports(); } } else diff --git a/passes/opt/opt_merge.cc b/passes/opt/opt_merge.cc index b519d33d6..ba8168e74 100644 --- a/passes/opt/opt_merge.cc +++ b/passes/opt/opt_merge.cc @@ -44,20 +44,16 @@ struct OptMergeWorker CellTypes ct; int total_count; - static vector> sorted_pmux_in(const dict &conn) + static Hasher hash_pmux_in(const SigSpec& sig_s, const SigSpec& sig_b, Hasher h) { - SigSpec sig_s = conn.at(ID::S); - SigSpec sig_b = conn.at(ID::B); - int s_width = GetSize(sig_s); int width = GetSize(sig_b) / s_width; - vector> sb_pairs; + hashlib::commutative_hash comm; for (int i = 0; i < s_width; i++) - sb_pairs.push_back(pair(sig_s[i], sig_b.extract(i*width, width))); + comm.eat(hash_ops>::hash({sig_s[i], sig_b.extract(i*width, width)})); - std::sort(sb_pairs.begin(), sb_pairs.end()); - return sb_pairs; + return comm.hash_into(h); } static void sort_pmux_conn(dict &conn) @@ -89,12 +85,10 @@ struct OptMergeWorker // (builtin || stdcell) && (unary || binary) && symmetrical if (cell->type.in(ID($and), ID($or), ID($xor), ID($xnor), ID($add), ID($mul), ID($logic_and), ID($logic_or), ID($_AND_), ID($_OR_), ID($_XOR_))) { - std::array inputs = { - assign_map(cell->getPort(ID::A)), - assign_map(cell->getPort(ID::B)) - }; - std::sort(inputs.begin(), inputs.end()); - h = hash_ops>::hash_into(inputs, h); + hashlib::commutative_hash comm; + comm.eat(hash_ops::hash(assign_map(cell->getPort(ID::A)))); + comm.eat(hash_ops::hash(assign_map(cell->getPort(ID::B)))); + h = comm.hash_into(h); } else if (cell->type.in(ID($reduce_xor), ID($reduce_xnor))) { SigSpec a = assign_map(cell->getPort(ID::A)); a.sort(); @@ -104,44 +98,31 @@ struct OptMergeWorker a.sort_and_unify(); h = a.hash_into(h); } else if (cell->type == ID($pmux)) { - dict conn = cell->connections(); - assign_map.apply(conn.at(ID::A)); - assign_map.apply(conn.at(ID::B)); - assign_map.apply(conn.at(ID::S)); - for (const auto& [s_bit, b_chunk] : sorted_pmux_in(conn)) { - h = s_bit.hash_into(h); - h = b_chunk.hash_into(h); - } + SigSpec sig_s = assign_map(cell->getPort(ID::S)); + SigSpec sig_b = assign_map(cell->getPort(ID::B)); + h = hash_pmux_in(sig_s, sig_b, h); h = assign_map(cell->getPort(ID::A)).hash_into(h); } else { - std::vector> conns; - for (const auto& conn : cell->connections()) { - conns.push_back(conn); + hashlib::commutative_hash comm; + for (const auto& [port, sig] : cell->connections()) { + if (cell->output(port)) + continue; + comm.eat(hash_ops>::hash({port, assign_map(sig)})); } - std::sort(conns.begin(), conns.end()); - for (const auto& [port, sig] : conns) { - if (!cell->output(port)) { - h = port.hash_into(h); - h = assign_map(sig).hash_into(h); - } - } - + h = comm.hash_into(h); if (RTLIL::builtin_ff_cell_types().count(cell->type)) h = initvals(cell->getPort(ID::Q)).hash_into(h); - } return h; } static Hasher hash_cell_parameters(const RTLIL::Cell *cell, Hasher h) { - using Paramvec = std::vector>; - Paramvec params; + hashlib::commutative_hash comm; for (const auto& param : cell->parameters) { - params.push_back(param); + comm.eat(hash_ops>::hash(param)); } - std::sort(params.begin(), params.end()); - return hash_ops::hash_into(params, h); + return comm.hash_into(h); } Hasher hash_cell_function(const RTLIL::Cell *cell, Hasher h) const @@ -227,7 +208,7 @@ struct OptMergeWorker } OptMergeWorker(RTLIL::Design *design, RTLIL::Module *module, bool mode_nomux, bool mode_share_all, bool mode_keepdc) : - design(design), module(module), assign_map(module), mode_share_all(mode_share_all) + design(design), module(module), mode_share_all(mode_share_all) { total_count = 0; ct.setup_internals(); diff --git a/passes/opt/opt_muxtree.cc b/passes/opt/opt_muxtree.cc index 100b1b495..e1ed5da08 100644 --- a/passes/opt/opt_muxtree.cc +++ b/passes/opt/opt_muxtree.cc @@ -23,6 +23,8 @@ #include "kernel/celltypes.h" #include #include +#include +#include #include USING_YOSYS_NAMESPACE @@ -291,14 +293,14 @@ struct OptMuxtreeWorker // database of known inactive signals // the payload is a reference counter used to manage the // list. when it is non-zero the signal in known to be inactive - vector known_inactive; + std::unordered_map known_inactive; // database of known active signals - vector known_active; + std::unordered_map known_active; // this is just used to keep track of visited muxes in order to prohibit // endless recursion in mux loops - vector visited_muxes; + std::unordered_set visited_muxes; }; void eval_mux_port(knowledge_t &knowledge, int mux_idx, int port_idx, bool do_replace_known, bool do_enable_ports, int abort_count) @@ -315,17 +317,18 @@ struct OptMuxtreeWorker if (i == port_idx) continue; if (muxinfo.ports[i].ctrl_sig >= 0) - knowledge.known_inactive.at(muxinfo.ports[i].ctrl_sig)++; + ++knowledge.known_inactive[muxinfo.ports[i].ctrl_sig]; } if (port_idx < GetSize(muxinfo.ports)-1 && !muxinfo.ports[port_idx].const_activated) - knowledge.known_active.at(muxinfo.ports[port_idx].ctrl_sig)++; + ++knowledge.known_active[muxinfo.ports[port_idx].ctrl_sig]; vector parent_muxes; for (int m : muxinfo.ports[port_idx].input_muxes) { - if (knowledge.visited_muxes[m]) + auto it = knowledge.visited_muxes.find(m); + if (it != knowledge.visited_muxes.end()) continue; - knowledge.visited_muxes[m] = true; + knowledge.visited_muxes.insert(it, m); parent_muxes.push_back(m); } for (int m : parent_muxes) { @@ -344,16 +347,24 @@ struct OptMuxtreeWorker return; } for (int m : parent_muxes) - knowledge.visited_muxes[m] = false; + knowledge.visited_muxes.erase(m); - if (port_idx < GetSize(muxinfo.ports)-1 && !muxinfo.ports[port_idx].const_activated) - knowledge.known_active.at(muxinfo.ports[port_idx].ctrl_sig)--; + if (port_idx < GetSize(muxinfo.ports)-1 && !muxinfo.ports[port_idx].const_activated) { + auto it = knowledge.known_active.find(muxinfo.ports[port_idx].ctrl_sig); + if (it != knowledge.known_active.end()) + if (--it->second == 0) + knowledge.known_active.erase(it); + } for (int i = 0; i < GetSize(muxinfo.ports); i++) { if (i == port_idx) continue; - if (muxinfo.ports[i].ctrl_sig >= 0) - knowledge.known_inactive.at(muxinfo.ports[i].ctrl_sig)--; + if (muxinfo.ports[i].ctrl_sig >= 0) { + auto it = knowledge.known_inactive.find(muxinfo.ports[i].ctrl_sig); + if (it != knowledge.known_inactive.end()) + if (--it->second == 0) + knowledge.known_inactive.erase(it); + } } } @@ -373,11 +384,11 @@ struct OptMuxtreeWorker vector bits = sig2bits(sig, false); for (int i = 0; i < GetSize(bits); i++) { if (bits[i] >= 0) { - if (knowledge.known_inactive.at(bits[i])) { + if (knowledge.known_inactive.count(bits[i]) > 0) { sig[i] = State::S0; did_something = true; } else - if (knowledge.known_active.at(bits[i])) { + if (knowledge.known_active.count(bits[i]) > 0) { sig[i] = State::S1; did_something = true; } @@ -435,7 +446,7 @@ struct OptMuxtreeWorker portinfo_t &portinfo = muxinfo.ports[port_idx]; if (portinfo.const_deactivated) continue; - if (knowledge.known_active.at(portinfo.ctrl_sig)) { + if (knowledge.known_active.count(portinfo.ctrl_sig) > 0) { eval_mux_port(knowledge, mux_idx, port_idx, do_replace_known, do_enable_ports, abort_count); return; } @@ -449,7 +460,7 @@ struct OptMuxtreeWorker if (portinfo.const_deactivated) continue; if (port_idx < GetSize(muxinfo.ports)-1) - if (knowledge.known_inactive.at(portinfo.ctrl_sig)) + if (knowledge.known_inactive.count(portinfo.ctrl_sig) > 0) continue; eval_mux_port(knowledge, mux_idx, port_idx, do_replace_known, do_enable_ports, abort_count); @@ -462,10 +473,7 @@ struct OptMuxtreeWorker { log_assert(glob_abort_cnt > 0); knowledge_t knowledge; - knowledge.known_inactive.resize(GetSize(bit2info)); - knowledge.known_active.resize(GetSize(bit2info)); - knowledge.visited_muxes.resize(GetSize(mux2info)); - knowledge.visited_muxes[mux_idx] = true; + knowledge.visited_muxes.insert(mux_idx); eval_mux(knowledge, mux_idx, true, root_enable_muxes.at(mux_idx), 3); } }; diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc index 377889fdc..b23ad6350 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -113,25 +113,84 @@ bool map_mux8; bool map_mux16; bool markgroups; -int map_autoidx; -SigMap assign_map; -RTLIL::Module *module; -std::vector signal_list; -dict signal_map; -FfInitVals initvals; + pool enabled_gates; bool cmos_cost; -bool had_init; -bool clk_polarity, en_polarity, arst_polarity, srst_polarity; -RTLIL::SigSpec clk_sig, en_sig, arst_sig, srst_sig; -dict pi_map, po_map; - -int undef_bits_lost; - -int map_signal(RTLIL::SigBit bit, gate_type_t gate_type = G(NONE), int in1 = -1, int in2 = -1, int in3 = -1, int in4 = -1) +struct AbcConfig { - assign_map.apply(bit); + std::string script_file; + std::string exe_file; + std::string map_src; + std::vector liberty_files; + std::vector genlib_files; + std::string constr_file; + std::vector lut_costs; + std::string delay_target; + std::string sop_inputs; + std::string sop_products; + std::string lutin_shared; + std::vector dont_use_cells; + bool cleanup = true; + bool keepff = false; + bool fast_mode = false; + bool show_tempdir = false; + bool sop_mode = false; + bool word_mode = false; + bool abc_dress = false; +}; + +struct AbcSigVal { + bool is_port; + + AbcSigVal(bool is_port = false) : is_port(is_port) {} + AbcSigVal &operator|=(const AbcSigVal &other) { + is_port |= other.is_port; + return *this; + } +}; + +using AbcSigMap = SigValMap; + +struct AbcModuleState { + const AbcConfig &config; + + int map_autoidx = 0; + std::vector signal_list; + dict signal_map; + FfInitVals &initvals; + bool had_init = false; + bool did_run_abc = false; + + bool clk_polarity = false; + bool en_polarity = false; + bool arst_polarity = false; + bool srst_polarity = false; + RTLIL::SigSpec clk_sig, en_sig, arst_sig, srst_sig; + dict pi_map, po_map; + + int undef_bits_lost = 0; + + std::string tempdir_name; + + AbcModuleState(const AbcConfig &config, FfInitVals &initvals) + : config(config), initvals(initvals) {} + + int map_signal(const AbcSigMap &assign_map, RTLIL::SigBit bit, gate_type_t gate_type = G(NONE), int in1 = -1, int in2 = -1, int in3 = -1, int in4 = -1); + void mark_port(const AbcSigMap &assign_map, RTLIL::SigSpec sig); + bool extract_cell(const AbcSigMap &assign_map, RTLIL::Module *module, RTLIL::Cell *cell, bool keepff); + std::string remap_name(RTLIL::IdString abc_name, RTLIL::Wire **orig_wire = nullptr); + void dump_loop_graph(FILE *f, int &nr, dict> &edges, pool &workpool, std::vector &in_counts); + void handle_loops(AbcSigMap &assign_map, RTLIL::Module *module); + void abc_module(RTLIL::Design *design, RTLIL::Module *module, AbcSigMap &assign_map, const std::vector &cells, + bool dff_mode, std::string clk_str); + void extract(AbcSigMap &assign_map, RTLIL::Design *design, RTLIL::Module *module); + void finish(); +}; + +int AbcModuleState::map_signal(const AbcSigMap &assign_map, RTLIL::SigBit bit, gate_type_t gate_type, int in1, int in2, int in3, int in4) +{ + AbcSigVal val = assign_map.apply_and_get_value(bit); if (bit == State::Sx) undef_bits_lost++; @@ -144,7 +203,7 @@ int map_signal(RTLIL::SigBit bit, gate_type_t gate_type = G(NONE), int in1 = -1, gate.in2 = -1; gate.in3 = -1; gate.in4 = -1; - gate.is_port = false; + gate.is_port = bit.wire != nullptr && val.is_port; gate.bit = bit; gate.init = initvals(bit); signal_list.push_back(gate); @@ -167,40 +226,40 @@ int map_signal(RTLIL::SigBit bit, gate_type_t gate_type = G(NONE), int in1 = -1, return gate.id; } -void mark_port(RTLIL::SigSpec sig) +void AbcModuleState::mark_port(const AbcSigMap &assign_map, RTLIL::SigSpec sig) { for (auto &bit : assign_map(sig)) if (bit.wire != nullptr && signal_map.count(bit) > 0) signal_list[signal_map[bit]].is_port = true; } -void extract_cell(RTLIL::Cell *cell, bool keepff) +bool AbcModuleState::extract_cell(const AbcSigMap &assign_map, RTLIL::Module *module, RTLIL::Cell *cell, bool keepff) { if (RTLIL::builtin_ff_cell_types().count(cell->type)) { FfData ff(&initvals, cell); gate_type_t type = G(FF); if (!ff.has_clk) - return; + return false; if (ff.has_gclk) - return; + return false; if (ff.has_aload) - return; + return false; if (ff.has_sr) - return; + return false; if (!ff.is_fine) - return; + return false; if (clk_polarity != ff.pol_clk) - return; + return false; if (clk_sig != assign_map(ff.sig_clk)) - return; + return false; if (ff.has_ce) { if (en_polarity != ff.pol_ce) - return; + return false; if (en_sig != assign_map(ff.sig_ce)) - return; + return false; } else { if (GetSize(en_sig) != 0) - return; + return false; } if (ff.val_init == State::S1) { type = G(FF1); @@ -211,50 +270,55 @@ void extract_cell(RTLIL::Cell *cell, bool keepff) } if (ff.has_arst) { if (arst_polarity != ff.pol_arst) - return; + return false; if (arst_sig != assign_map(ff.sig_arst)) - return; + return false; if (ff.val_arst == State::S1) { if (type == G(FF0)) - return; + return false; type = G(FF1); } else if (ff.val_arst == State::S0) { if (type == G(FF1)) - return; + return false; type = G(FF0); } } else { if (GetSize(arst_sig) != 0) - return; + return false; } if (ff.has_srst) { if (srst_polarity != ff.pol_srst) - return; + return false; if (srst_sig != assign_map(ff.sig_srst)) - return; + return false; if (ff.val_srst == State::S1) { if (type == G(FF0)) - return; + return false; type = G(FF1); } else if (ff.val_srst == State::S0) { if (type == G(FF1)) - return; + return false; type = G(FF0); } } else { if (GetSize(srst_sig) != 0) - return; + return false; } - if (keepff) - for (auto &c : ff.sig_q.chunks()) - if (c.wire != nullptr) - c.wire->attributes[ID::keep] = 1; + int gate_id = map_signal(assign_map, ff.sig_q, type, map_signal(assign_map, ff.sig_d)); + if (keepff) { + SigBit bit = ff.sig_q; + if (assign_map(bit).wire != nullptr) { + signal_list[gate_id].is_port = true; + } + if (bit.wire != nullptr) + bit.wire->attributes[ID::keep] = 1; + } - map_signal(ff.sig_q, type, map_signal(ff.sig_d)); + map_signal(assign_map, ff.sig_q, type, map_signal(assign_map, ff.sig_d)); ff.remove(); - return; + return true; } if (cell->type.in(ID($_BUF_), ID($_NOT_))) @@ -265,10 +329,10 @@ void extract_cell(RTLIL::Cell *cell, bool keepff) assign_map.apply(sig_a); assign_map.apply(sig_y); - map_signal(sig_y, cell->type == ID($_BUF_) ? G(BUF) : G(NOT), map_signal(sig_a)); + map_signal(assign_map, sig_y, cell->type == ID($_BUF_) ? G(BUF) : G(NOT), map_signal(assign_map, sig_a)); module->remove(cell); - return; + return true; } if (cell->type.in(ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_))) @@ -281,30 +345,30 @@ void extract_cell(RTLIL::Cell *cell, bool keepff) assign_map.apply(sig_b); assign_map.apply(sig_y); - int mapped_a = map_signal(sig_a); - int mapped_b = map_signal(sig_b); + int mapped_a = map_signal(assign_map, sig_a); + int mapped_b = map_signal(assign_map, sig_b); if (cell->type == ID($_AND_)) - map_signal(sig_y, G(AND), mapped_a, mapped_b); + map_signal(assign_map, sig_y, G(AND), mapped_a, mapped_b); else if (cell->type == ID($_NAND_)) - map_signal(sig_y, G(NAND), mapped_a, mapped_b); + map_signal(assign_map, sig_y, G(NAND), mapped_a, mapped_b); else if (cell->type == ID($_OR_)) - map_signal(sig_y, G(OR), mapped_a, mapped_b); + map_signal(assign_map, sig_y, G(OR), mapped_a, mapped_b); else if (cell->type == ID($_NOR_)) - map_signal(sig_y, G(NOR), mapped_a, mapped_b); + map_signal(assign_map, sig_y, G(NOR), mapped_a, mapped_b); else if (cell->type == ID($_XOR_)) - map_signal(sig_y, G(XOR), mapped_a, mapped_b); + map_signal(assign_map, sig_y, G(XOR), mapped_a, mapped_b); else if (cell->type == ID($_XNOR_)) - map_signal(sig_y, G(XNOR), mapped_a, mapped_b); + map_signal(assign_map, sig_y, G(XNOR), mapped_a, mapped_b); else if (cell->type == ID($_ANDNOT_)) - map_signal(sig_y, G(ANDNOT), mapped_a, mapped_b); + map_signal(assign_map, sig_y, G(ANDNOT), mapped_a, mapped_b); else if (cell->type == ID($_ORNOT_)) - map_signal(sig_y, G(ORNOT), mapped_a, mapped_b); + map_signal(assign_map, sig_y, G(ORNOT), mapped_a, mapped_b); else log_abort(); module->remove(cell); - return; + return true; } if (cell->type.in(ID($_MUX_), ID($_NMUX_))) @@ -319,14 +383,14 @@ void extract_cell(RTLIL::Cell *cell, bool keepff) assign_map.apply(sig_s); assign_map.apply(sig_y); - int mapped_a = map_signal(sig_a); - int mapped_b = map_signal(sig_b); - int mapped_s = map_signal(sig_s); + int mapped_a = map_signal(assign_map, sig_a); + int mapped_b = map_signal(assign_map, sig_b); + int mapped_s = map_signal(assign_map, sig_s); - map_signal(sig_y, cell->type == ID($_MUX_) ? G(MUX) : G(NMUX), mapped_a, mapped_b, mapped_s); + map_signal(assign_map, sig_y, cell->type == ID($_MUX_) ? G(MUX) : G(NMUX), mapped_a, mapped_b, mapped_s); module->remove(cell); - return; + return true; } if (cell->type.in(ID($_AOI3_), ID($_OAI3_))) @@ -341,14 +405,14 @@ void extract_cell(RTLIL::Cell *cell, bool keepff) assign_map.apply(sig_c); assign_map.apply(sig_y); - int mapped_a = map_signal(sig_a); - int mapped_b = map_signal(sig_b); - int mapped_c = map_signal(sig_c); + int mapped_a = map_signal(assign_map, sig_a); + int mapped_b = map_signal(assign_map, sig_b); + int mapped_c = map_signal(assign_map, sig_c); - map_signal(sig_y, cell->type == ID($_AOI3_) ? G(AOI3) : G(OAI3), mapped_a, mapped_b, mapped_c); + map_signal(assign_map, sig_y, cell->type == ID($_AOI3_) ? G(AOI3) : G(OAI3), mapped_a, mapped_b, mapped_c); module->remove(cell); - return; + return true; } if (cell->type.in(ID($_AOI4_), ID($_OAI4_))) @@ -365,19 +429,21 @@ void extract_cell(RTLIL::Cell *cell, bool keepff) assign_map.apply(sig_d); assign_map.apply(sig_y); - int mapped_a = map_signal(sig_a); - int mapped_b = map_signal(sig_b); - int mapped_c = map_signal(sig_c); - int mapped_d = map_signal(sig_d); + int mapped_a = map_signal(assign_map, sig_a); + int mapped_b = map_signal(assign_map, sig_b); + int mapped_c = map_signal(assign_map, sig_c); + int mapped_d = map_signal(assign_map, sig_d); - map_signal(sig_y, cell->type == ID($_AOI4_) ? G(AOI4) : G(OAI4), mapped_a, mapped_b, mapped_c, mapped_d); + map_signal(assign_map, sig_y, cell->type == ID($_AOI4_) ? G(AOI4) : G(OAI4), mapped_a, mapped_b, mapped_c, mapped_d); module->remove(cell); - return; + return true; } + + return false; } -std::string remap_name(RTLIL::IdString abc_name, RTLIL::Wire **orig_wire = nullptr) +std::string AbcModuleState::remap_name(RTLIL::IdString abc_name, RTLIL::Wire **orig_wire) { std::string abc_sname = abc_name.substr(1); if (abc_sname.compare(0, 9, "new_ys__n") == 0) @@ -410,7 +476,7 @@ std::string remap_name(RTLIL::IdString abc_name, RTLIL::Wire **orig_wire = nullp return stringf("\\%s_ix%d", abc_sname.c_str(), map_autoidx); // SILIMATE: Improve the naming } -void dump_loop_graph(FILE *f, int &nr, dict> &edges, pool &workpool, std::vector &in_counts) +void AbcModuleState::dump_loop_graph(FILE *f, int &nr, dict> &edges, pool &workpool, std::vector &in_counts) { if (f == nullptr) return; @@ -439,7 +505,13 @@ void dump_loop_graph(FILE *f, int &nr, dict> &edges, pool &w fprintf(f, "}\n"); } -void handle_loops() +void connect(AbcSigMap &assign_map, RTLIL::Module *module, const RTLIL::SigSig &conn) +{ + module->connect(conn); + assign_map.add(conn.first, conn.second); +} + +void AbcModuleState::handle_loops(AbcSigMap &assign_map, RTLIL::Module *module) { // http://en.wikipedia.org/wiki/Topological_sorting // (Kahn, Arthur B. (1962), "Topological sorting of large networks") @@ -544,7 +616,7 @@ void handle_loops() first_line = false; } - int id3 = map_signal(RTLIL::SigSpec(wire)); + int id3 = map_signal(assign_map, RTLIL::SigSpec(wire)); signal_list[id1].is_port = true; signal_list[id3].is_port = true; log_assert(id3 == int(in_edges_count.size())); @@ -563,7 +635,7 @@ void handle_loops() } edges[id1].swap(edges[id3]); - module->connect(RTLIL::SigSig(signal_list[id3].bit, signal_list[id1].bit)); + connect(assign_map, module, RTLIL::SigSig(signal_list[id3].bit, signal_list[id1].bit)); dump_loop_graph(dot_f, dot_nr, edges, workpool, in_edges_count); } } @@ -640,13 +712,15 @@ std::string replace_tempdir(std::string text, std::string tempdir_name, bool sho struct abc_output_filter { + const AbcModuleState &state; bool got_cr; int escape_seq_state; std::string linebuf; std::string tempdir_name; bool show_tempdir; - abc_output_filter(std::string tempdir_name, bool show_tempdir) : tempdir_name(tempdir_name), show_tempdir(show_tempdir) + abc_output_filter(const AbcModuleState& state, std::string tempdir_name, bool show_tempdir) + : state(state), tempdir_name(tempdir_name), show_tempdir(show_tempdir) { got_cr = false; escape_seq_state = 0; @@ -687,8 +761,8 @@ struct abc_output_filter int pi, po; if (sscanf(line.c_str(), "Start-point = pi%d. End-point = po%d.", &pi, &po) == 2) { log("ABC: Start-point = pi%d (%s). End-point = po%d (%s).\n", - pi, pi_map.count(pi) ? pi_map.at(pi).c_str() : "???", - po, po_map.count(po) ? po_map.at(po).c_str() : "???"); + pi, state.pi_map.count(pi) ? state.pi_map.at(pi).c_str() : "???", + po, state.po_map.count(po) ? state.po_map.at(po).c_str() : "???"); return; } @@ -697,37 +771,11 @@ struct abc_output_filter } }; -void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::string script_file, std::string exe_file, - std::vector &liberty_files, std::vector &genlib_files, std::string constr_file, - bool cleanup, vector lut_costs, bool dff_mode, std::string clk_str, bool keepff, std::string delay_target, - std::string sop_inputs, std::string sop_products, std::string lutin_shared, bool fast_mode, - const std::vector &cells, bool show_tempdir, bool sop_mode, bool word_mode, bool abc_dress, std::vector &dont_use_cells, const std::string& map_src) +void AbcModuleState::abc_module(RTLIL::Design *design, RTLIL::Module *module, AbcSigMap &assign_map, const std::vector &cells, + bool dff_mode, std::string clk_str) { - module = current_module; map_autoidx = autoidx++; - // SILIMATE: Create a map of all signals and their corresponding driver attributes - SigMap sigmap(module); - dict> sig_to_driver_attrs; - for (auto wire : module->wires()) - if (wire->port_input) - for (auto bit : sigmap(wire)) - sig_to_driver_attrs[bit] = wire->attributes; - for (auto cell : module->cells()) - for (auto &conn : cell->connections()) - if (cell->output(conn.first)) - for (auto bit : sigmap(conn.second)) { - if (GetSize(cell->attributes) > 0) - sig_to_driver_attrs[bit] = cell->attributes; - else - sig_to_driver_attrs[bit] = bit.wire->attributes; - } - - signal_map.clear(); - signal_list.clear(); - pi_map.clear(); - po_map.clear(); - if (clk_str != "$") { clk_polarity = true; @@ -798,39 +846,39 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin if (dff_mode && clk_sig.empty()) log_cmd_error("Clock domain %s not found.\n", clk_str.c_str()); - std::string tempdir_name; - if (cleanup) + if (config.cleanup) tempdir_name = get_base_tmpdir() + "/"; else tempdir_name = "_tmp_"; tempdir_name += proc_program_prefix() + "yosys-abc-XXXXXX"; tempdir_name = make_temp_dir(tempdir_name); log_header(design, "Extracting gate netlist of module `%s' to `%s/input.blif'..\n", - module->name.c_str(), replace_tempdir(tempdir_name, tempdir_name, show_tempdir).c_str()); + module->name.c_str(), replace_tempdir(tempdir_name, tempdir_name, config.show_tempdir).c_str()); std::string abc_script = stringf("read_blif \"%s/input.blif\"; ", tempdir_name.c_str()); - if (!liberty_files.empty() || !genlib_files.empty()) { + if (!config.liberty_files.empty() || !config.genlib_files.empty()) { std::string dont_use_args; - for (std::string dont_use_cell : dont_use_cells) { + for (std::string dont_use_cell : config.dont_use_cells) { dont_use_args += stringf("-X \"%s\" ", dont_use_cell.c_str()); } bool first_lib = true; - for (std::string liberty_file : liberty_files) { + for (std::string liberty_file : config.liberty_files) { abc_script += stringf("read_lib %s %s -w \"%s\" ; ", dont_use_args.c_str(), first_lib ? "" : "-m", liberty_file.c_str()); first_lib = false; } - for (std::string liberty_file : genlib_files) + for (std::string liberty_file : config.genlib_files) abc_script += stringf("read_library \"%s\"; ", liberty_file.c_str()); - if (!constr_file.empty()) - abc_script += stringf("read_constr -v \"%s\"; ", constr_file.c_str()); + if (!config.constr_file.empty()) + abc_script += stringf("read_constr -v \"%s\"; ", config.constr_file.c_str()); } else - if (!lut_costs.empty()) + if (!config.lut_costs.empty()) abc_script += stringf("read_lut %s/lutdefs.txt; ", tempdir_name.c_str()); else abc_script += stringf("read_library %s/stdcells.genlib; ", tempdir_name.c_str()); - if (!script_file.empty()) { + if (!config.script_file.empty()) { + const std::string &script_file = config.script_file; if (script_file[0] == '+') { for (size_t i = 1; i < script_file.size(); i++) if (script_file[i] == '\'') @@ -841,37 +889,38 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin abc_script += script_file[i]; } else abc_script += stringf("source %s", script_file.c_str()); - } else if (!lut_costs.empty()) { + } else if (!config.lut_costs.empty()) { bool all_luts_cost_same = true; - for (int this_cost : lut_costs) - if (this_cost != lut_costs.front()) + for (int this_cost : config.lut_costs) + if (this_cost != config.lut_costs.front()) all_luts_cost_same = false; - abc_script += fast_mode ? ABC_FAST_COMMAND_LUT : ABC_COMMAND_LUT; - if (all_luts_cost_same && !fast_mode) + abc_script += config.fast_mode ? ABC_FAST_COMMAND_LUT : ABC_COMMAND_LUT; + if (all_luts_cost_same && !config.fast_mode) abc_script += "; lutpack {S}"; - } else if (!liberty_files.empty() || !genlib_files.empty()) - abc_script += constr_file.empty() ? (fast_mode ? ABC_FAST_COMMAND_LIB : ABC_COMMAND_LIB) : (fast_mode ? ABC_FAST_COMMAND_CTR : ABC_COMMAND_CTR); - else if (sop_mode) - abc_script += fast_mode ? ABC_FAST_COMMAND_SOP : ABC_COMMAND_SOP; + } else if (!config.liberty_files.empty() || !config.genlib_files.empty()) + abc_script += config.constr_file.empty() ? + (config.fast_mode ? ABC_FAST_COMMAND_LIB : ABC_COMMAND_LIB) : (config.fast_mode ? ABC_FAST_COMMAND_CTR : ABC_COMMAND_CTR); + else if (config.sop_mode) + abc_script += config.fast_mode ? ABC_FAST_COMMAND_SOP : ABC_COMMAND_SOP; else - abc_script += fast_mode ? ABC_FAST_COMMAND_DFL : ABC_COMMAND_DFL; + abc_script += config.fast_mode ? ABC_FAST_COMMAND_DFL : ABC_COMMAND_DFL; - if (script_file.empty() && !delay_target.empty()) + if (config.script_file.empty() && !config.delay_target.empty()) for (size_t pos = abc_script.find("dretime;"); pos != std::string::npos; pos = abc_script.find("dretime;", pos+1)) abc_script = abc_script.substr(0, pos) + "dretime; retime -o {D};" + abc_script.substr(pos+8); for (size_t pos = abc_script.find("{D}"); pos != std::string::npos; pos = abc_script.find("{D}", pos)) - abc_script = abc_script.substr(0, pos) + delay_target + abc_script.substr(pos+3); + abc_script = abc_script.substr(0, pos) + config.delay_target + abc_script.substr(pos+3); for (size_t pos = abc_script.find("{I}"); pos != std::string::npos; pos = abc_script.find("{I}", pos)) - abc_script = abc_script.substr(0, pos) + sop_inputs + abc_script.substr(pos+3); + abc_script = abc_script.substr(0, pos) + config.sop_inputs + abc_script.substr(pos+3); for (size_t pos = abc_script.find("{P}"); pos != std::string::npos; pos = abc_script.find("{P}", pos)) - abc_script = abc_script.substr(0, pos) + sop_products + abc_script.substr(pos+3); + abc_script = abc_script.substr(0, pos) + config.sop_products + abc_script.substr(pos+3); for (size_t pos = abc_script.find("{S}"); pos != std::string::npos; pos = abc_script.find("{S}", pos)) - abc_script = abc_script.substr(0, pos) + lutin_shared + abc_script.substr(pos+3); - if (abc_dress) + abc_script = abc_script.substr(0, pos) + config.lutin_shared + abc_script.substr(pos+3); + if (config.abc_dress) abc_script += stringf("; dress \"%s/input.blif\"", tempdir_name.c_str()); abc_script += stringf("; write_blif %s/output.blif", tempdir_name.c_str()); abc_script = add_echos_to_abc_cmd(abc_script); @@ -906,34 +955,34 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin undef_bits_lost = 0; had_init = false; + std::vector kept_cells; for (auto c : cells) - extract_cell(c, keepff); + if (!extract_cell(assign_map, module, c, config.keepff)) + kept_cells.push_back(c); if (undef_bits_lost) log("Replacing %d occurrences of constant undef bits with constant zero bits\n", undef_bits_lost); - for (auto wire : module->wires()) { - if (wire->port_id > 0 || wire->get_bool_attribute(ID::keep)) - mark_port(wire); - } - - for (auto cell : module->cells()) - for (auto &port_it : cell->connections()) - mark_port(port_it.second); + // Wires with port_id > 0, ID::keep, and connections to cells outside our cell set have already + // been accounted for via AbcSigVal::is_port. Now we just need to account for + // connections to cells inside our cell set that weren't removed by extract_cell(). + for (auto cell : kept_cells) + for (auto &port_it : cell->connections()) + mark_port(assign_map, port_it.second); if (clk_sig.size() != 0) - mark_port(clk_sig); + mark_port(assign_map, clk_sig); if (en_sig.size() != 0) - mark_port(en_sig); + mark_port(assign_map, en_sig); if (arst_sig.size() != 0) - mark_port(arst_sig); + mark_port(assign_map, arst_sig); if (srst_sig.size() != 0) - mark_port(srst_sig); + mark_port(assign_map, srst_sig); - handle_loops(); + handle_loops(assign_map, module); buffer = stringf("%s/input.blif", tempdir_name.c_str()); f = fopen(buffer.c_str(), "wt"); @@ -1106,21 +1155,21 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin fprintf(f, "GATE MUX16 %d Y=(!S*!T*!U*!V*A)+(S*!T*!U*!V*B)+(!S*T*!U*!V*C)+(S*T*!U*!V*D)+(!S*!T*U*!V*E)+(S*!T*U*!V*F)+(!S*T*U*!V*G)+(S*T*U*!V*H)+(!S*!T*!U*V*I)+(S*!T*!U*V*J)+(!S*T*!U*V*K)+(S*T*!U*V*L)+(!S*!T*U*V*M)+(S*!T*U*V*N)+(!S*T*U*V*O)+(S*T*U*V*P); PIN * UNKNOWN 1 999 1 0 1 0\n", 8*cell_cost.at(ID($_MUX_))); fclose(f); - if (!lut_costs.empty()) { + if (!config.lut_costs.empty()) { buffer = stringf("%s/lutdefs.txt", tempdir_name.c_str()); f = fopen(buffer.c_str(), "wt"); if (f == nullptr) log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno)); - for (int i = 0; i < GetSize(lut_costs); i++) - fprintf(f, "%d %d.00 1.00\n", i+1, lut_costs.at(i)); + for (int i = 0; i < GetSize(config.lut_costs); i++) + fprintf(f, "%d %d.00 1.00\n", i+1, config.lut_costs.at(i)); fclose(f); } - buffer = stringf("\"%s\" -s -f %s/abc.script 2>&1", exe_file.c_str(), tempdir_name.c_str()); - log("Running ABC command: %s\n", replace_tempdir(buffer, tempdir_name, show_tempdir).c_str()); + buffer = stringf("\"%s\" -s -f %s/abc.script 2>&1", config.exe_file.c_str(), tempdir_name.c_str()); + log("Running ABC command: %s\n", replace_tempdir(buffer, tempdir_name, config.show_tempdir).c_str()); #ifndef YOSYS_LINK_ABC - abc_output_filter filt(tempdir_name, show_tempdir); + abc_output_filter filt(*this, tempdir_name, config.show_tempdir); int ret = run_command(buffer, std::bind(&abc_output_filter::next_line, filt, std::placeholders::_1)); #else string temp_stdouterr_name = stringf("%s/stdouterr.txt", tempdir_name.c_str()); @@ -1144,7 +1193,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin // These needs to be mutable, supposedly due to getopt char *abc_argv[5]; string tmp_script_name = stringf("%s/abc.script", tempdir_name.c_str()); - abc_argv[0] = strdup(exe_file.c_str()); + abc_argv[0] = strdup(config.exe_file.c_str()); abc_argv[1] = strdup("-s"); abc_argv[2] = strdup("-f"); abc_argv[3] = strdup(tmp_script_name.c_str()); @@ -1161,251 +1210,231 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin fclose(old_stdout); fclose(old_stderr); std::ifstream temp_stdouterr_r(temp_stdouterr_name); - abc_output_filter filt(tempdir_name, show_tempdir); + abc_output_filter filt(*this, tempdir_name, config.show_tempdir); for (std::string line; std::getline(temp_stdouterr_r, line); ) filt.next_line(line + "\n"); temp_stdouterr_r.close(); #endif - if (ret != 0) + if (ret != 0) { log_error("ABC: execution of command \"%s\" failed: return code %d.\n", buffer.c_str(), ret); - - buffer = stringf("%s/%s", tempdir_name.c_str(), "output.blif"); - std::ifstream ifs; - ifs.open(buffer); - if (ifs.fail()) - log_error("Can't open ABC output file `%s'.\n", buffer.c_str()); - - bool builtin_lib = liberty_files.empty() && genlib_files.empty(); - RTLIL::Design *mapped_design = new RTLIL::Design; - parse_blif(mapped_design, ifs, builtin_lib ? ID(DFF) : ID(_dff_), false, sop_mode); - - ifs.close(); - - log_header(design, "Re-integrating ABC results.\n"); - RTLIL::Module *mapped_mod = mapped_design->module(ID(netlist)); - SigMap mapped_sigmap(mapped_mod); - FfInitVals mapped_initvals(&mapped_sigmap, mapped_mod); - if (mapped_mod == nullptr) - log_error("ABC output file does not contain a module `netlist'.\n"); - for (auto w : mapped_mod->wires()) { - RTLIL::Wire *orig_wire = nullptr; - RTLIL::Wire *wire = module->addWire(remap_name(w->name, &orig_wire)); - if (orig_wire != nullptr) { - if (sig_to_driver_attrs.count(sigmap(orig_wire))) { - wire->attributes = sig_to_driver_attrs[sigmap(orig_wire)]; - sig_to_driver_attrs[mapped_sigmap(wire)] = wire->attributes; - log_debug("Matched wire %s to driver attributes:\n", orig_wire->name.c_str()); - for (auto &attr : wire->attributes) - log_debug(" %s = %s\n", attr.first.c_str(), attr.second.decode_string().c_str()); - } else { - log_debug("No driver attributes found for wire %s\n", orig_wire->name.c_str()); - } - } - if (markgroups) wire->attributes[ID::abcgroup] = map_autoidx; - design->select(module, wire); + return; } + did_run_abc = true; + return; + } + log("Don't call ABC as there is nothing to map.\n"); +} - dict cell_stats; - for (auto c : mapped_mod->cells()) - { - // SILIMATE: set output port to either Y or Q depending on the cell's ports - RTLIL::IdString output_port_name = (c->hasPort(ID::Y)) ? ID::Y : ID::Q; - c->attributes = sig_to_driver_attrs[mapped_sigmap(module->wire(remap_name(c->getPort(output_port_name).as_wire()->name)))]; - if (builtin_lib) - { - cell_stats[RTLIL::unescape_id(c->type)]++; - if (c->type.in(ID(ZERO), ID(ONE))) { - RTLIL::SigSig conn; - RTLIL::IdString name_y = remap_name(c->getPort(ID::Y).as_wire()->name); - conn.first = module->wire(name_y); - conn.second = RTLIL::SigSpec(c->type == ID(ZERO) ? 0 : 1, 1); - module->connect(conn); - continue; - } - if (c->type == ID(BUF)) { - RTLIL::SigSig conn; - RTLIL::IdString name_y = remap_name(c->getPort(ID::Y).as_wire()->name); - RTLIL::IdString name_a = remap_name(c->getPort(ID::A).as_wire()->name); - conn.first = module->wire(name_y); - conn.second = module->wire(name_a); - module->connect(conn); - continue; - } - if (c->type == ID(NOT)) { - // SILIMATE: use word-level primitives - RTLIL::Cell *cell = module->addCell(remap_name(c->name), word_mode ? ID($not) : ID($_NOT_)); // SILIMATE: use word-level primitives - cell->attributes = c->attributes; - if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; - if (!map_src.empty()) - cell->attributes[ID::src] = map_src; - for (auto name : {ID::A, ID::Y}) { - RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); - cell->setPort(name, module->wire(remapped_name)); - } - cell->fixup_parameters(); - design->select(module, cell); - continue; - } - if (c->type.in(ID(AND), ID(OR), ID(XOR), ID(NAND), ID(NOR), ID(XNOR), ID(ANDNOT), ID(ORNOT))) { - // SILIMATE: use word-level primitives - std::string cell_type; - if (c->type == ID(AND) && word_mode) - cell_type = "$and"; - else if (c->type == ID(OR) && word_mode) - cell_type = "$or"; - else if (c->type == ID(XOR) && word_mode) - cell_type = "$xor"; +void AbcModuleState::extract(AbcSigMap &assign_map, RTLIL::Design *design, RTLIL::Module *module) +{ + // SILIMATE: Create a map of all signals and their corresponding driver attributes + SigMap sigmap(module); + dict> sig_to_driver_attrs; + for (auto wire : module->wires()) + if (wire->port_input) + for (auto bit : sigmap(wire)) + sig_to_driver_attrs[bit] = wire->attributes; + for (auto cell : module->cells()) { + for (auto &conn : cell->connections()) { + if (cell->output(conn.first)) { + for (auto bit : sigmap(conn.second)) { + if (GetSize(cell->attributes) > 0) + sig_to_driver_attrs[bit] = cell->attributes; else - cell_type = stringf("$_%s_", c->type.c_str()+1); - - RTLIL::Cell *cell = module->addCell(remap_name(c->name), cell_type); - cell->attributes = c->attributes; - if (!map_src.empty()) - cell->attributes[ID::src] = map_src; - if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; - for (auto name : {ID::A, ID::B, ID::Y}) { - RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); - cell->setPort(name, module->wire(remapped_name)); - } - cell->fixup_parameters(); - design->select(module, cell); - continue; - } - if (c->type.in(ID(MUX), ID(NMUX))) { - // SILIMATE: use word-level primitives - std::string cell_type; - if (c->type == ID(MUX) && word_mode) - cell_type = "$mux"; - else - cell_type = stringf("$_%s_", c->type.c_str()+1); - - RTLIL::Cell *cell = module->addCell(remap_name(c->name), cell_type); - cell->attributes = c->attributes; - if (!map_src.empty()) - cell->attributes[ID::src] = map_src; - if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; - for (auto name : {ID::A, ID::B, ID::S, ID::Y}) { - RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); - cell->setPort(name, module->wire(remapped_name)); - } - cell->fixup_parameters(); - design->select(module, cell); - continue; - } - if (c->type == ID(MUX4)) { - RTLIL::Cell *cell = module->addCell(remap_name(c->name), ID($_MUX4_)); - if (!map_src.empty()) - cell->attributes[ID::src] = map_src; - if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; - for (auto name : {ID::A, ID::B, ID::C, ID::D, ID::S, ID::T, ID::Y}) { - RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); - cell->setPort(name, module->wire(remapped_name)); - } - design->select(module, cell); - continue; - } - if (c->type == ID(MUX8)) { - RTLIL::Cell *cell = module->addCell(remap_name(c->name), ID($_MUX8_)); - if (!map_src.empty()) - cell->attributes[ID::src] = map_src; - if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; - for (auto name : {ID::A, ID::B, ID::C, ID::D, ID::E, ID::F, ID::G, ID::H, ID::S, ID::T, ID::U, ID::Y}) { - RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); - cell->setPort(name, module->wire(remapped_name)); - } - design->select(module, cell); - continue; - } - if (c->type == ID(MUX16)) { - RTLIL::Cell *cell = module->addCell(remap_name(c->name), ID($_MUX16_)); - if (!map_src.empty()) - cell->attributes[ID::src] = map_src; - if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; - for (auto name : {ID::A, ID::B, ID::C, ID::D, ID::E, ID::F, ID::G, ID::H, ID::I, ID::J, ID::K, - ID::L, ID::M, ID::N, ID::O, ID::P, ID::S, ID::T, ID::U, ID::V, ID::Y}) { - RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); - cell->setPort(name, module->wire(remapped_name)); - } - design->select(module, cell); - continue; - } - if (c->type.in(ID(AOI3), ID(OAI3))) { - RTLIL::Cell *cell = module->addCell(remap_name(c->name), stringf("$_%s_", c->type.c_str()+1)); - if (!map_src.empty()) - cell->attributes[ID::src] = map_src; - if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; - for (auto name : {ID::A, ID::B, ID::C, ID::Y}) { - RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); - cell->setPort(name, module->wire(remapped_name)); - } - design->select(module, cell); - continue; - } - if (c->type.in(ID(AOI4), ID(OAI4))) { - RTLIL::Cell *cell = module->addCell(remap_name(c->name), stringf("$_%s_", c->type.c_str()+1)); - if (!map_src.empty()) - cell->attributes[ID::src] = map_src; - if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; - for (auto name : {ID::A, ID::B, ID::C, ID::D, ID::Y}) { - RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); - cell->setPort(name, module->wire(remapped_name)); - } - design->select(module, cell); - continue; - } - if (c->type == ID(DFF)) { - log_assert(clk_sig.size() == 1); - FfData ff(module, &initvals, remap_name(c->name)); - ff.width = 1; - ff.is_fine = true; - ff.has_clk = true; - ff.pol_clk = clk_polarity; - ff.sig_clk = clk_sig; - if (en_sig.size() != 0) { - log_assert(en_sig.size() == 1); - ff.has_ce = true; - ff.pol_ce = en_polarity; - ff.sig_ce = en_sig; - } - RTLIL::Const init = mapped_initvals(c->getPort(ID::Q)); - if (had_init) - ff.val_init = init; - else - ff.val_init = State::Sx; - if (arst_sig.size() != 0) { - log_assert(arst_sig.size() == 1); - ff.has_arst = true; - ff.pol_arst = arst_polarity; - ff.sig_arst = arst_sig; - ff.val_arst = init; - } - if (srst_sig.size() != 0) { - log_assert(srst_sig.size() == 1); - ff.has_srst = true; - ff.pol_srst = srst_polarity; - ff.sig_srst = srst_sig; - ff.val_srst = init; - } - ff.sig_d = module->wire(remap_name(c->getPort(ID::D).as_wire()->name)); - ff.sig_q = module->wire(remap_name(c->getPort(ID::Q).as_wire()->name)); - RTLIL::Cell *cell = ff.emit(); - if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; - design->select(module, cell); - continue; + sig_to_driver_attrs[bit] = bit.wire->attributes; } } - else - cell_stats[RTLIL::unescape_id(c->type)]++; + } + } - if (c->type.in(ID(_const0_), ID(_const1_))) { + if (!did_run_abc) { + return; + } + + std::string buffer = stringf("%s/%s", tempdir_name.c_str(), "output.blif"); + std::ifstream ifs; + ifs.open(buffer); + if (ifs.fail()) + log_error("Can't open ABC output file `%s'.\n", buffer.c_str()); + + bool builtin_lib = config.liberty_files.empty() && config.genlib_files.empty(); + RTLIL::Design *mapped_design = new RTLIL::Design; + parse_blif(mapped_design, ifs, builtin_lib ? ID(DFF) : ID(_dff_), false, config.sop_mode); + + ifs.close(); + + log_header(design, "Re-integrating ABC results.\n"); + RTLIL::Module *mapped_mod = mapped_design->module(ID(netlist)); + SigMap mapped_sigmap(mapped_mod); + FfInitVals mapped_initvals(&mapped_sigmap, mapped_mod); + if (mapped_mod == nullptr) + log_error("ABC output file does not contain a module `netlist'.\n"); + for (auto w : mapped_mod->wires()) { + RTLIL::Wire *orig_wire = nullptr; + RTLIL::Wire *wire = module->addWire(remap_name(w->name, &orig_wire)); + if (orig_wire != nullptr) { + if (sig_to_driver_attrs.count(sigmap(orig_wire))) { + wire->attributes = sig_to_driver_attrs[sigmap(orig_wire)]; + sig_to_driver_attrs[mapped_sigmap(wire)] = wire->attributes; + log_debug("Matched wire %s to driver attributes:\n", orig_wire->name.c_str()); + for (auto &attr : wire->attributes) + log_debug(" %s = %s\n", attr.first.c_str(), attr.second.decode_string().c_str()); + } else { + log_debug("No driver attributes found for wire %s\n", orig_wire->name.c_str()); + } + } + if (markgroups) wire->attributes[ID::abcgroup] = map_autoidx; + design->select(module, wire); + } + + dict cell_stats; + for (auto c : mapped_mod->cells()) + { + // SILIMATE: set output port to either Y or Q depending on the cell's ports + RTLIL::IdString output_port_name = (c->hasPort(ID::Y)) ? ID::Y : ID::Q; + c->attributes = sig_to_driver_attrs[mapped_sigmap(module->wire(remap_name(c->getPort(output_port_name).as_wire()->name)))]; + if (builtin_lib) + { + cell_stats[RTLIL::unescape_id(c->type)]++; + if (c->type.in(ID(ZERO), ID(ONE))) { RTLIL::SigSig conn; - conn.first = module->wire(remap_name(c->connections().begin()->second.as_wire()->name)); - conn.second = RTLIL::SigSpec(c->type == ID(_const0_) ? 0 : 1, 1); - module->connect(conn); + RTLIL::IdString name_y = remap_name(c->getPort(ID::Y).as_wire()->name); + conn.first = module->wire(name_y); + conn.second = RTLIL::SigSpec(c->type == ID(ZERO) ? 0 : 1, 1); + connect(assign_map, module, conn); continue; } + if (c->type == ID(BUF)) { + RTLIL::SigSig conn; + RTLIL::IdString name_y = remap_name(c->getPort(ID::Y).as_wire()->name); + RTLIL::IdString name_a = remap_name(c->getPort(ID::A).as_wire()->name); + conn.first = module->wire(name_y); + conn.second = module->wire(name_a); + connect(assign_map, module, conn); + continue; + } + if (c->type == ID(NOT)) { + // SILIMATE: use word-level primitives + RTLIL::Cell *cell = module->addCell(remap_name(c->name), config.word_mode ? ID($not) : ID($_NOT_)); // SILIMATE: use word-level primitives + cell->attributes = c->attributes; + if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; + if (!config.map_src.empty()) + cell->attributes[ID::src] = config.map_src; + for (auto name : {ID::A, ID::Y}) { + RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); + cell->setPort(name, module->wire(remapped_name)); + } + cell->fixup_parameters(); + design->select(module, cell); + continue; + } + if (c->type.in(ID(AND), ID(OR), ID(XOR), ID(NAND), ID(NOR), ID(XNOR), ID(ANDNOT), ID(ORNOT))) { + // SILIMATE: use word-level primitives + std::string cell_type; + if (c->type == ID(AND) && config.word_mode) + cell_type = "$and"; + else if (c->type == ID(OR) && config.word_mode) + cell_type = "$or"; + else if (c->type == ID(XOR) && config.word_mode) + cell_type = "$xor"; + else + cell_type = stringf("$_%s_", c->type.c_str()+1); - if (c->type == ID(_dff_)) { + RTLIL::Cell *cell = module->addCell(remap_name(c->name), cell_type); + cell->attributes = c->attributes; + if (!config.map_src.empty()) + cell->attributes[ID::src] = config.map_src; + if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; + for (auto name : {ID::A, ID::B, ID::Y}) { + RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); + cell->setPort(name, module->wire(remapped_name)); + } + cell->fixup_parameters(); + design->select(module, cell); + continue; + } + if (c->type.in(ID(MUX), ID(NMUX))) { + // SILIMATE: use word-level primitives + std::string cell_type; + if (c->type == ID(MUX) && config.word_mode) + cell_type = "$mux"; + else + cell_type = stringf("$_%s_", c->type.c_str()+1); + + RTLIL::Cell *cell = module->addCell(remap_name(c->name), cell_type); + cell->attributes = c->attributes; + if (!config.map_src.empty()) + cell->attributes[ID::src] = config.map_src; + if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; + for (auto name : {ID::A, ID::B, ID::S, ID::Y}) { + RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); + cell->setPort(name, module->wire(remapped_name)); + } + design->select(module, cell); + continue; + } + if (c->type == ID(MUX4)) { + RTLIL::Cell *cell = module->addCell(remap_name(c->name), ID($_MUX4_)); + if (!config.map_src.empty()) + cell->attributes[ID::src] = config.map_src; + if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; + for (auto name : {ID::A, ID::B, ID::C, ID::D, ID::S, ID::T, ID::Y}) { + RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); + cell->setPort(name, module->wire(remapped_name)); + } + design->select(module, cell); + continue; + } + if (c->type == ID(MUX8)) { + RTLIL::Cell *cell = module->addCell(remap_name(c->name), ID($_MUX8_)); + if (!config.map_src.empty()) + cell->attributes[ID::src] = config.map_src; + if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; + for (auto name : {ID::A, ID::B, ID::C, ID::D, ID::E, ID::F, ID::G, ID::H, ID::S, ID::T, ID::U, ID::Y}) { + RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); + cell->setPort(name, module->wire(remapped_name)); + } + design->select(module, cell); + continue; + } + if (c->type == ID(MUX16)) { + RTLIL::Cell *cell = module->addCell(remap_name(c->name), ID($_MUX16_)); + if (!config.map_src.empty()) + cell->attributes[ID::src] = config.map_src; + if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; + for (auto name : {ID::A, ID::B, ID::C, ID::D, ID::E, ID::F, ID::G, ID::H, ID::I, ID::J, ID::K, + ID::L, ID::M, ID::N, ID::O, ID::P, ID::S, ID::T, ID::U, ID::V, ID::Y}) { + RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); + cell->setPort(name, module->wire(remapped_name)); + } + design->select(module, cell); + continue; + } + if (c->type.in(ID(AOI3), ID(OAI3))) { + RTLIL::Cell *cell = module->addCell(remap_name(c->name), stringf("$_%s_", c->type.c_str()+1)); + if (!config.map_src.empty()) + cell->attributes[ID::src] = config.map_src; + if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; + for (auto name : {ID::A, ID::B, ID::C, ID::Y}) { + RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); + cell->setPort(name, module->wire(remapped_name)); + } + design->select(module, cell); + continue; + } + if (c->type.in(ID(AOI4), ID(OAI4))) { + RTLIL::Cell *cell = module->addCell(remap_name(c->name), stringf("$_%s_", c->type.c_str()+1)); + if (!config.map_src.empty()) + cell->attributes[ID::src] = config.map_src; + if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; + for (auto name : {ID::A, ID::B, ID::C, ID::D, ID::Y}) { + RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); + cell->setPort(name, module->wire(remapped_name)); + } + design->select(module, cell); + continue; + } + if (c->type == ID(DFF)) { log_assert(clk_sig.size() == 1); FfData ff(module, &initvals, remap_name(c->name)); ff.width = 1; @@ -1415,6 +1444,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin ff.sig_clk = clk_sig; if (en_sig.size() != 0) { log_assert(en_sig.size() == 1); + ff.has_ce = true; ff.pol_ce = en_polarity; ff.sig_ce = en_sig; } @@ -1425,12 +1455,14 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin ff.val_init = State::Sx; if (arst_sig.size() != 0) { log_assert(arst_sig.size() == 1); + ff.has_arst = true; ff.pol_arst = arst_polarity; ff.sig_arst = arst_sig; ff.val_arst = init; } if (srst_sig.size() != 0) { log_assert(srst_sig.size() == 1); + ff.has_srst = true; ff.pol_srst = srst_polarity; ff.sig_srst = srst_sig; ff.val_srst = init; @@ -1442,73 +1474,117 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin design->select(module, cell); continue; } + } + else + cell_stats[RTLIL::unescape_id(c->type)]++; - if (c->type == ID($lut) && GetSize(c->getPort(ID::A)) == 1 && c->getParam(ID::LUT).as_int() == 2) { - SigSpec my_a = module->wire(remap_name(c->getPort(ID::A).as_wire()->name)); - SigSpec my_y = module->wire(remap_name(c->getPort(ID::Y).as_wire()->name)); - module->connect(my_y, my_a); - continue; + if (c->type.in(ID(_const0_), ID(_const1_))) { + RTLIL::SigSig conn; + conn.first = module->wire(remap_name(c->connections().begin()->second.as_wire()->name)); + conn.second = RTLIL::SigSpec(c->type == ID(_const0_) ? 0 : 1, 1); + connect(assign_map, module, conn); + continue; + } + + if (c->type == ID(_dff_)) { + log_assert(clk_sig.size() == 1); + FfData ff(module, &initvals, remap_name(c->name)); + ff.width = 1; + ff.is_fine = true; + ff.has_clk = true; + ff.pol_clk = clk_polarity; + ff.sig_clk = clk_sig; + if (en_sig.size() != 0) { + log_assert(en_sig.size() == 1); + ff.pol_ce = en_polarity; + ff.sig_ce = en_sig; } - - RTLIL::Cell *cell = module->addCell(remap_name(c->name), c->type); - cell->attributes = c->attributes; - if (!map_src.empty()) - cell->attributes[ID::src] = map_src; + RTLIL::Const init = mapped_initvals(c->getPort(ID::Q)); + if (had_init) + ff.val_init = init; + else + ff.val_init = State::Sx; + if (arst_sig.size() != 0) { + log_assert(arst_sig.size() == 1); + ff.pol_arst = arst_polarity; + ff.sig_arst = arst_sig; + ff.val_arst = init; + } + if (srst_sig.size() != 0) { + log_assert(srst_sig.size() == 1); + ff.pol_srst = srst_polarity; + ff.sig_srst = srst_sig; + ff.val_srst = init; + } + ff.sig_d = module->wire(remap_name(c->getPort(ID::D).as_wire()->name)); + ff.sig_q = module->wire(remap_name(c->getPort(ID::Q).as_wire()->name)); + RTLIL::Cell *cell = ff.emit(); if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; - cell->parameters = c->parameters; - for (auto &conn : c->connections()) { - RTLIL::SigSpec newsig; - for (auto &c : conn.second.chunks()) { - if (c.width == 0) - continue; - log_assert(c.width == 1); - newsig.append(module->wire(remap_name(c.wire->name))); - } - cell->setPort(conn.first, newsig); - } design->select(module, cell); + continue; } - for (auto conn : mapped_mod->connections()) { - if (!conn.first.is_fully_const()) - conn.first = module->wire(remap_name(conn.first.as_wire()->name)); - if (!conn.second.is_fully_const()) - conn.second = module->wire(remap_name(conn.second.as_wire()->name)); - module->connect(conn); + if (c->type == ID($lut) && GetSize(c->getPort(ID::A)) == 1 && c->getParam(ID::LUT).as_int() == 2) { + SigSpec my_a = module->wire(remap_name(c->getPort(ID::A).as_wire()->name)); + SigSpec my_y = module->wire(remap_name(c->getPort(ID::Y).as_wire()->name)); + connect(assign_map, module, RTLIL::SigSig(my_a, my_y)); + continue; } - cell_stats.sort(); - for (auto &it : cell_stats) - log("ABC RESULTS: %15s cells: %8d\n", it.first.c_str(), it.second); - int in_wires = 0, out_wires = 0; - for (auto &si : signal_list) - if (si.is_port) { - char buffer[100]; - snprintf(buffer, 100, "\\ys__n%d", si.id); - RTLIL::SigSig conn; - if (si.type != G(NONE)) { - conn.first = si.bit; - conn.second = module->wire(remap_name(buffer)); - out_wires++; - } else { - conn.first = module->wire(remap_name(buffer)); - conn.second = si.bit; - in_wires++; - } - module->connect(conn); + RTLIL::Cell *cell = module->addCell(remap_name(c->name), c->type); + if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; + cell->parameters = c->parameters; + for (auto &conn : c->connections()) { + RTLIL::SigSpec newsig; + for (auto &c : conn.second.chunks()) { + if (c.width == 0) + continue; + log_assert(c.width == 1); + newsig.append(module->wire(remap_name(c.wire->name))); } - log("ABC RESULTS: internal signals: %8d\n", int(signal_list.size()) - in_wires - out_wires); - log("ABC RESULTS: input signals: %8d\n", in_wires); - log("ABC RESULTS: output signals: %8d\n", out_wires); - - delete mapped_design; - } - else - { - log("Don't call ABC as there is nothing to map.\n"); + cell->setPort(conn.first, newsig); + } + design->select(module, cell); } - if (cleanup) + for (auto conn : mapped_mod->connections()) { + if (!conn.first.is_fully_const()) + conn.first = module->wire(remap_name(conn.first.as_wire()->name)); + if (!conn.second.is_fully_const()) + conn.second = module->wire(remap_name(conn.second.as_wire()->name)); + connect(assign_map, module, conn); + } + + cell_stats.sort(); + for (auto &it : cell_stats) + log("ABC RESULTS: %15s cells: %8d\n", it.first.c_str(), it.second); + int in_wires = 0, out_wires = 0; + for (auto &si : signal_list) + if (si.is_port) { + char buffer[100]; + snprintf(buffer, 100, "\\ys__n%d", si.id); + RTLIL::SigSig conn; + if (si.type != G(NONE)) { + conn.first = si.bit; + conn.second = module->wire(remap_name(buffer)); + out_wires++; + } else { + conn.first = module->wire(remap_name(buffer)); + conn.second = si.bit; + in_wires++; + } + connect(assign_map, module, conn); + } + log("ABC RESULTS: internal signals: %8d\n", int(signal_list.size()) - in_wires - out_wires); + log("ABC RESULTS: input signals: %8d\n", in_wires); + log("ABC RESULTS: output signals: %8d\n", out_wires); + + delete mapped_design; +} + +void AbcModuleState::finish() +{ + if (config.cleanup) { log("Removing temp directory.\n"); remove_directory(tempdir_name); @@ -1517,6 +1593,47 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin log_pop(); } +// For every signal that connects cells from different sets, or a cell in a set to a cell not in any set, +// mark it as a port in `assign_map`. +void assign_cell_connection_ports(RTLIL::Module *module, const std::vector *> &cell_sets, + AbcSigMap &assign_map) +{ + pool cells_in_no_set; + for (RTLIL::Cell *cell : module->cells()) { + cells_in_no_set.insert(cell); + } + // For every canonical signal in `assign_map`, the index of the set it is connected to, + // or -1 if it connects a cell in one set to a cell in another set or not in any set. + dict signal_cell_set; + for (int i = 0; i < int(cell_sets.size()); ++i) { + for (RTLIL::Cell *cell : *cell_sets[i]) { + cells_in_no_set.erase(cell); + for (auto &port_it : cell->connections()) { + for (SigBit bit : port_it.second) { + assign_map.apply(bit); + auto it = signal_cell_set.find(bit); + if (it == signal_cell_set.end()) + signal_cell_set[bit] = i; + else if (it->second >= 0 && it->second != i) { + it->second = -1; + assign_map.addVal(bit, AbcSigVal(true)); + } + } + } + } + } + for (RTLIL::Cell *cell : cells_in_no_set) { + for (auto &port_it : cell->connections()) { + for (SigBit bit : port_it.second) { + assign_map.apply(bit); + auto it = signal_cell_set.find(bit); + if (it != signal_cell_set.end() && it->second >= 0) + assign_map.addVal(bit, AbcSigVal(true)); + } + } + } +} + struct AbcPass : public Pass { AbcPass() : Pass("abc", "use ABC for technology mapping") { } void help() override @@ -1640,8 +1757,6 @@ struct AbcPass : public Pass { log("\n"); log(" -sop\n"); log(" map to sum-of-product cells and inverters\n"); - log(" -map_src \n"); - log(" source attribution for cells created by ABC\n"); log("\n"); // log(" -mux4, -mux8, -mux16\n"); // log(" try to extract 4-input, 8-input, and/or 16-input muxes\n"); @@ -1720,72 +1835,52 @@ struct AbcPass : public Pass { log_header(design, "Executing ABC pass (technology mapping using ABC).\n"); log_push(); - assign_map.clear(); - signal_list.clear(); - signal_map.clear(); - initvals.clear(); - pi_map.clear(); - po_map.clear(); - - std::string exe_file = yosys_abc_executable; - std::string script_file, default_liberty_file, constr_file, clk_str; - std::vector liberty_files, genlib_files, dont_use_cells; - std::string delay_target, sop_inputs, sop_products, lutin_shared = "-S 1"; - bool fast_mode = false, dff_mode = false, keepff = false, cleanup = true; - bool show_tempdir = false, sop_mode = false, word_mode = false; - bool abc_dress = false; - vector lut_costs; - markgroups = false; - std::string map_src; - map_mux4 = false; - map_mux8 = false; - map_mux16 = false; - enabled_gates.clear(); - cmos_cost = false; + AbcConfig config; // get arguments from scratchpad first, then override by command arguments std::string lut_arg, luts_arg, g_arg; - exe_file = design->scratchpad_get_string("abc.exe", exe_file /* inherit default value if not set */); - script_file = design->scratchpad_get_string("abc.script", script_file); - default_liberty_file = design->scratchpad_get_string("abc.liberty", default_liberty_file); - constr_file = design->scratchpad_get_string("abc.constr", constr_file); + config.exe_file = design->scratchpad_get_string("abc.exe", yosys_abc_executable /* inherit default value if not set */); + config.script_file = design->scratchpad_get_string("abc.script", ""); + std::string default_liberty_file = design->scratchpad_get_string("abc.liberty", ""); + config.constr_file = design->scratchpad_get_string("abc.constr", ""); if (design->scratchpad.count("abc.D")) { - delay_target = "-D " + design->scratchpad_get_string("abc.D"); + config.delay_target = "-D " + design->scratchpad_get_string("abc.D"); } if (design->scratchpad.count("abc.I")) { - sop_inputs = "-I " + design->scratchpad_get_string("abc.I"); + config.sop_inputs = "-I " + design->scratchpad_get_string("abc.I"); } if (design->scratchpad.count("abc.P")) { - sop_products = "-P " + design->scratchpad_get_string("abc.P"); + config.sop_products = "-P " + design->scratchpad_get_string("abc.P"); } if (design->scratchpad.count("abc.S")) { - lutin_shared = "-S " + design->scratchpad_get_string("abc.S"); + config.lutin_shared = "-S " + design->scratchpad_get_string("abc.S"); + } else { + config.lutin_shared = "-S 1"; } lut_arg = design->scratchpad_get_string("abc.lut", lut_arg); luts_arg = design->scratchpad_get_string("abc.luts", luts_arg); - sop_mode = design->scratchpad_get_bool("abc.sop", sop_mode); - word_mode = design->scratchpad_get_bool("abc.word", word_mode); + config.sop_mode = design->scratchpad_get_bool("abc.sop", false); map_mux4 = design->scratchpad_get_bool("abc.mux4", map_mux4); map_mux8 = design->scratchpad_get_bool("abc.mux8", map_mux8); map_mux16 = design->scratchpad_get_bool("abc.mux16", map_mux16); - abc_dress = design->scratchpad_get_bool("abc.dress", abc_dress); + config.abc_dress = design->scratchpad_get_bool("abc.dress", false); g_arg = design->scratchpad_get_string("abc.g", g_arg); - fast_mode = design->scratchpad_get_bool("abc.fast", fast_mode); - dff_mode = design->scratchpad_get_bool("abc.dff", dff_mode); + config.fast_mode = design->scratchpad_get_bool("abc.fast", false); + bool dff_mode = design->scratchpad_get_bool("abc.dff", false); + std::string clk_str; if (design->scratchpad.count("abc.clk")) { clk_str = design->scratchpad_get_string("abc.clk"); dff_mode = true; } - keepff = design->scratchpad_get_bool("abc.keepff", keepff); - cleanup = !design->scratchpad_get_bool("abc.nocleanup", !cleanup); - keepff = design->scratchpad_get_bool("abc.keepff", keepff); - show_tempdir = design->scratchpad_get_bool("abc.showtmp", show_tempdir); + config.keepff = design->scratchpad_get_bool("abc.keepff", false); + config.cleanup = !design->scratchpad_get_bool("abc.nocleanup", false); + config.show_tempdir = design->scratchpad_get_bool("abc.showtmp", false); markgroups = design->scratchpad_get_bool("abc.markgroups", markgroups); if (design->scratchpad_get_bool("abc.debug")) { - cleanup = false; - show_tempdir = true; + config.cleanup = false; + config.show_tempdir = true; } size_t argidx, g_argidx = -1; @@ -1802,63 +1897,63 @@ struct AbcPass : public Pass { for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; if (arg == "-exe" && argidx+1 < args.size()) { - exe_file = args[++argidx]; + config.exe_file = args[++argidx]; continue; } if (arg == "-script" && argidx+1 < args.size()) { - script_file = args[++argidx]; + config.script_file = args[++argidx]; continue; } if (arg == "-liberty" && argidx+1 < args.size()) { - liberty_files.push_back(args[++argidx]); + config.liberty_files.push_back(args[++argidx]); continue; } if (arg == "-dont_use" && argidx+1 < args.size()) { - dont_use_cells.push_back(args[++argidx]); + config.dont_use_cells.push_back(args[++argidx]); continue; } if (arg == "-genlib" && argidx+1 < args.size()) { - genlib_files.push_back(args[++argidx]); + config.genlib_files.push_back(args[++argidx]); continue; } if (arg == "-constr" && argidx+1 < args.size()) { - constr_file = args[++argidx]; + config.constr_file = args[++argidx]; continue; } if (arg == "-D" && argidx+1 < args.size()) { - delay_target = "-D " + args[++argidx]; + config.delay_target = "-D " + args[++argidx]; continue; } if (arg == "-I" && argidx+1 < args.size()) { - sop_inputs = "-I " + args[++argidx]; + config.sop_inputs = "-I " + args[++argidx]; continue; } if (arg == "-P" && argidx+1 < args.size()) { - sop_products = "-P " + args[++argidx]; + config.sop_products = "-P " + args[++argidx]; continue; } if (arg == "-S" && argidx+1 < args.size()) { - lutin_shared = "-S " + args[++argidx]; + config.lutin_shared = "-S " + args[++argidx]; continue; } if (arg == "-lut" && argidx+1 < args.size()) { lut_arg = args[++argidx]; continue; } - if (arg == "-map_src" && argidx+1 < args.size()) { - map_src = args[++argidx]; - continue; - } if (arg == "-luts" && argidx+1 < args.size()) { luts_arg = args[++argidx]; continue; } + if (arg == "-map_src" && argidx+1 < args.size()) { + config.map_src = args[++argidx]; + continue; + } if (arg == "-sop") { - sop_mode = true; + config.sop_mode = true; continue; } if (arg == "-word") { - word_mode = true; + config.word_mode = true; continue; } if (arg == "-mux4") { @@ -1874,7 +1969,7 @@ struct AbcPass : public Pass { continue; } if (arg == "-dress") { - abc_dress = true; + config.abc_dress = true; continue; } if (arg == "-g" && argidx+1 < args.size()) { @@ -1886,7 +1981,7 @@ struct AbcPass : public Pass { continue; } if (arg == "-fast") { - fast_mode = true; + config.fast_mode = true; continue; } if (arg == "-dff") { @@ -1899,15 +1994,15 @@ struct AbcPass : public Pass { continue; } if (arg == "-keepff") { - keepff = true; + config.keepff = true; continue; } if (arg == "-nocleanup") { - cleanup = false; + config.cleanup = false; continue; } if (arg == "-showtmp") { - show_tempdir = true; + config.show_tempdir = true; continue; } if (arg == "-markgroups") { @@ -1918,25 +2013,25 @@ struct AbcPass : public Pass { } extra_args(args, argidx, design); - if (genlib_files.empty() && liberty_files.empty() && !default_liberty_file.empty()) - liberty_files.push_back(default_liberty_file); + if (config.genlib_files.empty() && config.liberty_files.empty() && !default_liberty_file.empty()) + config.liberty_files.push_back(default_liberty_file); - rewrite_filename(script_file); - if (!script_file.empty() && !is_absolute_path(script_file) && script_file[0] != '+') - script_file = std::string(pwd) + "/" + script_file; - for (int i = 0; i < GetSize(liberty_files); i++) { - rewrite_filename(liberty_files[i]); - if (!liberty_files[i].empty() && !is_absolute_path(liberty_files[i])) - liberty_files[i] = std::string(pwd) + "/" + liberty_files[i]; + rewrite_filename(config.script_file); + if (!config.script_file.empty() && !is_absolute_path(config.script_file) && config.script_file[0] != '+') + config.script_file = std::string(pwd) + "/" + config.script_file; + for (int i = 0; i < GetSize(config.liberty_files); i++) { + rewrite_filename(config.liberty_files[i]); + if (!config.liberty_files[i].empty() && !is_absolute_path(config.liberty_files[i])) + config.liberty_files[i] = std::string(pwd) + "/" + config.liberty_files[i]; } - for (int i = 0; i < GetSize(genlib_files); i++) { - rewrite_filename(genlib_files[i]); - if (!genlib_files[i].empty() && !is_absolute_path(genlib_files[i])) - genlib_files[i] = std::string(pwd) + "/" + genlib_files[i]; + for (int i = 0; i < GetSize(config.genlib_files); i++) { + rewrite_filename(config.genlib_files[i]); + if (!config.genlib_files[i].empty() && !is_absolute_path(config.genlib_files[i])) + config.genlib_files[i] = std::string(pwd) + "/" + config.genlib_files[i]; } - rewrite_filename(constr_file); - if (!constr_file.empty() && !is_absolute_path(constr_file)) - constr_file = std::string(pwd) + "/" + constr_file; + rewrite_filename(config.constr_file); + if (!config.constr_file.empty() && !is_absolute_path(config.constr_file)) + config.constr_file = std::string(pwd) + "/" + config.constr_file; // handle -lut argument if (!lut_arg.empty()) { @@ -1949,24 +2044,24 @@ struct AbcPass : public Pass { lut_mode = atoi(lut_arg.c_str()); lut_mode2 = lut_mode; } - lut_costs.clear(); + config.lut_costs.clear(); for (int i = 0; i < lut_mode; i++) - lut_costs.push_back(1); + config.lut_costs.push_back(1); for (int i = lut_mode; i < lut_mode2; i++) - lut_costs.push_back(2 << (i - lut_mode)); + config.lut_costs.push_back(2 << (i - lut_mode)); } //handle -luts argument if (!luts_arg.empty()){ - lut_costs.clear(); + config.lut_costs.clear(); for (auto &tok : split_tokens(luts_arg, ",")) { auto parts = split_tokens(tok, ":"); - if (GetSize(parts) == 0 && !lut_costs.empty()) - lut_costs.push_back(lut_costs.back()); + if (GetSize(parts) == 0 && !config.lut_costs.empty()) + config.lut_costs.push_back(config.lut_costs.back()); else if (GetSize(parts) == 1) - lut_costs.push_back(atoi(parts.at(0).c_str())); + config.lut_costs.push_back(atoi(parts.at(0).c_str())); else if (GetSize(parts) == 2) - while (GetSize(lut_costs) < std::atoi(parts.at(0).c_str())) - lut_costs.push_back(atoi(parts.at(1).c_str())); + while (GetSize(config.lut_costs) < std::atoi(parts.at(0).c_str())) + config.lut_costs.push_back(atoi(parts.at(1).c_str())); else log_cmd_error("Invalid -luts syntax.\n"); } @@ -2097,9 +2192,9 @@ struct AbcPass : public Pass { } } - if (!lut_costs.empty() && !(liberty_files.empty() && genlib_files.empty())) + if (!config.lut_costs.empty() && !(config.liberty_files.empty() && config.genlib_files.empty())) log_cmd_error("Got -lut and -liberty/-genlib! These two options are exclusive.\n"); - if (!constr_file.empty() && (liberty_files.empty() && genlib_files.empty())) + if (!config.constr_file.empty() && (config.liberty_files.empty() && config.genlib_files.empty())) log_cmd_error("Got -constr but no -liberty/-genlib!\n"); if (enabled_gates.empty()) { @@ -2126,12 +2221,26 @@ struct AbcPass : public Pass { continue; } + AbcSigMap assign_map; assign_map.set(mod); + // Create an FfInitVals and use it for all ABC runs. FfInitVals only cares about + // wires with the ID::init attribute and we don't add or remove any such wires + // in this pass. + FfInitVals initvals; initvals.set(&assign_map, mod); + for (auto wire : mod->wires()) + if (wire->port_id > 0 || wire->get_bool_attribute(ID::keep)) + assign_map.addVal(SigSpec(wire), AbcSigVal(true)); + if (!dff_mode || !clk_str.empty()) { - abc_module(design, mod, script_file, exe_file, liberty_files, genlib_files, constr_file, cleanup, lut_costs, dff_mode, clk_str, keepff, - delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, mod->selected_cells(), show_tempdir, sop_mode, word_mode, abc_dress, dont_use_cells, map_src); + std::vector cells = mod->selected_cells(); + assign_cell_connection_ports(mod, {&cells}, assign_map); + + AbcModuleState state(config, initvals); + state.abc_module(design, mod, assign_map, cells, dff_mode, clk_str); + state.extract(assign_map, design, mod); + state.finish(); continue; } @@ -2283,28 +2392,28 @@ struct AbcPass : public Pass { std::get<4>(it.first) ? "" : "!", log_signal(std::get<5>(it.first)), std::get<6>(it.first) ? "" : "!", log_signal(std::get<7>(it.first))); + { + std::vector*> cell_sets; + for (auto &it : assigned_cells) + cell_sets.push_back(&it.second); + assign_cell_connection_ports(mod, cell_sets, assign_map); + } for (auto &it : assigned_cells) { - clk_polarity = std::get<0>(it.first); - clk_sig = assign_map(std::get<1>(it.first)); - en_polarity = std::get<2>(it.first); - en_sig = assign_map(std::get<3>(it.first)); - arst_polarity = std::get<4>(it.first); - arst_sig = assign_map(std::get<5>(it.first)); - srst_polarity = std::get<6>(it.first); - srst_sig = assign_map(std::get<7>(it.first)); - abc_module(design, mod, script_file, exe_file, liberty_files, genlib_files, constr_file, cleanup, lut_costs, !clk_sig.empty(), "$", - keepff, delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, it.second, show_tempdir, sop_mode, word_mode, abc_dress, dont_use_cells, map_src); - assign_map.set(mod); + AbcModuleState state(config, initvals); + state.clk_polarity = std::get<0>(it.first); + state.clk_sig = assign_map(std::get<1>(it.first)); + state.en_polarity = std::get<2>(it.first); + state.en_sig = assign_map(std::get<3>(it.first)); + state.arst_polarity = std::get<4>(it.first); + state.arst_sig = assign_map(std::get<5>(it.first)); + state.srst_polarity = std::get<6>(it.first); + state.srst_sig = assign_map(std::get<7>(it.first)); + state.abc_module(design, mod, assign_map, it.second, !state.clk_sig.empty(), "$"); + state.extract(assign_map, design, mod); + state.finish(); } } - assign_map.clear(); - signal_list.clear(); - signal_map.clear(); - initvals.clear(); - pi_map.clear(); - po_map.clear(); - log_pop(); } } AbcPass; diff --git a/setup.py b/setup.py index 363fa5612..d31119d73 100644 --- a/setup.py +++ b/setup.py @@ -51,16 +51,27 @@ class libyosys_so_ext(Extension): ] def custom_build(self, bext: build_ext): + make_flags_split = shlex.split(os.getenv("makeFlags", "")) + # abc linking takes a lot of memory, best get it out of the way first + bext.spawn( + [ + "make", + f"-j{os.cpu_count() or 1}", + "yosys-abc", + *make_flags_split, + *self.args, + ] + ) + # build libyosys and share with abc out of the way bext.spawn( [ "make", f"-j{os.cpu_count() or 1}", self.name, - "yosys-abc", "share", + *make_flags_split, + *self.args, ] - + shlex.split(os.getenv("makeFlags", "")) - + self.args ) build_path = os.path.dirname(os.path.dirname(bext.get_ext_fullpath(self.name))) pyosys_path = os.path.join(build_path, "pyosys") @@ -87,6 +98,7 @@ class libyosys_so_ext(Extension): shutil.copytree("share", share_target) + class custom_build_ext(build_ext): def build_extension(self, ext) -> None: if not hasattr(ext, "custom_build"): @@ -102,8 +114,8 @@ setup( long_description=open(os.path.join(__dir__, "README.md")).read(), long_description_content_type="text/markdown", install_requires=["wheel", "setuptools"], + license="MIT", classifiers=[ - "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", "Intended Audience :: Developers", "Operating System :: POSIX :: Linux", diff --git a/tests/unit/kernel/hashTest.cc b/tests/unit/kernel/hashTest.cc new file mode 100644 index 000000000..319d064a7 --- /dev/null +++ b/tests/unit/kernel/hashTest.cc @@ -0,0 +1,67 @@ +#include +#include "kernel/yosys_common.h" + +#include + +YOSYS_NAMESPACE_BEGIN + +static Hasher hash(int x) +{ + Hasher h; + h.eat(x); + return h; +} + +TEST(CommutativeTest, basic) +{ + hashlib::commutative_hash comm1; + comm1.eat(hash(1)); + comm1.eat(hash(2)); + hashlib::commutative_hash comm2; + comm2.eat(hash(2)); + comm2.eat(hash(1)); + EXPECT_EQ(comm1.hash_into(Hasher()).yield(), comm2.hash_into(Hasher()).yield()); +} + +TEST(PoolHashTest, collisions) +{ + uint64_t collisions = 0; + std::unordered_set hashes; + for (int i = 0; i < 1000; ++i) { + for (int j = i + 1; j < 1000; ++j) { + pool p1; + p1.insert(i); + p1.insert(j); + auto h = p1.hash_into(Hasher()).yield(); + if (!hashes.insert(h).second) { + ++collisions; + } + } + } + std::cout << "pool collisions: " << collisions << std::endl; + EXPECT_LT(collisions, 10'000); +} + +TEST(PoolHashTest, subset_collisions) +{ + uint64_t collisions = 0; + std::unordered_set hashes; + for (int i = 0; i < 1000 * 1000; ++i) { + + pool p1; + for (int b = 0; i >> b; ++b) { + if ((i >> b) & 1) { + p1.insert(b); + } + } + auto h = p1.hash_into(Hasher()).yield(); + if (!hashes.insert(h).second) { + ++collisions; + } + + } + std::cout << "pool subset collisions: " << collisions << std::endl; + EXPECT_LT(collisions, 100); +} + +YOSYS_NAMESPACE_END diff --git a/tests/unit/kernel/ioTest.cc b/tests/unit/kernel/ioTest.cc index 7cb8498cb..43a71eb79 100644 --- a/tests/unit/kernel/ioTest.cc +++ b/tests/unit/kernel/ioTest.cc @@ -69,4 +69,14 @@ TEST(KernelStringfTest, dynamicWidthAndPrecision) EXPECT_EQ(stringf("%*.*f", 8, 4, 1.0), " 1.0000"); } +TEST(KernelStringfTest, dynamicPrecisionInt) +{ + EXPECT_EQ(stringf("%.*d", 4, 7), "0007"); +} + +TEST(KernelStringfTest, dynamicWidthAndPrecisionInt) +{ + EXPECT_EQ(stringf("%*.*d", 8, 4, 7), " 0007"); +} + YOSYS_NAMESPACE_END diff --git a/tests/various/abstract_initstates.ys b/tests/various/abstract_initstates.ys new file mode 100644 index 000000000..35d17cf62 --- /dev/null +++ b/tests/various/abstract_initstates.ys @@ -0,0 +1,30 @@ +read_verilog <