diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 24ae7c898..75d799fe1 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -16,7 +16,9 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 with: - submodules: true + submodules: true + persist-credentials: false + - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: diff --git a/.github/workflows/extra-builds.yml b/.github/workflows/extra-builds.yml index 1a00d0163..0c3146e41 100644 --- a/.github/workflows/extra-builds.yml +++ b/.github/workflows/extra-builds.yml @@ -25,7 +25,8 @@ jobs: steps: - uses: actions/checkout@v4 with: - submodules: true + submodules: true + persist-credentials: false - name: Build run: make vcxsrc YOSYS_VER=latest - uses: actions/upload-artifact@v4 @@ -59,7 +60,8 @@ jobs: steps: - uses: actions/checkout@v4 with: - submodules: true + submodules: true + persist-credentials: false - name: Build run: | WASI_SDK=wasi-sdk-19.0 @@ -95,6 +97,7 @@ jobs: - uses: actions/checkout@v4 with: submodules: true + persist-credentials: false - uses: cachix/install-nix-action@v26 with: install_url: https://releases.nixos.org/nix/nix-2.18.1/install diff --git a/.github/workflows/prepare-docs.yml b/.github/workflows/prepare-docs.yml index 79dfb7912..fb1fab426 100644 --- a/.github/workflows/prepare-docs.yml +++ b/.github/workflows/prepare-docs.yml @@ -20,7 +20,7 @@ jobs: # only run on push *or* pull_request, not both concurrent_skipping: ${{ env.docs_export && 'never' || 'same_content_newer'}} - id: docs_var - run: echo "docs_export=${{ env.docs_export }}" >> $GITHUB_OUTPUT + run: echo "docs_export=${docs_export}" >> $GITHUB_OUTPUT prepare-docs: # docs builds are needed for anything on main, any tagged versions, and any tag @@ -47,12 +47,12 @@ jobs: echo "ENABLE_VERIFIC_LIBERTY := 1" >> Makefile.conf echo "ENABLE_VERIFIC_YOSYSHQ_EXTENSIONS := 1" >> Makefile.conf echo "ENABLE_CCACHE := 1" >> Makefile.conf - make -j${{ env.procs }} ENABLE_LTO=1 + make -j$procs ENABLE_LTO=1 - name: Prepare docs shell: bash run: - make docs/prep -j${{ env.procs }} TARGETS= EXTRA_TARGETS= + make docs/prep -j$procs TARGETS= EXTRA_TARGETS= - name: Upload artifact uses: actions/upload-artifact@v4 @@ -72,7 +72,7 @@ jobs: - name: Test build docs shell: bash run: | - make -C docs html -j${{ env.procs }} TARGETS= EXTRA_TARGETS= + make -C docs html -j$procs TARGETS= EXTRA_TARGETS= - name: Trigger RTDs build if: ${{ needs.check_docs_rebuild.outputs.docs_export == 'true' }} diff --git a/.github/workflows/source-vendor.yml b/.github/workflows/source-vendor.yml index 4dddb9a20..dc9480ef6 100644 --- a/.github/workflows/source-vendor.yml +++ b/.github/workflows/source-vendor.yml @@ -10,6 +10,7 @@ jobs: uses: actions/checkout@v4 with: submodules: 'recursive' + persist-credentials: false - name: Create clean tarball run: | diff --git a/.github/workflows/test-build.yml b/.github/workflows/test-build.yml index 6a69caaea..b88662f0f 100644 --- a/.github/workflows/test-build.yml +++ b/.github/workflows/test-build.yml @@ -46,6 +46,7 @@ jobs: uses: actions/checkout@v4 with: submodules: true + persist-credentials: false - name: Setup environment uses: ./.github/actions/setup-build-env @@ -91,6 +92,8 @@ jobs: steps: - name: Checkout Yosys uses: actions/checkout@v4 + with: + persist-credentials: false - name: Setup environment uses: ./.github/actions/setup-build-env @@ -117,7 +120,7 @@ jobs: uses: actions/cache@v4 with: path: .local/ - key: ${{ matrix.os }}-${{ env.IVERILOG_GIT }} + key: ${{ matrix.os }}-${IVERILOG_GIT} - name: Build iverilog if: steps.cache-iverilog.outputs.cache-hit != 'true' @@ -169,6 +172,8 @@ jobs: steps: - name: Checkout Yosys uses: actions/checkout@v4 + with: + persist-credentials: false - name: Setup environment uses: ./.github/actions/setup-build-env @@ -190,7 +195,7 @@ jobs: - name: Run tests shell: bash run: | - make -C docs test -j${{ env.procs }} + make -C docs test -j$procs test-docs-build: name: Try build docs @@ -206,6 +211,7 @@ jobs: uses: actions/checkout@v4 with: submodules: true + persist-credentials: false - name: Runtime environment run: | @@ -215,7 +221,7 @@ jobs: run: | make config-clang echo "ENABLE_CCACHE := 1" >> Makefile.conf - make -j${{ env.procs }} + make -j$procs - name: Install doc prereqs shell: bash @@ -225,7 +231,7 @@ jobs: - name: Build docs shell: bash run: | - make docs DOC_TARGET=${{ matrix.docs-target }} -j${{ env.procs }} + make docs DOC_TARGET=${{ matrix.docs-target }} -j$procs - name: Store docs build artifact uses: actions/upload-artifact@v4 diff --git a/.github/workflows/test-compile.yml b/.github/workflows/test-compile.yml index 203c20bb4..7a706e69a 100644 --- a/.github/workflows/test-compile.yml +++ b/.github/workflows/test-compile.yml @@ -45,6 +45,7 @@ jobs: uses: actions/checkout@v4 with: submodules: true + persist-credentials: false - name: Setup environment uses: ./.github/actions/setup-build-env diff --git a/.github/workflows/test-verific.yml b/.github/workflows/test-verific.yml index 98b05e8dd..013c9f8ca 100644 --- a/.github/workflows/test-verific.yml +++ b/.github/workflows/test-verific.yml @@ -40,7 +40,7 @@ jobs: echo "ENABLE_VERIFIC_YOSYSHQ_EXTENSIONS := 1" >> Makefile.conf echo "ENABLE_CCACHE := 1" >> Makefile.conf echo "ENABLE_FUNCTIONAL_TESTS := 1" >> Makefile.conf - make -j${{ env.procs }} ENABLE_LTO=1 + make -j$procs ENABLE_LTO=1 - name: Install Yosys run: | @@ -51,6 +51,7 @@ jobs: with: repository: 'YosysHQ/sby' path: 'sby' + persist-credentials: false - name: Build SBY run: | @@ -58,7 +59,7 @@ jobs: - name: Run Yosys tests run: | - make -j${{ env.procs }} test + make -j$procs test - name: Run Verific specific Yosys tests run: | diff --git a/.github/workflows/update-flake-lock.yml b/.github/workflows/update-flake-lock.yml index c7aa6ecab..de7ef04d6 100644 --- a/.github/workflows/update-flake-lock.yml +++ b/.github/workflows/update-flake-lock.yml @@ -10,6 +10,8 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 + with: + persist-credentials: false - name: Install Nix uses: DeterminateSystems/nix-installer-action@main - name: Update flake.lock diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml index f73c68bdf..26dcba4a4 100644 --- a/.github/workflows/version.yml +++ b/.github/workflows/version.yml @@ -14,6 +14,7 @@ jobs: with: fetch-depth: 0 submodules: true + persist-credentials: false - name: Take last commit id: log run: echo "message=$(git log --no-merges -1 --oneline)" >> $GITHUB_OUTPUT diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index d66239a16..b01ce6b3a 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -1,6 +1,10 @@ name: Build Wheels for PyPI + +# run every Sunday at 10 AM on: workflow_dispatch: + schedule: + - cron: '0 10 * * 0' jobs: build_wheels: @@ -14,16 +18,12 @@ jobs: runner: "ubuntu-22.04", archs: "x86_64", }, - ## Aarch64 is disabled for now: GitHub is committing to EOY - ## for free aarch64 runners for open-source projects and - ## emulation times out: - ## https://github.com/orgs/community/discussions/19197#discussioncomment-10550689 - # { - # name: "Ubuntu 22.04", - # family: "linux", - # runner: "ubuntu-22.04", - # archs: "aarch64", - # }, + { + name: "Ubuntu 22.04", + family: "linux", + runner: "ubuntu-22.04-arm", + archs: "aarch64", + }, { name: "macOS 13", family: "macos", @@ -53,6 +53,7 @@ jobs: with: fetch-depth: 0 submodules: true + persist-credentials: false - if: ${{ matrix.os.family == 'linux' }} name: "[Linux] Set up QEMU" uses: docker/setup-qemu-action@v3 diff --git a/Makefile b/Makefile index 89723e99d..76ac4bb18 100644 --- a/Makefile +++ b/Makefile @@ -102,6 +102,7 @@ LIBS := $(LIBS) -lstdc++ -lm PLUGIN_LINKFLAGS := PLUGIN_LIBS := EXE_LINKFLAGS := +EXE_LIBS := ifeq ($(OS), MINGW) EXE_LINKFLAGS := -Wl,--export-all-symbols -Wl,--out-implib,libyosys_exe.a PLUGIN_LINKFLAGS += -L"$(LIBDIR)" @@ -159,7 +160,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.52+0 +YOSYS_VER := 0.52+63 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2) @@ -209,11 +210,11 @@ PYTHON_VERSION_TESTCODE := "import sys;t='{v[0]}.{v[1]}'.format(v=list(sys.versi PYTHON_VERSION := $(shell $(PYTHON_EXECUTABLE) -c ""$(PYTHON_VERSION_TESTCODE)"") PYTHON_MAJOR_VERSION := $(shell echo $(PYTHON_VERSION) | cut -f1 -d.) -ENABLE_PYTHON_CONFIG_EMBED ?= $(shell $(PYTHON_EXECUTABLE)-config --embed --libs > /dev/null && echo 1) -ifeq ($(ENABLE_PYTHON_CONFIG_EMBED),1) -PYTHON_CONFIG := $(PYTHON_EXECUTABLE)-config --embed -else PYTHON_CONFIG := $(PYTHON_EXECUTABLE)-config +PYTHON_CONFIG_FOR_EXE := $(PYTHON_CONFIG) +PYTHON_CONFIG_EMBED_AVAILABLE ?= $(shell $(PYTHON_EXECUTABLE)-config --embed --libs > /dev/null && echo 1) +ifeq ($(PYTHON_CONFIG_EMBED_AVAILABLE),1) +PYTHON_CONFIG_FOR_EXE := $(PYTHON_CONFIG) --embed endif PYTHON_DESTDIR := $(shell $(PYTHON_EXECUTABLE) -c "import site; print(site.getsitepackages()[-1]);") @@ -346,11 +347,11 @@ ifeq ($(ENABLE_PYOSYS),1) # python-config --ldflags includes -l and -L, but LINKFLAGS is only -L LINKFLAGS += $(filter-out -l%,$(shell $(PYTHON_CONFIG) --ldflags)) LIBS += $(shell $(PYTHON_CONFIG) --libs) +EXE_LIBS += $(filter-out $(LIBS),$(shell $(PYTHON_CONFIG_FOR_EXE) --libs)) CXXFLAGS += $(shell $(PYTHON_CONFIG) --includes) -DWITH_PYTHON -EXTRA_TARGETS += wheel # Detect name of boost_python library. Some distros use boost_python-py, other boost_python, some only use the major version number, some a concatenation of major and minor version numbers -CHECK_BOOST_PYTHON = (echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(LINKFLAGS) $(LIBS) -l$(1) - > /dev/null 2>&1 && echo "-l$(1)") +CHECK_BOOST_PYTHON = (echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(LINKFLAGS) $(EXE_LIBS) $(LIBS) -l$(1) - > /dev/null 2>&1 && echo "-l$(1)") BOOST_PYTHON_LIB ?= $(shell \ $(call CHECK_BOOST_PYTHON,boost_python-py$(subst .,,$(PYTHON_VERSION))) || \ $(call CHECK_BOOST_PYTHON,boost_python-py$(PYTHON_MAJOR_VERSION)) || \ @@ -735,7 +736,7 @@ share: $(EXTRA_TARGETS) @echo "" $(PROGRAM_PREFIX)yosys$(EXE): $(OBJS) - $(P) $(CXX) -o $(PROGRAM_PREFIX)yosys$(EXE) $(EXE_LINKFLAGS) $(LINKFLAGS) $(OBJS) $(LIBS) $(LIBS_VERIFIC) + $(P) $(CXX) -o $(PROGRAM_PREFIX)yosys$(EXE) $(EXE_LINKFLAGS) $(LINKFLAGS) $(OBJS) $(EXE_LIBS) $(LIBS) $(LIBS_VERIFIC) libyosys.so: $(filter-out kernel/driver.o,$(OBJS)) ifeq ($(OS), Darwin) @@ -890,6 +891,7 @@ SH_TEST_DIRS += tests/rpc SH_TEST_DIRS += tests/memfile SH_TEST_DIRS += tests/fmt SH_TEST_DIRS += tests/cxxrtl +SH_TEST_DIRS += tests/liberty ifeq ($(ENABLE_FUNCTIONAL_TESTS),1) SH_TEST_DIRS += tests/functional endif @@ -966,20 +968,6 @@ unit-test: libyosys.so clean-unit-test: @$(MAKE) -C $(UNITESTPATH) clean -ifeq ($(ENABLE_PYOSYS),1) -wheel: $(TARGETS) - $(PYTHON_EXECUTABLE) -m pip wheel . - -install-wheel: wheel - $(PYTHON_EXECUTABLE) -m pip install pyosys-$(YOSYS_MAJOR).$(YOSYS_MINOR).$(YOSYS_COMMIT)-*.whl --force-reinstall -else -wheel: - $(error Pyosys is not enabled. Set ENABLE_PYOSYS=1 to enable it.) - -install-wheel: - $(error Pyosys is not enabled. Set ENABLE_PYOSYS=1 to enable it.) -endif - install: $(TARGETS) $(EXTRA_TARGETS) $(INSTALL_SUDO) mkdir -p $(DESTDIR)$(BINDIR) $(INSTALL_SUDO) cp $(filter-out libyosys.so,$(TARGETS)) $(DESTDIR)$(BINDIR) @@ -999,7 +987,13 @@ ifeq ($(ENABLE_LIBYOSYS),1) $(INSTALL_SUDO) cp libyosys.so $(DESTDIR)$(LIBDIR)/ $(INSTALL_SUDO) $(STRIP) -S $(DESTDIR)$(LIBDIR)/libyosys.so ifeq ($(ENABLE_PYOSYS),1) - $(INSTALL_SUDO) @$(MAKE) install-wheel + $(INSTALL_SUDO) mkdir -p $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys + $(INSTALL_SUDO) cp libyosys.so $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys/libyosys.so + $(INSTALL_SUDO) cp -r share $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys +ifeq ($(ENABLE_ABC),1) + $(INSTALL_SUDO) cp yosys-abc $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys/yosys-abc +endif + $(INSTALL_SUDO) cp misc/__init__.py $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys/ endif endif ifeq ($(ENABLE_PLUGINS),1) @@ -1015,7 +1009,9 @@ uninstall: ifeq ($(ENABLE_LIBYOSYS),1) $(INSTALL_SUDO) rm -vf $(DESTDIR)$(LIBDIR)/libyosys.so ifeq ($(ENABLE_PYOSYS),1) - $(INSTALL_SUDO) $(PYTHON_EXECUTABLE) -m pip uninstall -y pyosys + $(INSTALL_SUDO) rm -vf $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys/libyosys.so + $(INSTALL_SUDO) rm -vf $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys/__init__.py + $(INSTALL_SUDO) rmdir $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys endif endif diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index b9958c5fb..052699ad6 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -200,7 +200,7 @@ bool is_extending_cell(RTLIL::IdString type) bool is_inlinable_cell(RTLIL::IdString type) { return is_unary_cell(type) || is_binary_cell(type) || type.in( - ID($mux), ID($concat), ID($slice), ID($pmux), ID($bmux), ID($demux)); + ID($mux), ID($concat), ID($slice), ID($pmux), ID($bmux), ID($demux), ID($bwmux)); } bool is_ff_cell(RTLIL::IdString type) @@ -1198,6 +1198,14 @@ struct CxxrtlWorker { f << ">("; dump_sigspec_rhs(cell->getPort(ID::S), for_debug); f << ").val()"; + // Bitwise muxes + } else if (cell->type == ID($bwmux)) { + dump_sigspec_rhs(cell->getPort(ID::A), for_debug); + f << ".bwmux("; + dump_sigspec_rhs(cell->getPort(ID::B), for_debug); + f << ","; + dump_sigspec_rhs(cell->getPort(ID::S), for_debug); + f << ").val()"; // Demuxes } else if (cell->type == ID($demux)) { dump_sigspec_rhs(cell->getPort(ID::A), for_debug); diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h index 9634b833b..37c84895f 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h @@ -498,6 +498,11 @@ struct value : public expr_base> { return result; } + CXXRTL_ALWAYS_INLINE + value bwmux(const value &b, const value &s) const { + return (bit_and(s.bit_not())).bit_or(b.bit_and(s)); + } + template value demux(const value &sel) const { static_assert(Bits << SelBits == ResultBits, "invalid sizes used in demux()"); diff --git a/backends/firrtl/firrtl.cc b/backends/firrtl/firrtl.cc index eac0c9719..ceb805dcb 100644 --- a/backends/firrtl/firrtl.cc +++ b/backends/firrtl/firrtl.cc @@ -1215,9 +1215,6 @@ struct FirrtlBackend : public Backend { } extra_args(f, filename, args, argidx); - if (!design->full_selection()) - log_cmd_error("This command only operates on fully selected designs!\n"); - log_header(design, "Executing FIRRTL backend.\n"); log_push(); @@ -1230,7 +1227,7 @@ struct FirrtlBackend : public Backend { autoid_counter = 0; // Get the top module, or a reasonable facsimile - we need something for the circuit name. - Module *top = design->top_module(); + Module *top = nullptr; Module *last = nullptr; // Generate module and wire names. for (auto module : design->modules()) { diff --git a/backends/rtlil/rtlil_backend.cc b/backends/rtlil/rtlil_backend.cc index 113f1a615..ae60ee6c7 100644 --- a/backends/rtlil/rtlil_backend.cc +++ b/backends/rtlil/rtlil_backend.cc @@ -304,8 +304,8 @@ void RTLIL_BACKEND::dump_conn(std::ostream &f, std::string indent, const RTLIL:: void RTLIL_BACKEND::dump_module(std::ostream &f, std::string indent, RTLIL::Module *module, RTLIL::Design *design, bool only_selected, bool flag_m, bool flag_n) { - bool print_header = flag_m || design->selected_whole_module(module->name); - bool print_body = !flag_n || !design->selected_whole_module(module->name); + bool print_header = flag_m || module->is_selected_whole(); + bool print_body = !flag_n || !module->is_selected_whole(); if (print_header) { diff --git a/docs/source/code_examples/extensions/my_cmd.cc b/docs/source/code_examples/extensions/my_cmd.cc index 36ddbe175..e6660469c 100644 --- a/docs/source/code_examples/extensions/my_cmd.cc +++ b/docs/source/code_examples/extensions/my_cmd.cc @@ -51,10 +51,10 @@ struct Test2Pass : public Pass { Test2Pass() : Pass("test2", "demonstrating sigmap on test module") { } void execute(std::vector, RTLIL::Design *design) override { - if (design->selection_stack.back().empty()) + if (design->selection().empty()) log_cmd_error("This command can't operator on an empty selection!\n"); - RTLIL::Module *module = design->modules_.at("\\test"); + RTLIL::Module *module = design->module("\\test"); RTLIL::SigSpec a(module->wire("\\a")), x(module->wire("\\x")), y(module->wire("\\y")); log("%d %d %d\n", a == x, x == y, y == a); // will print "0 0 0" diff --git a/docs/source/using_yosys/more_scripting/data_flow_tracking.rst b/docs/source/using_yosys/more_scripting/data_flow_tracking.rst new file mode 100644 index 000000000..aa13a2e69 --- /dev/null +++ b/docs/source/using_yosys/more_scripting/data_flow_tracking.rst @@ -0,0 +1,114 @@ +Dataflow tracking +------------------- + +Yosys can be used to answer questions such as "can this signal affect this other +signal?" via its *dataflow tracking* support. For this, four special cells, +`$get_tag`, `$set_tag`, `$overwrite_tag` and `$original_tag` are inserted into +the design (e.g. by a custom Yosys pass) and then the `dft_tag` is run, which +converts these cells into ordinary logic. Typically, one would then use `SBY`_ +to prove assertions involving these cells. + +.. _SBY: https://yosyshq.readthedocs.io/projects/sby + +Ordinarily in Yosys, the state of a bit is simply ``0`` or ``1`` (or one of the +special values, ``z`` and ``x``). During dataflow tracking they are augmented +with a set of tags. For example, the state of a bit could be ``0`` and the set +of tags ``"KEY"`` and ``"OVERFLOW"``. + +In addition to their usual operations on the logical bits, Yosys operations must +now also process the status of the tags. For this, tags are simply *forwarded* +or *propagated* (i.e. copied) from inputs to outputs, according to the following +general rule: + + A tag is forwarded from an input to an output if the input can affect the + output, for that particular state of all other inputs. + +For example, XOR, AND and OR cells propagate tags as follows: + +#. XOR simply forwards all tags from its inputs to its output, because inputs to + XOR can always affect the output. +#. AND forwards tags on a given input only if the other input is ``1``. Because + if one input is ``0``, the other input can never affect the output. +#. Similarly, OR forwards tags only if the other input is ``0``. + +There are two exceptions to this rule: + +#. In general, propagation is only determined approximately. For example, unless + the ``dft_tag`` code knows about a cell, it simply assumes the worst-case + behaviour that all inputs can affect all outputs. Further, the code also does + not consider that, when a signal affects multiple inputs of a cell, the + resulting simultaneous changes of the inputs can cancel each other out, for + example ``A ^ A`` or ``A ^ (B ^ A)`` is independent of ``A``, but its tags + would be propagated nonetheless. +#. If tag groups are used, the rules are modified (see below). + +Because of this propagation behaviour, we can answer questions about what +signals are affected by a certain signal, by injecting a tag at that point in +the circuit, and observing where the tag is visible. + +Example use cases +~~~~~~~~~~~~~~~~~~ + +As an example use case, consider a cryptographic processor which is not supposed +to expose its secret keys to the outside world. We can tag all key bits with the +``"KEY"`` tag and use `SBY`_ to formally verify that no external signal ever +carries the ``"KEY"`` tag, meaning that key information is not visible to the +outside. As a caveat, we have to manually clear the ``"KEY"`` tag during +cryptographic operations, as proving that the cryptographic operations +themselves do not leak key information is beyond the ability of Yosys. However +we can still easily detect, if e.g. an engineer forgot to remove debugging code +that allows reading back key data. + +As a different use case, we can modify all adders in the design to set the +``"OVERFLOW"`` tag on their output bits, if the addition overflowed, and then +add asserts to all flip-flop inputs and output signals that they do not carry +the ``"OVERFLOW"`` tag, i.e. that the results of overflowed additions never +affect system state. Note that in this particular example we use the ability of +tag insertion to be conditional on logic, in this case the overflow condition of +an adder. + +Semantics of dataflow tracking cells +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``$set_tag`` has inputs ``A``, ``SET``, ``CLR``, an output ``Y`` and a string +parameter ``TAG``. The logic value of ``A`` and all tags other than the one +named by the ``TAG`` parameter are simply copied to ``Y``. If ``SET`` is ``1``, +then the named tag is added to ``Y``. Otherwise, if ``CLR`` is ``1``, then the +named tag is removed. Otherwise, the tag is unchanged, i.e. it is present in +``Y`` if it is present in ``A``. + +``$get_tag`` has an input ``A`` and an output ``Y`` and a string parameter +``TAG``. ``$get_tag`` inspects ``A`` for the presence or absence of a tag of the +given name and sets ``Y`` to ``1`` if the tag is present. The logical value of +``A`` is completely ignored. + +``$overwrite_tag`` functions like ``$set_tag``, but lacks the ``Y`` output. +Instead of providing a modified version of the input signal, it modifies the +signal ``A`` "in-place", i.e. if a signal is input to ``$overwrite_tag``, that +is equivalent to interposing a ``$set_tag`` between its driver and all cells it +is connected to. The main purpose of ``$overwrite_tag`` is adding tags to +signals produced within a module that cannot or should not be modified itself. + +``$original_tag`` functions identically to ``$get_tag``, but ignores +``$overwrite_tag``, i.e. when converting the ``$overwrite_tag`` to ``$set_tag`` +as described above, it is equivalent to inserting the ``$get_tag`` *before* the +``$set_tag``. + +Tag groups +~~~~~~~~~~~~~~ + +Tag groups are an advanced feature that modify the propagation rule discussed +above. To use tag groups, simply name tags according to the schema +``"group:name"``. For example, ``"key:0"``, ``"key:a"``, ``"key:b"`` would be +three tags in the ``"key"`` group. + +The propagation rule is then amended by + + Inputs cannot block the propagation of each other's tags for tags of the same + group. + +For example, an AND gate will propagate a given tag on one input, if the other +input is either 1 or carries a tag of the same group. So if one input is ``0, +"key:a"`` and the other is ``0, "key:b"`` the result would be ``0, "key:a", +"key:b"``, rather than simply ``0``. Note that if we add an unrelated +``"overflow"`` tag to the first input, it would still not be propagated. \ No newline at end of file diff --git a/docs/source/using_yosys/more_scripting/index.rst b/docs/source/using_yosys/more_scripting/index.rst index 090b9e0b9..6265e5d0e 100644 --- a/docs/source/using_yosys/more_scripting/index.rst +++ b/docs/source/using_yosys/more_scripting/index.rst @@ -12,5 +12,6 @@ More scripting selections interactive_investigation model_checking + data_flow_tracking .. troubleshooting diff --git a/docs/source/yosys_internals/extending_yosys/extensions.rst b/docs/source/yosys_internals/extending_yosys/extensions.rst index b02c4cd99..d30dd2bae 100644 --- a/docs/source/yosys_internals/extending_yosys/extensions.rst +++ b/docs/source/yosys_internals/extending_yosys/extensions.rst @@ -237,7 +237,7 @@ Use ``log_cmd_error()`` to report a recoverable error: .. code:: C++ - if (design->selection_stack.back().empty()) + if (design->selection().empty()) log_cmd_error("This command can't operator on an empty selection!\n"); Use ``log_assert()`` and ``log_abort()`` instead of ``assert()`` and ``abort()``. diff --git a/kernel/consteval.h b/kernel/consteval.h index 05e94ab86..844120ef0 100644 --- a/kernel/consteval.h +++ b/kernel/consteval.h @@ -315,7 +315,7 @@ struct ConstEval Macc macc; macc.from_cell(cell); - for (auto &port : macc.ports) { + for (auto &port : macc.terms) { if (!eval(port.in_a, undef, cell)) return false; if (!eval(port.in_b, undef, cell)) diff --git a/kernel/cost.cc b/kernel/cost.cc index 5d897ab7a..985220f14 100644 --- a/kernel/cost.cc +++ b/kernel/cost.cc @@ -1,4 +1,5 @@ #include "kernel/cost.h" +#include "kernel/macc.h" USING_YOSYS_NAMESPACE @@ -148,6 +149,9 @@ unsigned int CellCosts::get(RTLIL::Cell *cell) log_assert(cell->hasPort(ID::Q) && "Weird flip flop"); log_debug("%s is ff\n", cell->name.c_str()); return cell->getParam(ID::WIDTH).as_int(); + } else if (cell->type.in(ID($mem), ID($mem_v2))) { + log_debug("%s is mem\n", cell->name.c_str()); + return cell->getParam(ID::WIDTH).as_int() * cell->getParam(ID::SIZE).as_int(); } else if (y_coef(cell->type)) { // linear with Y_WIDTH or WIDTH log_assert((cell->hasParam(ID::Y_WIDTH) || cell->hasParam(ID::WIDTH)) && "Unknown width"); @@ -173,6 +177,22 @@ unsigned int CellCosts::get(RTLIL::Cell *cell) unsigned int coef = cell->type == ID($mul) ? 3 : 5; log_debug("%s coef*(sum**2) %d * %d\n", cell->name.c_str(), coef, sum * sum); return coef * sum * sum; + } else if (cell->type.in(ID($macc), ID($macc_v2))) { + // quadratic per term + unsigned int cost_sum = 0; + Macc macc; + macc.from_cell(cell); + unsigned int y_width = cell->getParam(ID::Y_WIDTH).as_int(); + for (auto &term: macc.terms) { + if (term.in_b.empty()) { + // neglect addends + continue; + } + unsigned a_width = term.in_a.size(), b_width = term.in_b.size(); + unsigned int sum = a_width + b_width + std::min(y_width, a_width + b_width); + cost_sum += 3 * sum * sum; + } + return cost_sum; } else if (cell->type == ID($lut)) { int width = cell->getParam(ID::WIDTH).as_int(); unsigned int cost = 1U << (unsigned int)width; @@ -187,8 +207,8 @@ unsigned int CellCosts::get(RTLIL::Cell *cell) log_debug("%s is free\n", cell->name.c_str()); return 0; } - // TODO: $fsm $mem.* $macc - // ignored: $pow + // TODO: $fsm + // ignored: $pow $memrd $memwr $meminit (and v2 counterparts) log_warning("Can't determine cost of %s cell (%d parameters).\n", log_id(cell->type), GetSize(cell->parameters)); return 1; diff --git a/kernel/driver.cc b/kernel/driver.cc index a3d85bd90..eb1326ce0 100644 --- a/kernel/driver.cc +++ b/kernel/driver.cc @@ -111,7 +111,7 @@ void run(const char *command) log_last_error = ""; } catch (...) { while (GetSize(yosys_get_design()->selection_stack) > selSize) - yosys_get_design()->selection_stack.pop_back(); + yosys_get_design()->pop_selection(); throw; } } diff --git a/kernel/macc.h b/kernel/macc.h index 5fc6036ed..ceda1dceb 100644 --- a/kernel/macc.h +++ b/kernel/macc.h @@ -26,18 +26,18 @@ YOSYS_NAMESPACE_BEGIN struct Macc { - struct port_t { + struct term_t { RTLIL::SigSpec in_a, in_b; bool is_signed, do_subtract; }; - std::vector ports; + std::vector terms; void optimize(int width) { - std::vector new_ports; + std::vector new_terms; RTLIL::Const off(0, width); - for (auto &port : ports) + for (auto &port : terms) { if (GetSize(port.in_a) == 0 && GetSize(port.in_b) == 0) continue; @@ -68,25 +68,25 @@ struct Macc port.in_b.remove(GetSize(port.in_b)-1); } - new_ports.push_back(port); + new_terms.push_back(port); } if (off.as_bool()) { - port_t port; + term_t port; port.in_a = off; port.is_signed = false; port.do_subtract = false; - new_ports.push_back(port); + new_terms.push_back(port); } - new_ports.swap(ports); + new_terms.swap(terms); } void from_cell_v1(RTLIL::Cell *cell) { RTLIL::SigSpec port_a = cell->getPort(ID::A); - ports.clear(); + terms.clear(); auto config_bits = cell->getParam(ID::CONFIG); int config_cursor = 0; @@ -105,7 +105,7 @@ struct Macc { log_assert(config_cursor + 2 + 2*num_bits <= config_width); - port_t this_port; + term_t this_port; this_port.is_signed = config_bits[config_cursor++] == State::S1; this_port.do_subtract = config_bits[config_cursor++] == State::S1; @@ -126,11 +126,11 @@ struct Macc port_a_cursor += size_b; if (size_a || size_b) - ports.push_back(this_port); + terms.push_back(this_port); } for (auto bit : cell->getPort(ID::B)) - ports.push_back(port_t{{bit}, {}, false, false}); + terms.push_back(term_t{{bit}, {}, false, false}); log_assert(config_cursor == config_width); log_assert(port_a_cursor == GetSize(port_a)); @@ -148,7 +148,7 @@ struct Macc RTLIL::SigSpec port_b = cell->getPort(ID::B); RTLIL::SigSpec port_c = cell->getPort(ID::C); - ports.clear(); + terms.clear(); int nproducts = cell->getParam(ID::NPRODUCTS).as_int(); const Const &product_neg = cell->getParam(ID::PRODUCT_NEGATED); @@ -158,7 +158,7 @@ struct Macc const Const &b_signed = cell->getParam(ID::B_SIGNED); int ai = 0, bi = 0; for (int i = 0; i < nproducts; i++) { - port_t term; + term_t term; log_assert(a_signed[i] == b_signed[i]); term.is_signed = (a_signed[i] == State::S1); @@ -171,7 +171,7 @@ struct Macc bi += b_width; term.do_subtract = (product_neg[i] == State::S1); - ports.push_back(term); + terms.push_back(term); } log_assert(port_a.size() == ai); log_assert(port_b.size() == bi); @@ -182,7 +182,7 @@ struct Macc const Const &c_signed = cell->getParam(ID::C_SIGNED); int ci = 0; for (int i = 0; i < naddends; i++) { - port_t term; + term_t term; term.is_signed = (c_signed[i] == State::S1); int c_width = c_widths.extract(16 * i, 16).as_int(false); @@ -191,7 +191,7 @@ struct Macc ci += c_width; term.do_subtract = (addend_neg[i] == State::S1); - ports.push_back(term); + terms.push_back(term); } log_assert(port_c.size() == ci); } @@ -205,23 +205,23 @@ struct Macc Const c_signed, c_widths, addend_negated; SigSpec a, b, c; - for (int i = 0; i < (int) ports.size(); i++) { - SigSpec term_a = ports[i].in_a, term_b = ports[i].in_b; + for (int i = 0; i < (int) terms.size(); i++) { + SigSpec term_a = terms[i].in_a, term_b = terms[i].in_b; if (term_b.empty()) { // addend c_widths.append(Const(term_a.size(), 16)); - c_signed.append(ports[i].is_signed ? RTLIL::S1 : RTLIL::S0); - addend_negated.append(ports[i].do_subtract ? RTLIL::S1 : RTLIL::S0); + c_signed.append(terms[i].is_signed ? RTLIL::S1 : RTLIL::S0); + addend_negated.append(terms[i].do_subtract ? RTLIL::S1 : RTLIL::S0); c.append(term_a); naddends++; } else { // product a_widths.append(Const(term_a.size(), 16)); b_widths.append(Const(term_b.size(), 16)); - a_signed.append(ports[i].is_signed ? RTLIL::S1 : RTLIL::S0); - b_signed.append(ports[i].is_signed ? RTLIL::S1 : RTLIL::S0); - product_negated.append(ports[i].do_subtract ? RTLIL::S1 : RTLIL::S0); + a_signed.append(terms[i].is_signed ? RTLIL::S1 : RTLIL::S0); + b_signed.append(terms[i].is_signed ? RTLIL::S1 : RTLIL::S0); + product_negated.append(terms[i].do_subtract ? RTLIL::S1 : RTLIL::S0); a.append(term_a); b.append(term_b); nproducts++; @@ -265,7 +265,7 @@ struct Macc for (auto &bit : result.bits()) bit = State::S0; - for (auto &port : ports) + for (auto &port : terms) { if (!port.in_a.is_fully_const() || !port.in_b.is_fully_const()) return false; @@ -287,9 +287,9 @@ struct Macc bool is_simple_product() { - return ports.size() == 1 && - !ports[0].in_b.empty() && - !ports[0].do_subtract; + return terms.size() == 1 && + !terms[0].in_b.empty() && + !terms[0].do_subtract; } Macc(RTLIL::Cell *cell = nullptr) diff --git a/kernel/register.cc b/kernel/register.cc index fcb6ca769..c52bfb5b8 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -260,18 +260,18 @@ void Pass::call(RTLIL::Design *design, std::vector args) pass_register[args[0]]->execute(args, design); pass_register[args[0]]->post_execute(state); while (design->selection_stack.size() > orig_sel_stack_pos) - design->selection_stack.pop_back(); + design->pop_selection(); } void Pass::call_on_selection(RTLIL::Design *design, const RTLIL::Selection &selection, std::string command) { std::string backup_selected_active_module = design->selected_active_module; design->selected_active_module.clear(); - design->selection_stack.push_back(selection); + design->push_selection(selection); Pass::call(design, command); - design->selection_stack.pop_back(); + design->pop_selection(); design->selected_active_module = backup_selected_active_module; } @@ -279,11 +279,11 @@ void Pass::call_on_selection(RTLIL::Design *design, const RTLIL::Selection &sele { std::string backup_selected_active_module = design->selected_active_module; design->selected_active_module.clear(); - design->selection_stack.push_back(selection); + design->push_selection(selection); Pass::call(design, args); - design->selection_stack.pop_back(); + design->pop_selection(); design->selected_active_module = backup_selected_active_module; } @@ -291,12 +291,12 @@ void Pass::call_on_module(RTLIL::Design *design, RTLIL::Module *module, std::str { std::string backup_selected_active_module = design->selected_active_module; design->selected_active_module = module->name.str(); - design->selection_stack.push_back(RTLIL::Selection(false)); - design->selection_stack.back().select(module); + design->push_empty_selection(); + design->select(module); Pass::call(design, command); - design->selection_stack.pop_back(); + design->pop_selection(); design->selected_active_module = backup_selected_active_module; } @@ -304,12 +304,12 @@ void Pass::call_on_module(RTLIL::Design *design, RTLIL::Module *module, std::vec { std::string backup_selected_active_module = design->selected_active_module; design->selected_active_module = module->name.str(); - design->selection_stack.push_back(RTLIL::Selection(false)); - design->selection_stack.back().select(module); + design->push_empty_selection(); + design->select(module); Pass::call(design, args); - design->selection_stack.pop_back(); + design->pop_selection(); design->selected_active_module = backup_selected_active_module; } @@ -651,7 +651,7 @@ void Backend::backend_call(RTLIL::Design *design, std::ostream *f, std::string f } while (design->selection_stack.size() > orig_sel_stack_pos) - design->selection_stack.pop_back(); + design->pop_selection(); } struct SimHelper { diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index 3b9a4a8b1..dd78b202d 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -766,8 +766,23 @@ vector RTLIL::AttrObject::get_intvec_attribute(const RTLIL::IdString &id) c return data; } +bool RTLIL::Selection::boxed_module(const RTLIL::IdString &mod_name) const +{ + if (current_design != nullptr) { + auto module = current_design->module(mod_name); + return module && module->get_blackbox_attribute(); + } else { + log_warning("Unable to check if module is boxed for null design.\n"); + return false; + } +} + bool RTLIL::Selection::selected_module(const RTLIL::IdString &mod_name) const { + if (complete_selection) + return true; + if (!selects_boxes && boxed_module(mod_name)) + return false; if (full_selection) return true; if (selected_modules.count(mod_name) > 0) @@ -779,6 +794,10 @@ bool RTLIL::Selection::selected_module(const RTLIL::IdString &mod_name) const bool RTLIL::Selection::selected_whole_module(const RTLIL::IdString &mod_name) const { + if (complete_selection) + return true; + if (!selects_boxes && boxed_module(mod_name)) + return false; if (full_selection) return true; if (selected_modules.count(mod_name) > 0) @@ -788,6 +807,10 @@ bool RTLIL::Selection::selected_whole_module(const RTLIL::IdString &mod_name) co bool RTLIL::Selection::selected_member(const RTLIL::IdString &mod_name, const RTLIL::IdString &memb_name) const { + if (complete_selection) + return true; + if (!selects_boxes && boxed_module(mod_name)) + return false; if (full_selection) return true; if (selected_modules.count(mod_name) > 0) @@ -800,7 +823,17 @@ bool RTLIL::Selection::selected_member(const RTLIL::IdString &mod_name, const RT void RTLIL::Selection::optimize(RTLIL::Design *design) { - if (full_selection) { + if (design != current_design) { + current_design = design; + } + + if (selects_boxes && full_selection) + complete_selection = true; + if (complete_selection) { + full_selection = false; + selects_boxes = true; + } + if (selects_all()) { selected_modules.clear(); selected_members.clear(); return; @@ -810,7 +843,7 @@ void RTLIL::Selection::optimize(RTLIL::Design *design) del_list.clear(); for (auto mod_name : selected_modules) { - if (design->modules_.count(mod_name) == 0) + if (current_design->modules_.count(mod_name) == 0 || (!selects_boxes && boxed_module(mod_name))) del_list.push_back(mod_name); selected_members.erase(mod_name); } @@ -819,7 +852,7 @@ void RTLIL::Selection::optimize(RTLIL::Design *design) del_list.clear(); for (auto &it : selected_members) - if (design->modules_.count(it.first) == 0) + if (current_design->modules_.count(it.first) == 0 || (!selects_boxes && boxed_module(it.first))) del_list.push_back(it.first); for (auto mod_name : del_list) selected_members.erase(mod_name); @@ -827,7 +860,7 @@ void RTLIL::Selection::optimize(RTLIL::Design *design) for (auto &it : selected_members) { del_list.clear(); for (auto memb_name : it.second) - if (design->modules_[it.first]->count_id(memb_name) == 0) + if (current_design->modules_[it.first]->count_id(memb_name) == 0) del_list.push_back(memb_name); for (auto memb_name : del_list) it.second.erase(memb_name); @@ -838,8 +871,8 @@ void RTLIL::Selection::optimize(RTLIL::Design *design) for (auto &it : selected_members) if (it.second.size() == 0) del_list.push_back(it.first); - else if (it.second.size() == design->modules_[it.first]->wires_.size() + design->modules_[it.first]->memories.size() + - design->modules_[it.first]->cells_.size() + design->modules_[it.first]->processes.size()) + else if (it.second.size() == current_design->modules_[it.first]->wires_.size() + current_design->modules_[it.first]->memories.size() + + current_design->modules_[it.first]->cells_.size() + current_design->modules_[it.first]->processes.size()) add_list.push_back(it.first); for (auto mod_name : del_list) selected_members.erase(mod_name); @@ -848,13 +881,24 @@ void RTLIL::Selection::optimize(RTLIL::Design *design) selected_modules.insert(mod_name); } - if (selected_modules.size() == design->modules_.size()) { - full_selection = true; + if (selected_modules.size() == current_design->modules_.size()) { selected_modules.clear(); selected_members.clear(); + if (selects_boxes) + complete_selection = true; + else + full_selection = true; } } +void RTLIL::Selection::clear() +{ + full_selection = false; + complete_selection = false; + selected_modules.clear(); + selected_members.clear(); +} + RTLIL::Design::Design() : verilog_defines (new define_map_t) { @@ -863,7 +907,7 @@ RTLIL::Design::Design() hashidx_ = hashidx_count; refcount_modules_ = 0; - selection_stack.push_back(RTLIL::Selection()); + push_full_selection(); #ifdef WITH_PYTHON RTLIL::Design::get_all_designs()->insert(std::pair(hashidx_, this)); @@ -908,7 +952,7 @@ const RTLIL::Module *RTLIL::Design::module(const RTLIL::IdString& name) const return modules_.count(name) ? modules_.at(name) : NULL; } -RTLIL::Module *RTLIL::Design::top_module() +RTLIL::Module *RTLIL::Design::top_module() const { RTLIL::Module *module = nullptr; int module_count = 0; @@ -1066,6 +1110,7 @@ void RTLIL::Design::sort() void RTLIL::Design::check() { #ifndef NDEBUG + log_assert(!selection_stack.empty()); for (auto &it : modules_) { log_assert(this == it.second->design); log_assert(it.first == it.second->name); @@ -1089,27 +1134,21 @@ bool RTLIL::Design::selected_module(const RTLIL::IdString& mod_name) const { if (!selected_active_module.empty() && mod_name != selected_active_module) return false; - if (selection_stack.size() == 0) - return true; - return selection_stack.back().selected_module(mod_name); + return selection().selected_module(mod_name); } bool RTLIL::Design::selected_whole_module(const RTLIL::IdString& mod_name) const { if (!selected_active_module.empty() && mod_name != selected_active_module) return false; - if (selection_stack.size() == 0) - return true; - return selection_stack.back().selected_whole_module(mod_name); + return selection().selected_whole_module(mod_name); } bool RTLIL::Design::selected_member(const RTLIL::IdString& mod_name, const RTLIL::IdString& memb_name) const { if (!selected_active_module.empty() && mod_name != selected_active_module) return false; - if (selection_stack.size() == 0) - return true; - return selection_stack.back().selected_member(mod_name, memb_name); + return selection().selected_member(mod_name, memb_name); } bool RTLIL::Design::selected_module(RTLIL::Module *mod) const @@ -1122,37 +1161,86 @@ bool RTLIL::Design::selected_whole_module(RTLIL::Module *mod) const return selected_whole_module(mod->name); } -std::vector RTLIL::Design::selected_modules() const +void RTLIL::Design::push_selection(RTLIL::Selection sel) { - std::vector result; - result.reserve(modules_.size()); - for (auto &it : modules_) - if (selected_module(it.first) && !it.second->get_blackbox_attribute()) - result.push_back(it.second); - return result; + sel.current_design = this; + selection_stack.push_back(sel); } -std::vector RTLIL::Design::selected_whole_modules() const +void RTLIL::Design::push_empty_selection() { - std::vector result; - result.reserve(modules_.size()); - for (auto &it : modules_) - if (selected_whole_module(it.first) && !it.second->get_blackbox_attribute()) - result.push_back(it.second); - return result; + push_selection(RTLIL::Selection::EmptySelection(this)); } -std::vector RTLIL::Design::selected_whole_modules_warn(bool include_wb) const +void RTLIL::Design::push_full_selection() { + push_selection(RTLIL::Selection::FullSelection(this)); +} + +void RTLIL::Design::push_complete_selection() +{ + push_selection(RTLIL::Selection::CompleteSelection(this)); +} + +void RTLIL::Design::pop_selection() +{ + selection_stack.pop_back(); + // Default to a full_selection if we ran out of stack + if (selection_stack.empty()) + push_full_selection(); +} + +std::vector RTLIL::Design::selected_modules(RTLIL::SelectPartials partials, RTLIL::SelectBoxes boxes) const +{ + bool include_partials = partials == RTLIL::SELECT_ALL; + bool exclude_boxes = (boxes & RTLIL::SB_UNBOXED_ONLY) != 0; + bool ignore_wb = (boxes & RTLIL::SB_INCL_WB) != 0; std::vector result; result.reserve(modules_.size()); for (auto &it : modules_) - if (it.second->get_blackbox_attribute(include_wb)) - continue; - else if (selected_whole_module(it.first)) - result.push_back(it.second); - else if (selected_module(it.first)) - log_warning("Ignoring partially selected module %s.\n", log_id(it.first)); + if (selected_whole_module(it.first) || (include_partials && selected_module(it.first))) { + if (!(exclude_boxes && it.second->get_blackbox_attribute(ignore_wb))) + result.push_back(it.second); + else + switch (boxes) + { + case RTLIL::SB_UNBOXED_WARN: + log_warning("Ignoring boxed module %s.\n", log_id(it.first)); + break; + case RTLIL::SB_EXCL_BB_WARN: + log_warning("Ignoring blackbox module %s.\n", log_id(it.first)); + break; + case RTLIL::SB_UNBOXED_ERR: + log_error("Unsupported boxed module %s.\n", log_id(it.first)); + break; + case RTLIL::SB_EXCL_BB_ERR: + log_error("Unsupported blackbox module %s.\n", log_id(it.first)); + break; + case RTLIL::SB_UNBOXED_CMDERR: + log_cmd_error("Unsupported boxed module %s.\n", log_id(it.first)); + break; + case RTLIL::SB_EXCL_BB_CMDERR: + log_cmd_error("Unsupported blackbox module %s.\n", log_id(it.first)); + break; + default: + break; + } + } else if (!include_partials && selected_module(it.first)) { + switch(partials) + { + case RTLIL::SELECT_WHOLE_WARN: + log_warning("Ignoring partially selected module %s.\n", log_id(it.first)); + break; + case RTLIL::SELECT_WHOLE_ERR: + log_error("Unsupported partially selected module %s.\n", log_id(it.first)); + break; + case RTLIL::SELECT_WHOLE_CMDERR: + log_cmd_error("Unsupported partially selected module %s.\n", log_id(it.first)); + break; + default: + break; + } + } return result; } @@ -2052,7 +2140,7 @@ namespace { param(ID::TYPE); check_expected(); std::string scope_type = cell->getParam(ID::TYPE).decode_string(); - if (scope_type != "module" && scope_type != "struct") + if (scope_type != "module" && scope_type != "struct" && scope_type != "blackbox") error(__LINE__); return; } @@ -2284,6 +2372,13 @@ void RTLIL::Module::check() log_assert(!packed_memids.count(memid)); packed_memids.insert(memid); } + auto cell_mod = design->module(it.first); + if (cell_mod != nullptr) { + // assertion check below to make sure that there are no + // cases where a cell has a blackbox attribute since + // that is deprecated + log_assert(!it.second->get_blackbox_attribute()); + } } for (auto &it : processes) { @@ -2411,6 +2506,16 @@ bool RTLIL::Module::has_processes_warn() const return !processes.empty(); } +bool RTLIL::Module::is_selected() const +{ + return design->selected_module(this->name); +} + +bool RTLIL::Module::is_selected_whole() const +{ + return design->selected_whole_module(this->name); +} + std::vector RTLIL::Module::selected_wires() const { std::vector result; @@ -2431,6 +2536,40 @@ std::vector RTLIL::Module::selected_cells() const return result; } +std::vector RTLIL::Module::selected_memories() const +{ + std::vector result; + result.reserve(memories.size()); + for (auto &it : memories) + if (design->selected(this, it.second)) + result.push_back(it.second); + return result; +} + +std::vector RTLIL::Module::selected_processes() const +{ + std::vector result; + result.reserve(processes.size()); + for (auto &it : processes) + if (design->selected(this, it.second)) + result.push_back(it.second); + return result; +} + +std::vector RTLIL::Module::selected_members() const +{ + std::vector result; + auto cells = selected_cells(); + auto memories = selected_memories(); + auto wires = selected_wires(); + auto processes = selected_processes(); + result.insert(result.end(), cells.begin(), cells.end()); + result.insert(result.end(), memories.begin(), memories.end()); + result.insert(result.end(), wires.begin(), wires.end()); + result.insert(result.end(), processes.begin(), processes.end()); + return result; +} + void RTLIL::Module::add(RTLIL::Wire *wire) { log_assert(!wire->name.empty()); diff --git a/kernel/rtlil.h b/kernel/rtlil.h index f1bd96be7..96c8c523b 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -56,8 +56,33 @@ namespace RTLIL CONST_FLAG_REAL = 4 // only used for parameters }; + enum SelectPartials : unsigned char { + SELECT_ALL = 0, // include partial modules + SELECT_WHOLE_ONLY = 1, // ignore partial modules + SELECT_WHOLE_WARN = 2, // call log_warning on partial module + SELECT_WHOLE_ERR = 3, // call log_error on partial module + SELECT_WHOLE_CMDERR = 4 // call log_cmd_error on partial module + }; + + enum SelectBoxes : unsigned char { + SB_ALL = 0, // include boxed modules + SB_WARN = 1, // helper for log_warning (not for direct use) + SB_ERR = 2, // helper for log_error (not for direct use) + SB_CMDERR = 3, // helper for log_cmd_error (not for direct use) + SB_UNBOXED_ONLY = 4, // ignore boxed modules + SB_UNBOXED_WARN = 5, // call log_warning on boxed module + SB_UNBOXED_ERR = 6, // call log_error on boxed module + SB_UNBOXED_CMDERR = 7, // call log_cmd_error on boxed module + SB_INCL_WB = 8, // helper for white boxes (not for direct use) + SB_EXCL_BB_ONLY = 12, // ignore black boxes, but not white boxes + SB_EXCL_BB_WARN = 13, // call log_warning on black boxed module + SB_EXCL_BB_ERR = 14, // call log_error on black boxed module + SB_EXCL_BB_CMDERR = 15 // call log_cmd_error on black boxed module + }; + struct Const; struct AttrObject; + struct NamedObject; struct Selection; struct Monitor; struct Design; @@ -869,6 +894,11 @@ struct RTLIL::AttrObject vector get_intvec_attribute(const RTLIL::IdString &id) const; }; +struct RTLIL::NamedObject : public RTLIL::AttrObject +{ + RTLIL::IdString name; +}; + struct RTLIL::SigChunk { RTLIL::Wire *wire; @@ -1134,32 +1164,94 @@ public: struct RTLIL::Selection { + // selection includes boxed modules + bool selects_boxes; + // selection covers full design, including boxed modules + bool complete_selection; + // selection covers full design, not including boxed modules bool full_selection; pool selected_modules; dict> selected_members; + RTLIL::Design *current_design; - Selection(bool full = true) : full_selection(full) { } + // create a new selection + Selection( + // should the selection cover the full design + bool full = true, + // should the selection include boxed modules + bool boxes = false, + // the design to select from + RTLIL::Design *design = nullptr + ) : + selects_boxes(boxes), complete_selection(full && boxes), full_selection(full && !boxes), current_design(design) { } + // checks if the given module exists in the current design and is a + // boxed module, warning the user if the current design is not set + bool boxed_module(const RTLIL::IdString &mod_name) const; + + // checks if the given module is included in this selection bool selected_module(const RTLIL::IdString &mod_name) const; + + // checks if the given module is wholly included in this selection, + // i.e. not partially selected bool selected_whole_module(const RTLIL::IdString &mod_name) const; + + // checks if the given member from the given module is included in this + // selection bool selected_member(const RTLIL::IdString &mod_name, const RTLIL::IdString &memb_name) const; + + // optimizes this selection for the given design by: + // - removing non-existent modules and members, any boxed modules and + // their members (if selection does not include boxes), and any + // partially selected modules with no selected members; + // - marking partially selected modules as wholly selected if all + // members of that module are selected; and + // - marking selection as a complete_selection if all modules in the + // given design are selected, or a full_selection if it does not + // include boxes. void optimize(RTLIL::Design *design); + // checks if selection covers full design (may or may not include + // boxed-modules) + bool selects_all() const { + return full_selection || complete_selection; + } + + // add whole module to this selection template void select(T1 *module) { - if (!full_selection && selected_modules.count(module->name) == 0) { + if (!selects_all() && selected_modules.count(module->name) == 0) { selected_modules.insert(module->name); selected_members.erase(module->name); + if (module->get_blackbox_attribute()) + selects_boxes = true; } } + // add member of module to this selection template void select(T1 *module, T2 *member) { - if (!full_selection && selected_modules.count(module->name) == 0) + if (!selects_all() && selected_modules.count(module->name) == 0) { selected_members[module->name].insert(member->name); + if (module->get_blackbox_attribute()) + selects_boxes = true; + } } + // checks if selection is empty bool empty() const { - return !full_selection && selected_modules.empty() && selected_members.empty(); + return !selects_all() && selected_modules.empty() && selected_members.empty(); } + + // clear this selection, leaving it empty + void clear(); + + // create a new selection which is empty + static Selection EmptySelection(RTLIL::Design *design = nullptr) { return Selection(false, false, design); }; + + // create a new selection with all non-boxed modules + static Selection FullSelection(RTLIL::Design *design = nullptr) { return Selection(true, false, design); }; + + // create a new selection with all modules, including boxes + static Selection CompleteSelection(RTLIL::Design *design = nullptr) { return Selection(true, true, design); }; }; struct RTLIL::Monitor @@ -1213,7 +1305,7 @@ struct RTLIL::Design RTLIL::ObjRange modules(); RTLIL::Module *module(const RTLIL::IdString &name); const RTLIL::Module *module(const RTLIL::IdString &name) const; - RTLIL::Module *top_module(); + RTLIL::Module *top_module() const; bool has(const RTLIL::IdString &id) const { return modules_.count(id) != 0; @@ -1240,57 +1332,118 @@ struct RTLIL::Design void check(); void optimize(); + // checks if the given module is included in the current selection bool selected_module(const RTLIL::IdString &mod_name) const; + + // checks if the given module is wholly included in the current + // selection, i.e. not partially selected bool selected_whole_module(const RTLIL::IdString &mod_name) const; + + // checks if the given member from the given module is included in the + // current selection bool selected_member(const RTLIL::IdString &mod_name, const RTLIL::IdString &memb_name) const; + // checks if the given module is included in the current selection bool selected_module(RTLIL::Module *mod) const; + + // checks if the given module is wholly included in the current + // selection, i.e. not partially selected bool selected_whole_module(RTLIL::Module *mod) const; + // push the given selection to the selection stack + void push_selection(RTLIL::Selection sel); + // push a new selection to the selection stack, with nothing selected + void push_empty_selection(); + // push a new selection to the selection stack, with all non-boxed + // modules selected + void push_full_selection(); + // push a new selection to the selection stack, with all modules + // selected including boxes + void push_complete_selection(); + // pop the current selection from the stack, returning to a full + // selection (no boxes) if the stack is empty + void pop_selection(); + + // get the current selection RTLIL::Selection &selection() { return selection_stack.back(); } + // get the current selection const RTLIL::Selection &selection() const { return selection_stack.back(); } + // is the current selection a full selection (no boxes) bool full_selection() const { - return selection_stack.back().full_selection; + return selection().full_selection; } + // is the given module in the current selection template bool selected(T1 *module) const { return selected_module(module->name); } + // is the given member of the given module in the current selection template bool selected(T1 *module, T2 *member) const { return selected_member(module->name, member->name); } + // add whole module to the current selection template void select(T1 *module) { - if (selection_stack.size() > 0) { - RTLIL::Selection &sel = selection_stack.back(); - sel.select(module); - } + RTLIL::Selection &sel = selection(); + sel.select(module); } + // add member of module to the current selection template void select(T1 *module, T2 *member) { - if (selection_stack.size() > 0) { - RTLIL::Selection &sel = selection_stack.back(); - sel.select(module, member); - } + RTLIL::Selection &sel = selection(); + sel.select(module, member); } - std::vector selected_modules() const; - std::vector selected_whole_modules() const; - std::vector selected_whole_modules_warn(bool include_wb = false) const; + // returns all selected modules + std::vector selected_modules( + // controls if partially selected modules are included + RTLIL::SelectPartials partials = SELECT_ALL, + // controls if boxed modules are included + RTLIL::SelectBoxes boxes = SB_UNBOXED_WARN + ) const; + + // returns all selected modules, and may include boxes + std::vector all_selected_modules() const { return selected_modules(SELECT_ALL, SB_ALL); } + // returns all selected unboxed modules, silently ignoring any boxed + // modules in the selection + std::vector selected_unboxed_modules() const { return selected_modules(SELECT_ALL, SB_UNBOXED_ONLY); } + // returns all selected unboxed modules, warning the user if any boxed + // modules have been ignored + std::vector selected_unboxed_modules_warn() const { return selected_modules(SELECT_ALL, SB_UNBOXED_WARN); } + + [[deprecated("Use select_unboxed_whole_modules() to maintain prior behaviour, or consider one of the other selected whole module helpers.")]] + std::vector selected_whole_modules() const { return selected_modules(SELECT_WHOLE_ONLY, SB_UNBOXED_WARN); } + // returns all selected whole modules, silently ignoring partially + // selected modules, and may include boxes + std::vector all_selected_whole_modules() const { return selected_modules(SELECT_WHOLE_ONLY, SB_ALL); } + // returns all selected whole modules, warning the user if any partially + // selected or boxed modules have been ignored; optionally includes + // selected whole modules with the 'whitebox' attribute + std::vector selected_whole_modules_warn( + // should whole modules with the 'whitebox' attribute be + // included + bool include_wb = false + ) const { return selected_modules(SELECT_WHOLE_WARN, include_wb ? SB_EXCL_BB_WARN : SB_UNBOXED_WARN); } + // returns all selected unboxed whole modules, silently ignoring + // partially selected or boxed modules + std::vector selected_unboxed_whole_modules() const { return selected_modules(SELECT_WHOLE_ONLY, SB_UNBOXED_ONLY); } + // returns all selected unboxed whole modules, warning the user if any + // partially selected or boxed modules have been ignored + std::vector selected_unboxed_whole_modules_warn() const { return selected_modules(SELECT_WHOLE_WARN, SB_UNBOXED_WARN); } #ifdef WITH_PYTHON static std::map *get_all_designs(void); #endif }; -struct RTLIL::Module : public RTLIL::AttrObject +struct RTLIL::Module : public RTLIL::NamedObject { Hasher::hash_t hashidx_; [[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; } @@ -1313,7 +1466,6 @@ public: std::vector connections_; std::vector bindings_; - RTLIL::IdString name; idict avail_parameters; dict parameter_default_values; dict memories; @@ -1358,8 +1510,14 @@ public: bool has_memories_warn() const; bool has_processes_warn() const; + bool is_selected() const; + bool is_selected_whole() const; + std::vector selected_wires() const; std::vector selected_cells() const; + std::vector selected_memories() const; + std::vector selected_processes() const; + std::vector selected_members() const; template bool selected(T *member) const { return design->selected_member(name, member->name); @@ -1645,7 +1803,7 @@ namespace RTLIL_BACKEND { void dump_wire(std::ostream &f, std::string indent, const RTLIL::Wire *wire); } -struct RTLIL::Wire : public RTLIL::AttrObject +struct RTLIL::Wire : public RTLIL::NamedObject { Hasher::hash_t hashidx_; [[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; } @@ -1668,7 +1826,6 @@ public: void operator=(RTLIL::Wire &other) = delete; RTLIL::Module *module; - RTLIL::IdString name; int width, start_offset, port_id; bool port_input, port_output, upto, is_signed; @@ -1697,14 +1854,13 @@ inline int GetSize(RTLIL::Wire *wire) { return wire->width; } -struct RTLIL::Memory : public RTLIL::AttrObject +struct RTLIL::Memory : public RTLIL::NamedObject { Hasher::hash_t hashidx_; [[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; } Memory(); - RTLIL::IdString name; int width, start_offset, size; #ifdef WITH_PYTHON ~Memory(); @@ -1712,7 +1868,7 @@ struct RTLIL::Memory : public RTLIL::AttrObject #endif }; -struct RTLIL::Cell : public RTLIL::AttrObject +struct RTLIL::Cell : public RTLIL::NamedObject { Hasher::hash_t hashidx_; [[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; } @@ -1729,7 +1885,6 @@ public: void operator=(RTLIL::Cell &other) = delete; RTLIL::Module *module; - RTLIL::IdString name; RTLIL::IdString type; dict connections_; dict parameters; @@ -1822,7 +1977,7 @@ struct RTLIL::SyncRule RTLIL::SyncRule *clone() const; }; -struct RTLIL::Process : public RTLIL::AttrObject +struct RTLIL::Process : public RTLIL::NamedObject { Hasher::hash_t hashidx_; [[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; } @@ -1834,7 +1989,6 @@ protected: ~Process(); public: - RTLIL::IdString name; RTLIL::Module *module; RTLIL::CaseRule root_case; std::vector syncs; diff --git a/kernel/satgen.cc b/kernel/satgen.cc index 9e5fa9111..13f243b14 100644 --- a/kernel/satgen.cc +++ b/kernel/satgen.cc @@ -750,7 +750,7 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) std::vector tmp(GetSize(y), ez->CONST_FALSE); - for (auto &port : macc.ports) + for (auto &port : macc.terms) { std::vector in_a = importDefSigSpec(port.in_a, timestep); std::vector in_b = importDefSigSpec(port.in_b, timestep); diff --git a/kernel/tclapi.cc b/kernel/tclapi.cc index be39ca4a0..e970779d7 100644 --- a/kernel/tclapi.cc +++ b/kernel/tclapi.cc @@ -114,7 +114,7 @@ static int tcl_yosys_cmd(ClientData, Tcl_Interp *interp, int argc, const char *a if (in_repl) { auto design = yosys_get_design(); while (design->selection_stack.size() > 1) - design->selection_stack.pop_back(); + design->pop_selection(); log_reset_stack(); } Tcl_SetResult(interp, (char *)"Yosys command produced an error", TCL_STATIC); diff --git a/kernel/yosys.cc b/kernel/yosys.cc index a5520e6ef..9b0bc92ce 100644 --- a/kernel/yosys.cc +++ b/kernel/yosys.cc @@ -312,11 +312,11 @@ const char *create_prompt(RTLIL::Design *design, int recursion_counter) str += "yosys"; if (!design->selected_active_module.empty()) str += stringf(" [%s]", RTLIL::unescape_id(design->selected_active_module).c_str()); - if (!design->selection_stack.empty() && !design->selection_stack.back().full_selection) { + if (!design->full_selection()) { if (design->selected_active_module.empty()) str += "*"; - else if (design->selection_stack.back().selected_modules.size() != 1 || design->selection_stack.back().selected_members.size() != 0 || - design->selection_stack.back().selected_modules.count(design->selected_active_module) == 0) + else if (design->selection().selected_modules.size() != 1 || design->selection().selected_members.size() != 0 || + design->selection().selected_modules.count(design->selected_active_module) == 0) str += "*"; } snprintf(buffer, 100, "%s> ", str.c_str()); @@ -979,7 +979,7 @@ void shell(RTLIL::Design *design) Pass::call(design, command); } catch (log_cmd_error_exception) { while (design->selection_stack.size() > 1) - design->selection_stack.pop_back(); + design->pop_selection(); log_reset_stack(); } design->check(); diff --git a/misc/py_wrap_generator.py b/misc/py_wrap_generator.py index 03374c610..4857a9dc3 100644 --- a/misc/py_wrap_generator.py +++ b/misc/py_wrap_generator.py @@ -977,6 +977,7 @@ sources = [ WClass("IdString", link_types.ref_copy, None, "str()", ""), WClass("Const", link_types.ref_copy, None, "as_string()", ""), WClass("AttrObject", link_types.ref_copy, None, None, None), + WClass("NamedObject", link_types.ref_copy, None, None, None), WClass("Selection", link_types.ref_copy, None, None, None), WClass("Monitor", link_types.derive, None, None, None), WClass("CaseRule",link_types.ref_copy, None, None, None, True), diff --git a/passes/cmds/Makefile.inc b/passes/cmds/Makefile.inc index b15e91edb..af7e1bca6 100644 --- a/passes/cmds/Makefile.inc +++ b/passes/cmds/Makefile.inc @@ -54,3 +54,4 @@ OBJS += passes/cmds/portarcs.o OBJS += passes/cmds/wrapcell.o OBJS += passes/cmds/setenv.o OBJS += passes/cmds/abstract.o +OBJS += passes/cmds/test_select.o diff --git a/passes/cmds/add.cc b/passes/cmds/add.cc index c09517254..833d6006d 100644 --- a/passes/cmds/add.cc +++ b/passes/cmds/add.cc @@ -102,7 +102,7 @@ static void add_wire(RTLIL::Design *design, RTLIL::Module *module, std::string n RTLIL::Module *mod = design->module(cell->type); if (mod == nullptr) continue; - if (!design->selected_whole_module(mod->name)) + if (!mod->is_selected_whole()) continue; if (mod->get_blackbox_attribute()) continue; diff --git a/passes/cmds/design.cc b/passes/cmds/design.cc index 168d38563..efc72dfb7 100644 --- a/passes/cmds/design.cc +++ b/passes/cmds/design.cc @@ -213,22 +213,15 @@ struct DesignPass : public Pass { if (copy_from_design != design && argidx == args.size() && !import_mode) cmd_error(args, argidx, "Missing selection."); - RTLIL::Selection sel; if (argidx != args.size()) { handle_extra_select_args(this, args, argidx, args.size(), copy_from_design); - sel = copy_from_design->selection_stack.back(); - copy_from_design->selection_stack.pop_back(); argidx = args.size(); + } else { + copy_from_design->push_complete_selection(); } - for (auto mod : copy_from_design->modules()) { - if (sel.selected_whole_module(mod->name)) { - copy_src_modules.push_back(mod); - continue; - } - if (sel.selected_module(mod->name)) - log_cmd_error("Module %s is only partly selected.\n", log_id(mod->name)); - } + for (auto mod : copy_from_design->selected_modules(RTLIL::SELECT_WHOLE_CMDERR, RTLIL::SB_ALL)) + copy_src_modules.push_back(mod); if (import_mode) { std::vector candidates; @@ -246,6 +239,8 @@ struct DesignPass : public Pass { if (GetSize(candidates) == 1) copy_src_modules = std::move(candidates); } + + copy_from_design->pop_selection(); } extra_args(args, argidx, design, false); @@ -368,7 +363,7 @@ struct DesignPass : public Pass { design->selection_vars.clear(); design->selected_active_module.clear(); - design->selection_stack.push_back(RTLIL::Selection()); + design->push_full_selection(); } if (reset_mode || reset_vlog_mode || !load_name.empty() || push_mode || pop_mode) diff --git a/passes/cmds/scc.cc b/passes/cmds/scc.cc index 197bd9319..0f988e57a 100644 --- a/passes/cmds/scc.cc +++ b/passes/cmds/scc.cc @@ -340,7 +340,7 @@ struct SccPass : public Pass { int origSelectPos = design->selection_stack.size() - 1; extra_args(args, argidx, design); - RTLIL::Selection newSelection(false); + auto newSelection = RTLIL::Selection::EmptySelection(design); int scc_counter = 0; for (auto mod : design->selected_modules()) diff --git a/passes/cmds/select.cc b/passes/cmds/select.cc index aec4c964b..1d75091af 100644 --- a/passes/cmds/select.cc +++ b/passes/cmds/select.cc @@ -141,24 +141,42 @@ static bool match_attr(const dict &attributes, co return match_attr(attributes, match_expr, std::string(), 0); } +static void select_all(RTLIL::Design *design, RTLIL::Selection &lhs) +{ + if (!lhs.selects_all()) + return; + lhs.current_design = design; + lhs.selected_modules.clear(); + for (auto mod : design->modules()) { + if (!lhs.selects_boxes && mod->get_blackbox_attribute()) + continue; + lhs.selected_modules.insert(mod->name); + } + lhs.full_selection = false; + lhs.complete_selection = false; +} + static void select_op_neg(RTLIL::Design *design, RTLIL::Selection &lhs) { - if (lhs.full_selection) { - lhs.full_selection = false; - lhs.selected_modules.clear(); - lhs.selected_members.clear(); + if (lhs.selects_all()) { + lhs.clear(); return; } if (lhs.selected_modules.size() == 0 && lhs.selected_members.size() == 0) { - lhs.full_selection = true; + if (lhs.selects_boxes) + lhs.complete_selection = true; + else + lhs.full_selection = true; return; } - RTLIL::Selection new_sel(false); + auto new_sel = RTLIL::Selection::EmptySelection(); for (auto mod : design->modules()) { + if (!lhs.selects_boxes && mod->get_blackbox_attribute()) + continue; if (lhs.selected_whole_module(mod->name)) continue; if (!lhs.selected_module(mod->name)) { @@ -212,7 +230,7 @@ static void select_op_random(RTLIL::Design *design, RTLIL::Selection &lhs, int c } } - lhs = RTLIL::Selection(false); + lhs = RTLIL::Selection(false, lhs.selects_boxes, design); while (!objects.empty() && count-- > 0) { @@ -243,7 +261,7 @@ static void select_op_submod(RTLIL::Design *design, RTLIL::Selection &lhs) static void select_op_cells_to_modules(RTLIL::Design *design, RTLIL::Selection &lhs) { - RTLIL::Selection new_sel(false); + RTLIL::Selection new_sel(false, lhs.selects_boxes, design); for (auto mod : design->modules()) if (lhs.selected_module(mod->name)) for (auto cell : mod->cells()) @@ -254,7 +272,7 @@ static void select_op_cells_to_modules(RTLIL::Design *design, RTLIL::Selection & static void select_op_module_to_cells(RTLIL::Design *design, RTLIL::Selection &lhs) { - RTLIL::Selection new_sel(false); + RTLIL::Selection new_sel(false, lhs.selects_boxes, design); for (auto mod : design->modules()) for (auto cell : mod->cells()) if ((design->module(cell->type) != nullptr) && lhs.selected_whole_module(cell->type)) @@ -274,6 +292,8 @@ static void select_op_alias(RTLIL::Design *design, RTLIL::Selection &lhs) { for (auto mod : design->modules()) { + if (!lhs.selects_boxes && mod->get_blackbox_attribute()) + continue; if (lhs.selected_whole_module(mod->name)) continue; if (!lhs.selected_module(mod->name)) @@ -292,18 +312,38 @@ static void select_op_alias(RTLIL::Design *design, RTLIL::Selection &lhs) } } -static void select_op_union(RTLIL::Design*, RTLIL::Selection &lhs, const RTLIL::Selection &rhs) +static void select_op_union(RTLIL::Design* design, RTLIL::Selection &lhs, const RTLIL::Selection &rhs) { - if (rhs.full_selection) { - lhs.full_selection = true; - lhs.selected_modules.clear(); - lhs.selected_members.clear(); + if (lhs.complete_selection) + return; + else if (rhs.complete_selection) { + lhs.complete_selection = true; + lhs.optimize(design); return; } - if (lhs.full_selection) + if (rhs.selects_boxes) { + if (lhs.full_selection) { + select_all(design, lhs); + } + lhs.selects_boxes = true; + } + else if (lhs.full_selection) return; + if (rhs.full_selection) { + if (lhs.selects_boxes) { + auto new_rhs = RTLIL::Selection(rhs); + select_all(design, new_rhs); + for (auto mod : new_rhs.selected_modules) + lhs.selected_modules.insert(mod); + } else { + lhs.clear(); + lhs.full_selection = true; + } + return; + } + for (auto &it : rhs.selected_members) for (auto &it2 : it.second) lhs.selected_members[it.first].insert(it2); @@ -316,21 +356,31 @@ static void select_op_union(RTLIL::Design*, RTLIL::Selection &lhs, const RTLIL:: static void select_op_diff(RTLIL::Design *design, RTLIL::Selection &lhs, const RTLIL::Selection &rhs) { - if (rhs.full_selection) { - lhs.full_selection = false; - lhs.selected_modules.clear(); - lhs.selected_members.clear(); + if (rhs.complete_selection) { + lhs.clear(); return; } - if (lhs.full_selection) { - if (!rhs.full_selection && rhs.selected_modules.size() == 0 && rhs.selected_members.size() == 0) - return; - lhs.full_selection = false; - for (auto mod : design->modules()) - lhs.selected_modules.insert(mod->name); + if (rhs.full_selection) { + if (lhs.selects_boxes) { + auto new_rhs = RTLIL::Selection(rhs); + select_all(design, new_rhs); + select_all(design, lhs); + for (auto mod : new_rhs.selected_modules) { + lhs.selected_modules.erase(mod); + lhs.selected_members.erase(mod); + } + } else { + lhs.clear(); + } + return; } + if (rhs.empty() || lhs.empty()) + return; + + select_all(design, lhs); + for (auto &it : rhs.selected_modules) { lhs.selected_modules.erase(it); lhs.selected_members.erase(it); @@ -366,38 +416,46 @@ static void select_op_diff(RTLIL::Design *design, RTLIL::Selection &lhs, const R static void select_op_intersect(RTLIL::Design *design, RTLIL::Selection &lhs, const RTLIL::Selection &rhs) { - if (rhs.full_selection) + if (rhs.complete_selection) return; - if (lhs.full_selection) { - lhs.full_selection = false; - for (auto mod : design->modules()) - lhs.selected_modules.insert(mod->name); + if (rhs.full_selection && !lhs.selects_boxes) + return; + + if (lhs.empty()) + return; + + if (rhs.empty()) { + lhs.clear(); + return; } + select_all(design, lhs); + std::vector del_list; - for (auto &it : lhs.selected_modules) - if (rhs.selected_modules.count(it) == 0) { - if (rhs.selected_members.count(it) > 0) - for (auto &it2 : rhs.selected_members.at(it)) - lhs.selected_members[it].insert(it2); - del_list.push_back(it); - } + for (auto mod_name : lhs.selected_modules) { + if (rhs.selected_whole_module(mod_name)) + continue; + if (rhs.selected_module(mod_name)) + for (auto memb_name : rhs.selected_members.at(mod_name)) + lhs.selected_members[mod_name].insert(memb_name); + del_list.push_back(mod_name); + } for (auto &it : del_list) lhs.selected_modules.erase(it); del_list.clear(); for (auto &it : lhs.selected_members) { - if (rhs.selected_modules.count(it.first) > 0) + if (rhs.selected_whole_module(it.first)) continue; - if (rhs.selected_members.count(it.first) == 0) { + if (!rhs.selected_module(it.first)) { del_list.push_back(it.first); continue; } std::vector del_list2; for (auto &it2 : it.second) - if (rhs.selected_members.at(it.first).count(it2) == 0) + if (!rhs.selected_member(it.first, it2)) del_list2.push_back(it2); for (auto &it2 : del_list2) it.second.erase(it2); @@ -610,9 +668,7 @@ static void select_filter_active_mod(RTLIL::Design *design, RTLIL::Selection &se return; if (sel.full_selection) { - sel.full_selection = false; - sel.selected_modules.clear(); - sel.selected_members.clear(); + sel.clear(); sel.selected_modules.insert(design->selected_active_module); return; } @@ -645,8 +701,7 @@ static void select_stmt(RTLIL::Design *design, std::string arg, bool disable_emp if (arg[0] == '%') { if (arg == "%") { - if (design->selection_stack.size() > 0) - work_stack.push_back(design->selection_stack.back()); + work_stack.push_back(design->selection()); } else if (arg == "%%") { while (work_stack.size() > 1) { @@ -796,15 +851,16 @@ static void select_stmt(RTLIL::Design *design, std::string arg, bool disable_emp } } - work_stack.push_back(RTLIL::Selection()); + bool full_selection = (arg == "*" && arg_mod == "*"); + work_stack.push_back(RTLIL::Selection(full_selection, select_blackboxes, design)); RTLIL::Selection &sel = work_stack.back(); - if (arg == "*" && arg_mod == "*" && select_blackboxes) { + if (sel.full_selection) { + if (sel.selects_boxes) sel.optimize(design); select_filter_active_mod(design, work_stack.back()); return; } - sel.full_selection = false; for (auto mod : design->modules()) { if (!select_blackboxes && mod->get_blackbox_attribute()) @@ -945,38 +1001,33 @@ static void select_stmt(RTLIL::Design *design, std::string arg, bool disable_emp for (auto &it : arg_mod_found) { if (it.second == false && !disable_empty_warning) { - log_warning("Selection \"%s\" did not match any module.\n", it.first.c_str()); + std::string selection_str = select_blackboxes ? "=" : ""; + selection_str += it.first; + log_warning("Selection \"%s\" did not match any module.\n", selection_str.c_str()); } } for (auto &it : arg_memb_found) { if (it.second == false && !disable_empty_warning) { - log_warning("Selection \"%s\" did not match any object.\n", it.first.c_str()); + std::string selection_str = select_blackboxes ? "=" : ""; + selection_str += it.first; + log_warning("Selection \"%s\" did not match any object.\n", selection_str.c_str()); } } } static std::string describe_selection_for_assert(RTLIL::Design *design, RTLIL::Selection *sel, bool whole_modules = false) { + bool push_selection = &design->selection() != sel; + if (push_selection) design->push_selection(*sel); std::string desc = "Selection contains:\n"; - for (auto mod : design->modules()) + for (auto mod : design->all_selected_modules()) { - if (sel->selected_module(mod->name)) { - if (whole_modules && sel->selected_whole_module(mod->name)) - desc += stringf("%s\n", id2cstr(mod->name)); - for (auto wire : mod->wires()) - if (sel->selected_member(mod->name, wire->name)) - desc += stringf("%s/%s\n", id2cstr(mod->name), id2cstr(wire->name)); - for (auto &it : mod->memories) - if (sel->selected_member(mod->name, it.first)) - desc += stringf("%s/%s\n", id2cstr(mod->name), id2cstr(it.first)); - for (auto cell : mod->cells()) - if (sel->selected_member(mod->name, cell->name)) - desc += stringf("%s/%s\n", id2cstr(mod->name), id2cstr(cell->name)); - for (auto &it : mod->processes) - if (sel->selected_member(mod->name, it.first)) - desc += stringf("%s/%s\n", id2cstr(mod->name), id2cstr(it.first)); - } + if (whole_modules && sel->selected_whole_module(mod->name)) + desc += stringf("%s\n", id2cstr(mod->name)); + for (auto it : mod->selected_members()) + desc += stringf("%s/%s\n", id2cstr(mod->name), id2cstr(it->name)); } + if (push_selection) design->pop_selection(); return desc; } @@ -1001,9 +1052,9 @@ void handle_extra_select_args(Pass *pass, const vector &args, size_t arg work_stack.pop_back(); } if (work_stack.empty()) - design->selection_stack.push_back(RTLIL::Selection(false)); + design->push_empty_selection(); else - design->selection_stack.push_back(work_stack.back()); + design->push_selection(work_stack.back()); } // extern decl. in register.h @@ -1017,7 +1068,7 @@ RTLIL::Selection eval_select_args(const vector &args, RTLIL::Design *des work_stack.pop_back(); } if (work_stack.empty()) - return RTLIL::Selection(false); + return RTLIL::Selection::EmptySelection(design); return work_stack.back(); } @@ -1390,7 +1441,7 @@ struct SelectPass : public Pass { if (f.fail()) log_error("Can't open '%s' for reading: %s\n", read_file.c_str(), strerror(errno)); - RTLIL::Selection sel(false); + auto sel = RTLIL::Selection::EmptySelection(design); string line; while (std::getline(f, line)) { @@ -1431,7 +1482,7 @@ struct SelectPass : public Pass { log_cmd_error("Option -unset can not be combined with -list, -write, -count, -set, %s.\n", common_flagset); if (work_stack.size() == 0 && got_module) { - RTLIL::Selection sel; + auto sel = RTLIL::Selection::FullSelection(design); select_filter_active_mod(design, sel); work_stack.push_back(sel); } @@ -1441,16 +1492,16 @@ struct SelectPass : public Pass { work_stack.pop_back(); } - log_assert(design->selection_stack.size() > 0); + log_assert(!design->selection_stack.empty()); if (clear_mode) { - design->selection_stack.back() = RTLIL::Selection(true); + design->selection() = RTLIL::Selection::FullSelection(design); design->selected_active_module = std::string(); return; } if (none_mode) { - design->selection_stack.back() = RTLIL::Selection(false); + design->selection() = RTLIL::Selection::EmptySelection(design); return; } @@ -1465,28 +1516,17 @@ struct SelectPass : public Pass { if (f == nullptr) log_error("Can't open '%s' for writing: %s\n", write_file.c_str(), strerror(errno)); } - RTLIL::Selection *sel = &design->selection_stack.back(); if (work_stack.size() > 0) - sel = &work_stack.back(); + design->push_selection(work_stack.back()); + RTLIL::Selection *sel = &design->selection(); sel->optimize(design); - for (auto mod : design->modules()) + for (auto mod : design->all_selected_modules()) { if (sel->selected_whole_module(mod->name) && list_mode) log("%s\n", id2cstr(mod->name)); - if (sel->selected_module(mod->name) && !list_mod_mode) { - for (auto wire : mod->wires()) - if (sel->selected_member(mod->name, wire->name)) - LOG_OBJECT("%s/%s\n", id2cstr(mod->name), id2cstr(wire->name)) - for (auto &it : mod->memories) - if (sel->selected_member(mod->name, it.first)) - LOG_OBJECT("%s/%s\n", id2cstr(mod->name), id2cstr(it.first)) - for (auto cell : mod->cells()) - if (sel->selected_member(mod->name, cell->name)) - LOG_OBJECT("%s/%s\n", id2cstr(mod->name), id2cstr(cell->name)) - for (auto &it : mod->processes) - if (sel->selected_member(mod->name, it.first)) - LOG_OBJECT("%s/%s\n", id2cstr(mod->name), id2cstr(it.first)) - } + if (!list_mod_mode) + for (auto it : mod->selected_members()) + LOG_OBJECT("%s/%s\n", id2cstr(mod->name), id2cstr(it->name)) } if (count_mode) { @@ -1495,6 +1535,8 @@ struct SelectPass : public Pass { } if (f != nullptr) fclose(f); + if (work_stack.size() > 0) + design->pop_selection(); #undef LOG_OBJECT return; } @@ -1503,8 +1545,8 @@ struct SelectPass : public Pass { { if (work_stack.size() == 0) log_cmd_error("Nothing to add to selection.\n"); - select_op_union(design, design->selection_stack.back(), work_stack.back()); - design->selection_stack.back().optimize(design); + select_op_union(design, design->selection(), work_stack.back()); + design->selection().optimize(design); return; } @@ -1512,8 +1554,8 @@ struct SelectPass : public Pass { { if (work_stack.size() == 0) log_cmd_error("Nothing to delete from selection.\n"); - select_op_diff(design, design->selection_stack.back(), work_stack.back()); - design->selection_stack.back().optimize(design); + select_op_diff(design, design->selection(), work_stack.back()); + design->selection().optimize(design); return; } @@ -1553,23 +1595,13 @@ struct SelectPass : public Pass { if (work_stack.size() == 0) log_cmd_error("No selection to check.\n"); RTLIL::Selection *sel = &work_stack.back(); + design->push_selection(*sel); sel->optimize(design); - for (auto mod : design->modules()) - if (sel->selected_module(mod->name)) { - module_count++; - for (auto wire : mod->wires()) - if (sel->selected_member(mod->name, wire->name)) - total_count++; - for (auto &it : mod->memories) - if (sel->selected_member(mod->name, it.first)) - total_count++; - for (auto cell : mod->cells()) - if (sel->selected_member(mod->name, cell->name)) - total_count++; - for (auto &it : mod->processes) - if (sel->selected_member(mod->name, it.first)) - total_count++; - } + for (auto mod : design->all_selected_modules()) { + module_count++; + for ([[maybe_unused]] auto member_name : mod->selected_members()) + total_count++; + } if (assert_modcount >= 0 && assert_modcount != module_count) { log_error("Assertion failed: selection contains %d modules instead of the asserted %d:%s\n", @@ -1593,13 +1625,14 @@ struct SelectPass : public Pass { log_error("Assertion failed: selection contains %d elements, less than the minimum number %d:%s\n%s", total_count, assert_min, sel_str.c_str(), desc.c_str()); } + design->pop_selection(); return; } if (!set_name.empty()) { if (work_stack.size() == 0) - design->selection_vars[set_name] = RTLIL::Selection(false); + design->selection_vars[set_name] = RTLIL::Selection::EmptySelection(design); else design->selection_vars[set_name] = work_stack.back(); return; @@ -1613,7 +1646,7 @@ struct SelectPass : public Pass { } if (work_stack.size() == 0) { - RTLIL::Selection &sel = design->selection_stack.back(); + RTLIL::Selection &sel = design->selection(); if (sel.full_selection) log("*\n"); for (auto &it : sel.selected_modules) @@ -1624,8 +1657,8 @@ struct SelectPass : public Pass { return; } - design->selection_stack.back() = work_stack.back(); - design->selection_stack.back().optimize(design); + design->selection() = work_stack.back(); + design->selection().optimize(design); } } SelectPass; @@ -1665,7 +1698,8 @@ struct CdPass : public Pass { log_cmd_error("Invalid number of arguments.\n"); if (args.size() == 1 || args[1] == "/") { - design->selection_stack.back() = RTLIL::Selection(true); + design->pop_selection(); + design->push_full_selection(); design->selected_active_module = std::string(); return; } @@ -1674,7 +1708,8 @@ struct CdPass : public Pass { { string modname = design->selected_active_module; - design->selection_stack.back() = RTLIL::Selection(true); + design->pop_selection(); + design->push_full_selection(); design->selected_active_module = std::string(); while (1) @@ -1691,9 +1726,10 @@ struct CdPass : public Pass { continue; design->selected_active_module = modname; - design->selection_stack.back() = RTLIL::Selection(); - select_filter_active_mod(design, design->selection_stack.back()); - design->selection_stack.back().optimize(design); + design->pop_selection(); + design->push_full_selection(); + select_filter_active_mod(design, design->selection()); + design->selection().optimize(design); return; } @@ -1710,9 +1746,10 @@ struct CdPass : public Pass { if (design->module(modname) != nullptr) { design->selected_active_module = modname; - design->selection_stack.back() = RTLIL::Selection(); - select_filter_active_mod(design, design->selection_stack.back()); - design->selection_stack.back().optimize(design); + design->pop_selection(); + design->push_full_selection(); + select_filter_active_mod(design, design->selection()); + design->selection().optimize(design); return; } @@ -1759,7 +1796,7 @@ struct LsPass : public Pass { { std::vector matches; - for (auto mod : design->selected_modules()) + for (auto mod : design->all_selected_modules()) matches.push_back(mod->name); if (!matches.empty()) { diff --git a/passes/cmds/setattr.cc b/passes/cmds/setattr.cc index 710fa9ab4..f590c2fa9 100644 --- a/passes/cmds/setattr.cc +++ b/passes/cmds/setattr.cc @@ -96,32 +96,16 @@ struct SetattrPass : public Pass { } extra_args(args, argidx, design); - for (auto module : design->modules()) + for (auto module : design->all_selected_modules()) { if (flag_mod) { - if (design->selected_whole_module(module->name)) + if (module->is_selected_whole()) do_setunset(module->attributes, setunset_list); continue; } - if (!design->selected(module)) - continue; - - for (auto wire : module->wires()) - if (design->selected(module, wire)) - do_setunset(wire->attributes, setunset_list); - - for (auto &it : module->memories) - if (design->selected(module, it.second)) - do_setunset(it.second->attributes, setunset_list); - - for (auto cell : module->cells()) - if (design->selected(module, cell)) - do_setunset(cell->attributes, setunset_list); - - for (auto &it : module->processes) - if (design->selected(module, it.second)) - do_setunset(it.second->attributes, setunset_list); + for (auto memb : module->selected_members()) + do_setunset(memb->attributes, setunset_list); } } } SetattrPass; @@ -152,16 +136,8 @@ struct WbflipPass : public Pass { } extra_args(args, argidx, design); - for (Module *module : design->modules()) - { - if (!design->selected(module)) - continue; - - if (module->get_bool_attribute(ID::blackbox)) - continue; - + for (auto *module : design->selected_modules(RTLIL::SELECT_ALL, RTLIL::SB_EXCL_BB_ONLY)) module->set_bool_attribute(ID::whitebox, !module->get_bool_attribute(ID::whitebox)); - } } } WbflipPass; diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc index 82b5c6bcf..8a1bd58c4 100644 --- a/passes/cmds/show.cc +++ b/passes/cmds/show.cc @@ -802,8 +802,8 @@ struct ShowPass : public Pass { std::pair data; data.first = args[++argidx], argidx++; handle_extra_select_args(this, args, argidx, argidx+1, design); - data.second = design->selection_stack.back(); - design->selection_stack.pop_back(); + data.second = design->selection(); + design->pop_selection(); color_selections.push_back(data); continue; } @@ -811,8 +811,8 @@ struct ShowPass : public Pass { std::pair data; data.first = args[++argidx], argidx++; handle_extra_select_args(this, args, argidx, argidx+1, design); - data.second = design->selection_stack.back(); - design->selection_stack.pop_back(); + data.second = design->selection(); + design->pop_selection(); label_selections.push_back(data); continue; } diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index 23583a8a8..6fae312b4 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -468,7 +468,7 @@ struct StatPass : public Pass { first_module = false; } else { log("\n"); - log("=== %s%s ===\n", log_id(mod->name), design->selected_whole_module(mod->name) ? "" : " (partially selected)"); + log("=== %s%s ===\n", log_id(mod->name), mod->is_selected_whole() ? "" : " (partially selected)"); log("\n"); data.log_data(mod->name, false); } diff --git a/passes/cmds/test_select.cc b/passes/cmds/test_select.cc new file mode 100644 index 000000000..0076500ce --- /dev/null +++ b/passes/cmds/test_select.cc @@ -0,0 +1,172 @@ +#include "kernel/yosys.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct TestSelectPass : public Pass { + TestSelectPass() : Pass("test_select", "call internal selection methods on design for testing purposes") { } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" test_select [options]\n"); + log("\n"); + log("Test semantics of internal 'RTLIL::Design::selected_modules()' by modifying the\n"); + log("current selection to only include the results of the call.\n"); + log("\n"); + log("Includes partially selected modules by default, use one of the following options\n"); + log("to remove them instead:\n"); + log("\n"); + log(" -whole_only\n"); + log("\n"); + log(" -whole_warn\n"); + log(" -whole_err\n"); + log(" -whole_cmderr\n"); + log(" remove partially selected modules, raising warning, error, or cmd error\n"); + log("\n"); + log(" test_select -unboxed_only [options]\n"); + log("\n"); + log("Remove boxed modules from selection.\n"); + log("\n"); + log(" -include_wb\n"); + log(" don't remove white boxes from selection\n"); + log("\n"); + log(" -warn_boxes\n"); + log(" -err_boxes\n"); + log(" -cmderr_boxes\n"); + log(" raise warning, error, or cmd error if a box is removed\n"); + log("\n"); + } + void execute(vector args, RTLIL::Design *design) override + { + log_header(design, "Executing TEST_SELECTION pass.\n"); + bool whole_only = false; + bool whole_warn = false; + bool whole_err = false; + bool whole_cmderr = false; + int whole_opts = 0; + + bool warn_boxes = false; + bool err_boxes = false; + bool cmderr_boxes = false; + int box_level = 0; + + bool unboxed_only = false; + bool include_wb = false; + + int argidx; + for (argidx = 1; argidx < GetSize(args); argidx++) + { + if (args[argidx] == "-whole_only") { + whole_only = true; + whole_opts++; + continue; + } + if (args[argidx] == "-whole_warn") { + whole_warn = true; + whole_opts++; + continue; + } + if (args[argidx] == "-whole_err") { + whole_err = true; + whole_opts++; + continue; + } + if (args[argidx] == "-whole_cmderr") { + whole_cmderr = true; + whole_opts++; + continue; + } + if (args[argidx] == "-warn_boxes") { + warn_boxes = true; + box_level++; + continue; + } + if (args[argidx] == "-err_boxes") { + err_boxes = true; + box_level++; + continue; + } + if (args[argidx] == "-cmderr_boxes") { + cmderr_boxes = true; + box_level++; + continue; + } + if (args[argidx] == "-unboxed_only") { + unboxed_only = true; + continue; + } + if (args[argidx] == "-include_wb") { + include_wb = true; + continue; + } + break; + } + + if (whole_opts > 1) + log_cmd_error("Only one of -whole_only, -whole_warn, -whole_err, or -whole_cmderr may be selected.\n"); + + if (include_wb && !unboxed_only) + log_cmd_error("-include_wb option requires -unboxed_only.\n"); + + if (box_level > 0 && !unboxed_only) + log_cmd_error("-*_boxes options require -unboxed_only.\n"); + + if (box_level > 1) + log_cmd_error("Only one of -warn_boxes, -err_boxes, or -cmderr_boxes may be selected.\n"); + + extra_args(args, argidx, design, false); + + // construct enums + RTLIL::SelectPartials partials; + if (whole_only) + partials = RTLIL::SELECT_WHOLE_ONLY; + else if (whole_warn) + partials = RTLIL::SELECT_WHOLE_WARN; + else if (whole_err) + partials = RTLIL::SELECT_WHOLE_ERR; + else if (whole_cmderr) + partials = RTLIL::SELECT_WHOLE_CMDERR; + else + partials = RTLIL::SELECT_ALL; + + char boxes = RTLIL::SB_ALL; + if (warn_boxes) boxes |= RTLIL::SB_WARN; + if (err_boxes) boxes |= RTLIL::SB_ERR; + if (cmderr_boxes) boxes |= RTLIL::SB_CMDERR; + if (unboxed_only) boxes |= RTLIL::SB_UNBOXED_ONLY; + if (include_wb) boxes |= RTLIL::SB_INCL_WB; + + // get sub selection and store the results + auto sub_sel = design->selected_modules(partials, (RTLIL::SelectBoxes)boxes); + pool selected_modules; + dict> selected_members; + + for (auto *mod : sub_sel) { + if (mod->is_selected_whole()) { + log_debug(" Adding %s.\n", id2cstr(mod->name)); + selected_modules.insert(mod->name); + } else for (auto *memb : mod->selected_members()) { + log_debug(" Adding %s.%s.\n", id2cstr(mod->name), id2cstr(memb->name)); + selected_members[mod->name].insert(memb); + } + } + + // fully reset current selection + design->selection() = RTLIL::Selection::EmptySelection(design); + + // add back sub selection + for (auto modname : selected_modules) + design->selection().select(design->module(modname)); + for (auto &it : selected_members) { + auto mod = design->module(it.first); + for (auto memb : it.second) + design->selection().select(mod, memb); + } + + // optimize + design->selection().optimize(design); + } +} TestSelectPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/viz.cc b/passes/cmds/viz.cc index 9dd68bd00..131e799ab 100644 --- a/passes/cmds/viz.cc +++ b/passes/cmds/viz.cc @@ -950,8 +950,8 @@ struct VizPass : public Pass { auto type = arg == "-g" || arg == "-G" ? VizConfig::TYPE_G : arg == "-u" || arg == "-U" ? VizConfig::TYPE_U : arg == "-x" || arg == "-X" ? VizConfig::TYPE_X : VizConfig::TYPE_S; - config.groups.push_back({type, design->selection_stack.back()}); - design->selection_stack.pop_back(); + config.groups.push_back({type, design->selection()}); + design->pop_selection(); continue; } if (arg == "-0" || arg == "-1" || arg == "-2" || arg == "-3" || arg == "-4" || diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc index 8372c0339..d94b5cef6 100644 --- a/passes/hierarchy/hierarchy.cc +++ b/passes/hierarchy/hierarchy.cc @@ -978,6 +978,11 @@ struct HierarchyPass : public Pass { } } + bool verific_mod = false; +#ifdef YOSYS_ENABLE_VERIFIC + verific_mod = verific_import_pending; +#endif + if (top_mod == nullptr && !load_top_mod.empty()) { #ifdef YOSYS_ENABLE_VERIFIC if (verific_import_pending) { @@ -1418,13 +1423,18 @@ struct HierarchyPass : public Pass { if (m == nullptr) continue; - if (m->get_blackbox_attribute() && !cell->parameters.empty() && m->get_bool_attribute(ID::dynports)) { - IdString new_m_name = m->derive(design, cell->parameters, true); - if (new_m_name.empty()) - continue; - if (new_m_name != m->name) { - m = design->module(new_m_name); - blackbox_derivatives.insert(m); + bool boxed_params = false; + if (m->get_blackbox_attribute() && !cell->parameters.empty()) { + if (m->get_bool_attribute(ID::dynports)) { + IdString new_m_name = m->derive(design, cell->parameters, true); + if (new_m_name.empty()) + continue; + if (new_m_name != m->name) { + m = design->module(new_m_name); + blackbox_derivatives.insert(m); + } + } else { + boxed_params = true; } } @@ -1440,8 +1450,12 @@ struct HierarchyPass : public Pass { SigSpec sig = conn.second; - if (!keep_portwidths && GetSize(w) != GetSize(conn.second)) - { + bool resize_widths = !keep_portwidths && GetSize(w) != GetSize(conn.second); + if (resize_widths && verific_mod && boxed_params) + log_warning("Ignoring width mismatch on %s.%s.%s from verific, is port width parametrizable?\n", + log_id(module), log_id(cell), log_id(conn.first) + ); + else if (resize_widths) { if (GetSize(w) < GetSize(conn.second)) { int n = GetSize(conn.second) - GetSize(w); diff --git a/passes/hierarchy/submod.cc b/passes/hierarchy/submod.cc index 52fd59cf8..facc5d173 100644 --- a/passes/hierarchy/submod.cc +++ b/passes/hierarchy/submod.cc @@ -246,7 +246,7 @@ struct SubmodWorker SubmodWorker(RTLIL::Design *design, RTLIL::Module *module, bool copy_mode = false, bool hidden_mode = false, std::string opt_name = std::string()) : design(design), module(module), sigmap(module), copy_mode(copy_mode), hidden_mode(hidden_mode), opt_name(opt_name) { - if (!design->selected_whole_module(module->name) && opt_name.empty()) + if (!module->is_selected_whole() && opt_name.empty()) return; if (module->processes.size() > 0) { diff --git a/passes/memory/memory_libmap.cc b/passes/memory/memory_libmap.cc index b0d0498ea..a5b30c7ae 100644 --- a/passes/memory/memory_libmap.cc +++ b/passes/memory/memory_libmap.cc @@ -2232,11 +2232,11 @@ struct MemoryLibMapPass : public Pass { if (module->has_processes_warn()) continue; - MapWorker worker(module); + auto worker = std::make_unique(module); auto mems = Mem::get_selected_memories(module); for (auto &mem : mems) { - MemMapping map(worker, mem, lib, opts); + MemMapping map(*worker, mem, lib, opts); int idx = -1; int best = map.logic_cost; if (!map.logic_ok) { @@ -2259,7 +2259,7 @@ struct MemoryLibMapPass : public Pass { } else { map.emit(map.cfgs[idx]); // Rebuild indices after modifying module - worker = MapWorker(module); + worker = std::make_unique(module); } } } diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc index c37c03607..620b38813 100644 --- a/passes/opt/opt_clean.cc +++ b/passes/opt/opt_clean.cc @@ -734,7 +734,7 @@ struct CleanPass : public Pass { count_rm_cells = 0; count_rm_wires = 0; - for (auto module : design->selected_whole_modules()) { + for (auto module : design->selected_unboxed_whole_modules()) { if (module->has_processes()) continue; rmunused_module(module, purge_mode, ys_debug(), true); diff --git a/passes/opt/share.cc b/passes/opt/share.cc index dfc44758f..1d7ba7d98 100644 --- a/passes/opt/share.cc +++ b/passes/opt/share.cc @@ -110,7 +110,7 @@ struct ShareWorker // Code for sharing and comparing MACC cells // --------------------------------------------------- - static int bits_macc_port(const Macc::port_t &p, int width) + static int bits_macc_term(const Macc::term_t &p, int width) { if (GetSize(p.in_a) == 0 || GetSize(p.in_b) == 0) return min(max(GetSize(p.in_a), GetSize(p.in_b)), width); @@ -120,8 +120,8 @@ struct ShareWorker static int bits_macc(const Macc &m, int width) { int bits = 0; - for (auto &p : m.ports) - bits += bits_macc_port(p, width); + for (auto &p : m.terms) + bits += bits_macc_term(p, width); return bits; } @@ -132,7 +132,7 @@ struct ShareWorker return bits_macc(m, width); } - static bool cmp_macc_ports(const Macc::port_t &p1, const Macc::port_t &p2) + static bool cmp_macc_ports(const Macc::term_t &p1, const Macc::term_t &p2) { bool mul1 = GetSize(p1.in_a) && GetSize(p1.in_b); bool mul2 = GetSize(p2.in_a) && GetSize(p2.in_b); @@ -161,7 +161,7 @@ struct ShareWorker return false; } - int share_macc_ports(Macc::port_t &p1, Macc::port_t &p2, int w1, int w2, + int share_macc_ports(Macc::term_t &p1, Macc::term_t &p2, int w1, int w2, RTLIL::SigSpec act = RTLIL::SigSpec(), Macc *supermacc = nullptr, pool *supercell_aux = nullptr) { if (p1.do_subtract != p2.do_subtract) @@ -216,12 +216,12 @@ struct ShareWorker supercell_aux->insert(module->addMux(NEW_ID, sig_b2, sig_b1, act, sig_b)); } - Macc::port_t p; + Macc::term_t p; p.in_a = sig_a; p.in_b = sig_b; p.is_signed = force_signed; p.do_subtract = p1.do_subtract; - supermacc->ports.push_back(p); + supermacc->terms.push_back(p); } int score = 1000 + abs(GetSize(p1.in_a) - GetSize(p2.in_a)) * max(abs(GetSize(p1.in_b) - GetSize(p2.in_b)), 1); @@ -248,15 +248,15 @@ struct ShareWorker m1.optimize(w1); m2.optimize(w2); - std::sort(m1.ports.begin(), m1.ports.end(), cmp_macc_ports); - std::sort(m2.ports.begin(), m2.ports.end(), cmp_macc_ports); + std::sort(m1.terms.begin(), m1.terms.end(), cmp_macc_ports); + std::sort(m2.terms.begin(), m2.terms.end(), cmp_macc_ports); std::set m1_unmapped, m2_unmapped; - for (int i = 0; i < GetSize(m1.ports); i++) + for (int i = 0; i < GetSize(m1.terms); i++) m1_unmapped.insert(i); - for (int i = 0; i < GetSize(m2.ports); i++) + for (int i = 0; i < GetSize(m2.terms); i++) m2_unmapped.insert(i); while (1) @@ -265,7 +265,7 @@ struct ShareWorker for (int i : m1_unmapped) for (int j : m2_unmapped) { - int score = share_macc_ports(m1.ports[i], m2.ports[j], w1, w2); + int score = share_macc_ports(m1.terms[i], m2.terms[j], w1, w2); if (score >= 0 && (best_i < 0 || best_score > score)) best_i = i, best_j = j, best_score = score; } @@ -273,55 +273,55 @@ struct ShareWorker if (best_i >= 0) { m1_unmapped.erase(best_i); m2_unmapped.erase(best_j); - share_macc_ports(m1.ports[best_i], m2.ports[best_j], w1, w2, act, &supermacc, supercell_aux); + share_macc_ports(m1.terms[best_i], m2.terms[best_j], w1, w2, act, &supermacc, supercell_aux); } else break; } for (int i : m1_unmapped) { - RTLIL::SigSpec sig_a = m1.ports[i].in_a; - RTLIL::SigSpec sig_b = m1.ports[i].in_b; + RTLIL::SigSpec sig_a = m1.terms[i].in_a; + RTLIL::SigSpec sig_b = m1.terms[i].in_b; if (supercell_aux && GetSize(sig_a)) { sig_a = module->addWire(NEW_ID, GetSize(sig_a)); - supercell_aux->insert(module->addMux(NEW_ID, RTLIL::SigSpec(0, GetSize(sig_a)), m1.ports[i].in_a, act, sig_a)); + supercell_aux->insert(module->addMux(NEW_ID, RTLIL::SigSpec(0, GetSize(sig_a)), m1.terms[i].in_a, act, sig_a)); } if (supercell_aux && GetSize(sig_b)) { sig_b = module->addWire(NEW_ID, GetSize(sig_b)); - supercell_aux->insert(module->addMux(NEW_ID, RTLIL::SigSpec(0, GetSize(sig_b)), m1.ports[i].in_b, act, sig_b)); + supercell_aux->insert(module->addMux(NEW_ID, RTLIL::SigSpec(0, GetSize(sig_b)), m1.terms[i].in_b, act, sig_b)); } - Macc::port_t p; + Macc::term_t p; p.in_a = sig_a; p.in_b = sig_b; - p.is_signed = m1.ports[i].is_signed; - p.do_subtract = m1.ports[i].do_subtract; - supermacc.ports.push_back(p); + p.is_signed = m1.terms[i].is_signed; + p.do_subtract = m1.terms[i].do_subtract; + supermacc.terms.push_back(p); } for (int i : m2_unmapped) { - RTLIL::SigSpec sig_a = m2.ports[i].in_a; - RTLIL::SigSpec sig_b = m2.ports[i].in_b; + RTLIL::SigSpec sig_a = m2.terms[i].in_a; + RTLIL::SigSpec sig_b = m2.terms[i].in_b; if (supercell_aux && GetSize(sig_a)) { sig_a = module->addWire(NEW_ID, GetSize(sig_a)); - supercell_aux->insert(module->addMux(NEW_ID, m2.ports[i].in_a, RTLIL::SigSpec(0, GetSize(sig_a)), act, sig_a)); + supercell_aux->insert(module->addMux(NEW_ID, m2.terms[i].in_a, RTLIL::SigSpec(0, GetSize(sig_a)), act, sig_a)); } if (supercell_aux && GetSize(sig_b)) { sig_b = module->addWire(NEW_ID, GetSize(sig_b)); - supercell_aux->insert(module->addMux(NEW_ID, m2.ports[i].in_b, RTLIL::SigSpec(0, GetSize(sig_b)), act, sig_b)); + supercell_aux->insert(module->addMux(NEW_ID, m2.terms[i].in_b, RTLIL::SigSpec(0, GetSize(sig_b)), act, sig_b)); } - Macc::port_t p; + Macc::term_t p; p.in_a = sig_a; p.in_b = sig_b; - p.is_signed = m2.ports[i].is_signed; - p.do_subtract = m2.ports[i].do_subtract; - supermacc.ports.push_back(p); + p.is_signed = m2.terms[i].is_signed; + p.do_subtract = m2.terms[i].do_subtract; + supermacc.terms.push_back(p); } if (supercell) @@ -1000,6 +1000,61 @@ struct ShareWorker } } + pool> pattern_bits(const pool &activation_patterns) + { + pool> bits; + for (auto const &pattern : activation_patterns) { + for (int i = 0; i < GetSize(pattern.second); ++i) { + SigBit bit = pattern.first[i]; + State val = pattern.second[i]; + bits.emplace(bit, val); + } + } + return bits; + } + + bool onesided_restrict_activation_patterns( + pool &activation_patterns, const pool> &other_bits) + { + pool new_activation_patterns; + + bool simplified = false; + + for (auto const &pattern : activation_patterns) { + ssc_pair_t new_pair; + for (int i = 0; i < GetSize(pattern.second); ++i) { + SigBit bit = pattern.first[i]; + State val = pattern.second[i]; + if (other_bits.count({bit, val == State::S0 ? State::S1 : State::S0})) { + new_pair.first.append(bit); + new_pair.second.append(val); + } else { + simplified = true; + } + } + new_activation_patterns.emplace(std::move(new_pair)); + } + + activation_patterns = std::move(new_activation_patterns); + return simplified; + } + + // Only valid if the patterns on their own (i.e. without considering their input cone) are mutually exclusive! + bool restrict_activation_patterns(pool &activation_patterns, pool &other_activation_patterns) + { + pool> bits = pattern_bits(activation_patterns); + pool> other_bits = pattern_bits(other_activation_patterns); + + bool simplified = false; + simplified |= onesided_restrict_activation_patterns(activation_patterns, other_bits); + simplified |= onesided_restrict_activation_patterns(other_activation_patterns, bits); + + optimize_activation_patterns(activation_patterns); + optimize_activation_patterns(other_activation_patterns); + + return simplified; + } + RTLIL::SigSpec make_cell_activation_logic(const pool &activation_patterns, pool &supercell_aux) { RTLIL::Wire *all_cases_wire = module->addWire(NEW_ID, 0); @@ -1299,17 +1354,18 @@ struct ShareWorker other_cell_active.push_back(qcsat.ez->vec_eq(qcsat.importSig(p.first), qcsat.importSig(p.second))); all_ctrl_signals.append(p.first); } + int sub1 = qcsat.ez->expression(qcsat.ez->OpOr, cell_active); + int sub2 = qcsat.ez->expression(qcsat.ez->OpOr, other_cell_active); + bool pattern_only_solve = qcsat.ez->solve(qcsat.ez->AND(sub1, sub2)); qcsat.prepare(); - int sub1 = qcsat.ez->expression(qcsat.ez->OpOr, cell_active); if (!qcsat.ez->solve(sub1)) { log(" According to the SAT solver the cell %s is never active. Sharing is pointless, we simply remove it.\n", log_id(cell)); cells_to_remove.insert(cell); break; } - int sub2 = qcsat.ez->expression(qcsat.ez->OpOr, other_cell_active); if (!qcsat.ez->solve(sub2)) { log(" According to the SAT solver the cell %s is never active. Sharing is pointless, we simply remove it.\n", log_id(other_cell)); cells_to_remove.insert(other_cell); @@ -1317,28 +1373,43 @@ struct ShareWorker continue; } - qcsat.ez->non_incremental(); + pool optimized_cell_activation_patterns = filtered_cell_activation_patterns; + pool optimized_other_cell_activation_patterns = filtered_other_cell_activation_patterns; - all_ctrl_signals.sort_and_unify(); - std::vector sat_model = qcsat.importSig(all_ctrl_signals); - std::vector sat_model_values; + if (pattern_only_solve) { + qcsat.ez->non_incremental(); - qcsat.ez->assume(qcsat.ez->AND(sub1, sub2)); + all_ctrl_signals.sort_and_unify(); + std::vector sat_model = qcsat.importSig(all_ctrl_signals); + std::vector sat_model_values; - log(" Size of SAT problem: %zu cells, %d variables, %d clauses\n", - qcsat.imported_cells.size(), qcsat.ez->numCnfVariables(), qcsat.ez->numCnfClauses()); + qcsat.ez->assume(qcsat.ez->AND(sub1, sub2)); - if (qcsat.ez->solve(sat_model, sat_model_values)) { - log(" According to the SAT solver this pair of cells can not be shared.\n"); - log(" Model from SAT solver: %s = %d'", log_signal(all_ctrl_signals), GetSize(sat_model_values)); - for (int i = GetSize(sat_model_values)-1; i >= 0; i--) - log("%c", sat_model_values[i] ? '1' : '0'); - log("\n"); - continue; + log(" Size of SAT problem: %zu cells, %d variables, %d clauses\n", + qcsat.imported_cells.size(), qcsat.ez->numCnfVariables(), qcsat.ez->numCnfClauses()); + + if (qcsat.ez->solve(sat_model, sat_model_values)) { + log(" According to the SAT solver this pair of cells can not be shared.\n"); + log(" Model from SAT solver: %s = %d'", log_signal(all_ctrl_signals), GetSize(sat_model_values)); + for (int i = GetSize(sat_model_values)-1; i >= 0; i--) + log("%c", sat_model_values[i] ? '1' : '0'); + log("\n"); + continue; + } + + log(" According to the SAT solver this pair of cells can be shared.\n"); + } else { + log(" According to the SAT solver this pair of cells can be shared. (Pattern only case)\n"); + + if (restrict_activation_patterns(optimized_cell_activation_patterns, optimized_other_cell_activation_patterns)) { + for (auto &p : optimized_cell_activation_patterns) + log(" Simplified activation pattern for cell %s: %s = %s\n", log_id(cell), log_signal(p.first), log_signal(p.second)); + + for (auto &p : optimized_other_cell_activation_patterns) + log(" Simplified activation pattern for cell %s: %s = %s\n", log_id(other_cell), log_signal(p.first), log_signal(p.second)); + } } - log(" According to the SAT solver this pair of cells can be shared.\n"); - if (find_in_input_cone(cell, other_cell)) { log(" Sharing not possible: %s is in input cone of %s.\n", log_id(other_cell), log_id(cell)); continue; @@ -1354,20 +1425,20 @@ struct ShareWorker int cell_select_score = 0; int other_cell_select_score = 0; - for (auto &p : filtered_cell_activation_patterns) + for (auto &p : optimized_cell_activation_patterns) cell_select_score += p.first.size(); - for (auto &p : filtered_other_cell_activation_patterns) + for (auto &p : optimized_other_cell_activation_patterns) other_cell_select_score += p.first.size(); RTLIL::Cell *supercell; pool supercell_aux; if (cell_select_score <= other_cell_select_score) { - RTLIL::SigSpec act = make_cell_activation_logic(filtered_cell_activation_patterns, supercell_aux); + RTLIL::SigSpec act = make_cell_activation_logic(optimized_cell_activation_patterns, supercell_aux); supercell = make_supercell(cell, other_cell, act, supercell_aux); log(" Activation signal for %s: %s\n", log_id(cell), log_signal(act)); } else { - RTLIL::SigSpec act = make_cell_activation_logic(filtered_other_cell_activation_patterns, supercell_aux); + RTLIL::SigSpec act = make_cell_activation_logic(optimized_other_cell_activation_patterns, supercell_aux); supercell = make_supercell(other_cell, cell, act, supercell_aux); log(" Activation signal for %s: %s\n", log_id(other_cell), log_signal(act)); } diff --git a/passes/sat/cutpoint.cc b/passes/sat/cutpoint.cc index 88f995dda..7f0dc2fcf 100644 --- a/passes/sat/cutpoint.cc +++ b/passes/sat/cutpoint.cc @@ -37,10 +37,20 @@ struct CutpointPass : public Pass { log(" set cutpoint nets to undef (x). the default behavior is to create\n"); log(" an $anyseq cell and drive the cutpoint net from that\n"); log("\n"); + log(" -noscopeinfo\n"); + log(" do not create '$scopeinfo' cells that preserve attributes of cells that\n"); + log(" were removed by this pass\n"); + log("\n"); + log(" cutpoint -blackbox [options]\n"); + log("\n"); + log("Replace all instances of blackboxes in the design with a formal cut point.\n"); + log("\n"); } void execute(std::vector args, RTLIL::Design *design) override { - bool flag_undef = false; + bool flag_undef = false; + bool flag_scopeinfo = true; + bool flag_blackbox = false; log_header(design, "Executing CUTPOINT pass.\n"); @@ -51,26 +61,31 @@ struct CutpointPass : public Pass { flag_undef = true; continue; } + if (args[argidx] == "-noscopeinfo") { + flag_scopeinfo = false; + continue; + } + if (args[argidx] == "-blackbox") { + flag_blackbox = true; + continue; + } break; } extra_args(args, argidx, design); - for (auto module : design->selected_modules()) - { - if (design->selected_whole_module(module->name)) { - log("Making all outputs of module %s cut points, removing module contents.\n", log_id(module)); - module->new_connections(std::vector()); - for (auto cell : vector(module->cells())) - module->remove(cell); - vector output_wires; - for (auto wire : module->wires()) - if (wire->port_output) - output_wires.push_back(wire); - for (auto wire : output_wires) - module->connect(wire, flag_undef ? Const(State::Sx, GetSize(wire)) : module->Anyseq(NEW_ID, GetSize(wire))); - continue; - } + if (flag_blackbox) { + if (!design->full_selection()) + log_cmd_error("This command only operates on fully selected designs!\n"); + design->push_empty_selection(); + auto &selection = design->selection(); + for (auto module : design->modules()) + for (auto cell : module->cells()) + if (selection.boxed_module(cell->type)) + selection.select(module, cell); + } + for (auto module : design->all_selected_modules()) + { SigMap sigmap(module); pool cutpoint_bits; @@ -82,7 +97,26 @@ struct CutpointPass : public Pass { if (cell->output(conn.first)) module->connect(conn.second, flag_undef ? Const(State::Sx, GetSize(conn.second)) : module->Anyseq(NEW_ID, GetSize(conn.second))); } + + RTLIL::Cell *scopeinfo = nullptr; + auto cell_name = cell->name; + if (flag_scopeinfo && cell_name.isPublic()) { + auto scopeinfo = module->addCell(NEW_ID, ID($scopeinfo)); + scopeinfo->setParam(ID::TYPE, RTLIL::Const("blackbox")); + + for (auto const &attr : cell->attributes) + { + if (attr.first == ID::hdlname) + scopeinfo->attributes.insert(attr); + else + scopeinfo->attributes.emplace(stringf("\\cell_%s", RTLIL::unescape_id(attr.first).c_str()), attr.second); + } + } + module->remove(cell); + + if (scopeinfo != nullptr) + module->rename(scopeinfo, cell_name); } for (auto wire : module->selected_wires()) { diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc index 8fac93b98..dd2f1d255 100644 --- a/passes/sat/sim.cc +++ b/passes/sat/sim.cc @@ -2887,7 +2887,7 @@ struct SimPass : public Pass { if (!top_mod) log_cmd_error("Design has no top module, use the 'hierarchy' command to specify one.\n"); } else { - auto mods = design->selected_whole_modules(); + auto mods = design->selected_unboxed_whole_modules(); if (GetSize(mods) != 1) log_cmd_error("Only one top module must be selected.\n"); top_mod = mods.front(); @@ -3016,7 +3016,7 @@ struct Fst2TbPass : public Pass { if (!top_mod) log_cmd_error("Design has no top module, use the 'hierarchy' command to specify one.\n"); } else { - auto mods = design->selected_whole_modules(); + auto mods = design->selected_unboxed_whole_modules(); if (GetSize(mods) != 1) log_cmd_error("Only one top module must be selected.\n"); top_mod = mods.front(); diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index e6a910a0d..c9fe98a74 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -54,6 +54,7 @@ OBJS += passes/techmap/flowmap.o OBJS += passes/techmap/extractinv.o OBJS += passes/techmap/cellmatch.o OBJS += passes/techmap/clockgate.o +OBJS += passes/techmap/constmap.o endif ifeq ($(DISABLE_SPAWN),0) @@ -62,5 +63,5 @@ EXTRA_OBJS += passes/techmap/filterlib.o $(PROGRAM_PREFIX)yosys-filterlib$(EXE): passes/techmap/filterlib.o $(Q) mkdir -p $(dir $@) - $(P) $(CXX) -o $(PROGRAM_PREFIX)yosys-filterlib$(EXE) $(LINKFLAGS) $^ $(LIBS) + $(P) $(CXX) -o $(PROGRAM_PREFIX)yosys-filterlib$(EXE) $(LINKFLAGS) $^ $(EXE_LIBS) $(LIBS) endif diff --git a/passes/techmap/abc9.cc b/passes/techmap/abc9.cc index a96a82659..fe5cc7af1 100644 --- a/passes/techmap/abc9.cc +++ b/passes/techmap/abc9.cc @@ -306,9 +306,10 @@ struct Abc9Pass : public ScriptPass } run("design -stash $abc9"); run("design -load $abc9_map"); - run("proc"); + if (help_mode) run("select =*"); + else active_design->push_complete_selection(); run("wbflip"); - run("techmap -wb -map %$abc9 -map +/techmap.v A:abc9_flop"); + run("techmap -autoproc -wb -map %$abc9 -map +/techmap.v A:abc9_flop"); run("opt -nodffe -nosdff"); if (dff_mode || help_mode) { if (!help_mode) @@ -369,6 +370,8 @@ struct Abc9Pass : public ScriptPass if (saved_designs.count("$abc9_holes") || help_mode) { run("design -stash $abc9"); run("design -load $abc9_holes"); + if (help_mode) run("select =*"); + else active_design->push_complete_selection(); run("techmap -wb -map %$abc9 -map +/techmap.v"); run("opt -purge"); run("aigmap"); @@ -391,7 +394,7 @@ struct Abc9Pass : public ScriptPass } else { auto selected_modules = active_design->selected_modules(); - active_design->selection_stack.emplace_back(false); + active_design->push_empty_selection(); for (auto mod : selected_modules) { if (mod->processes.size() > 0) { @@ -400,8 +403,9 @@ struct Abc9Pass : public ScriptPass } log_push(); - active_design->selection().select(mod); + active_design->select(mod); + // this check does nothing because the above line adds the whole module to the selection if (!active_design->selected_whole_module(mod)) log_error("Can't handle partially selected module %s!\n", log_id(mod)); @@ -452,7 +456,7 @@ struct Abc9Pass : public ScriptPass log_pop(); } - active_design->selection_stack.pop_back(); + active_design->pop_selection(); } } diff --git a/passes/techmap/abc9_ops.cc b/passes/techmap/abc9_ops.cc index 4c7667a43..6cb569b5a 100644 --- a/passes/techmap/abc9_ops.cc +++ b/passes/techmap/abc9_ops.cc @@ -454,7 +454,7 @@ void prep_bypass(RTLIL::Design *design) void prep_dff(RTLIL::Design *design) { - auto r = design->selection_vars.insert(std::make_pair(ID($abc9_flops), RTLIL::Selection(false))); + auto r = design->selection_vars.insert(std::make_pair(ID($abc9_flops), RTLIL::Selection::EmptySelection(design))); auto &modules_sel = r.first->second; for (auto module : design->selected_modules()) diff --git a/passes/techmap/abc_new.cc b/passes/techmap/abc_new.cc index 5be823916..dfa2e2f71 100644 --- a/passes/techmap/abc_new.cc +++ b/passes/techmap/abc_new.cc @@ -139,7 +139,7 @@ struct AbcNewPass : public ScriptPass { if (!help_mode) { selected_modules = order_modules(active_design, active_design->selected_whole_modules_warn()); - active_design->selection_stack.emplace_back(false); + active_design->push_empty_selection(); } else { selected_modules = {nullptr}; run("foreach module in selection"); @@ -157,7 +157,7 @@ struct AbcNewPass : public ScriptPass { exe_options = abc_exe_options; log_header(active_design, "Mapping module '%s'.\n", log_id(mod)); log_push(); - active_design->selection().select(mod); + active_design->select(mod); } std::string script_save; @@ -194,7 +194,7 @@ struct AbcNewPass : public ScriptPass { } if (!help_mode) { - active_design->selection_stack.pop_back(); + active_design->pop_selection(); } } } diff --git a/passes/techmap/aigmap.cc b/passes/techmap/aigmap.cc index 4836ebe34..19e568a61 100644 --- a/passes/techmap/aigmap.cc +++ b/passes/techmap/aigmap.cc @@ -171,8 +171,7 @@ struct AigmapPass : public Pass { module->remove(cell); if (select_mode) { - log_assert(!design->selection_stack.empty()); - RTLIL::Selection& sel = design->selection_stack.back(); + RTLIL::Selection& sel = design->selection(); sel.selected_members[module->name] = std::move(new_sel); } diff --git a/passes/techmap/alumacc.cc b/passes/techmap/alumacc.cc index 591c51c74..3dc54e8bb 100644 --- a/passes/techmap/alumacc.cc +++ b/passes/techmap/alumacc.cc @@ -142,7 +142,7 @@ struct AlumaccWorker log(" creating $macc model for %s (%s).\n", log_id(cell), log_id(cell->type)); maccnode_t *n = new maccnode_t; - Macc::port_t new_port; + Macc::term_t new_term; n->cell = cell; n->y = sigmap(cell->getPort(ID::Y)); @@ -153,32 +153,32 @@ struct AlumaccWorker if (cell->type.in(ID($pos), ID($neg))) { - new_port.in_a = sigmap(cell->getPort(ID::A)); - new_port.is_signed = cell->getParam(ID::A_SIGNED).as_bool(); - new_port.do_subtract = cell->type == ID($neg); - n->macc.ports.push_back(new_port); + new_term.in_a = sigmap(cell->getPort(ID::A)); + new_term.is_signed = cell->getParam(ID::A_SIGNED).as_bool(); + new_term.do_subtract = cell->type == ID($neg); + n->macc.terms.push_back(new_term); } if (cell->type.in(ID($add), ID($sub))) { - new_port.in_a = sigmap(cell->getPort(ID::A)); - new_port.is_signed = cell->getParam(ID::A_SIGNED).as_bool(); - new_port.do_subtract = false; - n->macc.ports.push_back(new_port); + new_term.in_a = sigmap(cell->getPort(ID::A)); + new_term.is_signed = cell->getParam(ID::A_SIGNED).as_bool(); + new_term.do_subtract = false; + n->macc.terms.push_back(new_term); - new_port.in_a = sigmap(cell->getPort(ID::B)); - new_port.is_signed = cell->getParam(ID::B_SIGNED).as_bool(); - new_port.do_subtract = cell->type == ID($sub); - n->macc.ports.push_back(new_port); + new_term.in_a = sigmap(cell->getPort(ID::B)); + new_term.is_signed = cell->getParam(ID::B_SIGNED).as_bool(); + new_term.do_subtract = cell->type == ID($sub); + n->macc.terms.push_back(new_term); } if (cell->type.in(ID($mul))) { - new_port.in_a = sigmap(cell->getPort(ID::A)); - new_port.in_b = sigmap(cell->getPort(ID::B)); - new_port.is_signed = cell->getParam(ID::A_SIGNED).as_bool(); - new_port.do_subtract = false; - n->macc.ports.push_back(new_port); + new_term.in_a = sigmap(cell->getPort(ID::A)); + new_term.in_b = sigmap(cell->getPort(ID::B)); + new_term.is_signed = cell->getParam(ID::A_SIGNED).as_bool(); + new_term.do_subtract = false; + n->macc.terms.push_back(new_term); } log_assert(sig_macc.count(n->y) == 0); @@ -190,7 +190,7 @@ struct AlumaccWorker { std::vector port_sizes; - for (auto &port : macc.ports) { + for (auto &port : macc.terms) { if (port.is_signed != is_signed) return true; if (!port.is_signed && port.do_subtract) @@ -235,9 +235,9 @@ struct AlumaccWorker if (delete_nodes.count(n)) continue; - for (int i = 0; i < GetSize(n->macc.ports); i++) + for (int i = 0; i < GetSize(n->macc.terms); i++) { - auto &port = n->macc.ports[i]; + auto &port = n->macc.terms[i]; if (GetSize(port.in_b) > 0 || sig_macc.count(port.in_a) == 0) continue; @@ -253,13 +253,13 @@ struct AlumaccWorker log(" merging $macc model for %s into %s.\n", log_id(other_n->cell), log_id(n->cell)); bool do_subtract = port.do_subtract; - for (int j = 0; j < GetSize(other_n->macc.ports); j++) { + for (int j = 0; j < GetSize(other_n->macc.terms); j++) { if (do_subtract) - other_n->macc.ports[j].do_subtract = !other_n->macc.ports[j].do_subtract; + other_n->macc.terms[j].do_subtract = !other_n->macc.terms[j].do_subtract; if (j == 0) - n->macc.ports[i--] = other_n->macc.ports[j]; + n->macc.terms[i--] = other_n->macc.terms[j]; else - n->macc.ports.push_back(other_n->macc.ports[j]); + n->macc.terms.push_back(other_n->macc.terms[j]); } delete_nodes.insert(other_n); @@ -288,7 +288,7 @@ struct AlumaccWorker bool subtract_b = false; alunode_t *alunode; - for (auto &port : n->macc.ports) + for (auto &port : n->macc.terms) if (GetSize(port.in_b) > 0) { goto next_macc; } else if (GetSize(port.in_a) == 1 && !port.is_signed && !port.do_subtract) { diff --git a/passes/techmap/booth.cc b/passes/techmap/booth.cc index cd9012e6c..a692b99fe 100644 --- a/passes/techmap/booth.cc +++ b/passes/techmap/booth.cc @@ -227,9 +227,9 @@ struct BoothPassWorker { continue; } - A = macc.ports[0].in_a; - B = macc.ports[0].in_b; - is_signed = macc.ports[0].is_signed; + A = macc.terms[0].in_a; + B = macc.terms[0].in_b; + is_signed = macc.terms[0].is_signed; Y = cell->getPort(ID::Y); } else { continue; diff --git a/passes/techmap/clockgate.cc b/passes/techmap/clockgate.cc index b34ad1769..2305cfc94 100644 --- a/passes/techmap/clockgate.cc +++ b/passes/techmap/clockgate.cc @@ -333,7 +333,7 @@ struct ClockgatePass : public Pass { dict clk_nets; int gated_flop_count = 0; - for (auto module : design->selected_whole_modules()) { + for (auto module : design->selected_unboxed_whole_modules()) { for (auto cell : module->cells()) { if (!RTLIL::builtin_ff_cell_types().count(cell->type)) continue; diff --git a/passes/techmap/constmap.cc b/passes/techmap/constmap.cc new file mode 100644 index 000000000..f0757403d --- /dev/null +++ b/passes/techmap/constmap.cc @@ -0,0 +1,106 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2025 King Lok Chung + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/register.h" +#include "kernel/rtlil.h" +#include "kernel/log.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +static std::string celltype, cell_portname, cell_paramname; + +static RTLIL::Module *module; +static RTLIL::SigChunk value; + +void constmap_worker(RTLIL::SigSpec &sig) +{ + if (sig.is_fully_const()){ + value = module->addWire(NEW_ID, sig.size()); + RTLIL::Cell *cell = module->addCell(NEW_ID, celltype); + cell->setParam(cell_paramname, sig.as_const()); + cell->setPort(cell_portname, value); + sig = value; + } +} + +struct ConstmapPass : public Pass { + ConstmapPass() : Pass("constmap", "technology mapping of coarse constant value") { } + void help() override + { + log("\n"); + log(" constmap [options] [selection]\n"); + log("\n"); + log("Map constants to a driver cell.\n"); + log("\n"); + log(" -cell \n"); + log(" Replace constant bits with this cell.\n"); + log(" The value of the constant will be stored to the parameter specified.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) override + { + log_header(design, "Executing CONSTMAP pass (mapping to constant driver).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-cell" && argidx+3 < args.size()){ + celltype = RTLIL::escape_id(args[++argidx]); + cell_portname = RTLIL::escape_id(args[++argidx]); + cell_paramname = RTLIL::escape_id(args[++argidx]); + continue; + } + break; + } + extra_args(args, argidx, design); + + + if (design->has(celltype)) { + Module *existing = design->module(celltype); + bool has_port = false; + for (auto &p : existing->ports){ + if (p == cell_portname){ + has_port = true; + break; + } + } + if (!has_port) + log_cmd_error("Cell type '%s' does not have port '%s'.\n", celltype.c_str(), cell_portname.c_str()); + + bool has_param = false; + for (auto &p : existing->avail_parameters){ + if (p == cell_paramname) + has_param = true; + } + + if (!has_param) + log_cmd_error("Cell type '%s' does not have parameter '%s'.\n", celltype.c_str(), cell_paramname.c_str()); + } + + + for (auto mod : design->selected_modules()) + { + module = mod; + module->rewrite_sigspecs(constmap_worker); + } + } +} ConstmapPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc index d7a952603..5594d5443 100644 --- a/passes/techmap/libparse.cc +++ b/passes/techmap/libparse.cc @@ -75,7 +75,7 @@ bool LibertyInputStream::extend_buffer_once() buffer.resize(buf_end + chunk_size); } - size_t read_size = f.rdbuf()->sgetn(buffer.data() + buf_end, chunk_size); + size_t read_size = f.rdbuf()->sgetn((char *)buffer.data() + buf_end, chunk_size); buf_end += read_size; if (read_size < chunk_size) eof = true; @@ -417,7 +417,77 @@ int LibertyParser::lexer(std::string &str) return c; } -LibertyAst *LibertyParser::parse() +void LibertyParser::report_unexpected_token(int tok) +{ + std::string eReport; + switch(tok) + { + case 'n': + error("Unexpected newline."); + break; + case '[': + case ']': + case '}': + case '{': + case '\"': + case ':': + eReport = "Unexpected '"; + eReport += static_cast(tok); + eReport += "'."; + error(eReport); + break; + case EOF: + error("Unexpected end of file"); + break; + default: + eReport = "Unexpected token: "; + eReport += static_cast(tok); + error(eReport); + } +} + +// FIXME: the AST needs to be extended to store +// these vector ranges. +void LibertyParser::parse_vector_range(int tok) +{ + // parse vector range [A] or [A:B] + std::string arg; + tok = lexer(arg); + if (tok != 'v') + { + // expected a vector array index + error("Expected a number."); + } + else + { + // fixme: check for number A + } + tok = lexer(arg); + // optionally check for : in case of [A:B] + // if it isn't we just expect ']' + // as we have [A] + if (tok == ':') + { + tok = lexer(arg); + if (tok != 'v') + { + // expected a vector array index + error("Expected a number."); + } + else + { + // fixme: check for number B + tok = lexer(arg); + } + } + // expect a closing bracket of array range + if (tok != ']') + { + error("Expected ']' on array range."); + } +} + +LibertyAst *LibertyParser::parse(bool top_level) { std::string str; @@ -431,30 +501,17 @@ LibertyAst *LibertyParser::parse() while ((tok == 'n') || (tok == ';')) tok = lexer(str); - if (tok == '}' || tok < 0) + if (tok == EOF) { + if (top_level) + return NULL; + report_unexpected_token(tok); + } + + if (tok == '}') return NULL; if (tok != 'v') { - std::string eReport; - switch(tok) - { - case 'n': - error("Unexpected newline."); - break; - case '[': - case ']': - case '}': - case '{': - case '\"': - case ':': - eReport = "Unexpected '"; - eReport += static_cast(tok); - eReport += "'."; - error(eReport); - break; - default: - error(); - } + report_unexpected_token(tok); } LibertyAst *ast = new LibertyAst; @@ -472,7 +529,11 @@ LibertyAst *LibertyParser::parse() if (tok == ':' && ast->value.empty()) { tok = lexer(ast->value); if (tok == 'v') { - tok = lexer(str); + tok = lexer(str); + if (tok == '[') { + parse_vector_range(tok); + tok = lexer(str); + } } while (tok == '+' || tok == '-' || tok == '*' || tok == '/' || tok == '!') { ast->value += tok; @@ -503,67 +564,15 @@ LibertyAst *LibertyParser::parse() if (tok == ')') break; - // FIXME: the AST needs to be extended to store - // these vector ranges. if (tok == '[') { - // parse vector range [A] or [A:B] - std::string arg; - tok = lexer(arg); - if (tok != 'v') - { - // expected a vector array index - error("Expected a number."); - } - else - { - // fixme: check for number A - } - tok = lexer(arg); - // optionally check for : in case of [A:B] - // if it isn't we just expect ']' - // as we have [A] - if (tok == ':') - { - tok = lexer(arg); - if (tok != 'v') - { - // expected a vector array index - error("Expected a number."); - } - else - { - // fixme: check for number B - tok = lexer(arg); - } - } - // expect a closing bracket of array range - if (tok != ']') - { - error("Expected ']' on array range."); - } + parse_vector_range(tok); continue; } + if (tok == 'n') + continue; if (tok != 'v') { - std::string eReport; - switch(tok) - { - case 'n': - continue; - case '[': - case ']': - case '}': - case '{': - case '\"': - case ':': - eReport = "Unexpected '"; - eReport += static_cast(tok); - eReport += "'."; - error(eReport); - break; - default: - error(); - } + report_unexpected_token(tok); } ast->args.push_back(arg); } @@ -571,16 +580,22 @@ LibertyAst *LibertyParser::parse() } if (tok == '{') { + bool terminated = false; while (1) { - LibertyAst *child = parse(); - if (child == NULL) + LibertyAst *child = parse(false); + if (child == NULL) { + terminated = true; break; + } ast->children.push_back(child); } + if (!terminated) { + report_unexpected_token(EOF); + } break; } - error(); + report_unexpected_token(tok); } return ast; diff --git a/passes/techmap/libparse.h b/passes/techmap/libparse.h index 9332afebd..61dc83867 100644 --- a/passes/techmap/libparse.h +++ b/passes/techmap/libparse.h @@ -92,7 +92,7 @@ namespace Yosys class LibertyInputStream { std::istream &f; - std::vector buffer; + std::vector buffer; size_t buf_pos = 0; size_t buf_end = 0; bool eof = false; @@ -107,7 +107,7 @@ namespace Yosys LibertyInputStream(std::istream &f) : f(f) {} size_t buffered_size() { return buf_end - buf_pos; } - const char *buffered_data() { return buffer.data() + buf_pos; } + const unsigned char *buffered_data() { return buffer.data() + buf_pos; } int get() { if (buf_pos == buf_end) @@ -163,7 +163,9 @@ namespace Yosys */ int lexer(std::string &str); - LibertyAst *parse(); + void report_unexpected_token(int tok); + void parse_vector_range(int tok); + LibertyAst *parse(bool top_level); void error() const; void error(const std::string &str) const; @@ -172,18 +174,29 @@ namespace Yosys const LibertyAst *ast = nullptr; LibertyParser(std::istream &f) : f(f), line(1) { - shared_ast.reset(parse()); + shared_ast.reset(parse(true)); ast = shared_ast.get(); + if (!ast) { +#ifdef FILTERLIB + fprintf(stderr, "No entries found in liberty file.\n"); + exit(1); +#else + log_error("No entries found in liberty file.\n"); +#endif + } } #ifndef FILTERLIB LibertyParser(std::istream &f, const std::string &fname) : f(f), line(1) { shared_ast = LibertyAstCache::instance.cached_ast(fname); if (!shared_ast) { - shared_ast.reset(parse()); + shared_ast.reset(parse(true)); LibertyAstCache::instance.parsed_ast(fname, shared_ast); } ast = shared_ast.get(); + if (!ast) { + log_error("No entries found in liberty file `%s'.\n", fname.c_str()); + } } #endif }; diff --git a/passes/techmap/maccmap.cc b/passes/techmap/maccmap.cc index 3dde92438..42b615002 100644 --- a/passes/techmap/maccmap.cc +++ b/passes/techmap/maccmap.cc @@ -278,42 +278,42 @@ void maccmap(RTLIL::Module *module, RTLIL::Cell *cell, bool unmap) return; } - for (auto &port : macc.ports) - if (GetSize(port.in_b) == 0) - log(" %s %s (%d bits, %s)\n", port.do_subtract ? "sub" : "add", log_signal(port.in_a), - GetSize(port.in_a), port.is_signed ? "signed" : "unsigned"); + for (auto &term : macc.terms) + if (GetSize(term.in_b) == 0) + log(" %s %s (%d bits, %s)\n", term.do_subtract ? "sub" : "add", log_signal(term.in_a), + GetSize(term.in_a), term.is_signed ? "signed" : "unsigned"); else - log(" %s %s * %s (%dx%d bits, %s)\n", port.do_subtract ? "sub" : "add", log_signal(port.in_a), log_signal(port.in_b), - GetSize(port.in_a), GetSize(port.in_b), port.is_signed ? "signed" : "unsigned"); + log(" %s %s * %s (%dx%d bits, %s)\n", term.do_subtract ? "sub" : "add", log_signal(term.in_a), log_signal(term.in_b), + GetSize(term.in_a), GetSize(term.in_b), term.is_signed ? "signed" : "unsigned"); if (unmap) { typedef std::pair summand_t; std::vector summands; - RTLIL::SigSpec bit_ports; + RTLIL::SigSpec bit_terms; - for (auto &port : macc.ports) { + for (auto &term : macc.terms) { summand_t this_summand; - if (GetSize(port.in_b)) { + if (GetSize(term.in_b)) { this_summand.first = module->addWire(NEW_ID, width); - module->addMul(NEW_ID, port.in_a, port.in_b, this_summand.first, port.is_signed); - } else if (GetSize(port.in_a) == 1 && GetSize(port.in_b) == 0 && !port.is_signed && !port.do_subtract) { - // Mimic old 'bit_ports' treatment in case it's relevant for performance, + module->addMul(NEW_ID, term.in_a, term.in_b, this_summand.first, term.is_signed); + } else if (GetSize(term.in_a) == 1 && GetSize(term.in_b) == 0 && !term.is_signed && !term.do_subtract) { + // Mimic old 'bit_terms' treatment in case it's relevant for performance, // i.e. defer single-bit summands to be the last ones - bit_ports.append(port.in_a); + bit_terms.append(term.in_a); continue; - } else if (GetSize(port.in_a) != width) { + } else if (GetSize(term.in_a) != width) { this_summand.first = module->addWire(NEW_ID, width); - module->addPos(NEW_ID, port.in_a, this_summand.first, port.is_signed); + module->addPos(NEW_ID, term.in_a, this_summand.first, term.is_signed); } else { - this_summand.first = port.in_a; + this_summand.first = term.in_a; } - this_summand.second = port.do_subtract; + this_summand.second = term.do_subtract; summands.push_back(this_summand); } - for (auto &bit : bit_ports) + for (auto &bit : bit_terms) summands.push_back(summand_t(bit, false)); if (GetSize(summands) == 0) @@ -350,20 +350,20 @@ void maccmap(RTLIL::Module *module, RTLIL::Cell *cell, bool unmap) else { MaccmapWorker worker(module, width); - RTLIL::SigSpec bit_ports; + RTLIL::SigSpec bit_terms; - for (auto &port : macc.ports) { - // Mimic old 'bit_ports' treatment in case it's relevant for performance, + for (auto &term : macc.terms) { + // Mimic old 'bit_terms' treatment in case it's relevant for performance, // i.e. defer single-bit summands to be the last ones - if (GetSize(port.in_a) == 1 && GetSize(port.in_b) == 0 && !port.is_signed && !port.do_subtract) - bit_ports.append(port.in_a); - else if (GetSize(port.in_b) == 0) - worker.add(port.in_a, port.is_signed, port.do_subtract); + if (GetSize(term.in_a) == 1 && GetSize(term.in_b) == 0 && !term.is_signed && !term.do_subtract) + bit_terms.append(term.in_a); + else if (GetSize(term.in_b) == 0) + worker.add(term.in_a, term.is_signed, term.do_subtract); else - worker.add(port.in_a, port.in_b, port.is_signed, port.do_subtract); + worker.add(term.in_a, term.in_b, term.is_signed, term.do_subtract); } - for (auto bit : bit_ports) + for (auto bit : bit_terms) worker.add(bit, 0); module->connect(cell->getPort(ID::Y), worker.synth()); diff --git a/passes/techmap/nlutmap.cc b/passes/techmap/nlutmap.cc index 016789157..c823f10fe 100644 --- a/passes/techmap/nlutmap.cc +++ b/passes/techmap/nlutmap.cc @@ -42,7 +42,7 @@ struct NlutmapWorker RTLIL::Selection get_selection() { - RTLIL::Selection sel(false); + auto sel = RTLIL::Selection::EmptySelection(module->design); for (auto cell : module->cells()) if (!mapped_cells.count(cell)) sel.select(module, cell); diff --git a/passes/tests/test_cell.cc b/passes/tests/test_cell.cc index 39b7ccd3a..a34eafc2f 100644 --- a/passes/tests/test_cell.cc +++ b/passes/tests/test_cell.cc @@ -189,17 +189,17 @@ static RTLIL::Cell* create_gold_module(RTLIL::Design *design, RTLIL::IdString ce } else size_b = 0; - Macc::port_t this_port; + Macc::term_t this_term; wire_a->width += size_a; - this_port.in_a = RTLIL::SigSpec(wire_a, wire_a->width - size_a, size_a); + this_term.in_a = RTLIL::SigSpec(wire_a, wire_a->width - size_a, size_a); wire_a->width += size_b; - this_port.in_b = RTLIL::SigSpec(wire_a, wire_a->width - size_b, size_b); + this_term.in_b = RTLIL::SigSpec(wire_a, wire_a->width - size_b, size_b); - this_port.is_signed = xorshift32(2) == 1; - this_port.do_subtract = xorshift32(2) == 1; - macc.ports.push_back(this_port); + this_term.is_signed = xorshift32(2) == 1; + this_term.do_subtract = xorshift32(2) == 1; + macc.terms.push_back(this_term); } // Macc::to_cell sets the input ports macc.to_cell(cell); diff --git a/setup.py b/setup.py index b39b579e0..a199530d0 100644 --- a/setup.py +++ b/setup.py @@ -40,8 +40,6 @@ class libyosys_so_ext(Extension): ) self.args = [ "ENABLE_PYOSYS=1", - # Wheel meant to be imported from interpreter - "ENABLE_PYTHON_CONFIG_EMBED=0", # Would need to be installed separately by the user "ENABLE_TCL=0", "ENABLE_READLINE=0", @@ -87,9 +85,6 @@ class libyosys_so_ext(Extension): shutil.copytree("share", share_target) - # I don't know how debug info is getting here. - - class custom_build_ext(build_ext): def build_extension(self, ext) -> None: if not hasattr(ext, "custom_build"): diff --git a/tests/arch/xilinx/abc9_dff.ys b/tests/arch/xilinx/abc9_dff.ys index 79e5a322c..9101c6e4d 100644 --- a/tests/arch/xilinx/abc9_dff.ys +++ b/tests/arch/xilinx/abc9_dff.ys @@ -1,4 +1,5 @@ logger -nowarn "Yosys has only limited support for tri-state logic at the moment\. .*" +logger -nowarn "Ignoring boxed module .*\." read_verilog </dev/null > $x.filtered ../../yosys-filterlib -verilogsim $x > $x.verilogsim diff $x.filtered $x.filtered.ok && diff $x.verilogsim $x.verilogsim.ok -done +done || exit 1 for x in *.ys; do echo "Running $x.." ../../yosys -q -s $x -l ${x%.ys}.log -done +done || exit 1 + diff --git a/tests/liberty/semicolextra.lib.filtered.ok b/tests/liberty/semicolextra.lib.filtered.ok index 791b45084..10f9618cb 100644 --- a/tests/liberty/semicolextra.lib.filtered.ok +++ b/tests/liberty/semicolextra.lib.filtered.ok @@ -10,8 +10,8 @@ library(supergate) { clock : true ; } ff(IQ, IQN) { - clocked_on : CK ; - next_state : D ; + clocked_on : "CK" ; + next_state : "D" ; } pin(Q) { direction : output ; diff --git a/tests/liberty/semicolextra.lib.verilogsim.ok b/tests/liberty/semicolextra.lib.verilogsim.ok index c9eebd6ed..e3b14dbd2 100644 --- a/tests/liberty/semicolextra.lib.verilogsim.ok +++ b/tests/liberty/semicolextra.lib.verilogsim.ok @@ -4,7 +4,7 @@ module DFF (D, CK, Q); input CK; output Q; always @(posedge CK) begin - // D + // "D" IQ <= D; IQN <= ~(D); end diff --git a/tests/liberty/semicolmissing.lib.filtered.ok b/tests/liberty/semicolmissing.lib.filtered.ok index 29022cf80..2a00cc136 100644 --- a/tests/liberty/semicolmissing.lib.filtered.ok +++ b/tests/liberty/semicolmissing.lib.filtered.ok @@ -12,11 +12,11 @@ library(supergate) { } pin(CO) { direction : output ; - function : (((A * B)+(B * CI))+(CI * A)) ; + function : "(((A * B)+(B * CI))+(CI * A))" ; } pin(Y) { direction : output ; - function : ((A^B)^CI) ; + function : "((A^B)^CI)" ; } } } diff --git a/tests/liberty/semicolmissing.lib.verilogsim.ok b/tests/liberty/semicolmissing.lib.verilogsim.ok index 131ce2fdf..56d57209d 100644 --- a/tests/liberty/semicolmissing.lib.verilogsim.ok +++ b/tests/liberty/semicolmissing.lib.verilogsim.ok @@ -3,7 +3,7 @@ module fulladder (A, B, CI, CO, Y); input B; input CI; output CO; - assign CO = (((A&B)|(B&CI))|(CI&A)); // (((A * B)+(B * CI))+(CI * A)) + assign CO = (((A&B)|(B&CI))|(CI&A)); // "(((A * B)+(B * CI))+(CI * A))" output Y; - assign Y = ((A^B)^CI); // ((A^B)^CI) + assign Y = ((A^B)^CI); // "((A^B)^CI)" endmodule diff --git a/tests/sat/share.v b/tests/sat/share.v index e06fc8f1e..29e423137 100644 --- a/tests/sat/share.v +++ b/tests/sat/share.v @@ -30,3 +30,26 @@ module test_2( end endmodule + +module test_3( + input [3:0] s, + input [7:0] a, b, c, + output reg [7:0] y0, + output reg [7:0] y1, + output reg [7:0] y2, + output reg [7:0] y3, +); + wire is_onehot = s & (s - 1); + + always @* begin + y0 <= 0; + y1 <= 0; + y2 <= 0; + y3 <= 0; + if (s < 3) y0 <= b / c; + if (3 <= s && s < 6) y1 <= c / b; + if (6 <= s && s < 9) y2 <= a / b; + if (9 <= s && s < 12) y3 <= b / a; + end +endmodule + diff --git a/tests/sat/share.ys b/tests/sat/share.ys index f2f5d649d..ef88d55c3 100644 --- a/tests/sat/share.ys +++ b/tests/sat/share.ys @@ -3,11 +3,13 @@ proc;; copy test_1 gold_1 copy test_2 gold_2 -share test_1 test_2;; +copy test_3 gold_3 +share test_1 test_2 test_3;; select -assert-count 1 test_1/t:$mul select -assert-count 1 test_2/t:$mul select -assert-count 1 test_2/t:$div +select -assert-count 1 test_3/t:$div miter -equiv -flatten -make_outputs -make_outcmp gold_1 test_1 miter_1 sat -verify -prove trigger 0 -show-inputs -show-outputs miter_1 @@ -15,3 +17,5 @@ sat -verify -prove trigger 0 -show-inputs -show-outputs miter_1 miter -equiv -flatten -make_outputs -make_outcmp gold_2 test_2 miter_2 sat -verify -prove trigger 0 -show-inputs -show-outputs miter_2 +miter -equiv -flatten -make_outputs -make_outcmp gold_3 test_3 miter_3 +sat -verify -prove trigger 0 -show-inputs -show-outputs miter_3 diff --git a/tests/select/blackboxes.ys b/tests/select/boxes.v similarity index 51% rename from tests/select/blackboxes.ys rename to tests/select/boxes.v index 9bfe92c6b..696b26523 100644 --- a/tests/select/blackboxes.ys +++ b/tests/select/boxes.v @@ -1,4 +1,3 @@ -read_verilog -specify < o) = 1; +endspecify +endmodule + +(* whitebox *) +module wb(input a, b, output o); +assign o = a ^ b; +endmodule + +module some_mod(input a, b, c, output o); +assign o = a & (b | c); +endmodule +EOT + +hierarchy -top top +design -save hier + +select -assert-none t:$anyseq +select -assert-count 3 =t:?b +cutpoint -blackbox + +select -assert-none =t:?b +select -assert-none r:SOME_PARAM + +select -assert-count 3 t:$anyseq +select -assert-count 3 t:$scopeinfo +select -assert-count 3 t:$scopeinfo r:TYPE=blackbox %i +select -assert-count 3 t:$scopeinfo n:*cutpoint.cc* %i + +# -noscopeinfo works with -blackbox +design -load hier +cutpoint -blackbox -noscopeinfo +select -assert-none t:$scopeinfo + +# cutpoint -blackbox === cutpoint =A:whitebox =A:blackbox %u %C +# (simplified to =A:*box %C) +design -load hier +cutpoint -blackbox +rename -enumerate -pattern A_% t:$scopeinfo +rename -enumerate -pattern B_% t:$anyseq +rename -enumerate -pattern C_% w:*Anyseq* +design -save gold +select -write cutpoint.gold.sel =* + +design -load hier +cutpoint =A:*box %C +rename -enumerate -pattern A_% t:$scopeinfo +rename -enumerate -pattern B_% t:$anyseq +rename -enumerate -pattern C_% w:*Anyseq* +design -save gate +select -write cutpoint.gate.sel +select -read cutpoint.gold.sel +# nothing in gate but not gold +select -assert-none % %n + +design -load gold +select -read cutpoint.gate.sel +# nothing in gold but not gate +select -assert-none % %n