diff --git a/.editorconfig b/.editorconfig index f5444d81a..572b73bd2 100644 --- a/.editorconfig +++ b/.editorconfig @@ -10,3 +10,7 @@ insert_final_newline = true indent_style = space indent_size = 2 trim_trailing_whitespace = false + +[*.yml] +indent_style = space +indent_size = 2 diff --git a/.github/actions/setup-build-env/action.yml b/.github/actions/setup-build-env/action.yml index c5a0d14ab..0c8cd281c 100644 --- a/.github/actions/setup-build-env/action.yml +++ b/.github/actions/setup-build-env/action.yml @@ -14,7 +14,8 @@ runs: if: runner.os == 'macOS' shell: bash run: | - HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew install bison flex gawk libffi pkg-config bash autoconf llvm + HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew update + HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew install bison flex gawk libffi pkg-config bash autoconf llvm lld - name: Linux runtime environment if: runner.os == 'Linux' diff --git a/.github/workflows/prepare-docs.yml b/.github/workflows/prepare-docs.yml new file mode 100644 index 000000000..e0388c7a3 --- /dev/null +++ b/.github/workflows/prepare-docs.yml @@ -0,0 +1,78 @@ +name: Build docs artifact with Verific + +on: [push, pull_request] + +jobs: + check_docs_rebuild: + runs-on: ubuntu-latest + outputs: + skip_check: ${{ steps.skip_check.outputs.should_skip }} + docs_export: ${{ steps.docs_var.outputs.docs_export }} + env: + docs_export: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/docs-preview') || startsWith(github.ref, 'refs/tags/') }} + steps: + - id: skip_check + uses: fkirc/skip-duplicate-actions@v5 + with: + paths_ignore: '["**/README.md"]' + # don't cancel in case we're updating docs + cancel_others: 'false' + # 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 + + prepare-docs: + # docs builds are needed for anything on main, any tagged versions, and any tag + # or branch starting with docs-preview + needs: check_docs_rebuild + if: ${{ needs.check_docs_rebuild.outputs.should_skip != 'true' }} + runs-on: [self-hosted, linux, x64, fast] + steps: + - name: Checkout Yosys + uses: actions/checkout@v4 + with: + persist-credentials: false + submodules: true + + - name: Runtime environment + run: | + echo "procs=$(nproc)" >> $GITHUB_ENV + + - name: Build Yosys + run: | + make config-clang + echo "ENABLE_VERIFIC := 1" >> Makefile.conf + echo "ENABLE_VERIFIC_EDIF := 1" >> Makefile.conf + 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 + + - name: Prepare docs + shell: bash + run: + make docs/prep -j${{ env.procs }} TARGETS= EXTRA_TARGETS= + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: cmd-ref-${{ github.sha }} + path: | + docs/source/cmd + docs/source/generated + docs/source/_images + docs/source/code_examples + + - name: Test build docs + shell: bash + run: | + make -C docs html -j${{ env.procs }} TARGETS= EXTRA_TARGETS= + + - name: Trigger RTDs build + if: ${{ needs.check_docs_rebuild.outputs.docs_export == 'true' }} + uses: dfm/rtds-action@v1.1.0 + with: + webhook_url: ${{ secrets.RTDS_WEBHOOK_URL }} + webhook_token: ${{ secrets.RTDS_WEBHOOK_TOKEN }} + commit_ref: ${{ github.ref }} diff --git a/.github/workflows/test-build.yml b/.github/workflows/test-build.yml index 0eb2c65c5..e5aed6af3 100644 --- a/.github/workflows/test-build.yml +++ b/.github/workflows/test-build.yml @@ -100,6 +100,16 @@ jobs: cd iverilog echo "IVERILOG_GIT=$(git rev-parse HEAD)" >> $GITHUB_ENV + - name: Get vcd2fst + shell: bash + run: | + git clone https://github.com/mmicko/libwave.git + mkdir -p ${{ github.workspace }}/.local/ + cd libwave + cmake . -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/.local + make -j$procs + make install + - name: Cache iverilog id: cache-iverilog uses: actions/cache@v4 diff --git a/.github/workflows/test-verific.yml b/.github/workflows/test-verific.yml index 627a70d47..98b05e8dd 100644 --- a/.github/workflows/test-verific.yml +++ b/.github/workflows/test-verific.yml @@ -11,13 +11,11 @@ jobs: - id: skip_check uses: fkirc/skip-duplicate-actions@v5 with: - paths_ignore: '["**/README.md"]' - # don't cancel previous builds + paths_ignore: '["**/README.md", "docs/**", "guidelines/**"]' + # cancel previous builds if a new commit is pushed cancel_others: 'true' # only run on push *or* pull_request, not both concurrent_skipping: 'same_content_newer' - # we have special actions when running on main, so this should be off - skip_after_successful_duplicate: 'false' test-verific: needs: pre-job @@ -41,6 +39,7 @@ jobs: echo "ENABLE_VERIFIC_LIBERTY := 1" >> Makefile.conf 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 - name: Install Yosys @@ -70,51 +69,3 @@ jobs: if: ${{ github.ref == 'refs/heads/main' }} run: | make -C sby run_ci - - prepare-docs: - name: Generate docs artifact - needs: [pre-job, test-verific] - if: needs.pre-job.outputs.should_skip != 'true' - runs-on: [self-hosted, linux, x64, fast] - steps: - - name: Checkout Yosys - uses: actions/checkout@v4 - with: - persist-credentials: false - submodules: true - - name: Runtime environment - run: | - echo "procs=$(nproc)" >> $GITHUB_ENV - - - name: Build Yosys - run: | - make config-clang - echo "ENABLE_VERIFIC := 1" >> Makefile.conf - echo "ENABLE_VERIFIC_EDIF := 1" >> Makefile.conf - 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 - - - name: Prepare docs - shell: bash - run: - make docs/source/cmd/abc.rst docs/gen_examples docs/gen_images docs/guidelines docs/usage docs/reqs TARGETS= EXTRA_TARGETS= - - - name: Upload artifact - uses: actions/upload-artifact@v4 - with: - name: cmd-ref-${{ github.sha }} - path: | - docs/source/cmd - docs/source/generated - docs/source/_images - docs/source/code_examples - - - name: Trigger RTDs build - if: ${{ github.ref == 'refs/heads/main' }} - uses: dfm/rtds-action@v1.1.0 - with: - webhook_url: ${{ secrets.RTDS_WEBHOOK_URL }} - webhook_token: ${{ secrets.RTDS_WEBHOOK_TOKEN }} - commit_ref: ${{ github.ref }} diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml new file mode 100644 index 000000000..d66239a16 --- /dev/null +++ b/.github/workflows/wheels.yml @@ -0,0 +1,136 @@ +name: Build Wheels for PyPI +on: + workflow_dispatch: + +jobs: + build_wheels: + strategy: + fail-fast: false + matrix: + os: [ + { + name: "Ubuntu 22.04", + family: "linux", + 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: "macOS 13", + family: "macos", + runner: "macos-13", + archs: "x86_64", + }, + { + name: "macOS 14", + family: "macos", + runner: "macos-14", + archs: "arm64", + }, + ## Windows is disabled because of an issue with compiling FFI as + ## under MinGW in the GitHub Actions environment (SHELL variable has + ## whitespace.) + # { + # name: "Windows Server 2019", + # family: "windows", + # runner: "windows-2019", + # archs: "AMD64", + # }, + ] + name: Build Wheels | ${{ matrix.os.name }} | ${{ matrix.os.archs }} + runs-on: ${{ matrix.os.runner }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: true + - if: ${{ matrix.os.family == 'linux' }} + name: "[Linux] Set up QEMU" + uses: docker/setup-qemu-action@v3 + - uses: actions/setup-python@v5 + - name: Get Boost Source + shell: bash + run: | + mkdir -p boost + curl -L https://github.com/boostorg/boost/releases/download/boost-1.86.0/boost-1.86.0-b2-nodocs.tar.gz | tar --strip-components=1 -xzC boost + - name: Get FFI + shell: bash + run: | + mkdir -p ffi + curl -L https://github.com/libffi/libffi/releases/download/v3.4.6/libffi-3.4.6.tar.gz | tar --strip-components=1 -xzC ffi + ## Software installed by default in GitHub Action Runner VMs: + ## https://github.com/actions/runner-images + - if: ${{ matrix.os.family == 'macos' }} + name: "[macOS] Flex/Bison" + run: | + brew install flex bison + echo "PATH=$(brew --prefix flex)/bin:$PATH" >> $GITHUB_ENV + echo "PATH=$(brew --prefix bison)/bin:$PATH" >> $GITHUB_ENV + - if: ${{ matrix.os.family == 'windows' }} + name: "[Windows] Flex/Bison" + run: | + choco install winflexbison3 + - if: ${{ matrix.os.family == 'macos' && matrix.os.archs == 'arm64' }} + name: "[macOS/arm64] Install Python 3.8 (see: https://cibuildwheel.pypa.io/en/stable/faq/#macos-building-cpython-38-wheels-on-arm64)" + uses: actions/setup-python@v5 + with: + python-version: 3.8 + - name: Build wheels + uses: pypa/cibuildwheel@v2.21.1 + env: + # * APIs not supported by PyPy + # * Musllinux disabled because it increases build time from 48m to ~3h + CIBW_SKIP: > + pp* + *musllinux* + CIBW_ARCHS: ${{ matrix.os.archs }} + CIBW_BUILD_VERBOSITY: "1" + # manylinux2014 (default) does not have a modern enough C++ compiler for Yosys + CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_28 + CIBW_MANYLINUX_AARCH64_IMAGE: manylinux_2_28 + CIBW_BEFORE_ALL: bash ./.github/workflows/wheels/cibw_before_all.sh + CIBW_ENVIRONMENT: > + CXXFLAGS=-I./boost/pfx/include + LINKFLAGS=-L./boost/pfx/lib + PKG_CONFIG_PATH=./ffi/pfx/lib/pkgconfig + makeFlags='BOOST_PYTHON_LIB=./boost/pfx/lib/libboost_python*.a' + CIBW_ENVIRONMENT_MACOS: > + CXXFLAGS=-I./boost/pfx/include + LINKFLAGS=-L./boost/pfx/lib + PKG_CONFIG_PATH=./ffi/pfx/lib/pkgconfig + MACOSX_DEPLOYMENT_TARGET=11 + makeFlags='BOOST_PYTHON_LIB=./boost/pfx/lib/libboost_python*.a CONFIG=clang' + CIBW_BEFORE_BUILD: bash ./.github/workflows/wheels/cibw_before_build.sh + CIBW_TEST_COMMAND: python3 {project}/tests/arch/ecp5/add_sub.py + - uses: actions/upload-artifact@v4 + with: + name: python-wheels-${{ matrix.os.runner }} + path: ./wheelhouse/*.whl + upload_wheels: + name: Upload Wheels + runs-on: ubuntu-latest + needs: build_wheels + steps: + - uses: actions/download-artifact@v4 + with: + path: "." + pattern: python-wheels-* + merge-multiple: true + - run: | + ls + mkdir -p ./dist + mv *.whl ./dist + - name: Publish + uses: pypa/gh-action-pypi-publish@release/v1 + with: + password: ${{ secrets.PYPI_TOKEN }} + repository-url: ${{ vars.PYPI_INDEX || 'https://upload.pypi.org/legacy/' }} diff --git a/.github/workflows/wheels/_run_cibw_linux.py b/.github/workflows/wheels/_run_cibw_linux.py new file mode 100644 index 000000000..894470a5a --- /dev/null +++ b/.github/workflows/wheels/_run_cibw_linux.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# Copyright (C) 2024 Efabless Corporation +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +""" +This runs the cibuildwheel step from the wheels workflow locally. +""" + +import os +import yaml +import platform +import subprocess + +__dir__ = os.path.dirname(os.path.abspath(__file__)) + + +workflow = yaml.safe_load(open(os.path.join(os.path.dirname(__dir__), "wheels.yml"))) + +env = os.environ.copy() + +steps = workflow["jobs"]["build_wheels"]["steps"] +cibw_step = None +for step in steps: + if (step.get("uses") or "").startswith("pypa/cibuildwheel"): + cibw_step = step + break + +for key, value in cibw_step["env"].items(): + if key.endswith("WIN") or key.endswith("MAC"): + continue + env[key] = value + +env["CIBW_ARCHS"] = os.getenv("CIBW_ARCHS") or platform.machine() +subprocess.check_call(["cibuildwheel"], env=env) diff --git a/.github/workflows/wheels/cibw_before_all.sh b/.github/workflows/wheels/cibw_before_all.sh new file mode 100644 index 000000000..fbb8dcad8 --- /dev/null +++ b/.github/workflows/wheels/cibw_before_all.sh @@ -0,0 +1,23 @@ +set -e +set -x + +# Build-time dependencies +## Linux Docker Images +if command -v yum &> /dev/null; then + yum install -y flex bison +fi + +if command -v apk &> /dev/null; then + apk add flex bison +fi + +## macOS/Windows -- installed in GitHub Action itself, not container + +# Build Static FFI (platform-dependent but not Python version dependent) +cd ffi +## Ultimate libyosys.so will be shared, so we need fPIC for the static libraries +CFLAGS=-fPIC CXXFLAGS=-fPIC ./configure --prefix=$PWD/pfx +## Without this, SHELL has a space in its path which breaks the makefile +make install -j$(getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n hw.ncpu) +## Forces static library to be used in all situations +sed -i.bak 's@-L${toolexeclibdir} -lffi@${toolexeclibdir}/libffi.a@' ./pfx/lib/pkgconfig/libffi.pc diff --git a/.github/workflows/wheels/cibw_before_build.sh b/.github/workflows/wheels/cibw_before_build.sh new file mode 100644 index 000000000..018b0e0df --- /dev/null +++ b/.github/workflows/wheels/cibw_before_build.sh @@ -0,0 +1,34 @@ +set -e +set -x + +# Don't use objects from previous compiles on Windows/macOS +make clean + +# DEBUG: show python3 and python3-config outputs +if [ "$(uname)" != "Linux" ]; then + # https://github.com/pypa/cibuildwheel/issues/2021 + ln -s $(dirname $(readlink -f $(which python3)))/python3-config $(dirname $(which python3))/python3-config +fi +python3 --version +python3-config --includes + +# Build boost +cd ./boost +## Delete the artefacts from previous builds (if any) +rm -rf ./pfx +## Bootstrap bjam +./bootstrap.sh --prefix=./pfx +## Build Boost against current version of Python, only for +## static linkage (Boost is statically linked because system boost packages +## wildly vary in versions, including the libboost_python3 version) +./b2\ + -j$(getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n hw.ncpu)\ + --prefix=./pfx\ + --with-filesystem\ + --with-system\ + --with-python\ + cxxflags="$(python3-config --includes) -std=c++17 -fPIC"\ + cflags="$(python3-config --includes) -fPIC"\ + link=static\ + variant=release\ + install diff --git a/.gitignore b/.gitignore index e82343e89..5c3d8f4e9 100644 --- a/.gitignore +++ b/.gitignore @@ -45,4 +45,10 @@ __pycache__ /tests/unit/bintest/ /tests/unit/objtest/ /tests/ystests -/result \ No newline at end of file +/result +/dist +/*.egg-info +/build +/venv +/boost +/ffi diff --git a/.gitmodules b/.gitmodules index 1c7f18bc1..883965b40 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,6 @@ [submodule "abc"] path = abc - url = https://github.com/RapidFlex/abc.git - branch = yosys-experimental + url = https://github.com/YosysHQ/abc +[submodule "libs/cxxopts"] + path = libs/cxxopts + url = https://github.com/jarro2783/cxxopts diff --git a/.readthedocs.yaml b/.readthedocs.yaml index cb700dc1c..4a04219de 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -13,6 +13,7 @@ formats: sphinx: configuration: docs/source/conf.py + fail_on_warning: true python: install: diff --git a/Brewfile b/Brewfile index 18e4e2917..3696e40b0 100644 --- a/Brewfile +++ b/Brewfile @@ -11,3 +11,4 @@ brew "xdot" brew "bash" brew "boost-python3" brew "llvm" +brew "lld" diff --git a/CHANGELOG b/CHANGELOG index 3d22bfe60..4106885fa 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,9 +2,40 @@ List of major changes and improvements between releases ======================================================= -Yosys 0.44 .. Yosys 0.45-dev +Yosys 0.46 .. Yosys 0.47-dev -------------------------- +Yosys 0.45 .. Yosys 0.46 +-------------------------- + * Various + - Added new "functional backend" infrastructure with three example + backends (C++, SMTLIB and Rosette). + - Added new coarse-grain buffer cell type "$buf" to RTLIL. + - Added "-y" command line option to execute a Python script with + libyosys available as a built-in module. + - Added support for casting to type in Verilog frontend. + + * New commands and options + - Added "clockgate" pass for automatic clock gating cell insertion. + - Added "bufnorm" experimental pass to convert design into + buffered-normalized form. + - Added experimental "aiger2" and "xaiger2" backends, and an + experimental "abc_new" command + - Added "-force-detailed-loop-check" option to "check" pass. + - Added "-unit_delay" option to "read_liberty" pass. + + * Verific support + - Added left and right bound properties to wires when using + specific VHDL types. + +Yosys 0.44 .. Yosys 0.45 +-------------------------- + * Various + - Added cell types help messages. + + * New back-ends + - Added initial NG-Ultra support. ( synth_nanoxplore ) + Yosys 0.43 .. Yosys 0.44 -------------------------- * Various diff --git a/Makefile b/Makefile index 0888ae3b5..8d6490c52 100644 --- a/Makefile +++ b/Makefile @@ -38,6 +38,7 @@ ENABLE_LTO := 0 ENABLE_CCACHE := 0 # sccache is not always a drop-in replacement for ccache in practice ENABLE_SCCACHE := 0 +ENABLE_FUNCTIONAL_TESTS := 0 LINK_CURSES := 0 LINK_TERMCAP := 0 LINK_ABC := 0 @@ -153,7 +154,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.44+60 +YOSYS_VER := 0.46+34 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo @@ -169,7 +170,7 @@ endif OBJS = kernel/version_$(GIT_REV).o bumpversion: - sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 80ba43d.. | wc -l`/;" Makefile + sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline e97731b.. | wc -l`/;" Makefile ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 ABC_USE_NAMESPACE=abc VERBOSE=$(Q) @@ -598,6 +599,7 @@ $(eval $(call add_include_file,kernel/celltypes.h)) $(eval $(call add_include_file,kernel/consteval.h)) $(eval $(call add_include_file,kernel/constids.inc)) $(eval $(call add_include_file,kernel/cost.h)) +$(eval $(call add_include_file,kernel/drivertools.h)) $(eval $(call add_include_file,kernel/ff.h)) $(eval $(call add_include_file,kernel/ffinit.h)) $(eval $(call add_include_file,kernel/ffmerge.h)) @@ -616,6 +618,7 @@ $(eval $(call add_include_file,kernel/register.h)) $(eval $(call add_include_file,kernel/rtlil.h)) $(eval $(call add_include_file,kernel/satgen.h)) $(eval $(call add_include_file,kernel/scopeinfo.h)) +$(eval $(call add_include_file,kernel/sexpr.h)) $(eval $(call add_include_file,kernel/sigtools.h)) $(eval $(call add_include_file,kernel/timinginfo.h)) $(eval $(call add_include_file,kernel/utils.h)) @@ -637,7 +640,8 @@ $(eval $(call add_include_file,backends/rtlil/rtlil_backend.h)) OBJS += kernel/driver.o kernel/register.o kernel/rtlil.o kernel/log.o kernel/calc.o kernel/yosys.o OBJS += kernel/binding.o -OBJS += kernel/cellaigs.o kernel/celledges.o kernel/cost.o kernel/satgen.o kernel/scopeinfo.o kernel/qcsat.o kernel/mem.o kernel/ffmerge.o kernel/ff.o kernel/yw.o kernel/json.o kernel/fmt.o +OBJS += kernel/cellaigs.o kernel/celledges.o kernel/cost.o kernel/satgen.o kernel/scopeinfo.o kernel/qcsat.o kernel/mem.o kernel/ffmerge.o kernel/ff.o kernel/yw.o kernel/json.o kernel/fmt.o kernel/sexpr.o +OBJS += kernel/drivertools.o kernel/functional.o ifeq ($(ENABLE_ZLIB),1) OBJS += kernel/fstdata.o endif @@ -733,12 +737,18 @@ compile-only: $(OBJS) $(GENFILES) $(EXTRA_TARGETS) @echo " Compile successful." @echo "" +.PHONY: share +share: $(EXTRA_TARGETS) + @echo "" + @echo " Share directory created." + @echo "" + $(PROGRAM_PREFIX)yosys$(EXE): $(OBJS) $(P) $(CXX) -o $(PROGRAM_PREFIX)yosys$(EXE) $(EXE_LINKFLAGS) $(LINKFLAGS) $(OBJS) $(LIBS) $(LIBS_VERIFIC) libyosys.so: $(filter-out kernel/driver.o,$(OBJS)) ifeq ($(OS), Darwin) - $(P) $(CXX) -o libyosys.so -shared -Wl,-install_name,$(LIBDIR)/libyosys.so $(LINKFLAGS) $^ $(LIBS) $(LIBS_VERIFIC) + $(P) $(CXX) -o libyosys.so -shared -undefined dynamic_lookup -Wl,-install_name,$(LIBDIR)/libyosys.so $(LINKFLAGS) $^ $(LIBS) $(LIBS_VERIFIC) else $(P) $(CXX) -o libyosys.so -shared -Wl,-soname,$(LIBDIR)/libyosys.so $(LINKFLAGS) $^ $(LIBS) $(LIBS_VERIFIC) endif @@ -877,6 +887,7 @@ endif +cd tests/arch/anlogic && bash run-test.sh $(SEEDOPT) +cd tests/arch/gowin && bash run-test.sh $(SEEDOPT) +cd tests/arch/intel_alm && bash run-test.sh $(SEEDOPT) + +cd tests/arch/nanoxplore && bash run-test.sh $(SEEDOPT) +cd tests/arch/nexus && bash run-test.sh $(SEEDOPT) +cd tests/arch/quicklogic/pp3 && bash run-test.sh $(SEEDOPT) +cd tests/arch/quicklogic/qlf_k6n10f && bash run-test.sh $(SEEDOPT) @@ -888,6 +899,9 @@ endif +cd tests/xprop && bash run-test.sh $(SEEDOPT) +cd tests/fmt && bash run-test.sh +cd tests/cxxrtl && bash run-test.sh +ifeq ($(ENABLE_FUNCTIONAL_TESTS),1) + +cd tests/functional && bash run-test.sh +endif @echo "" @echo " Passed \"make test\"." @echo "" @@ -916,8 +930,8 @@ ystests: $(TARGETS) $(EXTRA_TARGETS) # Unit test unit-test: libyosys.so - @$(MAKE) -C $(UNITESTPATH) CXX="$(CXX)" CPPFLAGS="$(CPPFLAGS)" \ - CXXFLAGS="$(CXXFLAGS)" LIBS="$(LIBS)" ROOTPATH="$(CURDIR)" + @$(MAKE) -C $(UNITESTPATH) CXX="$(CXX)" CC="$(CC)" CPPFLAGS="$(CPPFLAGS)" \ + CXXFLAGS="$(CXXFLAGS)" LINKFLAGS="$(LINKFLAGS)" LIBS="$(LIBS)" ROOTPATH="$(CURDIR)" clean-unit-test: @$(MAKE) -C $(UNITESTPATH) clean @@ -971,16 +985,17 @@ docs/source/cmd/abc.rst: $(TARGETS) $(EXTRA_TARGETS) ./$(PROGRAM_PREFIX)yosys -p 'help -write-rst-command-reference-manual' PHONY: docs/gen_examples docs/gen_images docs/guidelines docs/usage docs/reqs -docs/gen_examples: +docs/gen_examples: $(TARGETS) $(Q) $(MAKE) -C docs examples -docs/gen_images: +docs/gen_images: $(TARGETS) $(Q) $(MAKE) -C docs images DOCS_GUIDELINE_FILES := GettingStarted CodingStyle -docs/guidelines docs/source/generated: +DOCS_GUIDELINE_SOURCE := $(addprefix guidelines/,$(DOCS_GUIDELINE_FILES)) +docs/guidelines docs/source/generated: $(DOCS_GUIDELINE_SOURCE) $(Q) mkdir -p docs/source/generated - $(Q) cp -f $(addprefix guidelines/,$(DOCS_GUIDELINE_FILES)) docs/source/generated + $(Q) cp -f $(DOCS_GUIDELINE_SOURCE) docs/source/generated # some commands return an error and print the usage text to stderr define DOC_USAGE_STDERR @@ -1009,8 +1024,11 @@ docs/usage: $(addprefix docs/source/generated/,$(DOCS_USAGE_STDOUT) $(DOCS_USAGE docs/reqs: $(Q) $(MAKE) -C docs reqs +.PHONY: docs/prep +docs/prep: docs/source/cmd/abc.rst docs/gen_examples docs/gen_images docs/guidelines docs/usage + DOC_TARGET ?= html -docs: docs/source/cmd/abc.rst docs/gen_examples docs/gen_images docs/guidelines docs/usage docs/reqs +docs: docs/prep $(Q) $(MAKE) -C docs $(DOC_TARGET) clean: @@ -1045,6 +1063,16 @@ coverage: lcov --capture -d . --no-external -o coverage.info genhtml coverage.info --output-directory coverage_html +clean_coverage: + find . -name "*.gcda" -type f -delete + +FUNC_KERNEL := functional.cc functional.h sexpr.cc sexpr.h compute_graph.h +FUNC_INCLUDES := $(addprefix --include *,functional/* $(FUNC_KERNEL)) +coverage_functional: + rm -rf coverage.info coverage_html + lcov --capture -d backends/functional -d kernel $(FUNC_INCLUDES) --no-external -o coverage.info + genhtml coverage.info --output-directory coverage_html + qtcreator: echo "$(CXXFLAGS)" | grep -o '\-D[^ ]*' | tr ' ' '\n' | sed 's/-D/#define /' | sed 's/=/ /'> qtcreator.config { for file in $(basename $(OBJS)); do \ diff --git a/README.md b/README.md index 7437bb283..d215d8442 100644 --- a/README.md +++ b/README.md @@ -629,11 +629,21 @@ following are used for building the website: $ sudo apt install pdf2svg faketime +Or for MacOS, using homebrew: + + $ brew install pdf2svg libfaketime + PDFLaTeX, included with most LaTeX distributions, is also needed during the build process for the website. Or, run the following: $ sudo apt install texlive-latex-base texlive-latex-extra latexmk +Or for MacOS, using homebrew: + + $ brew install basictex + $ sudo tlmgr update --self + $ sudo tlmgr install collection-latexextra latexmk tex-gyre + The Python package, Sphinx, is needed along with those listed in `docs/source/requirements.txt`: diff --git a/abc b/abc index 2188bc712..cac8f99ea 160000 --- a/abc +++ b/abc @@ -1 +1 @@ -Subproject commit 2188bc71228b0788569d83ad2b7e7b91ca5dcc09 +Subproject commit cac8f99eaa220a5e3db5caeb87cef0a975c953a2 diff --git a/backends/aiger/xaiger.cc b/backends/aiger/xaiger.cc index 68c2ff52f..3ca8b205a 100644 --- a/backends/aiger/xaiger.cc +++ b/backends/aiger/xaiger.cc @@ -18,32 +18,6 @@ * */ -// https://stackoverflow.com/a/46137633 -#ifdef _MSC_VER -#include -#define bswap32 _byteswap_ulong -#elif defined(__APPLE__) -#include -#define bswap32 OSSwapInt32 -#elif defined(__GNUC__) -#define bswap32 __builtin_bswap32 -#else -#include -inline static uint32_t bswap32(uint32_t x) -{ - // https://stackoverflow.com/a/27796212 - register uint32_t value = number_to_be_reversed; - uint8_t lolo = (value >> 0) & 0xFF; - uint8_t lohi = (value >> 8) & 0xFF; - uint8_t hilo = (value >> 16) & 0xFF; - uint8_t hihi = (value >> 24) & 0xFF; - return (hihi << 24) - | (hilo << 16) - | (lohi << 8) - | (lolo << 0); -} -#endif - #include "kernel/yosys.h" #include "kernel/sigtools.h" #include "kernel/utils.h" @@ -52,16 +26,6 @@ inline static uint32_t bswap32(uint32_t x) USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -inline int32_t to_big_endian(int32_t i32) { -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - return bswap32(i32); -#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ - return i32; -#else -#error "Unknown endianness" -#endif -} - void aiger_encode(std::ostream &f, int x) { log_assert(x >= 0); @@ -537,9 +501,12 @@ struct XAigerWriter f << "c"; - auto write_buffer = [](std::stringstream &buffer, int i32) { - int32_t i32_be = to_big_endian(i32); - buffer.write(reinterpret_cast(&i32_be), sizeof(i32_be)); + auto write_buffer = [](std::ostream &buffer, unsigned int u32) { + typedef unsigned char uchar; + unsigned char u32_be[4] = { + (uchar) (u32 >> 24), (uchar) (u32 >> 16), (uchar) (u32 >> 8), (uchar) u32 + }; + buffer.write((char *) u32_be, sizeof(u32_be)); }; std::stringstream h_buffer; auto write_h_buffer = std::bind(write_buffer, std::ref(h_buffer), std::placeholders::_1); @@ -640,14 +607,12 @@ struct XAigerWriter f << "r"; std::string buffer_str = r_buffer.str(); - int32_t buffer_size_be = to_big_endian(buffer_str.size()); - f.write(reinterpret_cast(&buffer_size_be), sizeof(buffer_size_be)); + write_buffer(f, buffer_str.size()); f.write(buffer_str.data(), buffer_str.size()); f << "s"; buffer_str = s_buffer.str(); - buffer_size_be = to_big_endian(buffer_str.size()); - f.write(reinterpret_cast(&buffer_size_be), sizeof(buffer_size_be)); + write_buffer(f, buffer_str.size()); f.write(buffer_str.data(), buffer_str.size()); RTLIL::Design *holes_design; @@ -664,22 +629,19 @@ struct XAigerWriter f << "a"; std::string buffer_str = a_buffer.str(); - int32_t buffer_size_be = to_big_endian(buffer_str.size()); - f.write(reinterpret_cast(&buffer_size_be), sizeof(buffer_size_be)); + write_buffer(f, buffer_str.size()); f.write(buffer_str.data(), buffer_str.size()); } } f << "h"; std::string buffer_str = h_buffer.str(); - int32_t buffer_size_be = to_big_endian(buffer_str.size()); - f.write(reinterpret_cast(&buffer_size_be), sizeof(buffer_size_be)); + write_buffer(f, buffer_str.size()); f.write(buffer_str.data(), buffer_str.size()); f << "i"; buffer_str = i_buffer.str(); - buffer_size_be = to_big_endian(buffer_str.size()); - f.write(reinterpret_cast(&buffer_size_be), sizeof(buffer_size_be)); + write_buffer(f, buffer_str.size()); f.write(buffer_str.data(), buffer_str.size()); //f << "o"; //buffer_str = o_buffer.str(); diff --git a/backends/aiger2/Makefile.inc b/backends/aiger2/Makefile.inc new file mode 100644 index 000000000..494b8d6c6 --- /dev/null +++ b/backends/aiger2/Makefile.inc @@ -0,0 +1 @@ +OBJS += backends/aiger2/aiger.o diff --git a/backends/aiger2/aiger.cc b/backends/aiger2/aiger.cc new file mode 100644 index 000000000..3fc6ccdba --- /dev/null +++ b/backends/aiger2/aiger.cc @@ -0,0 +1,1471 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) Martin PoviĊĦer + * + * 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. + * + */ + +// TODOs: +// - gracefully handling inout ports (an error message probably) +// - undriven wires +// - zero-width operands + +#include "kernel/register.h" +#include "kernel/celltypes.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +#define BITWISE_OPS ID($buf), ID($not), ID($mux), ID($and), ID($or), ID($xor), ID($xnor), ID($fa), \ + ID($bwmux) + +#define REDUCE_OPS ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool) + +#define LOGIC_OPS ID($logic_and), ID($logic_or), ID($logic_not) + +#define GATE_OPS ID($_BUF_), ID($_NOT_), ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), \ + ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_), ID($_MUX_), ID($_NMUX_), \ + ID($_AOI3_), ID($_OAI3_), ID($_AOI4_), ID($_OAI4_) + +#define CMP_OPS ID($eq), ID($ne), ID($lt), ID($le), ID($ge), ID($gt) + +// TODO +//#define ARITH_OPS ID($add), ID($sub), ID($neg) + +#define KNOWN_OPS BITWISE_OPS, REDUCE_OPS, LOGIC_OPS, GATE_OPS, ID($pos), CMP_OPS, \ + ID($pmux), ID($bmux) /*, ARITH_OPS*/ + +template +struct Index { + struct HierCursor; + struct ModuleInfo { + Module *module; + int len; + dict windices; + dict suboffsets; + pool found_blackboxes; + + bool indexing = false; + bool indexed = false; + }; + + dict modules; + + int index_wires(ModuleInfo &info, RTLIL::Module *m) + { + int sum = 0; + for (auto w : m->wires()) { + info.windices[w] = sum; + sum += w->width; + } + return sum; + } + + bool flatten = false; + bool inline_whiteboxes = false; + bool allow_blackboxes = false; + + int index_module(RTLIL::Module *m) + { + ModuleInfo &info = modules[m]; + + if (info.indexed) + return info.len; + + if (info.indexing && !info.indexed) + log_error("Hierarchy error\n"); + + info.module = m; + int pos = index_wires(info, m); + + for (auto cell : m->cells()) { + if (cell->type.in(KNOWN_OPS) || cell->type.in(ID($scopeinfo), ID($specify2), ID($specify3))) + continue; + + Module *submodule = m->design->module(cell->type); + + if (submodule && flatten && + !submodule->get_bool_attribute(ID::keep_hierarchy) && + !submodule->get_blackbox_attribute(inline_whiteboxes)) { + info.suboffsets[cell] = pos; + pos += index_module(submodule); + } else { + if (allow_blackboxes) { + info.found_blackboxes.insert(cell); + } else { + if (!submodule || submodule->get_blackbox_attribute()) + log_error("Unsupported cell type: %s (%s in %s)\n", + log_id(cell->type), log_id(cell), log_id(m)); + } + } + } + + info.len = pos; + return info.len; + } + + Design *design; + Module *top; + ModuleInfo *top_minfo; + std::vector lits; + + Index() + { + } + + void setup(RTLIL::Module *top) + { + design = top->design; + this->top = top; + + modules.reserve(top->design->modules().size()); + int nlits = index_module(top); + log_debug("allocating for %d literals\n", nlits); + lits.resize(nlits, Writer::EMPTY_LIT); + top_minfo = &modules.at(top); + } + + bool const_folding = true; + bool strashing = false; + dict, Lit> cache; + + Lit AND(Lit a, Lit b) + { + if (const_folding) { + if (a == CFALSE || b == CFALSE) + return CFALSE; + if (a == CTRUE) + return b; + if (b == CTRUE) + return a; + } + + if (!strashing) { + return (static_cast(this))->emit_gate(a, b); + } else { + if (a < b) std::swap(a, b); + auto pair = std::make_pair(a, b); + + if (!cache.count(pair)) { + Lit nl = (static_cast(this))->emit_gate(a, b); + cache[pair] = nl; + return nl; + } else { + return cache.at(pair); + } + } + } + + Lit NOT(Lit lit) + { + return Writer::negate(lit); + } + + Lit OR(Lit a, Lit b) + { + return NOT(AND(NOT(a), NOT(b))); + } + + Lit MUX(Lit a, Lit b, Lit s) + { + if (const_folding) { + if (a == b) + return a; + if (s == CFALSE) + return a; + if (s == CTRUE) + return b; + } + + return OR(AND(a, NOT(s)), AND(b, s)); + } + + Lit XOR(Lit a, Lit b) + { + return OR(AND(a, NOT(b)), AND(NOT(a), b)); + } + + Lit XNOR(Lit a, Lit b) + { + return NOT(OR(AND(a, NOT(b)), AND(NOT(a), b))); + } + + Lit CARRY(Lit a, Lit b, Lit c) + { + if (const_folding) { + if (c == CTRUE) { + return OR(a, b); + } else if (c == CFALSE) { + return AND(a, b); + } + } + return OR(AND(a, b), AND(c, OR(a, b))); + } + + Lit REDUCE(std::vector lits, bool op_xor=false) + { + std::vector next; + while (lits.size() > 1) { + next.clear(); + for (int i = 0; i < (int) lits.size(); i += 2) { + if (i + 1 >= (int) lits.size()) { + next.push_back(lits[i]); + } else { + Lit a = lits[i], b = lits[i + 1]; + next.push_back(op_xor ? XOR(a, b) : AND(a, b)); + } + } + next.swap(lits); + } + + if (lits.empty()) + return op_xor ? CFALSE : CTRUE; + else + return lits.front(); + } + + Lit impl_op(HierCursor &cursor, Cell *cell, IdString oport, int obit) + { + if (cell->type.in(REDUCE_OPS, LOGIC_OPS, CMP_OPS) && obit != 0) { + return CFALSE; + } else if (cell->type.in(CMP_OPS)) { + SigSpec aport = cell->getPort(ID::A); + bool asigned = cell->getParam(ID::A_SIGNED).as_bool(); + SigSpec bport = cell->getPort(ID::B); + bool bsigned = cell->getParam(ID::B_SIGNED).as_bool(); + + int width = std::max(aport.size(), bport.size()) + 1; + aport.extend_u0(width, asigned); + bport.extend_u0(width, bsigned); + + if (cell->type.in(ID($eq), ID($ne))) { + int carry = CTRUE; + for (int i = 0; i < width; i++) { + Lit a = visit(cursor, aport[i]); + Lit b = visit(cursor, bport[i]); + carry = AND(carry, XNOR(a, b)); + } + return (cell->type == ID($eq)) ? carry : /* $ne */ NOT(carry); + } else if (cell->type.in(ID($lt), ID($le), ID($gt), ID($ge))) { + if (cell->type.in(ID($gt), ID($ge))) + std::swap(aport, bport); + int carry = cell->type.in(ID($le), ID($ge)) ? CFALSE : CTRUE; + Lit a, b; + // TODO: this might not be the most economic structure; revisit at a later date + for (int i = 0; i < width; i++) { + a = visit(cursor, aport[i]); + b = visit(cursor, bport[i]); + if (i != width - 1) + carry = CARRY(a, NOT(b), carry); + } + return XOR(carry, XNOR(a, b)); + } else { + log_abort(); + } + } else if (cell->type.in(REDUCE_OPS, ID($logic_not))) { + SigSpec inport = cell->getPort(ID::A); + + std::vector lits; + for (int i = 0; i < inport.size(); i++) { + Lit lit = visit(cursor, inport[i]); + if (cell->type.in(ID($reduce_and), ID($reduce_xor), ID($reduce_xnor))) { + lits.push_back(lit); + } else if (cell->type.in(ID($reduce_or), ID($reduce_bool), ID($logic_not))) { + lits.push_back(NOT(lit)); + } else { + log_abort(); + } + } + + Lit acc = REDUCE(lits, cell->type.in(ID($reduce_xor), ID($reduce_xnor))); + + if (!cell->type.in(ID($reduce_xnor), ID($reduce_or), ID($reduce_bool))) + return acc; + else + return NOT(acc); + } else if (cell->type.in(ID($logic_and), ID($logic_or))) { + SigSpec aport = cell->getPort(ID::A); + SigSpec bport = cell->getPort(ID::B); + + log_assert(aport.size() > 0 && bport.size() > 0); // TODO + + Lit a = visit(cursor, aport[0]); + for (int i = 1; i < aport.size(); i++) { + Lit l = visit(cursor, aport[i]); + a = OR(a, l); + } + + Lit b = visit(cursor, bport[0]); + for (int i = 1; i < bport.size(); i++) { + Lit l = visit(cursor, bport[i]); + b = OR(b, l); + } + + if (cell->type == ID($logic_and)) + return AND(a, b); + else if (cell->type == ID($logic_or)) + return OR(a, b); + else + log_abort(); + } else if (cell->type.in(BITWISE_OPS, GATE_OPS, ID($pos))) { + SigSpec aport = cell->getPort(ID::A); + Lit a; + if (obit < aport.size()) { + a = visit(cursor, aport[obit]); + } else { + if (cell->getParam(ID::A_SIGNED).as_bool()) + a = visit(cursor, aport.msb()); + else + a = CFALSE; + } + + if (cell->type.in(ID($buf), ID($pos), ID($_BUF_))) { + return a; + } else if (cell->type.in(ID($not), ID($_NOT_))) { + return NOT(a); + } else { + SigSpec bport = cell->getPort(ID::B); + Lit b; + if (obit < bport.size()) { + b = visit(cursor, bport[obit]); + } else { + if (cell->getParam(ID::B_SIGNED).as_bool()) + b = visit(cursor, bport.msb()); + else + b = CFALSE; + } + + if (cell->type.in(ID($and), ID($_AND_))) { + return AND(a, b); + } else if (cell->type.in(ID($_NAND_))) { + return NOT(AND(a, b)); + } else if (cell->type.in(ID($or), ID($_OR_))) { + return OR(a, b); + } else if (cell->type.in(ID($_NOR_))) { + return NOT(OR(a, b)); + } else if (cell->type.in(ID($xor), ID($_XOR_))) { + return XOR(a, b); + } else if (cell->type.in(ID($xnor), ID($_XNOR_))) { + return NOT(XOR(a, b)); + } else if (cell->type.in(ID($_ANDNOT_))) { + return AND(a, NOT(b)); + } else if (cell->type.in(ID($_ORNOT_))) { + return OR(a, NOT(b)); + } else if (cell->type.in(ID($mux), ID($_MUX_))) { + Lit s = visit(cursor, cell->getPort(ID::S)); + return MUX(a, b, s); + } else if (cell->type.in(ID($bwmux))) { + Lit s = visit(cursor, cell->getPort(ID::S)[obit]); + return MUX(a, b, s); + } else if (cell->type.in(ID($_NMUX_))) { + Lit s = visit(cursor, cell->getPort(ID::S)[obit]); + return NOT(MUX(a, b, s)); + } else if (cell->type.in(ID($fa))) { + Lit c = visit(cursor, cell->getPort(ID::C)[obit]); + Lit ab = XOR(a, b); + if (oport == ID::Y) { + return XOR(ab, c); + } else /* oport == ID::X */ { + return OR(AND(a, b), AND(c, ab)); + } + } else if (cell->type.in(ID($_AOI3_), ID($_OAI3_), ID($_AOI4_), ID($_OAI4_))) { + Lit c, d; + + c = visit(cursor, cell->getPort(ID::C)[obit]); + if (/* 4 input types */ cell->type.in(ID($_AOI4_), ID($_OAI4_))) + d = visit(cursor, cell->getPort(ID::D)[obit]); + else + d = cell->type == ID($_AOI3_) ? 1 : 0; + + if (/* aoi */ cell->type.in(ID($_AOI3_), ID($_AOI4_))) + return NOT(OR(AND(a, b), AND(c, d))); + else + return NOT(AND(OR(a, b), OR(c, d))); + } else { + log_abort(); + } + } + } else if (cell->type == ID($pmux)) { + SigSpec aport = cell->getPort(ID::A); + SigSpec bport = cell->getPort(ID::B); + SigSpec sport = cell->getPort(ID::S); + int width = aport.size(); + + Lit a = visit(cursor, aport[obit]); + + std::vector bar, sels; + for (int i = 0; i < sport.size(); i++) { + Lit s = visit(cursor, sport[i]); + Lit b = visit(cursor, bport[width * i + obit]); + bar.push_back(NOT(AND(s, b))); + sels.push_back(NOT(s)); + } + + return OR(AND(REDUCE(sels), a), NOT(REDUCE(bar))); + } else if (cell->type == ID($bmux)) { + SigSpec aport = cell->getPort(ID::A); + SigSpec sport = cell->getPort(ID::S); + int width = cell->getParam(ID::WIDTH).as_int(); + + std::vector data; + for (int i = obit; i < aport.size(); i += width) + data.push_back(visit(cursor, aport[i])); + + std::vector next; + for (int i = 0; i < sport.size(); i++) { + Lit s = visit(cursor, sport[i]); + next.clear(); + for (int j = 0; j < (int) data.size(); j += 2) + next.push_back(MUX(data[j], data[j + 1], s)); + data.swap(next); + } + log_assert(data.size() == 1); + return data[0]; + } else { + log_abort(); + } + } + + struct HierCursor { + typedef std::pair Level; + std::vector levels; + int instance_offset = 0; + + HierCursor() + { + } + + ModuleInfo &leaf_minfo(Index &index) + { + if (levels.empty()) + return *index.top_minfo; + else + return levels.back().first; + } + + Module *leaf_module(Index &index) + { + return leaf_minfo(index).module; + } + + int bitwire_index(Index &index, SigBit bit) + { + log_assert(bit.wire != nullptr); + return instance_offset + leaf_minfo(index).windices[bit.wire] + bit.offset; + } + + Cell *exit(Index &index) + { + log_assert(!levels.empty()); + Cell *instance = levels.back().second; + + levels.pop_back(); + instance_offset -= leaf_minfo(index).suboffsets.at(instance); + + // return the instance we just exited + return instance; + } + + Module *enter(Index &index, Cell *cell) + { + Design *design = index.design; + auto &minfo = leaf_minfo(index); + log_assert(minfo.suboffsets.count(cell)); + Module *def = design->module(cell->type); + log_assert(def); + levels.push_back(Level(index.modules.at(def), cell)); + instance_offset += minfo.suboffsets.at(cell); + + // return the module definition we just entered + return def; + } + + bool is_top() + { + return levels.empty(); + } + + std::string path() + { + std::string ret; + bool first = true; + for (auto pair : levels) { + if (!first) + ret += "."; + if (!pair.second) + ret += RTLIL::unescape_id(pair.first.module->name); + else + ret += RTLIL::unescape_id(pair.second->name); + first = false; + } + return ret; + } + + int hash() const + { + int hash = 0; + for (auto pair : levels) + hash += (uintptr_t) pair.second; + return hash; + } + + bool operator==(const HierCursor &other) const + { + if (levels.size() != other.levels.size()) + return false; + + for (int i = 0; i < levels.size(); i++) + if (levels[i].second != other.levels[i].second) + return false; + + return true; + } + }; + + bool visit_hook(int, HierCursor&, SigBit) + { + return false; + } + + Lit visit(HierCursor &cursor, SigBit bit) + { + if (!bit.wire) { + if (bit == State::S1) + return CTRUE; + else if (bit == State::S0) + return CFALSE; + else if (bit == State::Sx) + return CFALSE; + else + log_error("Unhandled state %s\n", log_signal(bit)); + } + + int idx = cursor.bitwire_index(*this, bit); + if (lits[idx] != Writer::EMPTY_LIT) { + // literal already assigned + return lits[idx]; + } + + // provide means for the derived class to override + // the visit behavior + if ((static_cast(this))->visit_hook(idx, cursor, bit)) { + return lits[idx]; + } + + Lit ret; + if (!bit.wire->port_input) { + // an output of a cell + Cell *driver = bit.wire->driverCell(); + + if (driver->type.in(KNOWN_OPS)) { + ret = impl_op(cursor, driver, bit.wire->driverPort(), bit.offset); + } else { + Module *def = cursor.enter(*this, driver); + { + IdString portname = bit.wire->driverPort(); + Wire *w = def->wire(portname); + if (!w) + log_error("Output port %s on instance %s of %s doesn't exist\n", + log_id(portname), log_id(driver), log_id(def)); + if (bit.offset >= w->width) + log_error("Bit position %d of output port %s on instance %s of %s is out of range (port has width %d)\n", + bit.offset, log_id(portname), log_id(driver), log_id(def), w->width); + ret = visit(cursor, SigBit(w, bit.offset)); + } + cursor.exit(*this); + } + } else { + // a module input: we cannot be the top module, otherwise + // the branch for pre-existing literals would have been taken + log_assert(!cursor.is_top()); + + // step into the upper module + Cell *instance = cursor.exit(*this); + { + IdString portname = bit.wire->name; + if (!instance->hasPort(portname)) + log_error("Input port %s on instance %s of %s unconnected\n", + log_id(portname), log_id(instance), log_id(instance->type)); + auto &port = instance->getPort(portname); + if (bit.offset >= port.size()) + log_error("Bit %d of input port %s on instance %s of %s unconnected\n", + bit.offset, log_id(portname), log_id(instance), log_id(instance->type)); + ret = visit(cursor, port[bit.offset]); + } + cursor.enter(*this, instance); + } + + lits[idx] = ret; + return ret; + } + + Lit &pi_literal(SigBit bit, HierCursor *cursor=nullptr) + { + log_assert(bit.wire); + + if (!cursor) { + log_assert(bit.wire->module == top); + log_assert(bit.wire->port_input); + return lits[top_minfo->windices[bit.wire] + bit.offset]; + } else { + log_assert(bit.wire->module == cursor->leaf_module(*this)); + return lits[cursor->bitwire_index(*this, bit)]; + } + } + + Lit eval_po(SigBit bit, HierCursor *cursor=nullptr) + { + Lit ret; + if (!cursor) { + HierCursor cursor_; + ret = visit(cursor_, bit); + log_assert(cursor_.is_top()); + log_assert(cursor_.instance_offset == 0); + } else { + ret = visit(*cursor, bit); + } + return ret; + } + + void visit_hierarchy(std::function f, + HierCursor &cursor) + { + f(cursor); + + ModuleInfo &minfo = cursor.leaf_minfo(*this); + for (auto cell : minfo.module->cells()) { + if (minfo.suboffsets.count(cell)) { + cursor.enter(*this, cell); + visit_hierarchy(f, cursor); + cursor.exit(*this); + } + } + } + + void visit_hierarchy(std::function f) + { + HierCursor cursor; + visit_hierarchy(f, cursor); + } +}; + +struct AigerWriter : Index { + typedef unsigned int Lit; + + const static Lit CONST_FALSE = 0; + const static Lit CONST_TRUE = 1; + const static constexpr Lit EMPTY_LIT = std::numeric_limits::max(); + + static Lit negate(Lit lit) { + return lit ^ 1; + } + + std::ostream *f; + Lit lit_counter; + int ninputs, nlatches, noutputs, nands; + + void encode(int delta) + { + log_assert(delta >= 0); + unsigned int x = delta; + while (x & ~0x7f) { + f->put((x & 0x7f) | 0x80); + x = x >> 7; + } + f->put(x); + } + + Lit emit_gate(Lit a, Lit b) + { + Lit out = lit_counter; + nands++; + lit_counter += 2; + + if (a < b) std::swap(a, b); + encode(out - a); + encode(a - b); + return out; + } + + void reset_counters() + { + lit_counter = 2; + ninputs = nlatches = noutputs = nands = 0; + } + + void write_header() { + log_assert(lit_counter == (Lit) (ninputs + nlatches + nands) * 2 + 2); + + char buf[128]; + snprintf(buf, sizeof(buf) - 1, "aig %08d %08d %08d %08d %08d\n", + ninputs + nlatches + nands, ninputs, nlatches, noutputs, nands); + f->write(buf, strlen(buf)); + } + + void write(std::ostream *f) { + reset_counters(); + + auto file_start = f->tellp(); + + // populate inputs + std::vector inputs; + for (auto id : top->ports) { + Wire *w = top->wire(id); + log_assert(w); + if (w->port_input) + for (int i = 0; i < w->width; i++) { + pi_literal(SigBit(w, i)) = lit_counter; + inputs.push_back(SigBit(w, i)); + lit_counter += 2; + ninputs++; + } + } + + this->f = f; + // start with the header + write_header(); + // insert padding where output literals will go (once known) + for (auto id : top->ports) { + Wire *w = top->wire(id); + log_assert(w); + if (w->port_output) { + for (auto bit : SigSpec(w)) { + (void) bit; + char buf[16]; + snprintf(buf, sizeof(buf) - 1, "%08d\n", 0); + f->write(buf, strlen(buf)); + noutputs++; + } + } + } + auto data_start = f->tellp(); + + // now the guts + std::vector> outputs; + for (auto w : top->wires()) + if (w->port_output) { + for (auto bit : SigSpec(w)) + outputs.push_back({bit, eval_po(bit)}); + } + auto data_end = f->tellp(); + + // revisit header and the list of outputs + f->seekp(file_start); + write_header(); + for (auto pair : outputs) { + char buf[16]; + snprintf(buf, sizeof(buf) - 1, "%08d\n", pair.second); + f->write(buf, strlen(buf)); + } + // double check we arrived at the same offset for the + // main data section + log_assert(data_start == f->tellp()); + + f->seekp(data_end); + int i = 0; + for (auto pair : outputs) { + if (SigSpec(pair.first).is_wire()) { + char buf[32]; + snprintf(buf, sizeof(buf) - 1, "o%d ", i); + f->write(buf, strlen(buf)); + std::string name = RTLIL::unescape_id(pair.first.wire->name); + f->write(name.data(), name.size()); + f->put('\n'); + } + i++; + } + i = 0; + for (auto bit : inputs) { + if (SigSpec(bit).is_wire()) { + char buf[32]; + snprintf(buf, sizeof(buf) - 1, "i%d ", i); + f->write(buf, strlen(buf)); + std::string name = RTLIL::unescape_id(bit.wire->name); + f->write(name.data(), name.size()); + f->put('\n'); + } + i++; + } + } +}; + +struct XAigerAnalysis : Index { + const static int CONST_FALSE = 0; + const static int CONST_TRUE = 0; + const static constexpr int EMPTY_LIT = -1; + + XAigerAnalysis() + { + allow_blackboxes = true; + + // Disable const folding and strashing as literal values are not unique + const_folding = false; + strashing = false; + } + + static int negate(int lit) + { + return lit; + } + + int emit_gate(int a, int b) + { + return max(a, b) + 1; + } + + pool seen; + + bool visit_hook(int idx, HierCursor &cursor, SigBit bit) + { + log_assert(cursor.is_top()); // TOOD: fix analyzer to work with hierarchy + + if (bit.wire->port_input) + return false; + + Cell *driver = bit.wire->driverCell(); + if (!driver->type.isPublic()) + return false; + + Module *mod = design->module(driver->type); + log_assert(mod); + if (!mod->has_attribute(ID::abc9_box_id)) + return false; + + int max = 1; + for (auto wire : mod->wires()) + if (wire->port_input) + for (int i = 0; i < wire->width; i++) { + int ilevel = visit(cursor, driver->getPort(wire->name)[i]); + max = std::max(max, ilevel + 1); + } + lits[idx] = max; + + if (!seen.count(driver)) + seen.insert(driver); + + return true; + } + + void analyze(Module *top) + { + setup(top); + + for (auto id : top->ports) { + Wire *w = top->wire(id); + log_assert(w); + if (w->port_input) + for (int i = 0; i < w->width; i++) + pi_literal(SigBit(w, i)) = 0; + } + + HierCursor cursor; + for (auto box : top_minfo->found_blackboxes) { + Module *def = design->module(box->type); + if (!box->type.isPublic() || (def && !def->has_attribute(ID::abc9_box_id))) + for (auto &conn : box->connections_) + if (box->output(conn.first)) + for (auto bit : conn.second) + pi_literal(bit, &cursor) = 0; + } + + for (auto w : top->wires()) + if (w->port_output) { + for (auto bit : SigSpec(w)) + (void) eval_po(bit); + } + + for (auto box : top_minfo->found_blackboxes) { + Module *def = design->module(box->type); + if (!box->type.isPublic() || (def && !def->has_attribute(ID::abc9_box_id))) + for (auto &conn : box->connections_) + if (box->input(conn.first)) + for (auto bit : conn.second) + (void) eval_po(bit); + } + } +}; + +struct XAigerWriter : AigerWriter { + XAigerWriter() + { + allow_blackboxes = true; + } + + bool mapping_prep = false; + pool keep_wires; + std::ofstream map_file; + + typedef std::pair HierBit; + std::vector pos; + std::vector pis; + int proper_pos_counter = 0; + + pool driven_by_opaque_box; + + void ensure_pi(SigBit bit, HierCursor cursor={}, + bool box_port=false) + { + Lit &lit = pi_literal(bit, &cursor); + if (lit == EMPTY_LIT) { + lit = lit_counter; + pis.push_back(std::make_pair(bit, cursor)); + lit_counter += 2; + if (map_file.is_open() && !box_port) { + log_assert(cursor.is_top()); // TODO + driven_by_opaque_box.insert(bit); + map_file << "pi " << pis.size() - 1 << " " << bit.offset + << " " << bit.wire->name.c_str() << "\n"; + } + } else { + log_assert(!box_port); + } + } + + bool is_pi(SigBit bit, HierCursor cursor={}) + { + return pi_literal(bit, &cursor) != EMPTY_LIT; + } + + void pad_pi() + { + pis.push_back(std::make_pair(RTLIL::Sx, HierCursor{})); + lit_counter += 2; + } + + void append_box_ports(Cell *box, HierCursor &cursor, bool inputs) + { + for (auto &conn : box->connections_) { + bool is_input = box->input(conn.first); + bool is_output = box->output(conn.first); + + if (!(is_input || is_output) || (is_input && is_output)) + log_error("Ambiguous port direction on %s/%s\n", + log_id(box->type), log_id(conn.first)); + + if (is_input && inputs) { + int bitp = 0; + for (auto bit : conn.second) { + if (!bit.wire) { + bitp++; + continue; + } + + if (map_file.is_open()) { + log_assert(cursor.is_top()); + map_file << "pseudopo " << proper_pos_counter++ << " " << bitp + << " " << box->name.c_str() + << " " << conn.first.c_str() << "\n"; + } + + pos.push_back(std::make_pair(bit, cursor)); + + if (mapping_prep) + conn.second[bitp] = RTLIL::Sx; + + bitp++; + } + } else if (is_output && !inputs) { + for (auto &bit : conn.second) { + if (!bit.wire || bit.wire->port_input) + log_error("Bad connection"); + + + ensure_pi(bit, cursor); + keep_wires.insert(bit.wire); + } + } + } + } + + RTLIL::Module *holes_module; + + std::stringstream h_buffer; + static void write_be32(std::ostream &buffer, uint32_t u32) + { + typedef unsigned char uchar; + unsigned char u32_be[4] = { + (uchar) (u32 >> 24), (uchar) (u32 >> 16), (uchar) (u32 >> 8), (uchar) u32 + }; + buffer.write((char *) u32_be, sizeof(u32_be)); + } + + void prep_boxes(int pending_pos_num) + { + XAigerAnalysis analysis; + log_debug("preforming analysis on '%s'\n", log_id(top)); + analysis.analyze(top); + log_debug("analysis on '%s' done\n", log_id(top)); + + // boxes which have timing data, maybe a whitebox model + std::vector> nonopaque_boxes; + // boxes which are fully opaque + std::vector> opaque_boxes; + + log_debug("found boxes:\n"); + visit_hierarchy([&](HierCursor &cursor) { + auto &minfo = cursor.leaf_minfo(*this); + + for (auto box : minfo.found_blackboxes) { + log_debug(" - %s.%s (type %s): ", cursor.path().c_str(), + RTLIL::unescape_id(box->name).c_str(), + log_id(box->type)); + + Module *box_module = design->module(box->type), *box_derived; + + if (box_module && !box->parameters.empty()) { + // TODO: This is potentially costly even if a cached derivation exists + box_derived = design->module(box_module->derive(design, box->parameters)); + log_assert(box_derived); + } else { + box_derived = box_module; + } + + if (box_derived && box_derived->has_attribute(ID::abc9_box_id)) { + // This is an ABC9 box, we have timing data, maybe even a whitebox model + // These need to go last in the AIGER port list. + nonopaque_boxes.push_back(std::make_tuple(cursor, box, box_derived)); + log_debug("non-opaque\n"); + } else { + opaque_boxes.push_back(std::make_tuple(cursor, box, box_derived)); + log_debug("opaque\n"); + } + } + }); + + for (auto [cursor, box, def] : opaque_boxes) + append_box_ports(box, cursor, false); + + holes_module = design->addModule(NEW_ID); + std::vector holes_pis; + int boxes_ci_num = 0, boxes_co_num = 0; + + int box_seq = 0; + + std::vector boxes_order(analysis.seen.begin(), analysis.seen.end()); + std::reverse(boxes_order.begin(), boxes_order.end()); + + nonopaque_boxes.clear(); + for (auto box : boxes_order) { + HierCursor cursor; + Module *def = design->module(box->type); + nonopaque_boxes.push_back(std::make_tuple(cursor, box, def)); + } + + for (auto [cursor, box, def] : nonopaque_boxes) { + // use `def->name` not `box->type` as we want the derived type + Cell *holes_wb = holes_module->addCell(NEW_ID, def->name); + int holes_pi_idx = 0; + + if (map_file.is_open()) { + log_assert(cursor.is_top()); + map_file << "box " << box_seq << " " << box->name.c_str() << "\n"; + } + box_seq++; + + for (auto port_id : def->ports) { + Wire *port = def->wire(port_id); + log_assert(port); + + SigSpec conn = box->hasPort(port_id) ? box->getPort(port_id) : SigSpec{}; + + if (port->port_input && !port->port_output) { + // primary + for (int i = 0; i < port->width; i++) { + SigBit bit; + if (i < conn.size()) { + bit = conn[i]; + } else { + // FIXME: hierarchical path + log_warning("connection on port %s[%d] of instance %s (type %s) missing, using 1'bx\n", + log_id(port_id), i, log_id(box), log_id(box->type)); + bit = RTLIL::Sx; + } + + pos.push_back(std::make_pair(bit, cursor)); + } + boxes_co_num += port->width; + + if (mapping_prep && !conn.empty()) + box->setPort(port_id, SigSpec(RTLIL::Sx, conn.size())); + + // holes + SigSpec in_conn; + for (int i = 0; i < port->width; i++) { + while (holes_pi_idx >= (int) holes_pis.size()) { + Wire *w = holes_module->addWire(NEW_ID, 1); + w->port_input = true; + holes_module->ports.push_back(w->name); + holes_pis.push_back(w); + } + in_conn.append(holes_pis[i]); + holes_pi_idx++; + } + holes_wb->setPort(port_id, in_conn); + } else if (port->port_output && !port->port_input) { + // primary + for (int i = 0; i < port->width; i++) { + SigBit bit; + if (i < conn.size() && conn[i].is_wire()) { + bit = conn[i]; + } else { + // FIXME: hierarchical path + log_warning("connection on port %s[%d] of instance %s (type %s) missing\n", + log_id(port_id), i, log_id(box), log_id(box->type)); + pad_pi(); + continue; + } + + ensure_pi(bit, cursor, true); + keep_wires.insert(bit.wire); + } + boxes_ci_num += port->width; + + // holes + Wire *w = holes_module->addWire(NEW_ID, port->width); + w->port_output = true; + holes_module->ports.push_back(w->name); + holes_wb->setPort(port_id, w); + } else { + log_error("Ambiguous port direction on %s/%s\n", + log_id(box->type), log_id(port_id)); + } + } + } + + for (auto [cursor, box, def] : opaque_boxes) + append_box_ports(box, cursor, true); + + write_be32(h_buffer, 1); + write_be32(h_buffer, pis.size()); + log_debug("ciNum = %zu\n", pis.size()); + write_be32(h_buffer, pending_pos_num + pos.size()); + log_debug("coNum = %zu\n", pending_pos_num + pos.size()); + write_be32(h_buffer, pis.size() - boxes_ci_num); + log_debug("piNum = %zu\n", pis.size() - boxes_ci_num); + write_be32(h_buffer, pending_pos_num + pos.size() - boxes_co_num); + log_debug("poNum = %zu\n", pending_pos_num + pos.size() - boxes_co_num); + write_be32(h_buffer, nonopaque_boxes.size()); + + box_seq = 0; + for (auto [cursor, box, def] : nonopaque_boxes) { + int box_ci_num = 0, box_co_num = 0; + for (auto port_id : def->ports) { + Wire *port = def->wire(port_id); + log_assert(port); + if (port->port_input && !port->port_output) { + box_co_num += port->width; + } else if (port->port_output && !port->port_input) { + box_ci_num += port->width; + } else { + log_abort(); + } + } + + write_be32(h_buffer, box_co_num); + write_be32(h_buffer, box_ci_num); + write_be32(h_buffer, def->attributes.at(ID::abc9_box_id).as_int()); + write_be32(h_buffer, box_seq++); + } + } + + void clear_boxes() + { + design->remove(holes_module); + } + + void write(std::ostream *f) { + reset_counters(); + + for (auto w : top->wires()) + if (w->port_input) + for (int i = 0; i < w->width; i++) + ensure_pi(SigBit(w, i)); + + int proper_po_num = 0; + for (auto w : top->wires()) + if (w->port_output) + proper_po_num += w->width; + + prep_boxes(proper_po_num); + for (auto w : top->wires()) + if (w->port_output) + for (int i = 0; i < w->width; i++) { + if (map_file.is_open() && !driven_by_opaque_box.count(SigBit(w, i))) { + map_file << "po " << proper_pos_counter++ << " " << i + << " " << w->name.c_str() << "\n"; + } + pos.push_back(std::make_pair(SigBit(w, i), HierCursor{})); + } + + this->f = f; + // start with the header + ninputs = pis.size(); + noutputs = pos.size(); + write_header(); + // insert padding where output literals will go (once known) + for (auto _ : pos) { + char buf[16]; + snprintf(buf, sizeof(buf) - 1, "%08d\n", 0); + f->write(buf, strlen(buf)); + } + auto data_start = f->tellp(); + + // now the guts + std::vector outlits; + for (auto &pair : pos) + outlits.push_back(eval_po(pair.first, &pair.second)); + + // revisit header and the list of outputs + f->seekp(0); + ninputs = pis.size(); + noutputs = pos.size(); + write_header(); + for (auto lit : outlits) { + char buf[16]; + snprintf(buf, sizeof(buf) - 1, "%08d\n", lit); + f->write(buf, strlen(buf)); + } + // double check we arrived at the same offset for the + // main data section + log_assert(data_start == f->tellp()); + + // extensions + f->seekp(0, std::ios::end); + + f->put('c'); + + // insert empty 'r' and 's' sections (abc crashes if we provide 'a' without those) + f->put('r'); + write_be32(*f, 4); + write_be32(*f, 0); + f->put('s'); + write_be32(*f, 4); + write_be32(*f, 0); + + f->put('h'); + // TODO: get rid of std::string copy + std::string h_buffer_str = h_buffer.str(); + write_be32(*f, h_buffer_str.size()); + f->write(h_buffer_str.data(), h_buffer_str.size()); + +#if 1 + f->put('a'); + write_be32(*f, 0); // size to be filled later + auto holes_aiger_start = f->tellp(); + { + AigerWriter holes_writer; + holes_writer.flatten = true; + holes_writer.inline_whiteboxes = true; + holes_writer.setup(holes_module); + holes_writer.write(f); + } + auto holes_aiger_size = f->tellp() - holes_aiger_start; + f->seekp(holes_aiger_start, std::ios::beg); + f->seekp(-4, std::ios::cur); + write_be32(*f, holes_aiger_size); +#endif + f->seekp(0, std::ios::end); + + if (mapping_prep) { + std::vector to_remove_cells; + for (auto cell : top->cells()) + if (!top_minfo->found_blackboxes.count(cell)) + to_remove_cells.push_back(cell); + for (auto cell : to_remove_cells) + top->remove(cell); + pool to_remove; + for (auto wire : top->wires()) + if (!wire->port_input && !wire->port_output && !keep_wires.count(wire)) + to_remove.insert(wire); + top->remove(to_remove); + } + + clear_boxes(); + } +}; + +struct Aiger2Backend : Backend { + Aiger2Backend() : Backend("aiger2", "(experimental) write design to AIGER file") + { + experimental(); + } + + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" write_aiger2 [options] [filename]\n"); + log("\n"); + log("Write the selected module to an AIGER file.\n"); + log("\n"); + log(" -strash\n"); + log(" perform structural hashing while writing\n"); + log("\n"); + log(" -flatten\n"); + log(" allow descending into submodules and write a flattened view of the design\n"); + log(" hierarchy starting at the selected top\n"); + log("\n"); + log("This command is able to ingest all combinational cells except for:\n"); + log("\n"); + pool supported = {KNOWN_OPS}; + CellTypes ct; + ct.setup_internals_eval(); + log(" "); + int col = 0; + for (auto pair : ct.cell_types) + if (!supported.count(pair.first)) { + if (col + pair.first.size() + 2 > 72) { + log("\n "); + col = 0; + } + col += pair.first.size() + 2; + log("%s, ", log_id(pair.first)); + } + log("\n"); + log("\n"); + log("And all combinational gates except for:\n"); + log("\n"); + CellTypes ct2; + ct2.setup_stdcells(); + log(" "); + col = 0; + for (auto pair : ct2.cell_types) + if (!supported.count(pair.first)) { + if (col + pair.first.size() + 2 > 72) { + log("\n "); + col = 0; + } + col += pair.first.size() + 2; + log("%s, ", log_id(pair.first)); + } + log("\n"); + } + + void execute(std::ostream *&f, std::string filename, std::vector args, Design *design) override + { + log_header(design, "Executing AIGER2 backend.\n"); + + size_t argidx; + AigerWriter writer; + writer.const_folding = true; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-strash") + writer.strashing = true; + else if (args[argidx] == "-flatten") + writer.flatten = true; + else + break; + } + extra_args(f, filename, args, argidx); + + Module *top = design->top_module(); + + if (!top || !design->selected_whole_module(top)) + log_cmd_error("No top module selected\n"); + + design->bufNormalize(true); + writer.setup(top); + writer.write(f); + + // we are leaving the sacred land, un-bufnormalize + // (if not, this will lead to bugs: the buf-normalized + // flag must not be kept on past the code that can work + // with it) + design->bufNormalize(false); + } +} Aiger2Backend; + +struct XAiger2Backend : Backend { + XAiger2Backend() : Backend("xaiger2", "(experimental) write module to XAIGER file") + { + experimental(); + } + + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" write_xaiger2 [options] [filename]\n"); + log("\n"); + log("Write the selected module to a XAIGER file including the 'h' and 'a' extensions\n"); + log("with box information for ABC.\n"); + log("\n"); + log(" -strash\n"); + log(" perform structural hashing while writing\n"); + log("\n"); + log(" -flatten\n"); + log(" allow descending into submodules and write a flattened view of the design\n"); + log(" hierarchy starting at the selected top\n"); + log("\n"); + log(" -mapping_prep\n"); + log(" after the file is written, prepare the module for reintegration of\n"); + log(" a mapping in a subsequent command. all cells which are not blackboxed nor\n"); + log(" whiteboxed are removed from the design as well as all wires which only\n"); + log(" connect to removed cells\n"); + log(" (conflicts with -flatten)\n"); + log("\n"); + log(" -map2 \n"); + log(" write a map2 file which 'read_xaiger2 -sc_mapping' can read to\n"); + log(" reintegrate a mapping\n"); + log(" (conflicts with -flatten)\n"); + log("\n"); + } + + void execute(std::ostream *&f, std::string filename, std::vector args, Design *design) override + { + log_header(design, "Executing XAIGER2 backend.\n"); + + size_t argidx; + XAigerWriter writer; + std::string map_filename; + writer.const_folding = true; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-strash") + writer.strashing = true; + else if (args[argidx] == "-flatten") + writer.flatten = true; + else if (args[argidx] == "-mapping_prep") + writer.mapping_prep = true; + else if (args[argidx] == "-map2" && argidx + 1 < args.size()) + map_filename = args[++argidx]; + else + break; + } + extra_args(f, filename, args, argidx); + + Module *top = design->top_module(); + + if (!top || !design->selected_whole_module(top)) + log_cmd_error("No top module selected\n"); + + if (!map_filename.empty()) { + writer.map_file.open(map_filename); + if (!writer.map_file) + log_cmd_error("Failed to open '%s' for writing\n", map_filename.c_str()); + } + + design->bufNormalize(true); + writer.setup(top); + writer.write(f); + + // we are leaving the sacred land, un-bufnormalize + // (if not, this will lead to bugs: the buf-normalized + // flag must not be kept on past the code that can work + // with it) + design->bufNormalize(false); + } +} XAiger2Backend; + +PRIVATE_NAMESPACE_END diff --git a/backends/blif/blif.cc b/backends/blif/blif.cc index 788b7f951..049a3c680 100644 --- a/backends/blif/blif.cc +++ b/backends/blif/blif.cc @@ -387,7 +387,7 @@ struct BlifDumper auto &inputs = cell->getPort(ID::A); auto width = cell->parameters.at(ID::WIDTH).as_int(); auto depth = cell->parameters.at(ID::DEPTH).as_int(); - vector table = cell->parameters.at(ID::TABLE).bits; + vector table = cell->parameters.at(ID::TABLE).to_bits(); while (GetSize(table) < 2*width*depth) table.push_back(State::S0); log_assert(inputs.size() == width); diff --git a/backends/btor/btor.cc b/backends/btor/btor.cc index 9cfd967e5..c3637bc8f 100644 --- a/backends/btor/btor.cc +++ b/backends/btor/btor.cc @@ -711,9 +711,9 @@ struct BtorWorker Const initval; for (int i = 0; i < GetSize(sig_q); i++) if (initbits.count(sig_q[i])) - initval.bits.push_back(initbits.at(sig_q[i]) ? State::S1 : State::S0); + initval.bits().push_back(initbits.at(sig_q[i]) ? State::S1 : State::S0); else - initval.bits.push_back(State::Sx); + initval.bits().push_back(State::Sx); int nid_init_val = -1; @@ -1042,7 +1042,7 @@ struct BtorWorker Const c(bit.data); while (i+GetSize(c) < GetSize(sig) && sig[i+GetSize(c)].wire == nullptr) - c.bits.push_back(sig[i+GetSize(c)].data); + c.bits().push_back(sig[i+GetSize(c)].data); if (consts.count(c) == 0) { int sid = get_bv_sid(GetSize(c)); diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index 8dc14863d..a56bfc036 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -328,7 +328,7 @@ struct FlowGraph { node_comb_defs[node].insert(chunk.wire); } } - for (auto bit : sig.bits()) + for (auto bit : sig) bit_has_state[bit] |= is_ff; // Only comb defs of an entire wire in the right order can be inlined. if (!is_ff && sig.is_wire()) { @@ -864,7 +864,7 @@ struct CxxrtlWorker { if (!module->has_attribute(ID(cxxrtl_template))) return {}; - if (module->attributes.at(ID(cxxrtl_template)).flags != RTLIL::CONST_FLAG_STRING) + if (!(module->attributes.at(ID(cxxrtl_template)).flags & RTLIL::CONST_FLAG_STRING)) log_cmd_error("Attribute `cxxrtl_template' of module `%s' is not a string.\n", log_id(module)); std::vector param_names = split_by(module->get_string_attribute(ID(cxxrtl_template)), " \t"); @@ -1665,15 +1665,15 @@ struct CxxrtlWorker { switch (bit) { case RTLIL::S0: case RTLIL::S1: - compare_mask.bits.push_back(RTLIL::S1); - compare_value.bits.push_back(bit); + compare_mask.bits().push_back(RTLIL::S1); + compare_value.bits().push_back(bit); break; case RTLIL::Sx: case RTLIL::Sz: case RTLIL::Sa: - compare_mask.bits.push_back(RTLIL::S0); - compare_value.bits.push_back(RTLIL::S0); + compare_mask.bits().push_back(RTLIL::S0); + compare_value.bits().push_back(RTLIL::S0); break; default: @@ -3028,7 +3028,7 @@ struct CxxrtlWorker { if (init == RTLIL::Const()) { init = RTLIL::Const(State::Sx, GetSize(bit.wire)); } - init[bit.offset] = port.init_value[i]; + init.bits()[bit.offset] = port.init_value[i]; } } } diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h index d1d6bd8dc..9634b833b 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h @@ -1127,7 +1127,7 @@ struct fmt_part { } case UNICHAR: { - uint32_t codepoint = val.template get(); + uint32_t codepoint = val.template zcast<32>().template get(); if (codepoint >= 0x10000) buf += (char)(0xf0 | (codepoint >> 18)); else if (codepoint >= 0x800) diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl_vcd.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl_vcd.h index cb2ccf5fc..e8be70028 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl_vcd.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl_vcd.h @@ -50,9 +50,13 @@ class vcd_writer { void emit_scope(const std::vector &scope) { assert(!streaming); - while (current_scope.size() > scope.size() || - (current_scope.size() > 0 && - current_scope[current_scope.size() - 1] != scope[current_scope.size() - 1])) { + size_t same_scope_count = 0; + while ((same_scope_count < current_scope.size()) && + (same_scope_count < scope.size()) && + (current_scope[same_scope_count] == scope[same_scope_count])) { + same_scope_count++; + } + while (current_scope.size() > same_scope_count) { buffer += "$upscope $end\n"; current_scope.pop_back(); } @@ -123,6 +127,8 @@ class vcd_writer { bool bit_curr = var.curr[bit / (8 * sizeof(chunk_t))] & (1 << (bit % (8 * sizeof(chunk_t)))); buffer += (bit_curr ? '1' : '0'); } + if (var.width == 0) + buffer += '0'; buffer += ' '; emit_ident(var.ident); buffer += '\n'; diff --git a/backends/edif/edif.cc b/backends/edif/edif.cc index 553eb23d6..c664c41eb 100644 --- a/backends/edif/edif.cc +++ b/backends/edif/edif.cc @@ -334,20 +334,20 @@ struct EdifBackend : public Backend { auto add_prop = [&](IdString name, Const val) { if ((val.flags & RTLIL::CONST_FLAG_STRING) != 0) *f << stringf("\n (property %s (string \"%s\"))", EDIF_DEF(name), val.decode_string().c_str()); - else if (val.bits.size() <= 32 && RTLIL::SigSpec(val).is_fully_def()) + else if (val.size() <= 32 && RTLIL::SigSpec(val).is_fully_def()) *f << stringf("\n (property %s (integer %u))", EDIF_DEF(name), val.as_int()); else { std::string hex_string = ""; - for (size_t i = 0; i < val.bits.size(); i += 4) { + for (size_t i = 0; i < val.size(); i += 4) { int digit_value = 0; - if (i+0 < val.bits.size() && val.bits.at(i+0) == RTLIL::State::S1) digit_value |= 1; - if (i+1 < val.bits.size() && val.bits.at(i+1) == RTLIL::State::S1) digit_value |= 2; - if (i+2 < val.bits.size() && val.bits.at(i+2) == RTLIL::State::S1) digit_value |= 4; - if (i+3 < val.bits.size() && val.bits.at(i+3) == RTLIL::State::S1) digit_value |= 8; + if (i+0 < val.size() && val.at(i+0) == RTLIL::State::S1) digit_value |= 1; + if (i+1 < val.size() && val.at(i+1) == RTLIL::State::S1) digit_value |= 2; + if (i+2 < val.size() && val.at(i+2) == RTLIL::State::S1) digit_value |= 4; + if (i+3 < val.size() && val.at(i+3) == RTLIL::State::S1) digit_value |= 8; char digit_str[2] = { "0123456789abcdef"[digit_value], 0 }; hex_string = std::string(digit_str) + hex_string; } - *f << stringf("\n (property %s (string \"%d'h%s\"))", EDIF_DEF(name), GetSize(val.bits), hex_string.c_str()); + *f << stringf("\n (property %s (string \"%d'h%s\"))", EDIF_DEF(name), GetSize(val), hex_string.c_str()); } }; for (auto module : sorted_modules) diff --git a/backends/firrtl/firrtl.cc b/backends/firrtl/firrtl.cc index dc76dbeec..eac0c9719 100644 --- a/backends/firrtl/firrtl.cc +++ b/backends/firrtl/firrtl.cc @@ -149,7 +149,7 @@ std::string dump_const(const RTLIL::Const &data) // Numeric (non-real) parameter. else { - int width = data.bits.size(); + int width = data.size(); // If a standard 32-bit int, then emit standard int value like "56" or // "-56". Firrtl supports negative-valued int literals. @@ -163,7 +163,7 @@ std::string dump_const(const RTLIL::Const &data) for (int i = 0; i < width; i++) { - switch (data.bits[i]) + switch (data[i]) { case State::S0: break; case State::S1: int_val |= (1 << i); break; @@ -205,7 +205,7 @@ std::string dump_const(const RTLIL::Const &data) for (int i = width - 1; i >= 0; i--) { log_assert(i < width); - switch (data.bits[i]) + switch (data[i]) { case State::S0: res_str += "0"; break; case State::S1: res_str += "1"; break; diff --git a/backends/functional/Makefile.inc b/backends/functional/Makefile.inc new file mode 100644 index 000000000..16d1c0542 --- /dev/null +++ b/backends/functional/Makefile.inc @@ -0,0 +1,4 @@ +OBJS += backends/functional/cxx.o +OBJS += backends/functional/smtlib.o +OBJS += backends/functional/smtlib_rosette.o +OBJS += backends/functional/test_generic.o diff --git a/backends/functional/cxx.cc b/backends/functional/cxx.cc new file mode 100644 index 000000000..1f677120a --- /dev/null +++ b/backends/functional/cxx.cc @@ -0,0 +1,277 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2024 Emily Schmidt + * Copyright (C) 2024 National Technology and Engineering Solutions of Sandia, LLC + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/functional.h" +#include + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +const char *reserved_keywords[] = { + "alignas","alignof","and","and_eq","asm","atomic_cancel","atomic_commit", + "atomic_noexcept","auto","bitand","bitor","bool","break","case", + "catch","char","char16_t","char32_t","char8_t","class","co_await", + "co_return","co_yield","compl","concept","const","const_cast","consteval", + "constexpr","constinit","continue","decltype","default","delete", + "do","double","dynamic_cast","else","enum","explicit","export", + "extern","false","float","for","friend","goto","if","inline", + "int","long","mutable","namespace","new","noexcept","not","not_eq", + "nullptr","operator","or","or_eq","private","protected","public", + "reflexpr","register","reinterpret_cast","requires","return","short", + "signed","sizeof","static","static_log_assert","static_cast","struct", + "switch","synchronized","template","this","thread_local","throw", + "true","try","typedef","typeid","typename","union","unsigned", + "using","virtual","void","volatile","wchar_t","while","xor","xor_eq", + nullptr +}; + +template struct CxxScope : public Functional::Scope { + CxxScope() { + for(const char **p = reserved_keywords; *p != nullptr; p++) + this->reserve(*p); + } + bool is_character_legal(char c, int index) override { + return isascii(c) && (isalpha(c) || (isdigit(c) && index > 0) || c == '_' || c == '$'); + } +}; + +struct CxxType { + Functional::Sort sort; + CxxType(Functional::Sort sort) : sort(sort) {} + std::string to_string() const { + if(sort.is_memory()) { + return stringf("Memory<%d, %d>", sort.addr_width(), sort.data_width()); + } else if(sort.is_signal()) { + return stringf("Signal<%d>", sort.width()); + } else { + log_error("unknown sort"); + } + } +}; + +using CxxWriter = Functional::Writer; + +struct CxxStruct { + std::string name; + dict types; + CxxScope scope; + CxxStruct(std::string name) : name(name) + { + scope.reserve("fn"); + scope.reserve("visit"); + } + void insert(IdString name, CxxType type) { + scope(name, name); + types.insert({name, type}); + } + void print(CxxWriter &f) { + f.print("\tstruct {} {{\n", name); + for (auto p : types) { + f.print("\t\t{} {};\n", p.second.to_string(), scope(p.first, p.first)); + } + f.print("\n\t\ttemplate void visit(T &&fn) {{\n"); + for (auto p : types) { + f.print("\t\t\tfn(\"{}\", {});\n", RTLIL::unescape_id(p.first), scope(p.first, p.first)); + } + f.print("\t\t}}\n"); + f.print("\t}};\n\n"); + }; + std::string operator[](IdString field) { + return scope(field, field); + } +}; + +std::string cxx_const(RTLIL::Const const &value) { + std::stringstream ss; + ss << "Signal<" << value.size() << ">(" << std::hex << std::showbase; + if(value.size() > 32) ss << "{"; + for(int i = 0; i < value.size(); i += 32) { + if(i > 0) ss << ", "; + ss << value.extract(i, 32).as_int(); + } + if(value.size() > 32) ss << "}"; + ss << ")"; + return ss.str(); +} + +template struct CxxPrintVisitor : public Functional::AbstractVisitor { + using Node = Functional::Node; + CxxWriter &f; + NodePrinter np; + CxxStruct &input_struct; + CxxStruct &state_struct; + CxxPrintVisitor(CxxWriter &f, NodePrinter np, CxxStruct &input_struct, CxxStruct &state_struct) : f(f), np(np), input_struct(input_struct), state_struct(state_struct) { } + template void print(const char *fmt, Args&&... args) { + f.print_with(np, fmt, std::forward(args)...); + } + void buf(Node, Node n) override { print("{}", n); } + void slice(Node, Node a, int offset, int out_width) override { print("{0}.slice<{2}>({1})", a, offset, out_width); } + void zero_extend(Node, Node a, int out_width) override { print("{}.zero_extend<{}>()", a, out_width); } + void sign_extend(Node, Node a, int out_width) override { print("{}.sign_extend<{}>()", a, out_width); } + void concat(Node, Node a, Node b) override { print("{}.concat({})", a, b); } + void add(Node, Node a, Node b) override { print("{} + {}", a, b); } + void sub(Node, Node a, Node b) override { print("{} - {}", a, b); } + void mul(Node, Node a, Node b) override { print("{} * {}", a, b); } + void unsigned_div(Node, Node a, Node b) override { print("{} / {}", a, b); } + void unsigned_mod(Node, Node a, Node b) override { print("{} % {}", a, b); } + void bitwise_and(Node, Node a, Node b) override { print("{} & {}", a, b); } + void bitwise_or(Node, Node a, Node b) override { print("{} | {}", a, b); } + void bitwise_xor(Node, Node a, Node b) override { print("{} ^ {}", a, b); } + void bitwise_not(Node, Node a) override { print("~{}", a); } + void unary_minus(Node, Node a) override { print("-{}", a); } + void reduce_and(Node, Node a) override { print("{}.all()", a); } + void reduce_or(Node, Node a) override { print("{}.any()", a); } + void reduce_xor(Node, Node a) override { print("{}.parity()", a); } + void equal(Node, Node a, Node b) override { print("{} == {}", a, b); } + void not_equal(Node, Node a, Node b) override { print("{} != {}", a, b); } + void signed_greater_than(Node, Node a, Node b) override { print("{}.signed_greater_than({})", a, b); } + void signed_greater_equal(Node, Node a, Node b) override { print("{}.signed_greater_equal({})", a, b); } + void unsigned_greater_than(Node, Node a, Node b) override { print("{} > {}", a, b); } + void unsigned_greater_equal(Node, Node a, Node b) override { print("{} >= {}", a, b); } + void logical_shift_left(Node, Node a, Node b) override { print("{} << {}", a, b); } + void logical_shift_right(Node, Node a, Node b) override { print("{} >> {}", a, b); } + void arithmetic_shift_right(Node, Node a, Node b) override { print("{}.arithmetic_shift_right({})", a, b); } + void mux(Node, Node a, Node b, Node s) override { print("{2}.any() ? {1} : {0}", a, b, s); } + void constant(Node, RTLIL::Const const & value) override { print("{}", cxx_const(value)); } + void input(Node, IdString name, IdString kind) override { log_assert(kind == ID($input)); print("input.{}", input_struct[name]); } + void state(Node, IdString name, IdString kind) override { log_assert(kind == ID($state)); print("current_state.{}", state_struct[name]); } + void memory_read(Node, Node mem, Node addr) override { print("{}.read({})", mem, addr); } + void memory_write(Node, Node mem, Node addr, Node data) override { print("{}.write({}, {})", mem, addr, data); } +}; + +bool equal_def(RTLIL::Const const &a, RTLIL::Const const &b) { + if(a.size() != b.size()) return false; + for(int i = 0; i < a.size(); i++) + if((a[i] == State::S1) != (b[i] == State::S1)) + return false; + return true; +} + +struct CxxModule { + Functional::IR ir; + CxxStruct input_struct, output_struct, state_struct; + std::string module_name; + + explicit CxxModule(Module *module) : + ir(Functional::IR::from_module(module)), + input_struct("Inputs"), + output_struct("Outputs"), + state_struct("State") + { + for (auto input : ir.inputs()) + input_struct.insert(input->name, input->sort); + for (auto output : ir.outputs()) + output_struct.insert(output->name, output->sort); + for (auto state : ir.states()) + state_struct.insert(state->name, state->sort); + module_name = CxxScope().unique_name(module->name); + } + void write_header(CxxWriter &f) { + f.print("#include \"sim.h\"\n\n"); + } + void write_struct_def(CxxWriter &f) { + f.print("struct {} {{\n", module_name); + input_struct.print(f); + output_struct.print(f); + state_struct.print(f); + f.print("\tstatic void eval(Inputs const &, Outputs &, State const &, State &);\n"); + f.print("\tstatic void initialize(State &);\n"); + f.print("}};\n\n"); + } + void write_initial_def(CxxWriter &f) { + f.print("void {0}::initialize({0}::State &state)\n{{\n", module_name); + for (auto state : ir.states()) { + if (state->sort.is_signal()) + f.print("\tstate.{} = {};\n", state_struct[state->name], cxx_const(state->initial_value_signal())); + else if (state->sort.is_memory()) { + f.print("\t{{\n"); + f.print("\t\tstd::array, {}> mem;\n", state->sort.data_width(), 1<sort.addr_width()); + const auto &contents = state->initial_value_memory(); + f.print("\t\tmem.fill({});\n", cxx_const(contents.default_value())); + for(auto range : contents) + for(auto addr = range.base(); addr < range.limit(); addr++) + if(!equal_def(range[addr], contents.default_value())) + f.print("\t\tmem[{}] = {};\n", addr, cxx_const(range[addr])); + f.print("\t\tstate.{} = mem;\n", state_struct[state->name]); + f.print("\t}}\n"); + } + } + f.print("}}\n\n"); + } + void write_eval_def(CxxWriter &f) { + f.print("void {0}::eval({0}::Inputs const &input, {0}::Outputs &output, {0}::State const ¤t_state, {0}::State &next_state)\n{{\n", module_name); + CxxScope locals; + locals.reserve("input"); + locals.reserve("output"); + locals.reserve("current_state"); + locals.reserve("next_state"); + auto node_name = [&](Functional::Node n) { return locals(n.id(), n.name()); }; + CxxPrintVisitor printVisitor(f, node_name, input_struct, state_struct); + for (auto node : ir) { + f.print("\t{} {} = ", CxxType(node.sort()).to_string(), node_name(node)); + node.visit(printVisitor); + f.print(";\n"); + } + for (auto state : ir.states()) + f.print("\tnext_state.{} = {};\n", state_struct[state->name], node_name(state->next_value())); + for (auto output : ir.outputs()) + f.print("\toutput.{} = {};\n", output_struct[output->name], node_name(output->value())); + f.print("}}\n\n"); + } +}; + +struct FunctionalCxxBackend : public Backend +{ + FunctionalCxxBackend() : Backend("functional_cxx", "convert design to C++ using the functional backend") {} + + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log("TODO: add help message\n"); + log("\n"); + } + + void printCxx(std::ostream &stream, std::string, Module *module) + { + CxxWriter f(stream); + CxxModule mod(module); + mod.write_header(f); + mod.write_struct_def(f); + mod.write_eval_def(f); + mod.write_initial_def(f); + } + + void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override + { + log_header(design, "Executing Functional C++ backend.\n"); + + size_t argidx = 1; + extra_args(f, filename, args, argidx, design); + + for (auto module : design->selected_modules()) { + log("Dumping module `%s'.\n", module->name.c_str()); + printCxx(*f, filename, module); + } + } +} FunctionalCxxBackend; + +PRIVATE_NAMESPACE_END diff --git a/backends/functional/cxx_runtime/sim.h b/backends/functional/cxx_runtime/sim.h new file mode 100644 index 000000000..941f067b9 --- /dev/null +++ b/backends/functional/cxx_runtime/sim.h @@ -0,0 +1,418 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2024 Emily Schmidt + * Copyright (C) 2024 National Technology and Engineering Solutions of Sandia, LLC + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef SIM_H +#define SIM_H + +#include +#include +#include +#include +#include + +template +class Signal { + template friend class Signal; + std::array _bits; +public: + Signal() { } + Signal(uint32_t val) + { + for(size_t i = 0; i < n; i++) + if(i < 32) + _bits[i] = val & (1< vals) + { + size_t k, i; + + k = 0; + for (auto val : vals) { + for(i = 0; i < 32; i++) + if(i + k < n) + _bits[i + k] = val & (1< + static Signal from_array(T vals) + { + size_t k, i; + Signal ret; + + k = 0; + for (auto val : vals) { + for(i = 0; i < 32; i++) + if(i + k < n) + ret._bits[i + k] = val & (1< ret; + for(size_t i = 0; i < n; i++) + if(i < 32) + ret._bits[i] = val & (1< ret; + for(size_t i = 0; i < n; i++) + ret._bits[i] = b; + return ret; + } + + int size() const { return n; } + bool operator[](int i) const { assert(n >= 0 && i < n); return _bits[i]; } + + template + Signal slice(size_t offset) const + { + Signal ret; + + assert(offset + m <= n); + std::copy(_bits.begin() + offset, _bits.begin() + offset + m, ret._bits.begin()); + return ret; + } + + bool any() const + { + for(int i = 0; i < n; i++) + if(_bits[i]) + return true; + return false; + } + + bool all() const + { + for(int i = 0; i < n; i++) + if(!_bits[i]) + return false; + return true; + } + + bool parity() const + { + bool result = false; + for(int i = 0; i < n; i++) + result ^= _bits[i]; + return result; + } + + bool sign() const { return _bits[n-1]; } + + template + T as_numeric() const + { + T ret = 0; + for(size_t i = 0; i < std::min(sizeof(T) * 8, n); i++) + if(_bits[i]) + ret |= ((T)1)< + T as_numeric_clamped() const + { + for(size_t i = sizeof(T) * 8; i < n; i++) + if(_bits[i]) + return ~((T)0); + return as_numeric(); + } + + uint32_t as_int() const { return as_numeric(); } + +private: + std::string as_string_p2(int b) const { + std::string ret; + for(int i = (n - 1) - (n - 1) % b; i >= 0; i -= b) + ret += "0123456789abcdef"[(*this >> Signal<32>(i)).as_int() & ((1< t = *this; + Signal b = 10; + do{ + ret += (char)('0' + (t % b).as_int()); + t = t / b; + }while(t.any()); + std::reverse(ret.begin(), ret.end()); + return ret; + } +public: + std::string as_string(int base = 16, bool showbase = true) const { + std::string ret; + if(showbase) { + ret += std::to_string(n); + switch(base) { + case 2: ret += "'b"; break; + case 8: ret += "'o"; break; + case 10: ret += "'d"; break; + case 16: ret += "'h"; break; + default: assert(0); + } + } + switch(base) { + case 2: return ret + as_string_p2(1); + case 8: return ret + as_string_p2(3); + case 10: return ret + as_string_b10(); + case 16: return ret + as_string_p2(4); + default: assert(0); + } + } + friend std::ostream &operator << (std::ostream &os, Signal const &s) { return os << s.as_string(); } + + Signal operator ~() const + { + Signal ret; + for(size_t i = 0; i < n; i++) + ret._bits[i] = !_bits[i]; + return ret; + } + + Signal operator -() const + { + Signal ret; + int x = 1; + for(size_t i = 0; i < n; i++) { + x += (int)!_bits[i]; + ret._bits[i] = (x & 1) != 0; + x >>= 1; + } + return ret; + } + + Signal operator +(Signal const &b) const + { + Signal ret; + int x = 0; + for(size_t i = 0; i < n; i++){ + x += (int)_bits[i] + (int)b._bits[i]; + ret._bits[i] = x & 1; + x >>= 1; + } + return ret; + } + + Signal operator -(Signal const &b) const + { + Signal ret; + int x = 1; + for(size_t i = 0; i < n; i++){ + x += (int)_bits[i] + (int)!b._bits[i]; + ret._bits[i] = x & 1; + x >>= 1; + } + return ret; + } + + Signal operator *(Signal const &b) const + { + Signal ret; + int x = 0; + for(size_t i = 0; i < n; i++){ + for(size_t j = 0; j <= i; j++) + x += (int)_bits[j] & (int)b._bits[i-j]; + ret._bits[i] = x & 1; + x >>= 1; + } + return ret; + } + +private: + Signal divmod(Signal const &b, bool modulo) const + { + if(!b.any()) return 0; + Signal q = 0; + Signal r = 0; + for(size_t i = n; i-- != 0; ){ + r = r << Signal<1>(1); + r._bits[0] = _bits[i]; + if(r >= b){ + r = r - b; + q._bits[i] = true; + } + } + return modulo ? r : q; + } +public: + + Signal operator /(Signal const &b) const { return divmod(b, false); } + Signal operator %(Signal const &b) const { return divmod(b, true); } + + bool operator ==(Signal const &b) const + { + for(size_t i = 0; i < n; i++) + if(_bits[i] != b._bits[i]) + return false; + return true; + } + + bool operator >=(Signal const &b) const + { + for(size_t i = n; i-- != 0; ) + if(_bits[i] != b._bits[i]) + return _bits[i]; + return true; + } + + bool operator >(Signal const &b) const + { + for(size_t i = n; i-- != 0; ) + if(_bits[i] != b._bits[i]) + return _bits[i]; + return false; + } + + bool operator !=(Signal const &b) const { return !(*this == b); } + bool operator <=(Signal const &b) const { return b <= *this; } + bool operator <(Signal const &b) const { return b < *this; } + + bool signed_greater_than(Signal const &b) const + { + if(_bits[n-1] != b._bits[n-1]) + return b._bits[n-1]; + return *this > b; + } + + bool signed_greater_equal(Signal const &b) const + { + if(_bits[n-1] != b._bits[n-1]) + return b._bits[n-1]; + return *this >= b; + } + + Signal operator &(Signal const &b) const + { + Signal ret; + for(size_t i = 0; i < n; i++) + ret._bits[i] = _bits[i] && b._bits[i]; + return ret; + } + + Signal operator |(Signal const &b) const + { + Signal ret; + for(size_t i = 0; i < n; i++) + ret._bits[i] = _bits[i] || b._bits[i]; + return ret; + } + + Signal operator ^(Signal const &b) const + { + Signal ret; + for(size_t i = 0; i < n; i++) + ret._bits[i] = _bits[i] != b._bits[i]; + return ret; + } + + template + Signal operator <<(Signal const &b) const + { + Signal ret = 0; + size_t amount = b.template as_numeric_clamped(); + if(amount < n) + std::copy(_bits.begin(), _bits.begin() + (n - amount), ret._bits.begin() + amount); + return ret; + } + + template + Signal operator >>(Signal const &b) const + { + Signal ret = 0; + size_t amount = b.template as_numeric_clamped(); + if(amount < n) + std::copy(_bits.begin() + amount, _bits.end(), ret._bits.begin()); + return ret; + } + + template + Signal arithmetic_shift_right(Signal const &b) const + { + Signal ret = Signal::repeat(sign()); + size_t amount = b.template as_numeric_clamped(); + if(amount < n) + std::copy(_bits.begin() + amount, _bits.end(), ret._bits.begin()); + return ret; + } + + template + Signal concat(Signal const& b) const + { + Signal ret; + std::copy(_bits.begin(), _bits.end(), ret._bits.begin()); + std::copy(b._bits.begin(), b._bits.end(), ret._bits.begin() + n); + return ret; + } + + template + Signal zero_extend() const + { + assert(m >= n); + Signal ret = 0; + std::copy(_bits.begin(), _bits.end(), ret._bits.begin()); + return ret; + } + + template + Signal sign_extend() const + { + assert(m >= n); + Signal ret = Signal::repeat(sign()); + std::copy(_bits.begin(), _bits.end(), ret._bits.begin()); + return ret; + } +}; + +template +class Memory { + std::array, 1< _contents; +public: + Memory() {} + Memory(std::array, 1< const &contents) : _contents(contents) {} + Signal read(Signal addr) const + { + return _contents[addr.template as_numeric()]; + } + Memory write(Signal addr, Signal data) const + { + Memory ret = *this; + ret._contents[addr.template as_numeric()] = data; + return ret; + } +}; + +#endif diff --git a/backends/functional/smtlib.cc b/backends/functional/smtlib.cc new file mode 100644 index 000000000..3eacf407c --- /dev/null +++ b/backends/functional/smtlib.cc @@ -0,0 +1,295 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2024 Emily Schmidt + * Copyright (C) 2024 National Technology and Engineering Solutions of Sandia, LLC + * + * 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/functional.h" +#include "kernel/yosys.h" +#include "kernel/sexpr.h" +#include + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +using SExprUtil::list; + +const char *reserved_keywords[] = { + // reserved keywords from the smtlib spec + "BINARY", "DECIMAL", "HEXADECIMAL", "NUMERAL", "STRING", "_", "!", "as", "let", "exists", "forall", "match", "par", + "assert", "check-sat", "check-sat-assuming", "declare-const", "declare-datatype", "declare-datatypes", + "declare-fun", "declare-sort", "define-fun", "define-fun-rec", "define-funs-rec", "define-sort", + "exit", "get-assertions", "symbol", "sort", "get-assignment", "get-info", "get-model", + "get-option", "get-proof", "get-unsat-assumptions", "get-unsat-core", "get-value", + "pop", "push", "reset", "reset-assertions", "set-info", "set-logic", "set-option", + + // reserved for our own purposes + "pair", "Pair", "first", "second", + "inputs", "state", + nullptr +}; + +struct SmtScope : public Functional::Scope { + SmtScope() { + for(const char **p = reserved_keywords; *p != nullptr; p++) + reserve(*p); + } + bool is_character_legal(char c, int index) override { + return isascii(c) && (isalpha(c) || (isdigit(c) && index > 0) || strchr("~!@$%^&*_-+=<>.?/", c)); + } +}; + +struct SmtSort { + Functional::Sort sort; + SmtSort(Functional::Sort sort) : sort(sort) {} + SExpr to_sexpr() const { + if(sort.is_memory()) { + return list("Array", list("_", "BitVec", sort.addr_width()), list("_", "BitVec", sort.data_width())); + } else if(sort.is_signal()) { + return list("_", "BitVec", sort.width()); + } else { + log_error("unknown sort"); + } + } +}; + +class SmtStruct { + struct Field { + SmtSort sort; + std::string accessor; + }; + idict field_names; + vector fields; + SmtScope &scope; +public: + std::string name; + SmtStruct(std::string name, SmtScope &scope) : scope(scope), name(name) {} + void insert(IdString field_name, SmtSort sort) { + field_names(field_name); + auto accessor = scope.unique_name("\\" + name + "_" + RTLIL::unescape_id(field_name)); + fields.emplace_back(Field{sort, accessor}); + } + void write_definition(SExprWriter &w) { + w.open(list("declare-datatype", name)); + w.open(list()); + w.open(list(name)); + for(const auto &field : fields) + w << list(field.accessor, field.sort.to_sexpr()); + w.close(3); + } + template void write_value(SExprWriter &w, Fn fn) { + if(field_names.empty()) { + // Zero-argument constructors in SMTLIB must not be called as functions. + w << name; + } else { + w.open(list(name)); + for(auto field_name : field_names) { + w << fn(field_name); + w.comment(RTLIL::unescape_id(field_name), true); + } + w.close(); + } + } + SExpr access(SExpr record, IdString name) { + size_t i = field_names.at(name); + return list(fields[i].accessor, std::move(record)); + } +}; + +std::string smt_const(RTLIL::Const const &c) { + std::string s = "#b"; + for(int i = c.size(); i-- > 0; ) + s += c[i] == State::S1 ? '1' : '0'; + return s; +} + +struct SmtPrintVisitor : public Functional::AbstractVisitor { + using Node = Functional::Node; + std::function n; + SmtStruct &input_struct; + SmtStruct &state_struct; + + SmtPrintVisitor(SmtStruct &input_struct, SmtStruct &state_struct) : input_struct(input_struct), state_struct(state_struct) {} + + SExpr from_bool(SExpr &&arg) { + return list("ite", std::move(arg), "#b1", "#b0"); + } + SExpr to_bool(SExpr &&arg) { + return list("=", std::move(arg), "#b1"); + } + SExpr extract(SExpr &&arg, int offset, int out_width = 1) { + return list(list("_", "extract", offset + out_width - 1, offset), std::move(arg)); + } + + SExpr buf(Node, Node a) override { return n(a); } + SExpr slice(Node, Node a, int offset, int out_width) override { return extract(n(a), offset, out_width); } + SExpr zero_extend(Node, Node a, int out_width) override { return list(list("_", "zero_extend", out_width - a.width()), n(a)); } + SExpr sign_extend(Node, Node a, int out_width) override { return list(list("_", "sign_extend", out_width - a.width()), n(a)); } + SExpr concat(Node, Node a, Node b) override { return list("concat", n(b), n(a)); } + SExpr add(Node, Node a, Node b) override { return list("bvadd", n(a), n(b)); } + SExpr sub(Node, Node a, Node b) override { return list("bvsub", n(a), n(b)); } + SExpr mul(Node, Node a, Node b) override { return list("bvmul", n(a), n(b)); } + SExpr unsigned_div(Node, Node a, Node b) override { return list("bvudiv", n(a), n(b)); } + SExpr unsigned_mod(Node, Node a, Node b) override { return list("bvurem", n(a), n(b)); } + SExpr bitwise_and(Node, Node a, Node b) override { return list("bvand", n(a), n(b)); } + SExpr bitwise_or(Node, Node a, Node b) override { return list("bvor", n(a), n(b)); } + SExpr bitwise_xor(Node, Node a, Node b) override { return list("bvxor", n(a), n(b)); } + SExpr bitwise_not(Node, Node a) override { return list("bvnot", n(a)); } + SExpr unary_minus(Node, Node a) override { return list("bvneg", n(a)); } + SExpr reduce_and(Node, Node a) override { return from_bool(list("=", n(a), smt_const(RTLIL::Const(State::S1, a.width())))); } + SExpr reduce_or(Node, Node a) override { return from_bool(list("distinct", n(a), smt_const(RTLIL::Const(State::S0, a.width())))); } + SExpr reduce_xor(Node, Node a) override { + vector s { "bvxor" }; + for(int i = 0; i < a.width(); i++) + s.push_back(extract(n(a), i)); + return s; + } + SExpr equal(Node, Node a, Node b) override { return from_bool(list("=", n(a), n(b))); } + SExpr not_equal(Node, Node a, Node b) override { return from_bool(list("distinct", n(a), n(b))); } + SExpr signed_greater_than(Node, Node a, Node b) override { return from_bool(list("bvsgt", n(a), n(b))); } + SExpr signed_greater_equal(Node, Node a, Node b) override { return from_bool(list("bvsge", n(a), n(b))); } + SExpr unsigned_greater_than(Node, Node a, Node b) override { return from_bool(list("bvugt", n(a), n(b))); } + SExpr unsigned_greater_equal(Node, Node a, Node b) override { return from_bool(list("bvuge", n(a), n(b))); } + + SExpr extend(SExpr &&a, int in_width, int out_width) { + if(in_width < out_width) + return list(list("_", "zero_extend", out_width - in_width), std::move(a)); + else + return std::move(a); + } + SExpr logical_shift_left(Node, Node a, Node b) override { return list("bvshl", n(a), extend(n(b), b.width(), a.width())); } + SExpr logical_shift_right(Node, Node a, Node b) override { return list("bvlshr", n(a), extend(n(b), b.width(), a.width())); } + SExpr arithmetic_shift_right(Node, Node a, Node b) override { return list("bvashr", n(a), extend(n(b), b.width(), a.width())); } + SExpr mux(Node, Node a, Node b, Node s) override { return list("ite", to_bool(n(s)), n(b), n(a)); } + SExpr constant(Node, RTLIL::Const const &value) override { return smt_const(value); } + SExpr memory_read(Node, Node mem, Node addr) override { return list("select", n(mem), n(addr)); } + SExpr memory_write(Node, Node mem, Node addr, Node data) override { return list("store", n(mem), n(addr), n(data)); } + + SExpr input(Node, IdString name, IdString kind) override { log_assert(kind == ID($input)); return input_struct.access("inputs", name); } + SExpr state(Node, IdString name, IdString kind) override { log_assert(kind == ID($state)); return state_struct.access("state", name); } +}; + +struct SmtModule { + Functional::IR ir; + SmtScope scope; + std::string name; + + SmtStruct input_struct; + SmtStruct output_struct; + SmtStruct state_struct; + + SmtModule(Module *module) + : ir(Functional::IR::from_module(module)) + , scope() + , name(scope.unique_name(module->name)) + , input_struct(scope.unique_name(module->name.str() + "_Inputs"), scope) + , output_struct(scope.unique_name(module->name.str() + "_Outputs"), scope) + , state_struct(scope.unique_name(module->name.str() + "_State"), scope) + { + scope.reserve(name + "-initial"); + for (auto input : ir.inputs()) + input_struct.insert(input->name, input->sort); + for (auto output : ir.outputs()) + output_struct.insert(output->name, output->sort); + for (auto state : ir.states()) + state_struct.insert(state->name, state->sort); + } + + void write_eval(SExprWriter &w) + { + w.push(); + w.open(list("define-fun", name, + list(list("inputs", input_struct.name), + list("state", state_struct.name)), + list("Pair", output_struct.name, state_struct.name))); + auto inlined = [&](Functional::Node n) { + return n.fn() == Functional::Fn::constant; + }; + SmtPrintVisitor visitor(input_struct, state_struct); + auto node_to_sexpr = [&](Functional::Node n) -> SExpr { + if(inlined(n)) + return n.visit(visitor); + else + return scope(n.id(), n.name()); + }; + visitor.n = node_to_sexpr; + for(auto n : ir) + if(!inlined(n)) { + w.open(list("let", list(list(node_to_sexpr(n), n.visit(visitor)))), false); + w.comment(SmtSort(n.sort()).to_sexpr().to_string(), true); + } + w.open(list("pair")); + output_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.output(name).value()); }); + state_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.state(name).next_value()); }); + w.pop(); + } + + void write_initial(SExprWriter &w) + { + std::string initial = name + "-initial"; + w << list("declare-const", initial, state_struct.name); + for (auto state : ir.states()) { + if(state->sort.is_signal()) + w << list("assert", list("=", state_struct.access(initial, state->name), smt_const(state->initial_value_signal()))); + else if(state->sort.is_memory()) { + const auto &contents = state->initial_value_memory(); + for(int i = 0; i < 1<sort.addr_width(); i++) { + auto addr = smt_const(RTLIL::Const(i, state->sort.addr_width())); + w << list("assert", list("=", list("select", state_struct.access(initial, state->name), addr), smt_const(contents[i]))); + } + } + } + } + + void write(std::ostream &out) + { + SExprWriter w(out); + + input_struct.write_definition(w); + output_struct.write_definition(w); + state_struct.write_definition(w); + + w << list("declare-datatypes", + list(list("Pair", 2)), + list(list("par", list("X", "Y"), list(list("pair", list("first", "X"), list("second", "Y")))))); + + write_eval(w); + write_initial(w); + } +}; + +struct FunctionalSmtBackend : public Backend { + FunctionalSmtBackend() : Backend("functional_smt2", "Generate SMT-LIB from Functional IR") {} + + void help() override { log("\nFunctional SMT Backend.\n\n"); } + + void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override + { + log_header(design, "Executing Functional SMT Backend.\n"); + + size_t argidx = 1; + extra_args(f, filename, args, argidx, design); + + for (auto module : design->selected_modules()) { + log("Processing module `%s`.\n", module->name.c_str()); + SmtModule smt(module); + smt.write(*f); + } + } +} FunctionalSmtBackend; + +PRIVATE_NAMESPACE_END diff --git a/backends/functional/smtlib_rosette.cc b/backends/functional/smtlib_rosette.cc new file mode 100644 index 000000000..ea14da854 --- /dev/null +++ b/backends/functional/smtlib_rosette.cc @@ -0,0 +1,308 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2024 Emily Schmidt + * Copyright (C) 2024 National Technology and Engineering Solutions of Sandia, LLC + * + * 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/functional.h" +#include "kernel/yosys.h" +#include "kernel/sexpr.h" +#include + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +using SExprUtil::list; + +const char *reserved_keywords[] = { + // reserved keywords from the racket spec + "struct", "lambda", "values", "extract", "concat", "bv", "let", "define", "cons", "list", "read", "write", + "stream", "error", "raise", "exit", "for", "begin", "when", "unless", "module", "require", "provide", "apply", + "if", "cond", "even", "odd", "any", "and", "or", "match", "command-line", "ffi-lib", "thread", "kill", "sync", + "future", "touch", "subprocess", "make-custodian", "custodian-shutdown-all", "current-custodian", "make", "tcp", + "connect", "prepare", "malloc", "free", "_fun", "_cprocedure", "build", "path", "file", "peek", "bytes", + "flush", "with", "lexer", "parser", "syntax", "interface", "send", "make-object", "new", "instantiate", + "define-generics", "set", + + // reserved for our own purposes + "inputs", "state", "name", + nullptr +}; + +struct SmtrScope : public Functional::Scope { + SmtrScope() { + for(const char **p = reserved_keywords; *p != nullptr; p++) + reserve(*p); + } + bool is_character_legal(char c, int index) override { + return isascii(c) && (isalpha(c) || (isdigit(c) && index > 0) || strchr("@$%^&_+=.", c)); + } +}; + +struct SmtrSort { + Functional::Sort sort; + SmtrSort(Functional::Sort sort) : sort(sort) {} + SExpr to_sexpr() const { + if(sort.is_memory()) { + return list("list", list("bitvector", sort.addr_width()), list("bitvector", sort.data_width())); + } else if(sort.is_signal()) { + return list("bitvector", sort.width()); + } else { + log_error("unknown sort"); + } + } +}; + +class SmtrStruct { + struct Field { + SmtrSort sort; + std::string accessor; + std::string name; + }; + idict field_names; + vector fields; + SmtrScope &global_scope; + SmtrScope local_scope; +public: + std::string name; + SmtrStruct(std::string name, SmtrScope &scope) : global_scope(scope), local_scope(), name(name) {} + void insert(IdString field_name, SmtrSort sort) { + field_names(field_name); + auto base_name = local_scope.unique_name(field_name); + auto accessor = name + "-" + base_name; + global_scope.reserve(accessor); + fields.emplace_back(Field{sort, accessor, base_name}); + } + void write_definition(SExprWriter &w) { + vector field_list; + for(const auto &field : fields) { + field_list.emplace_back(field.name); + } + w.push(); + w.open(list("struct", name, field_list, "#:transparent")); + if (field_names.size()) { + for (const auto &field : fields) { + auto bv_type = field.sort.to_sexpr(); + w.comment(field.name + " " + bv_type.to_string()); + } + } + w.pop(); + } + template void write_value(SExprWriter &w, Fn fn) { + w.open(list(name)); + for(auto field_name : field_names) { + w << fn(field_name); + w.comment(RTLIL::unescape_id(field_name), true); + } + w.close(); + } + SExpr access(SExpr record, IdString name) { + size_t i = field_names.at(name); + return list(fields[i].accessor, std::move(record)); + } +}; + +std::string smt_const(RTLIL::Const const &c) { + std::string s = "#b"; + for(int i = c.size(); i-- > 0; ) + s += c[i] == State::S1 ? '1' : '0'; + return s; +} + +struct SmtrPrintVisitor : public Functional::AbstractVisitor { + using Node = Functional::Node; + std::function n; + SmtrStruct &input_struct; + SmtrStruct &state_struct; + + SmtrPrintVisitor(SmtrStruct &input_struct, SmtrStruct &state_struct) : input_struct(input_struct), state_struct(state_struct) {} + + SExpr from_bool(SExpr &&arg) { + return list("bool->bitvector", std::move(arg)); + } + SExpr to_bool(SExpr &&arg) { + return list("bitvector->bool", std::move(arg)); + } + SExpr to_list(SExpr &&arg) { + return list("bitvector->bits", std::move(arg)); + } + + SExpr buf(Node, Node a) override { return n(a); } + SExpr slice(Node, Node a, int offset, int out_width) override { return list("extract", offset + out_width - 1, offset, n(a)); } + SExpr zero_extend(Node, Node a, int out_width) override { return list("zero-extend", n(a), list("bitvector", out_width)); } + SExpr sign_extend(Node, Node a, int out_width) override { return list("sign-extend", n(a), list("bitvector", out_width)); } + SExpr concat(Node, Node a, Node b) override { return list("concat", n(b), n(a)); } + SExpr add(Node, Node a, Node b) override { return list("bvadd", n(a), n(b)); } + SExpr sub(Node, Node a, Node b) override { return list("bvsub", n(a), n(b)); } + SExpr mul(Node, Node a, Node b) override { return list("bvmul", n(a), n(b)); } + SExpr unsigned_div(Node, Node a, Node b) override { return list("bvudiv", n(a), n(b)); } + SExpr unsigned_mod(Node, Node a, Node b) override { return list("bvurem", n(a), n(b)); } + SExpr bitwise_and(Node, Node a, Node b) override { return list("bvand", n(a), n(b)); } + SExpr bitwise_or(Node, Node a, Node b) override { return list("bvor", n(a), n(b)); } + SExpr bitwise_xor(Node, Node a, Node b) override { return list("bvxor", n(a), n(b)); } + SExpr bitwise_not(Node, Node a) override { return list("bvnot", n(a)); } + SExpr unary_minus(Node, Node a) override { return list("bvneg", n(a)); } + SExpr reduce_and(Node, Node a) override { return list("apply", "bvand", to_list(n(a))); } + SExpr reduce_or(Node, Node a) override { return list("apply", "bvor", to_list(n(a))); } + SExpr reduce_xor(Node, Node a) override { return list("apply", "bvxor", to_list(n(a))); } + SExpr equal(Node, Node a, Node b) override { return from_bool(list("bveq", n(a), n(b))); } + SExpr not_equal(Node, Node a, Node b) override { return from_bool(list("not", list("bveq", n(a), n(b)))); } + SExpr signed_greater_than(Node, Node a, Node b) override { return from_bool(list("bvsgt", n(a), n(b))); } + SExpr signed_greater_equal(Node, Node a, Node b) override { return from_bool(list("bvsge", n(a), n(b))); } + SExpr unsigned_greater_than(Node, Node a, Node b) override { return from_bool(list("bvugt", n(a), n(b))); } + SExpr unsigned_greater_equal(Node, Node a, Node b) override { return from_bool(list("bvuge", n(a), n(b))); } + + SExpr extend(SExpr &&a, int in_width, int out_width) { + if(in_width < out_width) + return list("zero-extend", std::move(a), list("bitvector", out_width)); + else + return std::move(a); + } + SExpr logical_shift_left(Node, Node a, Node b) override { return list("bvshl", n(a), extend(n(b), b.width(), a.width())); } + SExpr logical_shift_right(Node, Node a, Node b) override { return list("bvlshr", n(a), extend(n(b), b.width(), a.width())); } + SExpr arithmetic_shift_right(Node, Node a, Node b) override { return list("bvashr", n(a), extend(n(b), b.width(), a.width())); } + SExpr mux(Node, Node a, Node b, Node s) override { return list("if", to_bool(n(s)), n(b), n(a)); } + SExpr constant(Node, RTLIL::Const const& value) override { return list("bv", smt_const(value), value.size()); } + SExpr memory_read(Node, Node mem, Node addr) override { return list("list-ref-bv", n(mem), n(addr)); } + SExpr memory_write(Node, Node mem, Node addr, Node data) override { return list("list-set-bv", n(mem), n(addr), n(data)); } + + SExpr input(Node, IdString name, IdString kind) override { log_assert(kind == ID($input)); return input_struct.access("inputs", name); } + SExpr state(Node, IdString name, IdString kind) override { log_assert(kind == ID($state)); return state_struct.access("state", name); } +}; + +struct SmtrModule { + Functional::IR ir; + SmtrScope scope; + std::string name; + + SmtrStruct input_struct; + SmtrStruct output_struct; + SmtrStruct state_struct; + + SmtrModule(Module *module) + : ir(Functional::IR::from_module(module)) + , scope() + , name(scope.unique_name(module->name)) + , input_struct(scope.unique_name(module->name.str() + "_Inputs"), scope) + , output_struct(scope.unique_name(module->name.str() + "_Outputs"), scope) + , state_struct(scope.unique_name(module->name.str() + "_State"), scope) + { + scope.reserve(name + "_initial"); + for (auto input : ir.inputs()) + input_struct.insert(input->name, input->sort); + for (auto output : ir.outputs()) + output_struct.insert(output->name, output->sort); + for (auto state : ir.states()) + state_struct.insert(state->name, state->sort); + } + + void write(std::ostream &out) + { + SExprWriter w(out); + + input_struct.write_definition(w); + output_struct.write_definition(w); + state_struct.write_definition(w); + + w.push(); + w.open(list("define", list(name, "inputs", "state"))); + auto inlined = [&](Functional::Node n) { + return n.fn() == Functional::Fn::constant; + }; + SmtrPrintVisitor visitor(input_struct, state_struct); + auto node_to_sexpr = [&](Functional::Node n) -> SExpr { + if(inlined(n)) + return n.visit(visitor); + else + return scope(n.id(), n.name()); + }; + visitor.n = node_to_sexpr; + for(auto n : ir) + if(!inlined(n)) { + w.open(list("let", list(list(node_to_sexpr(n), n.visit(visitor)))), false); + w.comment(SmtrSort(n.sort()).to_sexpr().to_string(), true); + } + w.open(list("cons")); + output_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.output(name).value()); }); + state_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.state(name).next_value()); }); + w.pop(); + + w.push(); + auto initial = name + "_initial"; + w.open(list("define", initial)); + w.open(list(state_struct.name)); + for (auto state : ir.states()) { + if (state->sort.is_signal()) + w << list("bv", smt_const(state->initial_value_signal()), state->sort.width()); + else if (state->sort.is_memory()) { + const auto &contents = state->initial_value_memory(); + w.open(list("list")); + for(int i = 0; i < 1<sort.addr_width(); i++) { + w << list("bv", smt_const(contents[i]), state->sort.data_width()); + } + w.close(); + } + } + w.pop(); + } +}; + +struct FunctionalSmtrBackend : public Backend { + FunctionalSmtrBackend() : Backend("functional_rosette", "Generate Rosette compatible Racket from Functional IR") {} + + void help() override { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" write_functional_rosette [options] [selection] [filename]\n"); + log("\n"); + log("Functional Rosette Backend.\n"); + log("\n"); + log(" -provides\n"); + log(" include 'provide' statement(s) for loading output as a module\n"); + log("\n"); + } + + void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override + { + auto provides = false; + + log_header(design, "Executing Functional Rosette Backend.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-provides") + provides = true; + else + break; + } + extra_args(f, filename, args, argidx); + + *f << "#lang rosette/safe\n"; + if (provides) { + *f << "(provide (all-defined-out))\n"; + } + + for (auto module : design->selected_modules()) { + log("Processing module `%s`.\n", module->name.c_str()); + SmtrModule smtr(module); + smtr.write(*f); + } + } +} FunctionalSmtrBackend; + +PRIVATE_NAMESPACE_END diff --git a/backends/functional/test_generic.cc b/backends/functional/test_generic.cc new file mode 100644 index 000000000..a9dfd0c70 --- /dev/null +++ b/backends/functional/test_generic.cc @@ -0,0 +1,156 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2024 Emily Schmidt + * Copyright (C) 2024 National Technology and Engineering Solutions of Sandia, LLC + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/functional.h" +#include + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct MemContentsTest { + int addr_width, data_width; + MemContents state; + using addr_t = MemContents::addr_t; + std::map reference; + MemContentsTest(int addr_width, int data_width) : addr_width(addr_width), data_width(data_width), state(addr_width, data_width, RTLIL::Const(State::S0, data_width)) {} + void check() { + state.check(); + for(auto addr = 0; addr < (1<second != state[addr]) goto error; + } else { + if(state.count_range(addr, addr + 1) != 0) goto error; + } + } + return; + error: + printf("FAIL\n"); + int digits = (data_width + 3) / 4; + + for(auto addr = 0; addr < (1<second : state.default_value(); + std::string ref_string = stringf("%.*x", digits, ref_value.as_int()); + bool sta_def = state.count_range(addr, addr + 1) == 1; + RTLIL::Const sta_value = state[addr]; + std::string sta_string = stringf("%.*x", digits, sta_value.as_int()); + if(ref_def && sta_def) { + if(ref_value == sta_value) printf("%s%s", ref_string.c_str(), string(digits, ' ').c_str()); + else printf("%s%s", ref_string.c_str(), sta_string.c_str()); + } else if(ref_def) { + printf("%s%s", ref_string.c_str(), string(digits, 'M').c_str()); + } else if(sta_def) { + printf("%s%s", sta_string.c_str(), string(digits, 'X').c_str()); + } else { + printf("%s", string(2*digits, ' ').c_str()); + } + printf(" "); + if(addr % 8 == 7) printf("\n"); + } + printf("\n"); + //log_abort(); + } + void clear_range(addr_t begin_addr, addr_t end_addr) { + for(auto addr = begin_addr; addr != end_addr; addr++) + reference.erase(addr); + state.clear_range(begin_addr, end_addr); + check(); + } + void insert_concatenated(addr_t addr, RTLIL::Const const &values) { + addr_t words = ((addr_t) values.size() + data_width - 1) / data_width; + for(addr_t i = 0; i < words; i++) { + reference.erase(addr + i); + reference.emplace(addr + i, values.extract(i * data_width, data_width)); + } + state.insert_concatenated(addr, values); + check(); + } + template void run(Rnd &rnd, int n) { + std::uniform_int_distribution addr_dist(0, (1< length_dist(10); + std::uniform_int_distribution data_dist(0, ((uint64_t)1< 0) { + addr_t low = addr_dist(rnd); + //addr_t length = std::min((1< high) std::swap(low, high); + if((rnd() & 7) == 0) { + log_debug("clear %.2x to %.2x\n", (int)low, (int)high); + clear_range(low, high + 1); + } else { + log_debug("insert %.2x to %.2x\n", (int)low, (int)high); + RTLIL::Const values; + for(addr_t addr = low; addr <= high; addr++) { + RTLIL::Const word(data_dist(rnd), data_width); + values.bits().insert(values.bits().end(), word.begin(), word.end()); + } + insert_concatenated(low, values); + } + } + } + +}; + +struct FunctionalTestGeneric : public Pass +{ + FunctionalTestGeneric() : Pass("test_generic", "test the generic compute graph") {} + + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log("TODO: add help message\n"); + log("\n"); + } + + void execute(std::vector args, RTLIL::Design *design) override + { + log_header(design, "Executing Test Generic.\n"); + + size_t argidx = 1; + extra_args(args, argidx, design); +/* + MemContentsTest test(8, 16); + + std::random_device seed_dev; + std::mt19937 rnd(23); //seed_dev()); + test.run(rnd, 1000); +*/ + + for (auto module : design->selected_modules()) { + log("Dumping module `%s'.\n", module->name.c_str()); + auto fir = Functional::IR::from_module(module); + for(auto node : fir) + std::cout << RTLIL::unescape_id(node.name()) << " = " << node.to_string([](auto n) { return RTLIL::unescape_id(n.name()); }) << "\n"; + for(auto output : fir.all_outputs()) + std::cout << RTLIL::unescape_id(output->kind) << " " << RTLIL::unescape_id(output->name) << " = " << RTLIL::unescape_id(output->value().name()) << "\n"; + for(auto state : fir.all_states()) + std::cout << RTLIL::unescape_id(state->kind) << " " << RTLIL::unescape_id(state->name) << " = " << RTLIL::unescape_id(state->next_value().name()) << "\n"; + } + } +} FunctionalCxxBackend; + +PRIVATE_NAMESPACE_END diff --git a/backends/intersynth/intersynth.cc b/backends/intersynth/intersynth.cc index 59173c4a2..dcf107de3 100644 --- a/backends/intersynth/intersynth.cc +++ b/backends/intersynth/intersynth.cc @@ -176,11 +176,11 @@ struct IntersynthBackend : public Backend { } } for (auto ¶m : cell->parameters) { - celltype_code += stringf(" cfg:%d %s", int(param.second.bits.size()), log_id(param.first)); - if (param.second.bits.size() != 32) { + celltype_code += stringf(" cfg:%d %s", int(param.second.size()), log_id(param.first)); + if (param.second.size() != 32) { node_code += stringf(" %s '", log_id(param.first)); - for (int i = param.second.bits.size()-1; i >= 0; i--) - node_code += param.second.bits[i] == State::S1 ? "1" : "0"; + for (int i = param.second.size()-1; i >= 0; i--) + node_code += param.second[i] == State::S1 ? "1" : "0"; } else node_code += stringf(" %s 0x%x", log_id(param.first), param.second.as_int()); } diff --git a/backends/rtlil/rtlil_backend.cc b/backends/rtlil/rtlil_backend.cc index 032954d8c..462401fb6 100644 --- a/backends/rtlil/rtlil_backend.cc +++ b/backends/rtlil/rtlil_backend.cc @@ -33,13 +33,13 @@ YOSYS_NAMESPACE_BEGIN void RTLIL_BACKEND::dump_const(std::ostream &f, const RTLIL::Const &data, int width, int offset, bool autoint) { if (width < 0) - width = data.bits.size() - offset; - if ((data.flags & RTLIL::CONST_FLAG_STRING) == 0 || width != (int)data.bits.size()) { + width = data.size() - offset; + if ((data.flags & RTLIL::CONST_FLAG_STRING) == 0 || width != (int)data.size()) { if (width == 32 && autoint) { int32_t val = 0; for (int i = 0; i < width; i++) { - log_assert(offset+i < (int)data.bits.size()); - switch (data.bits[offset+i]) { + log_assert(offset+i < (int)data.size()); + switch (data[offset+i]) { case State::S0: break; case State::S1: val |= 1 << i; break; default: val = -1; break; @@ -58,8 +58,8 @@ void RTLIL_BACKEND::dump_const(std::ostream &f, const RTLIL::Const &data, int wi f << "x"; } else { for (int i = offset+width-1; i >= offset; i--) { - log_assert(i < (int)data.bits.size()); - switch (data.bits[i]) { + log_assert(i < (int)data.size()); + switch (data[i]) { case State::S0: f << stringf("0"); break; case State::S1: f << stringf("1"); break; case RTLIL::Sx: f << stringf("x"); break; @@ -125,6 +125,10 @@ void RTLIL_BACKEND::dump_wire(std::ostream &f, std::string indent, const RTLIL:: dump_const(f, it.second); f << stringf("\n"); } + if (wire->driverCell_) { + f << stringf("%s" "# driver %s %s\n", indent.c_str(), + wire->driverCell()->name.c_str(), wire->driverPort().c_str()); + } f << stringf("%s" "wire ", indent.c_str()); if (wire->width != 1) f << stringf("width %d ", wire->width); diff --git a/backends/simplec/simplec.cc b/backends/simplec/simplec.cc index e283dcf7c..e70c62a71 100644 --- a/backends/simplec/simplec.cc +++ b/backends/simplec/simplec.cc @@ -657,7 +657,7 @@ struct SimplecWorker { SigSpec sig = sigmaps.at(module)(w); Const val = w->attributes.at(ID::init); - val.bits.resize(GetSize(sig), State::Sx); + val.bits().resize(GetSize(sig), State::Sx); for (int i = 0; i < GetSize(sig); i++) if (val[i] == State::S0 || val[i] == State::S1) { diff --git a/backends/smt2/smt2.cc b/backends/smt2/smt2.cc index c702d5e7e..55c5c03f5 100644 --- a/backends/smt2/smt2.cc +++ b/backends/smt2/smt2.cc @@ -1077,14 +1077,14 @@ struct Smt2Worker RTLIL::SigSpec sig = sigmap(wire); Const val = wire->attributes.at(ID::init); - val.bits.resize(GetSize(sig), State::Sx); + val.bits().resize(GetSize(sig), State::Sx); if (bvmode && GetSize(sig) > 1) { Const mask(State::S1, GetSize(sig)); bool use_mask = false; for (int i = 0; i < GetSize(sig); i++) if (val[i] != State::S0 && val[i] != State::S1) { - val[i] = State::S0; - mask[i] = State::S0; + val.bits()[i] = State::S0; + mask.bits()[i] = State::S0; use_mask = true; } if (use_mask) @@ -1359,10 +1359,10 @@ struct Smt2Worker for (int k = 0; k < GetSize(initword); k++) { if (initword[k] == State::S0 || initword[k] == State::S1) { gen_init_constr = true; - initmask[k] = State::S1; + initmask.bits()[k] = State::S1; } else { - initmask[k] = State::S0; - initword[k] = State::S0; + initmask.bits()[k] = State::S0; + initword.bits()[k] = State::S0; } } diff --git a/backends/smt2/smtbmc.py b/backends/smt2/smtbmc.py index c3bdcebbe..d17bad3fb 100644 --- a/backends/smt2/smtbmc.py +++ b/backends/smt2/smtbmc.py @@ -1454,6 +1454,10 @@ def write_trace(steps_start, steps_stop, index, allregs=False): if outywfile is not None: write_yw_trace(steps, index, allregs) +def escape_path_segment(segment): + if "." in segment: + return f"\\{segment} " + return segment def print_failed_asserts_worker(mod, state, path, extrainfo, infomap, infokey=()): assert mod in smt.modinfo @@ -1464,7 +1468,8 @@ def print_failed_asserts_worker(mod, state, path, extrainfo, infomap, infokey=() for cellname, celltype in smt.modinfo[mod].cells.items(): cell_infokey = (mod, cellname, infokey) - if print_failed_asserts_worker(celltype, "(|%s_h %s| %s)" % (mod, cellname, state), path + "." + cellname, extrainfo, infomap, cell_infokey): + cell_path = path + "." + escape_path_segment(cellname) + if print_failed_asserts_worker(celltype, "(|%s_h %s| %s)" % (mod, cellname, state), cell_path, extrainfo, infomap, cell_infokey): found_failed_assert = True for assertfun, assertinfo in smt.modinfo[mod].asserts.items(): @@ -1497,7 +1502,7 @@ def print_anyconsts_worker(mod, state, path): assert mod in smt.modinfo for cellname, celltype in smt.modinfo[mod].cells.items(): - print_anyconsts_worker(celltype, "(|%s_h %s| %s)" % (mod, cellname, state), path + "." + cellname) + print_anyconsts_worker(celltype, "(|%s_h %s| %s)" % (mod, cellname, state), path + "." + escape_path_segment(cellname)) for fun, info in smt.modinfo[mod].anyconsts.items(): if info[1] is None: @@ -1517,18 +1522,21 @@ def print_anyconsts(state): print_anyconsts_worker(topmod, "s%d" % state, topmod) -def get_cover_list(mod, base): +def get_cover_list(mod, base, path=None): + path = path or mod assert mod in smt.modinfo cover_expr = list() + # A tuple of path and cell name cover_desc = list() for expr, desc in smt.modinfo[mod].covers.items(): cover_expr.append("(ite (|%s| %s) #b1 #b0)" % (expr, base)) - cover_desc.append(desc) + cover_desc.append((path, desc)) for cell, submod in smt.modinfo[mod].cells.items(): - e, d = get_cover_list(submod, "(|%s_h %s| %s)" % (mod, cell, base)) + cell_path = path + "." + escape_path_segment(cell) + e, d = get_cover_list(submod, "(|%s_h %s| %s)" % (mod, cell, base), cell_path) cover_expr += e cover_desc += d @@ -1544,7 +1552,8 @@ def get_assert_map(mod, base, path, key_base=()): assert_map[(expr, key_base)] = ("(|%s| %s)" % (expr, base), path, desc) for cell, submod in smt.modinfo[mod].cells.items(): - assert_map.update(get_assert_map(submod, "(|%s_h %s| %s)" % (mod, cell, base), path + "." + cell, (mod, cell, key_base))) + cell_path = path + "." + escape_path_segment(cell) + assert_map.update(get_assert_map(submod, "(|%s_h %s| %s)" % (mod, cell, base), cell_path, (mod, cell, key_base))) return assert_map @@ -1903,7 +1912,9 @@ elif covermode: new_cover_mask.append(cover_mask[i]) continue - print_msg("Reached cover statement at %s in step %d." % (cover_desc[i], step)) + path = cover_desc[i][0] + name = cover_desc[i][1] + print_msg("Reached cover statement in step %d at %s: %s" % (step, path, name)) new_cover_mask.append("0") cover_mask = "".join(new_cover_mask) @@ -1933,7 +1944,7 @@ elif covermode: if "1" in cover_mask: for i in range(len(cover_mask)): if cover_mask[i] == "1": - print_msg("Unreached cover statement at %s." % cover_desc[i]) + print_msg("Unreached cover statement at %s: %s" % (cover_desc[i][0], cover_desc[i][1])) else: # not tempind, covermode active_assert_keys = get_assert_keys() diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index 31bbc996f..04b87b40d 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -191,7 +191,7 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o { bool set_signed = (data.flags & RTLIL::CONST_FLAG_SIGNED) != 0; if (width < 0) - width = data.bits.size() - offset; + width = data.size() - offset; if (width == 0) { // See IEEE 1364-2005 Clause 5.1.14. f << "{0{1'b0}}"; @@ -199,14 +199,14 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o } if (nostr) goto dump_hex; - if ((data.flags & RTLIL::CONST_FLAG_STRING) == 0 || width != (int)data.bits.size()) { + if ((data.flags & RTLIL::CONST_FLAG_STRING) == 0 || width != (int)data.size()) { if (width == 32 && !no_decimal && !nodec) { int32_t val = 0; for (int i = offset+width-1; i >= offset; i--) { - log_assert(i < (int)data.bits.size()); - if (data.bits[i] != State::S0 && data.bits[i] != State::S1) + log_assert(i < (int)data.size()); + if (data[i] != State::S0 && data[i] != State::S1) goto dump_hex; - if (data.bits[i] == State::S1) + if (data[i] == State::S1) val |= 1 << (i - offset); } if (decimal) @@ -221,8 +221,8 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o goto dump_bin; vector bin_digits, hex_digits; for (int i = offset; i < offset+width; i++) { - log_assert(i < (int)data.bits.size()); - switch (data.bits[i]) { + log_assert(i < (int)data.size()); + switch (data[i]) { case State::S0: bin_digits.push_back('0'); break; case State::S1: bin_digits.push_back('1'); break; case RTLIL::Sx: bin_digits.push_back('x'); break; @@ -275,8 +275,8 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o if (width == 0) f << stringf("0"); for (int i = offset+width-1; i >= offset; i--) { - log_assert(i < (int)data.bits.size()); - switch (data.bits[i]) { + log_assert(i < (int)data.size()); + switch (data[i]) { case State::S0: f << stringf("0"); break; case State::S1: f << stringf("1"); break; case RTLIL::Sx: f << stringf("x"); break; @@ -318,10 +318,10 @@ void dump_reg_init(std::ostream &f, SigSpec sig) for (auto bit : active_sigmap(sig)) { if (active_initdata.count(bit)) { - initval.bits.push_back(active_initdata.at(bit)); + initval.bits().push_back(active_initdata.at(bit)); gotinit = true; } else { - initval.bits.push_back(State::Sx); + initval.bits().push_back(State::Sx); } } @@ -751,7 +751,7 @@ void dump_memory(std::ostream &f, std::string indent, Mem &mem) if (port.wide_log2) { Const addr_lo; for (int i = 0; i < port.wide_log2; i++) - addr_lo.bits.push_back(State(sub >> i & 1)); + addr_lo.bits().push_back(State(sub >> i & 1)); os << "{"; os << temp_id; os << ", "; diff --git a/docs/Makefile b/docs/Makefile index 701157ee6..6dbf6f490 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -2,7 +2,7 @@ # # You can set these variables from the command line. -SPHINXOPTS = +SPHINXOPTS = -W --keep-going SPHINXBUILD = sphinx-build PAPER = BUILDDIR = build @@ -238,7 +238,7 @@ Makefile-%: FORCE $(MAKE) -C $(@D) $(*F) CODE_EXAMPLES := $(wildcard source/code_examples/*/Makefile) -TEST_EXAMPLES := $(addsuffix -all,$(CODE_EXAMPLES)) +TEST_EXAMPLES := $(addsuffix -examples,$(CODE_EXAMPLES)) CLEAN_EXAMPLES := $(addsuffix -clean,$(CODE_EXAMPLES)) test-examples: $(TEST_EXAMPLES) clean-examples: $(CLEAN_EXAMPLES) @@ -250,6 +250,7 @@ test-macros: .PHONY: images images: $(MAKE) -C source/_images + $(MAKE) -C source/_images convert .PHONY: reqs reqs: diff --git a/docs/source/_images/Makefile b/docs/source/_images/Makefile index 955805f9c..cc00050f1 100644 --- a/docs/source/_images/Makefile +++ b/docs/source/_images/Makefile @@ -1,4 +1,4 @@ -all: examples all_tex tidy +all: examples all_tex # set a fake time in pdf generation to prevent unnecessary differences in output FAKETIME := TZ='Z' faketime -f '2022-01-01 00:00:00 x0,001' @@ -8,24 +8,22 @@ FAKETIME := TZ='Z' faketime -f '2022-01-01 00:00:00 x0,001' CODE_EXAMPLES := ../code_examples/*/Makefile examples: $(CODE_EXAMPLES) -# target to convert specified dot file(s) +# target to convert all dot files +# needs to be run *after* examples, otherwise no dot files will be found .PHONY: convert -TARG_DOT ?= -convert: $(TARG_DOT:.dot=.pdf) $(TARG_DOT:.dot=.svg) +DOT_FILES := $(shell find . -name *.dot) +convert: $(DOT_FILES:.dot=.pdf) $(DOT_FILES:.dot=.svg) -# use empty FORCE target because .PHONY ignores % expansion, using find allows -# us to generate everything in one pass, since we don't know all of the possible -# outputs until the sub-makes run +# use empty FORCE target because .PHONY ignores % expansion FORCE: ../%/Makefile: FORCE @make -C $(@D) dots @mkdir -p $* - @find $(@D) -name *.dot -exec cp -u {} -t $* \; - @find $* -name *.dot -printf "%p " | xargs -i make --no-print-directory convert TARG_DOT="{}" + @find $(@D) -name *.dot -exec rsync -t {} $* \; # find and build all tex files .PHONY: all_tex -TEX_FILES := $(wildcard **/*.tex) +TEX_FILES := $(shell find . -name *.tex) all_tex: $(TEX_FILES:.tex=.pdf) $(TEX_FILES:.tex=.svg) %.pdf: %.dot diff --git a/docs/source/code_examples/.gitignore b/docs/source/code_examples/.gitignore index b4a858a01..0eca4be80 100644 --- a/docs/source/code_examples/.gitignore +++ b/docs/source/code_examples/.gitignore @@ -1,2 +1,5 @@ *.dot *.pdf +*.out +*.log +*.stat diff --git a/docs/source/code_examples/extensions/Makefile b/docs/source/code_examples/extensions/Makefile index 8b5fa106a..2e621d70b 100644 --- a/docs/source/code_examples/extensions/Makefile +++ b/docs/source/code_examples/extensions/Makefile @@ -2,9 +2,10 @@ PROGRAM_PREFIX := YOSYS ?= ../../../../$(PROGRAM_PREFIX)yosys -.PHONY: all dots -all: dots test0.log test1.log test2.log +.PHONY: all dots examples +all: dots examples dots: test1.dot +examples: test0.log test1.log test2.log CXXFLAGS=$(shell $(YOSYS)-config --cxxflags) DATDIR=$(shell $(YOSYS)-config --datdir) diff --git a/docs/source/code_examples/fifo/Makefile b/docs/source/code_examples/fifo/Makefile index 0a1186a62..4836dac06 100644 --- a/docs/source/code_examples/fifo/Makefile +++ b/docs/source/code_examples/fifo/Makefile @@ -10,8 +10,10 @@ MAPDOT_NAMES += rdata_map_ffs rdata_map_luts rdata_map_cells DOTS := $(addsuffix .dot,$(DOT_NAMES)) MAPDOTS := $(addsuffix .dot,$(MAPDOT_NAMES)) -all: dots fifo.out fifo.stat +.PHONY: all dots examples +all: dots examples dots: $(DOTS) $(MAPDOTS) +examples: fifo.out fifo.stat $(DOTS) fifo.out: fifo.v fifo.ys $(YOSYS) fifo.ys -l fifo.out -Q -T @@ -22,3 +24,4 @@ $(MAPDOTS) fifo.stat: fifo.v fifo_map.ys .PHONY: clean clean: rm -f *.dot + rm -f fifo.out fifo.stat diff --git a/docs/source/code_examples/fifo/fifo.out b/docs/source/code_examples/fifo/fifo.out deleted file mode 100644 index ac132ee6c..000000000 --- a/docs/source/code_examples/fifo/fifo.out +++ /dev/null @@ -1,425 +0,0 @@ - --- Executing script file `fifo.ys' -- -$ yosys fifo.v - --- Parsing `fifo.v' using frontend ` -vlog2k' -- - -1. Executing Verilog-2005 frontend: fifo.v -Parsing Verilog input from `fifo.v' to AST representation. -Storing AST representation for module `$abstract\addr_gen'. -Storing AST representation for module `$abstract\fifo'. -Successfully finished Verilog frontend. -echo on - -yosys> hierarchy -top addr_gen - -2. Executing HIERARCHY pass (managing design hierarchy). - -3. Executing AST frontend in derive mode using pre-parsed AST for module `\addr_gen'. -Generating RTLIL representation for module `\addr_gen'. - -3.1. Analyzing design hierarchy.. -Top module: \addr_gen - -3.2. Analyzing design hierarchy.. -Top module: \addr_gen -Removing unused module `$abstract\fifo'. -Removing unused module `$abstract\addr_gen'. -Removed 2 unused modules. - -yosys> select -module addr_gen - -yosys [addr_gen]> select -list -addr_gen -addr_gen/$1\addr[7:0] -addr_gen/$add$fifo.v:19$3_Y -addr_gen/$eq$fifo.v:16$2_Y -addr_gen/$0\addr[7:0] -addr_gen/addr -addr_gen/rst -addr_gen/clk -addr_gen/en -addr_gen/$add$fifo.v:19$3 -addr_gen/$eq$fifo.v:16$2 -addr_gen/$proc$fifo.v:0$4 -addr_gen/$proc$fifo.v:12$1 - -yosys [addr_gen]> select t:* - -yosys [addr_gen]*> select -list -addr_gen/$add$fifo.v:19$3 -addr_gen/$eq$fifo.v:16$2 - -yosys [addr_gen]*> select -set new_cells % - -yosys [addr_gen]*> select -clear - -yosys> show -format dot -prefix addr_gen_show addr_gen - -4. Generating Graphviz representation of design. -Writing dot description to `addr_gen_show.dot'. -Dumping module addr_gen to page 1. - -yosys> show -format dot -prefix new_cells_show -notitle @new_cells - -5. Generating Graphviz representation of design. -Writing dot description to `new_cells_show.dot'. -Dumping selected parts of module addr_gen to page 1. - -yosys> show -color maroon3 @new_cells -color cornflowerblue p:* -notitle -format dot -prefix addr_gen_hier - -6. Generating Graphviz representation of design. -Writing dot description to `addr_gen_hier.dot'. -Dumping module addr_gen to page 1. - -yosys> proc -noopt - -7. Executing PROC pass (convert processes to netlists). - -yosys> proc_clean - -7.1. Executing PROC_CLEAN pass (remove empty switches from decision trees). -Cleaned up 0 empty switches. - -yosys> proc_rmdead - -7.2. Executing PROC_RMDEAD pass (remove dead branches from decision trees). -Marked 2 switch rules as full_case in process $proc$fifo.v:12$1 in module addr_gen. -Removed a total of 0 dead cases. - -yosys> proc_prune - -7.3. Executing PROC_PRUNE pass (remove redundant assignments in processes). -Removed 0 redundant assignments. -Promoted 1 assignment to connection. - -yosys> proc_init - -7.4. Executing PROC_INIT pass (extract init attributes). -Found init rule in `\addr_gen.$proc$fifo.v:0$4'. - Set init value: \addr = 8'00000000 - -yosys> proc_arst - -7.5. Executing PROC_ARST pass (detect async resets in processes). -Found async reset \rst in `\addr_gen.$proc$fifo.v:12$1'. - -yosys> proc_rom - -7.6. Executing PROC_ROM pass (convert switches to ROMs). -Converted 0 switches. - - -yosys> proc_mux - -7.7. Executing PROC_MUX pass (convert decision trees to multiplexers). -Creating decoders for process `\addr_gen.$proc$fifo.v:0$4'. -Creating decoders for process `\addr_gen.$proc$fifo.v:12$1'. - 1/1: $0\addr[7:0] - -yosys> proc_dlatch - -7.8. Executing PROC_DLATCH pass (convert process syncs to latches). - -yosys> proc_dff - -7.9. Executing PROC_DFF pass (convert process syncs to FFs). -Creating register for signal `\addr_gen.\addr' using process `\addr_gen.$proc$fifo.v:12$1'. - created $adff cell `$procdff$10' with positive edge clock and positive level reset. - -yosys> proc_memwr - -7.10. Executing PROC_MEMWR pass (convert process memory writes to cells). - -yosys> proc_clean - -7.11. Executing PROC_CLEAN pass (remove empty switches from decision trees). -Removing empty process `addr_gen.$proc$fifo.v:0$4'. -Found and cleaned up 2 empty switches in `\addr_gen.$proc$fifo.v:12$1'. -Removing empty process `addr_gen.$proc$fifo.v:12$1'. -Cleaned up 2 empty switches. - -yosys> select -set new_cells t:$mux t:*dff - -yosys> show -color maroon3 @new_cells -notitle -format dot -prefix addr_gen_proc - -8. Generating Graphviz representation of design. -Writing dot description to `addr_gen_proc.dot'. -Dumping module addr_gen to page 1. - -yosys> opt_expr - -9. Executing OPT_EXPR pass (perform const folding). -Optimizing module addr_gen. - -yosys> clean -Removed 0 unused cells and 4 unused wires. - -yosys> select -set new_cells t:$eq - -yosys> show -color cornflowerblue @new_cells -notitle -format dot -prefix addr_gen_clean - -10. Generating Graphviz representation of design. -Writing dot description to `addr_gen_clean.dot'. -Dumping module addr_gen to page 1. - -yosys> design -reset - -yosys> read_verilog fifo.v - -11. Executing Verilog-2005 frontend: fifo.v -Parsing Verilog input from `fifo.v' to AST representation. -Generating RTLIL representation for module `\addr_gen'. -Generating RTLIL representation for module `\fifo'. -Successfully finished Verilog frontend. - -yosys> hierarchy -check -top fifo - -12. Executing HIERARCHY pass (managing design hierarchy). - -12.1. Analyzing design hierarchy.. -Top module: \fifo -Used module: \addr_gen -Parameter \MAX_DATA = 256 - -12.2. Executing AST frontend in derive mode using pre-parsed AST for module `\addr_gen'. -Parameter \MAX_DATA = 256 -Generating RTLIL representation for module `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000'. -Parameter \MAX_DATA = 256 -Found cached RTLIL representation for module `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000'. - -12.3. Analyzing design hierarchy.. -Top module: \fifo -Used module: $paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000 - -12.4. Analyzing design hierarchy.. -Top module: \fifo -Used module: $paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000 -Removing unused module `\addr_gen'. -Removed 1 unused modules. - -yosys> proc - -13. Executing PROC pass (convert processes to netlists). - -yosys> proc_clean - -13.1. Executing PROC_CLEAN pass (remove empty switches from decision trees). -Cleaned up 0 empty switches. - -yosys> proc_rmdead - -13.2. Executing PROC_RMDEAD pass (remove dead branches from decision trees). -Marked 2 switch rules as full_case in process $proc$fifo.v:62$24 in module fifo. -Marked 1 switch rules as full_case in process $proc$fifo.v:36$16 in module fifo. -Marked 2 switch rules as full_case in process $proc$fifo.v:12$32 in module $paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000. -Removed a total of 0 dead cases. - -yosys> proc_prune - -13.3. Executing PROC_PRUNE pass (remove redundant assignments in processes). -Removed 0 redundant assignments. -Promoted 6 assignments to connections. - -yosys> proc_init - -13.4. Executing PROC_INIT pass (extract init attributes). -Found init rule in `\fifo.$proc$fifo.v:0$31'. - Set init value: \count = 9'000000000 -Found init rule in `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000.$proc$fifo.v:0$35'. - Set init value: \addr = 8'00000000 - -yosys> proc_arst - -13.5. Executing PROC_ARST pass (detect async resets in processes). -Found async reset \rst in `\fifo.$proc$fifo.v:62$24'. -Found async reset \rst in `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000.$proc$fifo.v:12$32'. - -yosys> proc_rom - -13.6. Executing PROC_ROM pass (convert switches to ROMs). -Converted 0 switches. - - -yosys> proc_mux - -13.7. Executing PROC_MUX pass (convert decision trees to multiplexers). -Creating decoders for process `\fifo.$proc$fifo.v:0$31'. -Creating decoders for process `\fifo.$proc$fifo.v:62$24'. - 1/1: $0\count[8:0] -Creating decoders for process `\fifo.$proc$fifo.v:36$16'. - 1/3: $1$memwr$\data$fifo.v:38$15_EN[7:0]$22 - 2/3: $1$memwr$\data$fifo.v:38$15_DATA[7:0]$21 - 3/3: $1$memwr$\data$fifo.v:38$15_ADDR[7:0]$20 -Creating decoders for process `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000.$proc$fifo.v:0$35'. -Creating decoders for process `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000.$proc$fifo.v:12$32'. - 1/1: $0\addr[7:0] - -yosys> proc_dlatch - -13.8. Executing PROC_DLATCH pass (convert process syncs to latches). - -yosys> proc_dff - -13.9. Executing PROC_DFF pass (convert process syncs to FFs). -Creating register for signal `\fifo.\count' using process `\fifo.$proc$fifo.v:62$24'. - created $adff cell `$procdff$55' with positive edge clock and positive level reset. -Creating register for signal `\fifo.\rdata' using process `\fifo.$proc$fifo.v:36$16'. - created $dff cell `$procdff$56' with positive edge clock. -Creating register for signal `\fifo.$memwr$\data$fifo.v:38$15_ADDR' using process `\fifo.$proc$fifo.v:36$16'. - created $dff cell `$procdff$57' with positive edge clock. -Creating register for signal `\fifo.$memwr$\data$fifo.v:38$15_DATA' using process `\fifo.$proc$fifo.v:36$16'. - created $dff cell `$procdff$58' with positive edge clock. -Creating register for signal `\fifo.$memwr$\data$fifo.v:38$15_EN' using process `\fifo.$proc$fifo.v:36$16'. - created $dff cell `$procdff$59' with positive edge clock. -Creating register for signal `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000.\addr' using process `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000.$proc$fifo.v:12$32'. - created $adff cell `$procdff$60' with positive edge clock and positive level reset. - -yosys> proc_memwr - -13.10. Executing PROC_MEMWR pass (convert process memory writes to cells). - -yosys> proc_clean - -13.11. Executing PROC_CLEAN pass (remove empty switches from decision trees). -Removing empty process `fifo.$proc$fifo.v:0$31'. -Found and cleaned up 2 empty switches in `\fifo.$proc$fifo.v:62$24'. -Removing empty process `fifo.$proc$fifo.v:62$24'. -Found and cleaned up 1 empty switch in `\fifo.$proc$fifo.v:36$16'. -Removing empty process `fifo.$proc$fifo.v:36$16'. -Removing empty process `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000.$proc$fifo.v:0$35'. -Found and cleaned up 2 empty switches in `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000.$proc$fifo.v:12$32'. -Removing empty process `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000.$proc$fifo.v:12$32'. -Cleaned up 5 empty switches. - -yosys> opt_expr -keepdc - -13.12. Executing OPT_EXPR pass (perform const folding). -Optimizing module fifo. -Optimizing module $paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000. - -yosys> select -set new_cells t:$memrd - -yosys> show -color maroon3 c:fifo_reader -color cornflowerblue @new_cells -notitle -format dot -prefix rdata_proc o:rdata %ci* - -14. Generating Graphviz representation of design. -Writing dot description to `rdata_proc.dot'. -Dumping selected parts of module fifo to page 1. - -yosys> flatten - -15. Executing FLATTEN pass (flatten design). -Deleting now unused module $paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000. - - -yosys> clean -Removed 3 unused cells and 25 unused wires. - -yosys> select -set rdata_path o:rdata %ci* - -yosys> select -set new_cells @rdata_path o:rdata %ci3 %d i:* %d - -yosys> show -color maroon3 @new_cells -notitle -format dot -prefix rdata_flat @rdata_path - -16. Generating Graphviz representation of design. -Writing dot description to `rdata_flat.dot'. -Dumping selected parts of module fifo to page 1. - -yosys> opt_dff - -17. Executing OPT_DFF pass (perform DFF optimizations). -Adding EN signal on $procdff$55 ($adff) from module fifo (D = $0\count[8:0], Q = \count). -Adding EN signal on $flatten\fifo_writer.$procdff$60 ($adff) from module fifo (D = $flatten\fifo_writer.$procmux$51_Y, Q = \fifo_writer.addr). -Adding EN signal on $flatten\fifo_reader.$procdff$60 ($adff) from module fifo (D = $flatten\fifo_reader.$procmux$51_Y, Q = \fifo_reader.addr). - -yosys> select -set new_cells t:$adffe - -yosys> show -color maroon3 @new_cells -notitle -format dot -prefix rdata_adffe o:rdata %ci* - -18. Generating Graphviz representation of design. -Writing dot description to `rdata_adffe.dot'. -Dumping selected parts of module fifo to page 1. - -yosys> wreduce - -19. Executing WREDUCE pass (reducing word size of cells). -Removed top 31 bits (of 32) from port B of cell fifo.$add$fifo.v:66$27 ($add). -Removed top 23 bits (of 32) from port Y of cell fifo.$add$fifo.v:66$27 ($add). -Removed top 31 bits (of 32) from port B of cell fifo.$sub$fifo.v:68$30 ($sub). -Removed top 23 bits (of 32) from port Y of cell fifo.$sub$fifo.v:68$30 ($sub). -Removed top 1 bits (of 2) from port B of cell fifo.$auto$opt_dff.cc:195:make_patterns_logic$66 ($ne). -Removed cell fifo.$flatten\fifo_writer.$procmux$53 ($mux). -Removed top 31 bits (of 32) from port B of cell fifo.$flatten\fifo_writer.$add$fifo.v:19$34 ($add). -Removed top 24 bits (of 32) from port Y of cell fifo.$flatten\fifo_writer.$add$fifo.v:19$34 ($add). -Removed cell fifo.$flatten\fifo_reader.$procmux$53 ($mux). -Removed top 31 bits (of 32) from port B of cell fifo.$flatten\fifo_reader.$add$fifo.v:19$34 ($add). -Removed top 24 bits (of 32) from port Y of cell fifo.$flatten\fifo_reader.$add$fifo.v:19$34 ($add). -Removed top 23 bits (of 32) from wire fifo.$add$fifo.v:66$27_Y. -Removed top 24 bits (of 32) from wire fifo.$flatten\fifo_reader.$add$fifo.v:19$34_Y. - -yosys> show -notitle -format dot -prefix rdata_wreduce o:rdata %ci* - -20. Generating Graphviz representation of design. -Writing dot description to `rdata_wreduce.dot'. -Dumping selected parts of module fifo to page 1. - -yosys> opt_clean - -21. Executing OPT_CLEAN pass (remove unused cells and wires). -Finding unused cells or wires in module \fifo.. -Removed 0 unused cells and 4 unused wires. - - -yosys> memory_dff - -22. Executing MEMORY_DFF pass (merging $dff cells to $memrd). -Checking read port `\data'[0] in module `\fifo': merging output FF to cell. - Write port 0: non-transparent. - -yosys> select -set new_cells t:$memrd_v2 - -yosys> show -color maroon3 @new_cells -notitle -format dot -prefix rdata_memrdv2 o:rdata %ci* - -23. Generating Graphviz representation of design. -Writing dot description to `rdata_memrdv2.dot'. -Dumping selected parts of module fifo to page 1. - -yosys> alumacc - -24. Executing ALUMACC pass (create $alu and $macc cells). -Extracting $alu and $macc cells in module fifo: - creating $macc model for $add$fifo.v:66$27 ($add). - creating $macc model for $flatten\fifo_reader.$add$fifo.v:19$34 ($add). - creating $macc model for $flatten\fifo_writer.$add$fifo.v:19$34 ($add). - creating $macc model for $sub$fifo.v:68$30 ($sub). - creating $alu model for $macc $sub$fifo.v:68$30. - creating $alu model for $macc $flatten\fifo_writer.$add$fifo.v:19$34. - creating $alu model for $macc $flatten\fifo_reader.$add$fifo.v:19$34. - creating $alu model for $macc $add$fifo.v:66$27. - creating $alu cell for $add$fifo.v:66$27: $auto$alumacc.cc:485:replace_alu$80 - creating $alu cell for $flatten\fifo_reader.$add$fifo.v:19$34: $auto$alumacc.cc:485:replace_alu$83 - creating $alu cell for $flatten\fifo_writer.$add$fifo.v:19$34: $auto$alumacc.cc:485:replace_alu$86 - creating $alu cell for $sub$fifo.v:68$30: $auto$alumacc.cc:485:replace_alu$89 - created 4 $alu and 0 $macc cells. - -yosys> select -set new_cells t:$alu t:$macc - -yosys> show -color maroon3 @new_cells -notitle -format dot -prefix rdata_alumacc o:rdata %ci* - -25. Generating Graphviz representation of design. -Writing dot description to `rdata_alumacc.dot'. -Dumping selected parts of module fifo to page 1. - -yosys> memory_collect - -26. Executing MEMORY_COLLECT pass (generating $mem cells). - -yosys> select -set new_cells t:$mem_v2 - -yosys> select -set rdata_path @new_cells %ci*:-$mem_v2[WR_DATA,WR_ADDR,WR_EN] @new_cells %co* %% - -yosys> show -color maroon3 @new_cells -notitle -format dot -prefix rdata_coarse @rdata_path - -27. Generating Graphviz representation of design. -Writing dot description to `rdata_coarse.dot'. -Dumping selected parts of module fifo to page 1. diff --git a/docs/source/code_examples/fifo/fifo.stat b/docs/source/code_examples/fifo/fifo.stat deleted file mode 100644 index 263c618e3..000000000 --- a/docs/source/code_examples/fifo/fifo.stat +++ /dev/null @@ -1,57 +0,0 @@ - -yosys> stat - -2. Printing statistics. - -=== fifo === - - Number of wires: 28 - Number of wire bits: 219 - Number of public wires: 9 - Number of public wire bits: 45 - Number of memories: 1 - Number of memory bits: 2048 - Number of processes: 3 - Number of cells: 9 - $add 1 - $logic_and 2 - $logic_not 2 - $memrd 1 - $sub 1 - addr_gen 2 - -=== addr_gen === - - Number of wires: 8 - Number of wire bits: 60 - Number of public wires: 4 - Number of public wire bits: 11 - Number of memories: 0 - Number of memory bits: 0 - Number of processes: 2 - Number of cells: 2 - $add 1 - $eq 1 - - -yosys> stat -top fifo - -17. Printing statistics. - -=== fifo === - - Number of wires: 94 - Number of wire bits: 260 - Number of public wires: 94 - Number of public wire bits: 260 - Number of memories: 0 - Number of memory bits: 0 - Number of processes: 0 - Number of cells: 138 - $scopeinfo 2 - SB_CARRY 26 - SB_DFF 26 - SB_DFFER 25 - SB_LUT4 58 - SB_RAM40_4K 1 - diff --git a/docs/source/code_examples/intro/Makefile b/docs/source/code_examples/intro/Makefile index 009c82c62..31df6b623 100644 --- a/docs/source/code_examples/intro/Makefile +++ b/docs/source/code_examples/intro/Makefile @@ -4,8 +4,10 @@ YOSYS ?= ../../../../$(PROGRAM_PREFIX)yosys DOTS = counter_00.dot counter_01.dot counter_02.dot counter_03.dot -all: dots +.PHONY: all dots examples +all: dots examples dots: $(DOTS) +examples: $(DOTS): counter.v counter.ys mycells.lib $(YOSYS) counter.ys diff --git a/docs/source/code_examples/macc/Makefile b/docs/source/code_examples/macc/Makefile index e93fe0657..f06c623a5 100644 --- a/docs/source/code_examples/macc/Makefile +++ b/docs/source/code_examples/macc/Makefile @@ -4,8 +4,10 @@ YOSYS ?= ../../../../$(PROGRAM_PREFIX)yosys DOTS = macc_simple_xmap.dot macc_xilinx_xmap.dot -all: dots +.PHONY: all dots examples +all: dots examples dots: $(DOTS) +examples: macc_simple_xmap.dot: macc_simple_*.v macc_simple_test.ys $(YOSYS) macc_simple_test.ys diff --git a/docs/source/code_examples/opt/Makefile b/docs/source/code_examples/opt/Makefile index 4cb51e90b..aa7962ca5 100644 --- a/docs/source/code_examples/opt/Makefile +++ b/docs/source/code_examples/opt/Makefile @@ -6,14 +6,14 @@ DOT_NAMES = opt_share opt_muxtree opt_merge opt_expr DOTS := $(addsuffix .dot,$(DOT_NAMES)) -all: dots +.PHONY: all dots examples +all: dots examples dots: $(DOTS) +examples: -%_full.dot: %.ys +%.dot: %.ys $(YOSYS) $< - -%.dot: %_full.dot - gvpack -u $*_full.dot -o $@ + gvpack -u -o $@ $*_full.dot .PHONY: clean clean: diff --git a/docs/source/code_examples/scrambler/Makefile b/docs/source/code_examples/scrambler/Makefile index de475b8b1..a9b56f8d3 100644 --- a/docs/source/code_examples/scrambler/Makefile +++ b/docs/source/code_examples/scrambler/Makefile @@ -2,9 +2,10 @@ PROGRAM_PREFIX := YOSYS ?= ../../../../$(PROGRAM_PREFIX)yosys -.PHONY: all dots -all: dots +.PHONY: all dots examples +all: dots examples dots: scrambler_p01.dot scrambler_p02.dot +examples: scrambler_p01.dot scrambler_p02.dot: scrambler.ys scrambler.v $(YOSYS) scrambler.ys diff --git a/docs/source/code_examples/selections/Makefile b/docs/source/code_examples/selections/Makefile index bb506ff38..46a09060a 100644 --- a/docs/source/code_examples/selections/Makefile +++ b/docs/source/code_examples/selections/Makefile @@ -11,14 +11,15 @@ MEMDEMO_DOTS := $(addsuffix .dot,$(MEMDEMO)) SUBMOD = submod_00 submod_01 submod_02 submod_03 SUBMOD_DOTS := $(addsuffix .dot,$(SUBMOD)) -.PHONY: all dots -all: dots +.PHONY: all dots examples +all: dots examples dots: select.dot $(SUMPROD_DOTS) $(MEMDEMO_DOTS) $(SUBMOD_DOTS) +examples: sumprod.out select.dot: select.v select.ys $(YOSYS) select.ys -$(SUMPROD_DOTS): sumprod.v sumprod.ys +$(SUMPROD_DOTS) sumprod.out: sumprod.v sumprod.ys $(YOSYS) sumprod.ys $(MEMDEMO_DOTS): memdemo.v memdemo.ys @@ -30,3 +31,4 @@ $(SUBMOD_DOTS): memdemo.v submod.ys .PHONY: clean clean: rm -rf *.dot + rm -f sumprod.out diff --git a/docs/source/code_examples/selections/sumprod.out b/docs/source/code_examples/selections/sumprod.out deleted file mode 100644 index f7c259499..000000000 --- a/docs/source/code_examples/selections/sumprod.out +++ /dev/null @@ -1,38 +0,0 @@ - - - attribute \src "sumprod.v:4.21-4.25" - wire width 8 output 5 \prod - - attribute \src "sumprod.v:10.17-10.26" - cell $mul $mul$sumprod.v:10$4 - parameter \A_SIGNED 0 - parameter \A_WIDTH 8 - parameter \B_SIGNED 0 - parameter \B_WIDTH 8 - parameter \Y_WIDTH 8 - connect \A $mul$sumprod.v:10$3_Y - connect \B \c - connect \Y \prod - end - - - attribute \src "sumprod.v:10.17-10.22" - wire width 8 $mul$sumprod.v:10$3_Y - - attribute \src "sumprod.v:3.21-3.22" - wire width 8 input 3 \c - - attribute \src "sumprod.v:4.21-4.25" - wire width 8 output 5 \prod - - attribute \src "sumprod.v:10.17-10.26" - cell $mul $mul$sumprod.v:10$4 - parameter \A_SIGNED 0 - parameter \A_WIDTH 8 - parameter \B_SIGNED 0 - parameter \B_WIDTH 8 - parameter \Y_WIDTH 8 - connect \A $mul$sumprod.v:10$3_Y - connect \B \c - connect \Y \prod - end diff --git a/docs/source/code_examples/show/Makefile b/docs/source/code_examples/show/Makefile index 4b269a4ab..649691299 100644 --- a/docs/source/code_examples/show/Makefile +++ b/docs/source/code_examples/show/Makefile @@ -8,9 +8,10 @@ EXAMPLE_DOTS := $(addsuffix .dot,$(EXAMPLE)) CMOS = cmos_00 cmos_01 CMOS_DOTS := $(addsuffix .dot,$(CMOS)) -.PHONY: all dots -all: dots example.out +.PHONY: all dots examples +all: dots examples dots: splice.dot $(EXAMPLE_DOTS) $(CMOS_DOTS) +examples: example.out splice.dot: splice.v $(YOSYS) -p 'prep -top splice_demo; show -format dot -prefix splice' splice.v @@ -27,3 +28,4 @@ $(CMOS_DOTS): cmos.v cmos.ys .PHONY: clean clean: rm -rf *.dot + rm -f example.out diff --git a/docs/source/code_examples/show/example.out b/docs/source/code_examples/show/example.out deleted file mode 100644 index b8569e640..000000000 --- a/docs/source/code_examples/show/example.out +++ /dev/null @@ -1,54 +0,0 @@ - --- Executing script file `example_lscd.ys' -- - -1. Executing Verilog-2005 frontend: example.v -Parsing Verilog input from `example.v' to AST representation. -Generating RTLIL representation for module `\example'. -Successfully finished Verilog frontend. -echo on - -yosys> ls - -1 modules: - example - -yosys> cd example - -yosys [example]> ls - -8 wires: - $0\y[1:0] - $add$example.v:5$2_Y - $ternary$example.v:5$3_Y - a - b - c - clk - y - -2 cells: - $add$example.v:5$2 - $ternary$example.v:5$3 - -1 processes: - $proc$example.v:3$1 - -yosys [example]> dump $2 - - - attribute \src "example.v:5.22-5.27" - cell $add $add$example.v:5$2 - parameter \Y_WIDTH 2 - parameter \B_WIDTH 1 - parameter \A_WIDTH 1 - parameter \B_SIGNED 0 - parameter \A_SIGNED 0 - connect \Y $add$example.v:5$2_Y - connect \B \b - connect \A \a - end - -yosys [example]> cd .. - -yosys> echo off -echo off diff --git a/docs/source/code_examples/stubnets/Makefile b/docs/source/code_examples/stubnets/Makefile index ec501f006..17f700c88 100644 --- a/docs/source/code_examples/stubnets/Makefile +++ b/docs/source/code_examples/stubnets/Makefile @@ -1,6 +1,7 @@ -.PHONY: all dots -all: dots +.PHONY: all dots examples +all: dots examples dots: +examples: .PHONY: test test: stubnets.so diff --git a/docs/source/code_examples/synth_flow/Makefile b/docs/source/code_examples/synth_flow/Makefile index 7db1c12f4..0dd37ed4d 100644 --- a/docs/source/code_examples/synth_flow/Makefile +++ b/docs/source/code_examples/synth_flow/Makefile @@ -9,9 +9,10 @@ YOSYS ?= ../../../../$(PROGRAM_PREFIX)yosys DOTS = $(addsuffix .dot,$(DOT_TARGETS)) -.PHONY: all dots -all: dots +.PHONY: all dots examples +all: dots examples dots: $(DOTS) +examples: %.dot: %.v %.ys $(YOSYS) -p 'script $*.ys; show -notitle -prefix $* -format dot' diff --git a/docs/source/code_examples/techmap/Makefile b/docs/source/code_examples/techmap/Makefile index e900fea4c..6736b0f1d 100644 --- a/docs/source/code_examples/techmap/Makefile +++ b/docs/source/code_examples/techmap/Makefile @@ -2,9 +2,10 @@ PROGRAM_PREFIX := YOSYS ?= ../../../../$(PROGRAM_PREFIX)yosys -.PHONY: all dots -all: dots +.PHONY: all dots examples +all: dots examples dots: red_or3x1.dot sym_mul.dot mymul.dot mulshift.dot addshift.dot +examples: red_or3x1.dot: red_or3x1_* $(YOSYS) red_or3x1_test.ys diff --git a/docs/source/conf.py b/docs/source/conf.py index 23efe2b43..8e30fcc7c 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -5,7 +5,7 @@ import os project = 'YosysHQ Yosys' author = 'YosysHQ GmbH' copyright ='2024 YosysHQ GmbH' -yosys_ver = "0.44" +yosys_ver = "0.46" # select HTML theme html_theme = 'furo' @@ -41,23 +41,44 @@ html_static_path = ['_static', "_images"] pygments_style = 'colorful' highlight_language = 'none' -extensions = ['sphinx.ext.autosectionlabel', 'sphinxcontrib.bibtex', 'rtds_action'] +extensions = ['sphinx.ext.autosectionlabel', 'sphinxcontrib.bibtex'] -# rtds_action -rtds_action_github_repo = "YosysHQ/yosys" -rtds_action_path = "." -rtds_action_artifact_prefix = "cmd-ref-" -rtds_action_github_token = os.environ["GITHUB_TOKEN"] +if os.getenv("READTHEDOCS"): + # Use rtds_action if we are building on read the docs and have a github token env var + if os.getenv("GITHUB_TOKEN"): + extensions += ['rtds_action'] + rtds_action_github_repo = "YosysHQ/yosys" + rtds_action_path = "." + rtds_action_artifact_prefix = "cmd-ref-" + rtds_action_github_token = os.environ["GITHUB_TOKEN"] + else: + # We're on read the docs but have no github token, this is probably a PR preview build + html_theme_options["announcement"] = 'Missing content? Check PR preview limitations.' + html_theme_options["light_css_variables"]["color-announcement-background"] = "var(--color-admonition-title-background--caution)" + html_theme_options["light_css_variables"]["color-announcement-text"] = "var(--color-content-foreground)" # Ensure that autosectionlabel will produce unique names autosectionlabel_prefix_document = True autosectionlabel_maxdepth = 1 +# include todos for previews +extensions.append('sphinx.ext.todo') + # set version -if os.getenv("READTHEDOCS") and os.getenv("READTHEDOCS_VERSION") == "latest": - release = yosys_ver + "-dev" +if os.getenv("READTHEDOCS"): + rtds_version = os.getenv("READTHEDOCS_VERSION") + if rtds_version == "latest": + release = yosys_ver + "-dev" + todo_include_todos = False + elif rtds_version.startswith("docs"): + release = rtds_version + todo_include_todos = True + else: + release = yosys_ver + todo_include_todos = False else: release = yosys_ver + todo_include_todos = True # assign figure numbers numfig = True @@ -72,10 +93,6 @@ latex_elements = { ''' } -# include todos during rewrite -extensions.append('sphinx.ext.todo') -todo_include_todos = False - # custom cmd-ref parsing/linking sys.path += [os.path.dirname(__file__) + "/../"] extensions.append('util.cmdref') diff --git a/docs/source/using_yosys/more_scripting/index.rst b/docs/source/using_yosys/more_scripting/index.rst index 490a5a7ad..090b9e0b9 100644 --- a/docs/source/using_yosys/more_scripting/index.rst +++ b/docs/source/using_yosys/more_scripting/index.rst @@ -3,6 +3,8 @@ More scripting .. todo:: brief overview for the more scripting index +.. todo:: troubleshooting document(?) + .. toctree:: :maxdepth: 3 diff --git a/docs/source/using_yosys/more_scripting/troubleshooting.rst b/docs/source/using_yosys/more_scripting/troubleshooting.rst deleted file mode 100644 index a17a552d2..000000000 --- a/docs/source/using_yosys/more_scripting/troubleshooting.rst +++ /dev/null @@ -1,6 +0,0 @@ -Troubleshooting -~~~~~~~~~~~~~~~ - -.. todo:: troubleshooting document(?) - -See :doc:`/cmd/bugpoint` diff --git a/docs/source/using_yosys/synthesis/cell_libs.rst b/docs/source/using_yosys/synthesis/cell_libs.rst index 92b6dab3f..4e800bdf2 100644 --- a/docs/source/using_yosys/synthesis/cell_libs.rst +++ b/docs/source/using_yosys/synthesis/cell_libs.rst @@ -90,8 +90,10 @@ Mapping to hardware For this example, we are using a Liberty file to describe a cell library which our internal cell library will be mapped to: +.. todo:: find a Liberty pygments style? + .. literalinclude:: /code_examples/intro/mycells.lib - :language: Liberty + :language: text :linenos: :name: mycells-lib :caption: :file:`mycells.lib` diff --git a/docs/source/yosys_internals/extending_yosys/build_verific.rst b/docs/source/yosys_internals/extending_yosys/build_verific.rst index b20517bd3..2585ebae4 100644 --- a/docs/source/yosys_internals/extending_yosys/build_verific.rst +++ b/docs/source/yosys_internals/extending_yosys/build_verific.rst @@ -81,8 +81,10 @@ The following features, along with their corresponding Yosys build parameters, are required for the Yosys-Verific patch: * RTL elaboration with - * SystemVerilog with ``ENABLE_VERIFIC_SYSTEMVERILOG``, and/or - * VHDL support with ``ENABLE_VERIFIC_VHDL``. + + * SystemVerilog with ``ENABLE_VERIFIC_SYSTEMVERILOG``, and/or + * VHDL support with ``ENABLE_VERIFIC_VHDL``. + * Hierarchy tree support and static elaboration with ``ENABLE_VERIFIC_HIER_TREE``. diff --git a/docs/source/yosys_internals/extending_yosys/functional_ir.rst b/docs/source/yosys_internals/extending_yosys/functional_ir.rst new file mode 100644 index 000000000..a763e0508 --- /dev/null +++ b/docs/source/yosys_internals/extending_yosys/functional_ir.rst @@ -0,0 +1,94 @@ +Writing a new backend using FunctionalIR +=========================================== + +To simplify the writing of backends for functional languages or similar targets, Yosys provides an alternative intermediate representation called FunctionalIR which maps more directly on those targets. + +FunctionalIR represents the design as a function ``(inputs, current_state) -> (outputs, next_state)``. +This function is broken down into a series of assignments to variables. +Each assignment is a simple operation, such as an addition. +Complex operations are broken up into multiple steps. +For example, an RTLIL addition will be translated into a sign/zero extension of the inputs, followed by an addition. + +Like SSA form, each variable is assigned to exactly once. +We can thus treat variables and assignments as equivalent and, since this is a graph-like representation, those variables are also called "nodes". +Unlike RTLIL's cells and wires representation, this representation is strictly ordered (topologically sorted) with definitions preceding their use. + +Every node has a "sort" (the FunctionalIR term for what might otherwise be called a "type"). The sorts available are + +- ``bit[n]`` for an ``n``-bit bitvector, and +- ``memory[n,m]`` for an immutable array of ``2**n`` values of sort ``bit[m]``. + +In terms of actual code, Yosys provides a class ``Functional::IR`` that represents a design in FunctionalIR. +``Functional::IR::from_module`` generates an instance from an RTLIL module. +The entire design is stored as a whole in an internal data structure. +To access the design, the ``Functional::Node`` class provides a reference to a particular node in the design. +The ``Functional::IR`` class supports the syntax ``for(auto node : ir)`` to iterate over every node. + +``Functional::IR`` also keeps track of inputs, outputs and states. +By a "state" we mean a pair of a "current state" input and a "next state" output. +One such pair is created for every register and for every memory. +Every input, output and state has a name (equal to their name in RTLIL), a sort and a kind. +The kind field usually remains as the default value ``$input``, ``$output`` or ``$state``, however some RTLIL cells such as ``$assert`` or ``$anyseq`` generate auxiliary inputs/outputs/states that are given a different kind to distinguish them from ordinary RTLIL inputs/outputs/states. + +- To access an individual input/output/state, use ``ir.input(name, kind)``, ``ir.output(name, kind)`` or ``ir.state(name, kind)``. ``kind`` defaults to the default kind. +- To iterate over all inputs/outputs/states of a certain kind, methods ``ir.inputs``, ``ir.outputs``, ``ir.states`` are provided. Their argument defaults to the default kinds mentioned. +- To iterate over inputs/outputs/states of any kind, use ``ir.all_inputs``, ``ir.all_outputs`` and ``ir.all_states``. +- Outputs have a node that indicate the value of the output, this can be retrieved via ``output.value()``. +- States have a node that indicate the next value of the state, this can be retrieved via ``state.next_value()``. + They also have an initial value that is accessed as either ``state.initial_value_signal()`` or ``state.initial_value_memory()``, depending on their sort. + +Each node has a "function", which defines its operation (for a complete list of functions and a specification of their operation, see ``functional.h``). +Functions are represented as an enum ``Functional::Fn`` and the function field can be accessed as ``node.fn()``. +Since the most common operation is a switch over the function that also accesses the arguments, the ``Node`` class provides a method ``visit`` that implements the visitor pattern. +For example, for an addition node ``node`` with arguments ``n1`` and ``n2``, ``node.visit(visitor)`` would call ``visitor.add(node, n1, n2)``. +Thus typically one would implement a class with a method for every function. +Visitors should inherit from either ``Functional::AbstractVisitor`` or ``Functional::DefaultVisitor``. +The former will produce a compiler error if a case is unhandled, the latter will call ``default_handler(node)`` instead. +Visitor methods should be marked as ``override`` to provide compiler errors if the arguments are wrong. + +Utility classes +----------------- + +``functional.h`` also provides utility classes that are independent of the main FunctionalIR representation but are likely to be useful for backends. + +``Functional::Writer`` provides a simple formatting class that wraps a ``std::ostream`` and provides the following methods: + +- ``writer << value`` wraps ``os << value``. +- ``writer.print(fmt, value0, value1, value2, ...)`` replaces ``{0}``, ``{1}``, ``{2}``, etc in the string ``fmt`` with ``value0``, ``value1``, ``value2``, resp. + Each value is formatted using ``os << value``. + It is also possible to write ``{}`` to refer to one past the last index, i.e. ``{1} {} {} {7} {}`` is equivalent to ``{1} {2} {3} {7} {8}``. +- ``writer.print_with(fn, fmt, value0, value1, value2, ...)`` functions much the same as ``print`` but it uses ``os << fn(value)`` to print each value and falls back to ``os << value`` if ``fn(value)`` is not legal. + +``Functional::Scope`` keeps track of variable names in a target language. +It is used to translate between different sets of legal characters and to avoid accidentally re-defining identifiers. +Users should derive a class from ``Scope`` and supply the following: + +- ``Scope`` takes a template argument that specifies a type that's used to uniquely distinguish variables. + Typically this would be ``int`` (if variables are used for ``Functional::IR`` nodes) or ``IdString``. +- The derived class should provide a constructor that calls ``reserve`` for every reserved word in the target language. +- A method ``bool is_legal_character(char c, int index)`` has to be provided that returns ``true`` iff ``c`` is legal in an identifier at position ``index``. + +Given an instance ``scope`` of the derived class, the following methods are then available: + +- ``scope.reserve(std::string name)`` marks the given name as being in-use +- ``scope.unique_name(IdString suggestion)`` generates a previously unused name and attempts to make it similar to ``suggestion``. +- ``scope(Id id, IdString suggestion)`` functions similar to ``unique_name``, except that multiple calls with the same ``id`` are guaranteed to retrieve the same name (independent of ``suggestion``). + +``sexpr.h`` provides classes that represent and pretty-print s-expressions. +S-expressions can be constructed with ``SExpr::list``, for example ``SExpr expr = SExpr::list("add", "x", SExpr::list("mul", "y", "z"))`` represents ``(add x (mul y z))`` +(by adding ``using SExprUtil::list`` to the top of the file, ``list`` can be used as shorthand for ``SExpr::list``). +For prettyprinting, ``SExprWriter`` wraps an ``std::ostream`` and provides the following methods: + +- ``writer << sexpr`` writes the provided expression to the output, breaking long lines and adding appropriate indentation. +- ``writer.open(sexpr)`` is similar to ``writer << sexpr`` but will omit the last closing parenthesis. + Further arguments can then be added separately with ``<<`` or ``open``. + This allows for printing large s-expressions without needing to construct the whole expression in memory first. +- ``writer.open(sexpr, false)`` is similar to ``writer.open(sexpr)`` but further arguments will not be indented. + This is used to avoid unlimited indentation on structures with unlimited nesting. +- ``writer.close(n = 1)`` closes the last ``n`` open s-expressions. +- ``writer.push()`` and ``writer.pop()`` are used to automatically close s-expressions. + ``writer.pop()`` closes all s-expressions opened since the last call to ``writer.push()``. +- ``writer.comment(string)`` writes a comment on a separate-line. + ``writer.comment(string, true)`` appends a comment to the last printed s-expression. +- ``writer.flush()`` flushes any buffering and should be called before any direct access to the underlying ``std::ostream``. It does not close unclosed parentheses. +- The destructor calls ``flush`` but also closes all unclosed parentheses. diff --git a/docs/source/yosys_internals/extending_yosys/index.rst b/docs/source/yosys_internals/extending_yosys/index.rst index 2a4d2dfef..88f36d526 100644 --- a/docs/source/yosys_internals/extending_yosys/index.rst +++ b/docs/source/yosys_internals/extending_yosys/index.rst @@ -10,5 +10,6 @@ of interest for developers looking to customise Yosys builds. extensions build_verific + functional_ir test_suites diff --git a/frontends/aiger/aigerparse.cc b/frontends/aiger/aigerparse.cc index 0178514e1..37ace27fd 100644 --- a/frontends/aiger/aigerparse.cc +++ b/frontends/aiger/aigerparse.cc @@ -448,7 +448,7 @@ void AigerReader::parse_xaiger() bool success = ce.eval(o); log_assert(success); log_assert(o.wire == nullptr); - lut_mask[gray] = o.data; + lut_mask.bits()[gray] = o.data; } RTLIL::Cell *output_cell = module->cell(stringf("$and$aiger%d$%d", aiger_autoidx, rootNodeID)); log_assert(output_cell); diff --git a/frontends/aiger2/Makefile.inc b/frontends/aiger2/Makefile.inc new file mode 100644 index 000000000..b4a9f6b89 --- /dev/null +++ b/frontends/aiger2/Makefile.inc @@ -0,0 +1,2 @@ + +OBJS += frontends/aiger2/xaiger.o diff --git a/frontends/aiger2/xaiger.cc b/frontends/aiger2/xaiger.cc new file mode 100644 index 000000000..04beee396 --- /dev/null +++ b/frontends/aiger2/xaiger.cc @@ -0,0 +1,473 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) Martin PoviĊĦer + * + * 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" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +uint32_t read_be32(std::istream &f) { + return ((uint32_t) f.get() << 24) | + ((uint32_t) f.get() << 16) | + ((uint32_t) f.get() << 8) | (uint32_t) f.get(); +} + +IdString read_idstring(std::istream &f) +{ + std::string str; + std::getline(f, str, '\0'); + if (!f.good()) + log_error("failed to read string\n"); + return RTLIL::escape_id(str); +} + +struct Xaiger2Frontend : public Frontend { + Xaiger2Frontend() : Frontend("xaiger2", "(experimental) read XAIGER file") + { + experimental(); + } + + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" read_xaiger2 -sc_mapping [options] \n"); + log("\n"); + log("Read a standard cell mapping from a XAIGER file into an existing module.\n"); + log("\n"); + log(" -module_name \n"); + log(" name of the target module\n"); + log("\n"); + log(" -map2 \n"); + log(" read file with symbol information\n"); + log("\n"); + } + + void read_sc_mapping(std::istream *&f, std::string filename, std::vector args, Design *design) + { + IdString module_name; + std::string map_filename; + + size_t argidx; + for (argidx = 2; argidx < args.size(); argidx++) { + std::string arg = args[argidx]; + if (arg == "-module_name" && argidx + 1 < args.size()) { + module_name = RTLIL::escape_id(args[++argidx]); + continue; + } + if (arg == "-map2" && argidx + 1 < args.size()) { + map_filename = args[++argidx]; + continue; + } + break; + } + extra_args(f, filename, args, argidx, true); + + if (map_filename.empty()) + log_error("A '-map2' argument required\n"); + if (module_name.empty()) + log_error("A '-module_name' argument required\n"); + + Module *module = design->module(module_name); + if (!module) + log_error("Module '%s' not found\n", log_id(module_name)); + + std::ifstream map_file; + map_file.open(map_filename); + if (!map_file) + log_error("Failed to open map file '%s'\n", map_filename.c_str()); + + unsigned int M, I, L, O, A; + std::string header; + if (!(*f >> header >> M >> I >> L >> O >> A) || header != "aig") + log_error("Bad header\n"); + std::string line; + std::getline(*f, line); + log_debug("M=%u I=%u L=%u O=%u A=%u\n", M, I, L, O, A); + + if (L != 0) + log_error("Latches unsupported\n"); + if (I + L + A != M) + log_error("Inconsistent header\n"); + + std::vector outputs; + for (int i = 0; i < (int) O; i++) { + int po; + *f >> po; + log_assert(f->get() == '\n'); + outputs.push_back(po); + } + + std::vector> boxes; + std::vector retained_boxes; + std::vector bits(2 + 2*M, RTLIL::Sm); + bits[0] = RTLIL::S0; + bits[1] = RTLIL::S1; + + std::string type; + while (map_file >> type) { + if (type == "pi") { + int pi_idx; + int woffset; + std::string name; + if (!(map_file >> pi_idx >> woffset >> name)) + log_error("Bad map file (1)\n"); + int lit = (2 * pi_idx) + 2; + if (lit < 0 || lit >= (int) bits.size()) + log_error("Bad map file (2)\n"); + Wire *w = module->wire(name); + if (!w || woffset < 0 || woffset >= w->width) + log_error("Map file references non-existent signal bit %s[%d]\n", + name.c_str(), woffset); + bits[lit] = SigBit(w, woffset); + } else if (type == "box") { + int box_seq; + std::string name; + if (!(map_file >> box_seq >> name)) + log_error("Bad map file (20)\n"); + if (box_seq < 0) + log_error("Bad map file (21)\n"); + + Cell *box = module->cell(RTLIL::escape_id(name)); + if (!box) + log_error("Map file references non-existent box %s\n", + name.c_str()); + + Module *def = design->module(box->type); + if (def && !box->parameters.empty()) { + // TODO: This is potentially costly even if a cached derivation exists + def = design->module(def->derive(design, box->parameters)); + log_assert(def); + } + + if (!def) + log_error("Bad map file (22)\n"); + + if (box_seq >= (int) boxes.size()) { + boxes.resize(box_seq + 1); + retained_boxes.resize(box_seq + 1); + } + boxes[box_seq] = std::make_pair(box, def); + } else { + std::string scratch; + std::getline(map_file, scratch); + } + } + + for (int i = 0; i < (int) A; i++) { + while (f->get() & 0x80 && !f->eof()); + while (f->get() & 0x80 && !f->eof()); + } + + if (f->get() != 'c') + log_error("Missing 'c' ahead of extensions\n"); + if (f->peek() == '\n') + f->get(); + auto extensions_start = f->tellg(); + + log_debug("reading 'h' (first pass)\n"); + for (int c = f->get(); c != EOF; c = f->get()) { + if (c == 'h') { + uint32_t len, ci_num, co_num, pi_num, po_num, no_boxes; + len = read_be32(*f); + read_be32(*f); + ci_num = read_be32(*f); + co_num = read_be32(*f); + pi_num = read_be32(*f); + po_num = read_be32(*f); + no_boxes = read_be32(*f); + + log_debug("len=%u ci_num=%u co_num=%u pi_num=%u po_nun=%u no_boxes=%u\n", + len, ci_num, co_num, pi_num, po_num, no_boxes); + + int ci_counter = 0; + for (uint32_t i = 0; i < no_boxes; i++) { + uint32_t box_inputs, box_outputs, box_id, box_seq; + box_inputs = read_be32(*f); + box_outputs = read_be32(*f); + box_id = read_be32(*f); + box_seq = read_be32(*f); + + log("box_seq=%d boxes.size=%d\n", box_seq, (int) boxes.size()); + log_assert(box_seq < boxes.size()); + + auto [cell, def] = boxes[box_seq]; + log_assert(cell && def); + retained_boxes[box_seq] = true; + + int box_ci_idx = 0; + for (auto port_id : def->ports) { + Wire *port = def->wire(port_id); + if (port->port_output) { + if (!cell->hasPort(port_id) || cell->getPort(port_id).size() != port->width) + log_error("Malformed design (1)\n"); + + SigSpec &conn = cell->connections_[port_id]; + for (int j = 0; j < port->width; j++) { + if (conn[j].wire && conn[j].wire->port_output) + conn[j] = module->addWire(module->uniquify( + stringf("$box$%s$%s$%d", + cell->name.isPublic() ? cell->name.c_str() + 1 : cell->name.c_str(), + port_id.isPublic() ? port_id.c_str() + 1 : port_id.c_str(), + j))); + + bits[2*(pi_num + ci_counter + box_ci_idx++) + 2] = conn[j]; + } + } + } + + log_assert(box_ci_idx == (int) box_outputs); + ci_counter += box_ci_idx; + } + log_assert(pi_num + ci_counter == ci_num); + } else if (c == '\n') { + break; + } else if (c == 'c') { + break; + } else { + uint32_t len = read_be32(*f); + f->ignore(len); + log_debug(" section '%c' (%d): ignoring %d bytes\n", c, c, len); + } + } + + log_debug("reading 'M' (second pass)\n"); + + f->seekg(extensions_start); + bool read_mapping = false; + uint32_t no_cells, no_instances; + for (int c = f->get(); c != EOF; c = f->get()) { + if (c == 'M') { + uint32_t len = read_be32(*f); + read_mapping = true; + + no_cells = read_be32(*f); + no_instances = read_be32(*f); + + log_debug("M: len=%u no_cells=%u no_instances=%u\n", len, no_cells, no_instances); + + struct MappingCell { + RTLIL::IdString type; + RTLIL::IdString out; + std::vector ins; + }; + std::vector cells; + cells.resize(no_cells); + + for (unsigned i = 0; i < no_cells; ++i) { + auto &cell = cells[i]; + cell.type = read_idstring(*f); + cell.out = read_idstring(*f); + uint32_t nins = read_be32(*f); + for (uint32_t j = 0; j < nins; j++) + cell.ins.push_back(read_idstring(*f)); + log_debug("M: Cell %s (out %s, ins", log_id(cell.type), log_id(cell.out)); + for (auto in : cell.ins) + log_debug(" %s", log_id(in)); + log_debug(")\n"); + } + + for (unsigned i = 0; i < no_instances; ++i) { + uint32_t cell_id = read_be32(*f); + uint32_t out_lit = read_be32(*f); + + log_assert(out_lit < bits.size()); + log_assert(bits[out_lit] == RTLIL::Sm); + log_assert(cell_id < cells.size()); + auto &cell = cells[cell_id]; + Cell *instance = module->addCell(module->uniquify(stringf("$sc%d", out_lit)), cell.type); + auto out_w = module->addWire(module->uniquify(stringf("$lit%d", out_lit))); + instance->setPort(cell.out, out_w); + bits[out_lit] = out_w; + for (auto in : cell.ins) { + uint32_t in_lit = read_be32(*f); + log_assert(out_lit < bits.size()); + log_assert(bits[in_lit] != RTLIL::Sm); + instance->setPort(in, bits[in_lit]); + } + } + } else if (c == '\n') { + break; + } else if (c == 'c') { + break; + } else { + uint32_t len = read_be32(*f); + f->ignore(len); + log_debug(" section '%c' (%d): ignoring %d bytes\n", c, c, len); + } + } + + if (!read_mapping) + log_error("Missing mapping (no 'M' section)\n"); + + log("Read %d instances with cell library of size %d.\n", + no_instances, no_cells); + + f->seekg(extensions_start); + log_debug("reading 'h' (second pass)\n"); + int co_counter = 0; + for (int c = f->get(); c != EOF; c = f->get()) { + if (c == 'h') { + uint32_t len, ci_num, co_num, pi_num, po_num, no_boxes; + len = read_be32(*f); + read_be32(*f); + ci_num = read_be32(*f); + co_num = read_be32(*f); + pi_num = read_be32(*f); + po_num = read_be32(*f); + no_boxes = read_be32(*f); + + log_debug("len=%u ci_num=%u co_num=%u pi_num=%u po_nun=%u no_boxes=%u\n", + len, ci_num, co_num, pi_num, po_num, no_boxes); + + for (uint32_t i = 0; i < no_boxes; i++) { + uint32_t box_inputs, box_outputs, box_id, box_seq; + box_inputs = read_be32(*f); + box_outputs = read_be32(*f); + box_id = read_be32(*f); + box_seq = read_be32(*f); + + log("box_seq=%d boxes.size=%d\n", box_seq, (int) boxes.size()); + log_assert(box_seq < boxes.size()); + + auto [cell, def] = boxes[box_seq]; + log_assert(cell && def); + + int box_co_idx = 0; + for (auto port_id : def->ports) { + Wire *port = def->wire(port_id); + SigSpec conn; + if (port->port_input) { + if (!cell->hasPort(port_id) || cell->getPort(port_id).size() != port->width) + log_error("Malformed design (2)\n"); + + SigSpec conn; + for (int j = 0; j < port->width; j++) { + log_assert(co_counter + box_co_idx < (int) outputs.size()); + int lit = outputs[co_counter + box_co_idx++]; + log_assert(lit >= 0 && lit < (int) bits.size()); + SigBit bit = bits[lit]; + if (bit == RTLIL::Sm) + log_error("Malformed mapping (1)\n"); + conn.append(bit); + } + cell->setPort(port_id, conn); + } + } + + log_assert(box_co_idx == (int) box_inputs); + co_counter += box_co_idx; + } + log_assert(po_num + co_counter == co_num); + } else if (c == '\n') { + break; + } else if (c == 'c') { + break; + } else { + uint32_t len = read_be32(*f); + f->ignore(len); + log_debug(" section '%c' (%d): ignoring %d bytes\n", c, c, len); + } + } + + while (true) { + std::string scratch; + std::getline(*f, scratch); + if (f->eof()) + break; + log_assert(!f->fail()); + log("input file: %s\n", scratch.c_str()); + } + + log_debug("co_counter=%d\n", co_counter); + + // TODO: seek without close/open + map_file.close(); + map_file.open(map_filename); + while (map_file >> type) { + if (type == "po") { + int po_idx; + int woffset; + std::string name; + if (!(map_file >> po_idx >> woffset >> name)) + log_error("Bad map file (3)\n"); + po_idx += co_counter; + if (po_idx < 0 || po_idx >= (int) outputs.size()) + log_error("Bad map file (4)\n"); + int lit = outputs[po_idx]; + if (lit < 0 || lit >= (int) bits.size()) + log_error("Bad map file (5)\n"); + if (bits[lit] == RTLIL::Sm) + log_error("Bad map file (6)\n"); + Wire *w = module->wire(name); + if (!w || woffset < 0 || woffset >= w->width) + log_error("Map file references non-existent signal bit %s[%d]\n", + name.c_str(), woffset); + module->connect(SigBit(w, woffset), bits[lit]); + } else if (type == "pseudopo") { + int po_idx; + int poffset; + std::string box_name; + std::string box_port; + if (!(map_file >> po_idx >> poffset >> box_name >> box_port)) + log_error("Bad map file (7)\n"); + po_idx += co_counter; + if (po_idx < 0 || po_idx >= (int) outputs.size()) + log_error("Bad map file (8)\n"); + int lit = outputs[po_idx]; + if (lit < 0 || lit >= (int) bits.size()) + log_error("Bad map file (9)\n"); + if (bits[lit] == RTLIL::Sm) + log_error("Bad map file (10)\n"); + Cell *cell = module->cell(box_name); + if (!cell || !cell->hasPort(box_port)) + log_error("Map file references non-existent box port %s/%s\n", + box_name.c_str(), box_port.c_str()); + SigSpec &port = cell->connections_[box_port]; + if (poffset < 0 || poffset >= port.size()) + log_error("Map file references non-existent box port bit %s/%s[%d]\n", + box_name.c_str(), box_port.c_str(), poffset); + port[poffset] = bits[lit]; + } else { + std::string scratch; + std::getline(map_file, scratch); + } + } + + int box_seq = 0; + for (auto [cell, def] : boxes) { + if (!retained_boxes[box_seq++]) + module->remove(cell); + } + } + + void execute(std::istream *&f, std::string filename, std::vector args, Design *design) override + { + log_header(design, "Executing XAIGER2 frontend.\n"); + + if (args.size() > 1 && args[1] == "-sc_mapping") { + read_sc_mapping(f, filename, args, design); + return; + } + + log_cmd_error("Mode '-sc_mapping' must be selected\n"); + } +} Xaiger2Frontend; + +PRIVATE_NAMESPACE_END diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index d21b8f02d..935273c9f 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -41,6 +41,8 @@ namespace AST { std::string current_filename; void (*set_line_num)(int) = NULL; int (*get_line_num)() = NULL; + unsigned long long astnodes = 0; + unsigned long long astnode_count() { return astnodes; } } // instantiate global variables (private API) @@ -204,6 +206,7 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch static unsigned int hashidx_count = 123456789; hashidx_count = mkhash_xorshift(hashidx_count); hashidx_ = hashidx_count; + astnodes++; this->type = type; filename = current_filename; @@ -292,6 +295,7 @@ void AstNode::delete_children() // AstNode destructor AstNode::~AstNode() { + astnodes--; delete_children(); } @@ -474,6 +478,10 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const fprintf(f, ";\n"); break; + case AST_WIRETYPE: + fprintf(f, "%s", id2vl(str).c_str()); + break; + case AST_MEMORY: fprintf(f, "%s" "memory", indent.c_str()); if (is_signed) @@ -690,7 +698,17 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const break; case AST_CAST_SIZE: - children[0]->dumpVlog(f, ""); + switch (children[0]->type) + { + case AST_WIRE: + if (children[0]->children.size() > 0) + children[0]->children[0]->dumpVlog(f, ""); + else + fprintf(f, "%d'", children[0]->range_left - children[0]->range_right + 1); + break; + default: + children[0]->dumpVlog(f, ""); + } fprintf(f, "'("); children[1]->dumpVlog(f, ""); fprintf(f, ")"); @@ -933,15 +951,7 @@ RTLIL::Const AstNode::asAttrConst() const { log_assert(type == AST_CONSTANT); - RTLIL::Const val; - val.bits = bits; - - if (is_string) { - val.flags |= RTLIL::CONST_FLAG_STRING; - log_assert(val.decode_string() == str); - } - - return val; + return is_string ? RTLIL::Const(str) : RTLIL::Const(bits); } RTLIL::Const AstNode::asParaConst() const @@ -987,7 +997,7 @@ uint64_t AstNode::asInt(bool is_signed) uint64_t ret = 0; for (int i = 0; i < 64; i++) - if (v.bits.at(i) == RTLIL::State::S1) + if (v.at(i) == RTLIL::State::S1) ret |= uint64_t(1) << i; return ret; @@ -1005,15 +1015,15 @@ double AstNode::asReal(bool is_signed) { RTLIL::Const val(bits); - bool is_negative = is_signed && !val.bits.empty() && val.bits.back() == RTLIL::State::S1; + bool is_negative = is_signed && !val.empty() && val.back() == RTLIL::State::S1; if (is_negative) - val = const_neg(val, val, false, false, val.bits.size()); + val = const_neg(val, val, false, false, val.size()); double v = 0; - for (size_t i = 0; i < val.bits.size(); i++) + for (size_t i = 0; i < val.size(); i++) // IEEE Std 1800-2012 Par 6.12.2: Individual bits that are x or z in // the net or the variable shall be treated as zero upon conversion. - if (val.bits.at(i) == RTLIL::State::S1) + if (val.at(i) == RTLIL::State::S1) v += exp2(i); if (is_negative) v *= -1; @@ -1036,15 +1046,15 @@ RTLIL::Const AstNode::realAsConst(int width) #else if (!std::isfinite(v)) { #endif - result.bits = std::vector(width, RTLIL::State::Sx); + result = std::vector(width, RTLIL::State::Sx); } else { bool is_negative = v < 0; if (is_negative) v *= -1; for (int i = 0; i < width; i++, v /= 2) - result.bits.push_back((fmod(floor(v), 2) != 0) ? RTLIL::State::S1 : RTLIL::State::S0); + result.bits().push_back((fmod(floor(v), 2) != 0) ? RTLIL::State::S1 : RTLIL::State::S0); if (is_negative) - result = const_neg(result, result, false, false, result.bits.size()); + result = const_neg(result, result, false, false, result.size()); } return result; } @@ -1749,16 +1759,7 @@ static std::string serialize_param_value(const RTLIL::Const &val) { res.push_back('r'); res += stringf("%d", GetSize(val)); res.push_back('\''); - for (int i = GetSize(val) - 1; i >= 0; i--) { - switch (val.bits[i]) { - case RTLIL::State::S0: res.push_back('0'); break; - case RTLIL::State::S1: res.push_back('1'); break; - case RTLIL::State::Sx: res.push_back('x'); break; - case RTLIL::State::Sz: res.push_back('z'); break; - case RTLIL::State::Sa: res.push_back('?'); break; - case RTLIL::State::Sm: res.push_back('m'); break; - } - } + res.append(val.as_string("?")); return res; } @@ -1850,7 +1851,7 @@ std::string AstModule::derive_common(RTLIL::Design *design, const dictsecond.flags & RTLIL::CONST_FLAG_STRING) != 0) child->children[0] = AstNode::mkconst_str(it->second.decode_string()); else - child->children[0] = AstNode::mkconst_bits(it->second.bits, (it->second.flags & RTLIL::CONST_FLAG_SIGNED) != 0); + child->children[0] = AstNode::mkconst_bits(it->second.to_bits(), (it->second.flags & RTLIL::CONST_FLAG_SIGNED) != 0); rewritten.insert(it->first); } @@ -1863,7 +1864,7 @@ std::string AstModule::derive_common(RTLIL::Design *design, const dictchildren.push_back(AstNode::mkconst_str(param.second.decode_string())); else - defparam->children.push_back(AstNode::mkconst_bits(param.second.bits, (param.second.flags & RTLIL::CONST_FLAG_SIGNED) != 0)); + defparam->children.push_back(AstNode::mkconst_bits(param.second.to_bits(), (param.second.flags & RTLIL::CONST_FLAG_SIGNED) != 0)); new_ast->children.push_back(defparam); } diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index 6c4a1e15a..bdff015e3 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -410,6 +410,9 @@ namespace AST extern void (*set_line_num)(int); extern int (*get_line_num)(); + // for stats + unsigned long long astnode_count(); + // set set_line_num and get_line_num to internal dummy functions (done by simplify() and AstModule::derive // to control the filename and linenum properties of new nodes not generated by a frontend parser) void use_internal_line_num(); diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index 3d47bd3c0..ea7da521c 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -735,10 +735,10 @@ struct AST_INTERNAL::ProcessGenerator for (auto sync : proc->syncs) { if (sync->type == RTLIL::STp) { triggers.append(sync->signal); - polarity.bits.push_back(RTLIL::S1); + polarity.bits().push_back(RTLIL::S1); } else if (sync->type == RTLIL::STn) { triggers.append(sync->signal); - polarity.bits.push_back(RTLIL::S0); + polarity.bits().push_back(RTLIL::S0); } } @@ -832,10 +832,10 @@ struct AST_INTERNAL::ProcessGenerator for (auto sync : proc->syncs) { if (sync->type == RTLIL::STp) { triggers.append(sync->signal); - polarity.bits.push_back(RTLIL::S1); + polarity.bits().push_back(RTLIL::S1); } else if (sync->type == RTLIL::STn) { triggers.append(sync->signal); - polarity.bits.push_back(RTLIL::S0); + polarity.bits().push_back(RTLIL::S0); } } @@ -892,7 +892,7 @@ struct AST_INTERNAL::ProcessGenerator RTLIL::Const priority_mask = RTLIL::Const(0, cur_idx); for (int i = 0; i < portid; i++) { int new_bit = port_map[std::make_pair(memid, i)]; - priority_mask.bits[new_bit] = orig_priority_mask.bits[i]; + priority_mask.bits()[new_bit] = orig_priority_mask[i]; } action.priority_mask = priority_mask; sync->mem_write_actions.push_back(action); diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 3d8478ef1..6d78369cc 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -1500,11 +1500,69 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin } break; + case AST_CAST_SIZE: { + int width = 1; + AstNode *node; + AstNode *child = children[0]; + + if (child->type == AST_WIRE) { + if (child->children.size() == 0) { + // Base type (e.g., int) + width = child->range_left - child->range_right +1; + node = mkconst_int(width, child->is_signed); + } else { + // User defined type + log_assert(child->children[0]->type == AST_WIRETYPE); + + const std::string &type_name = child->children[0]->str; + if (!current_scope.count(type_name)) + input_error("Unknown identifier `%s' used as type name\n", type_name.c_str()); + AstNode *resolved_type_node = current_scope.at(type_name); + if (resolved_type_node->type != AST_TYPEDEF) + input_error("`%s' does not name a type\n", type_name.c_str()); + log_assert(resolved_type_node->children.size() == 1); + AstNode *template_node = resolved_type_node->children[0]; + + // Ensure typedef itself is fully simplified + while (template_node->simplify(const_fold, stage, width_hint, sign_hint)) {}; + + switch (template_node->type) + { + case AST_WIRE: { + if (template_node->children.size() > 0 && template_node->children[0]->type == AST_RANGE) + width = range_width(this, template_node->children[0]); + child->delete_children(); + node = mkconst_int(width, true); + break; + } + + case AST_STRUCT: + case AST_UNION: { + child->delete_children(); + width = size_packed_struct(template_node, 0); + node = mkconst_int(width, false); + break; + } + + default: + log_error("Don't know how to translate static cast of type %s\n", type2str(template_node->type).c_str()); + } + } + + delete child; + children.erase(children.begin()); + children.insert(children.begin(), node); + } + + detect_width_simple = true; + children_are_self_determined = true; + break; + } + case AST_TO_BITS: case AST_TO_SIGNED: case AST_TO_UNSIGNED: case AST_SELFSZ: - case AST_CAST_SIZE: case AST_CONCAT: case AST_REPLICATE: case AST_REDUCE_AND: @@ -1660,8 +1718,8 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin if (v->type == AST_CONSTANT && v->bits_only_01()) { RTLIL::Const case_item_expr = v->bitsAsConst(width_hint, sign_hint); RTLIL::Const match = const_eq(case_expr, case_item_expr, sign_hint, sign_hint, 1); - log_assert(match.bits.size() == 1); - if (match.bits.front() == RTLIL::State::S1) { + log_assert(match.size() == 1); + if (match.front() == RTLIL::State::S1) { while (i+1 < GetSize(children)) delete children[++i]; goto keep_const_cond; @@ -1963,7 +2021,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin if (children[1]->type != AST_CONSTANT) input_error("Right operand of to_bits expression is not constant!\n"); RTLIL::Const new_value = children[1]->bitsAsConst(children[0]->bitsAsConst().as_int(), children[1]->is_signed); - newNode = mkconst_bits(new_value.bits, children[1]->is_signed); + newNode = mkconst_bits(new_value.to_bits(), children[1]->is_signed); goto apply_newNode; } @@ -2126,7 +2184,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin log_file_warning(filename, location.first_line, "converting real value %e to binary %s.\n", children[0]->realvalue, log_signal(constvalue)); delete children[0]; - children[0] = mkconst_bits(constvalue.bits, sign_hint); + children[0] = mkconst_bits(constvalue.to_bits(), sign_hint); fixup_hierarchy_flags(); did_something = true; } @@ -2135,7 +2193,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin RTLIL::SigSpec sig(children[0]->bits); sig.extend_u0(width, children[0]->is_signed); AstNode *old_child_0 = children[0]; - children[0] = mkconst_bits(sig.as_const().bits, is_signed); + children[0] = mkconst_bits(sig.as_const().to_bits(), is_signed); delete old_child_0; fixup_hierarchy_flags(); } @@ -3435,8 +3493,8 @@ skip_dynamic_range_lvalue_expansion:; delete buf; uint32_t result = 0; - for (size_t i = 0; i < arg_value.bits.size(); i++) - if (arg_value.bits.at(i) == RTLIL::State::S1) + for (size_t i = 0; i < arg_value.size(); i++) + if (arg_value.at(i) == RTLIL::State::S1) result = i + 1; newNode = mkconst_int(result, true); @@ -4115,14 +4173,14 @@ replace_fcall_later:; case AST_BIT_NOT: if (children[0]->type == AST_CONSTANT) { RTLIL::Const y = RTLIL::const_not(children[0]->bitsAsConst(width_hint, sign_hint), dummy_arg, sign_hint, false, width_hint); - newNode = mkconst_bits(y.bits, sign_hint); + newNode = mkconst_bits(y.to_bits(), sign_hint); } break; case AST_TO_SIGNED: case AST_TO_UNSIGNED: if (children[0]->type == AST_CONSTANT) { RTLIL::Const y = children[0]->bitsAsConst(width_hint, sign_hint); - newNode = mkconst_bits(y.bits, type == AST_TO_SIGNED); + newNode = mkconst_bits(y.to_bits(), type == AST_TO_SIGNED); } break; if (0) { case AST_BIT_AND: const_func = RTLIL::const_and; } @@ -4132,7 +4190,7 @@ replace_fcall_later:; if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) { RTLIL::Const y = const_func(children[0]->bitsAsConst(width_hint, sign_hint), children[1]->bitsAsConst(width_hint, sign_hint), sign_hint, sign_hint, width_hint); - newNode = mkconst_bits(y.bits, sign_hint); + newNode = mkconst_bits(y.to_bits(), sign_hint); } break; if (0) { case AST_REDUCE_AND: const_func = RTLIL::const_reduce_and; } @@ -4142,13 +4200,13 @@ replace_fcall_later:; if (0) { case AST_REDUCE_BOOL: const_func = RTLIL::const_reduce_bool; } if (children[0]->type == AST_CONSTANT) { RTLIL::Const y = const_func(RTLIL::Const(children[0]->bits), dummy_arg, false, false, -1); - newNode = mkconst_bits(y.bits, false); + newNode = mkconst_bits(y.to_bits(), false); } break; case AST_LOGIC_NOT: if (children[0]->type == AST_CONSTANT) { RTLIL::Const y = RTLIL::const_logic_not(RTLIL::Const(children[0]->bits), dummy_arg, children[0]->is_signed, false, -1); - newNode = mkconst_bits(y.bits, false); + newNode = mkconst_bits(y.to_bits(), false); } else if (children[0]->isConst()) { newNode = mkconst_int(children[0]->asReal(sign_hint) == 0, false, 1); @@ -4159,7 +4217,7 @@ replace_fcall_later:; if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) { RTLIL::Const y = const_func(RTLIL::Const(children[0]->bits), RTLIL::Const(children[1]->bits), children[0]->is_signed, children[1]->is_signed, -1); - newNode = mkconst_bits(y.bits, false); + newNode = mkconst_bits(y.to_bits(), false); } else if (children[0]->isConst() && children[1]->isConst()) { if (type == AST_LOGIC_AND) @@ -4176,7 +4234,7 @@ replace_fcall_later:; if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) { RTLIL::Const y = const_func(children[0]->bitsAsConst(width_hint, sign_hint), RTLIL::Const(children[1]->bits), sign_hint, type == AST_POW ? children[1]->is_signed : false, width_hint); - newNode = mkconst_bits(y.bits, sign_hint); + newNode = mkconst_bits(y.to_bits(), sign_hint); } else if (type == AST_POW && children[0]->isConst() && children[1]->isConst()) { newNode = new AstNode(AST_REALVALUE); @@ -4196,7 +4254,7 @@ replace_fcall_later:; bool cmp_signed = children[0]->is_signed && children[1]->is_signed; RTLIL::Const y = const_func(children[0]->bitsAsConst(cmp_width, cmp_signed), children[1]->bitsAsConst(cmp_width, cmp_signed), cmp_signed, cmp_signed, 1); - newNode = mkconst_bits(y.bits, false); + newNode = mkconst_bits(y.to_bits(), false); } else if (children[0]->isConst() && children[1]->isConst()) { bool cmp_signed = (children[0]->type == AST_REALVALUE || children[0]->is_signed) && (children[1]->type == AST_REALVALUE || children[1]->is_signed); @@ -4221,7 +4279,7 @@ replace_fcall_later:; if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) { RTLIL::Const y = const_func(children[0]->bitsAsConst(width_hint, sign_hint), children[1]->bitsAsConst(width_hint, sign_hint), sign_hint, sign_hint, width_hint); - newNode = mkconst_bits(y.bits, sign_hint); + newNode = mkconst_bits(y.to_bits(), sign_hint); } else if (children[0]->isConst() && children[1]->isConst()) { newNode = new AstNode(AST_REALVALUE); @@ -4240,7 +4298,7 @@ replace_fcall_later:; if (0) { case AST_NEG: const_func = RTLIL::const_neg; } if (children[0]->type == AST_CONSTANT) { RTLIL::Const y = const_func(children[0]->bitsAsConst(width_hint, sign_hint), dummy_arg, sign_hint, false, width_hint); - newNode = mkconst_bits(y.bits, sign_hint); + newNode = mkconst_bits(y.to_bits(), sign_hint); } else if (children[0]->isConst()) { newNode = new AstNode(AST_REALVALUE); @@ -4268,10 +4326,10 @@ replace_fcall_later:; newNode->realvalue = choice->asReal(sign_hint); } else { RTLIL::Const y = choice->bitsAsConst(width_hint, sign_hint); - if (choice->is_string && y.bits.size() % 8 == 0 && sign_hint == false) - newNode = mkconst_str(y.bits); + if (choice->is_string && y.size() % 8 == 0 && sign_hint == false) + newNode = mkconst_str(y.to_bits()); else - newNode = mkconst_bits(y.bits, sign_hint); + newNode = mkconst_bits(y.to_bits(), sign_hint); } } else if (choice->isConst()) { @@ -4280,11 +4338,11 @@ replace_fcall_later:; } else if (children[1]->type == AST_CONSTANT && children[2]->type == AST_CONSTANT) { RTLIL::Const a = children[1]->bitsAsConst(width_hint, sign_hint); RTLIL::Const b = children[2]->bitsAsConst(width_hint, sign_hint); - log_assert(a.bits.size() == b.bits.size()); - for (size_t i = 0; i < a.bits.size(); i++) - if (a.bits[i] != b.bits[i]) - a.bits[i] = RTLIL::State::Sx; - newNode = mkconst_bits(a.bits, sign_hint); + log_assert(a.size() == b.size()); + for (size_t i = 0; i < a.size(); i++) + if (a[i] != b[i]) + a.bits()[i] = RTLIL::State::Sx; + newNode = mkconst_bits(a.to_bits(), sign_hint); } else if (children[1]->isConst() && children[2]->isConst()) { newNode = new AstNode(AST_REALVALUE); if (children[1]->asReal(sign_hint) == children[2]->asReal(sign_hint)) @@ -4305,7 +4363,7 @@ replace_fcall_later:; val = children[1]->bitsAsUnsizedConst(width); else val = children[1]->bitsAsConst(width); - newNode = mkconst_bits(val.bits, children[1]->is_signed); + newNode = mkconst_bits(val.to_bits(), children[1]->is_signed); } break; case AST_CONCAT: @@ -4890,7 +4948,7 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, target->str = str; target->id2ast = id2ast; target->was_checked = true; - block->children.push_back(new AstNode(AST_ASSIGN_EQ, target, mkconst_bits(data.extract(i*wordsz + pos, clen).bits, false))); + block->children.push_back(new AstNode(AST_ASSIGN_EQ, target, mkconst_bits(data.extract(i*wordsz + pos, clen).to_bits(), false))); pos = epos; } } @@ -5245,7 +5303,7 @@ bool AstNode::is_simple_const_expr() bool AstNode::replace_variables(std::map &variables, AstNode *fcall, bool must_succeed) { if (type == AST_IDENTIFIER && variables.count(str)) { - int offset = variables.at(str).offset, width = variables.at(str).val.bits.size(); + int offset = variables.at(str).offset, width = variables.at(str).val.size(); if (!children.empty()) { if (children.size() != 1 || children.at(0)->type != AST_RANGE) { if (!must_succeed) @@ -5268,7 +5326,7 @@ bool AstNode::replace_variables(std::map &varia offset -= variables.at(str).offset; if (variables.at(str).range_swapped) offset = -offset; - std::vector &var_bits = variables.at(str).val.bits; + std::vector &var_bits = variables.at(str).val.bits(); std::vector new_bits(var_bits.begin() + offset, var_bits.begin() + offset + width); AstNode *newNode = mkconst_bits(new_bits, variables.at(str).is_signed); newNode->cloneInto(this); @@ -5399,7 +5457,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed) } if (stmt->children.at(0)->children.empty()) { - variables[stmt->children.at(0)->str].val = stmt->children.at(1)->bitsAsConst(variables[stmt->children.at(0)->str].val.bits.size()); + variables[stmt->children.at(0)->str].val = stmt->children.at(1)->bitsAsConst(variables[stmt->children.at(0)->str].val.size()); } else { AstNode *range = stmt->children.at(0)->children.at(0); if (!range->range_valid) { @@ -5410,12 +5468,12 @@ AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed) int offset = min(range->range_left, range->range_right); int width = std::abs(range->range_left - range->range_right) + 1; varinfo_t &v = variables[stmt->children.at(0)->str]; - RTLIL::Const r = stmt->children.at(1)->bitsAsConst(v.val.bits.size()); + RTLIL::Const r = stmt->children.at(1)->bitsAsConst(v.val.size()); for (int i = 0; i < width; i++) { int index = i + offset - v.offset; if (v.range_swapped) index = -index; - v.val.bits.at(index) = r.bits.at(i); + v.val.bits().at(index) = r.at(i); } } @@ -5558,7 +5616,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed) log_abort(); } - result = AstNode::mkconst_bits(variables.at(str).val.bits, variables.at(str).is_signed); + result = AstNode::mkconst_bits(variables.at(str).val.to_bits(), variables.at(str).is_signed); finished: delete block; diff --git a/frontends/blif/blifparse.cc b/frontends/blif/blifparse.cc index 731656866..f6b894563 100644 --- a/frontends/blif/blifparse.cc +++ b/frontends/blif/blifparse.cc @@ -149,7 +149,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool if (buffer[0] == '.') { if (lutptr) { - for (auto &bit : lutptr->bits) + for (auto &bit : lutptr->bits()) if (bit == RTLIL::State::Sx) bit = lut_default_state; lutptr = NULL; @@ -321,9 +321,9 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool const_v = Const(str); } else { int n = strlen(v); - const_v.bits.resize(n); + const_v.bits().resize(n); for (int i = 0; i < n; i++) - const_v.bits[i] = v[n-i-1] != '0' ? State::S1 : State::S0; + const_v.bits()[i] = v[n-i-1] != '0' ? State::S1 : State::S0; } if (!strcmp(cmd, ".attr")) { if (obj_attributes == nullptr) { @@ -566,16 +566,16 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool for (int i = 0; i < input_len; i++) switch (input[i]) { case '0': - sopcell->parameters[ID::TABLE].bits.push_back(State::S1); - sopcell->parameters[ID::TABLE].bits.push_back(State::S0); + sopcell->parameters[ID::TABLE].bits().push_back(State::S1); + sopcell->parameters[ID::TABLE].bits().push_back(State::S0); break; case '1': - sopcell->parameters[ID::TABLE].bits.push_back(State::S0); - sopcell->parameters[ID::TABLE].bits.push_back(State::S1); + sopcell->parameters[ID::TABLE].bits().push_back(State::S0); + sopcell->parameters[ID::TABLE].bits().push_back(State::S1); break; default: - sopcell->parameters[ID::TABLE].bits.push_back(State::S0); - sopcell->parameters[ID::TABLE].bits.push_back(State::S0); + sopcell->parameters[ID::TABLE].bits().push_back(State::S0); + sopcell->parameters[ID::TABLE].bits().push_back(State::S0); break; } @@ -605,7 +605,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool goto try_next_value; } } - lutptr->bits.at(i) = !strcmp(output, "0") ? RTLIL::State::S0 : RTLIL::State::S1; + lutptr->bits().at(i) = !strcmp(output, "0") ? RTLIL::State::S0 : RTLIL::State::S1; try_next_value:; } diff --git a/frontends/liberty/liberty.cc b/frontends/liberty/liberty.cc index 16fed54f2..e183cbf10 100644 --- a/frontends/liberty/liberty.cc +++ b/frontends/liberty/liberty.cc @@ -465,6 +465,9 @@ struct LibertyFrontend : public Frontend { log(" -setattr \n"); log(" set the specified attribute (to the value 1) on all loaded modules\n"); log("\n"); + log(" -unit_delay\n"); + log(" import combinational timing arcs under the unit delay model\n"); + log("\n"); } void execute(std::istream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { @@ -475,6 +478,7 @@ struct LibertyFrontend : public Frontend { bool flag_ignore_miss_func = false; bool flag_ignore_miss_dir = false; bool flag_ignore_miss_data_latch = false; + bool flag_unit_delay = false; std::vector attributes; size_t argidx; @@ -514,6 +518,10 @@ struct LibertyFrontend : public Frontend { attributes.push_back(RTLIL::escape_id(args[++argidx])); continue; } + if (arg == "-unit_delay") { + flag_unit_delay = true; + continue; + } break; } extra_args(f, filename, args, argidx); @@ -652,6 +660,7 @@ struct LibertyFrontend : public Frontend { continue; RTLIL::Wire *wire = module->wires_.at(RTLIL::escape_id(node->args.at(0))); + log_assert(wire); if (dir && dir->value == "inout") { wire->port_input = true; @@ -690,6 +699,43 @@ struct LibertyFrontend : public Frontend { } module->connect(RTLIL::SigSig(wire, out_sig)); } + + if (flag_unit_delay) { + pool done; + + for (auto timing : node->children) + if (timing->id == "timing" && timing->args.empty()) { + auto type = timing->find("timing_type"); + auto related_pin = timing->find("related_pin"); + if (!type || type->value != "combinational" || !related_pin) + continue; + + Wire *related = module->wire(RTLIL::escape_id(related_pin->value)); + if (!related) + log_error("Failed to find related pin %s for timing of pin %s on %s\n", + related_pin->value.c_str(), log_id(wire), log_id(module)); + + if (done.count(related)) + continue; + + RTLIL::Cell *spec = module->addCell(NEW_ID, ID($specify2)); + spec->setParam(ID::SRC_WIDTH, 1); + spec->setParam(ID::DST_WIDTH, 1); + spec->setParam(ID::T_FALL_MAX, 1000); + spec->setParam(ID::T_FALL_TYP, 1000); + spec->setParam(ID::T_FALL_MIN, 1000); + spec->setParam(ID::T_RISE_MAX, 1000); + spec->setParam(ID::T_RISE_TYP, 1000); + spec->setParam(ID::T_RISE_MIN, 1000); + spec->setParam(ID::SRC_DST_POL, false); + spec->setParam(ID::SRC_DST_PEN, false); + spec->setParam(ID::FULL, false); + spec->setPort(ID::EN, Const(1, 1)); + spec->setPort(ID::SRC, related); + spec->setPort(ID::DST, wire); + done.insert(related); + } + } } } diff --git a/frontends/rtlil/rtlil_parser.y b/frontends/rtlil/rtlil_parser.y index 3d9862ebb..deb37d9a6 100644 --- a/frontends/rtlil/rtlil_parser.y +++ b/frontends/rtlil/rtlil_parser.y @@ -447,7 +447,7 @@ constant: bits.pop_back(); $$ = new RTLIL::Const; for (auto it = bits.begin(); it != bits.end(); it++) - $$->bits.push_back(*it); + $$->bits().push_back(*it); if (is_signed) { $$->flags |= RTLIL::CONST_FLAG_SIGNED; } diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index 2dd8aa095..0cdf772a8 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -236,23 +236,6 @@ RTLIL::IdString VerificImporter::new_verific_id(Verific::DesignObj *obj) return s; } -RTLIL::Const mkconst_str(const std::string &str) -{ - RTLIL::Const val; - std::vector data; - data.reserve(str.size() * 8); - for (size_t i = 0; i < str.size(); i++) { - unsigned char ch = str[str.size() - i - 1]; - for (int j = 0; j < 8; j++) { - data.push_back((ch & 1) ? State::S1 : State::S0); - ch = ch >> 1; - } - } - val.bits = data; - val.flags |= RTLIL::CONST_FLAG_STRING; - return val; -} - static const RTLIL::Const extract_vhdl_boolean(std::string &val) { if (val == "false") @@ -295,7 +278,7 @@ static const RTLIL::Const extract_vhdl_char(std::string &val) static const RTLIL::Const extract_real_value(std::string &val) { - RTLIL::Const c = mkconst_str(val); + RTLIL::Const c(val); c.flags |= RTLIL::CONST_FLAG_REAL; return c; } @@ -333,7 +316,7 @@ static const RTLIL::Const extract_vhdl_const(const char *value, bool output_sig } else if (val == "true") { c = RTLIL::Const::from_string("1"); } else { - c = mkconst_str(val); + c = RTLIL::Const(val); log_warning("encoding value '%s' as string.\n", value); } if (is_signed) @@ -364,7 +347,7 @@ static const RTLIL::Const extract_verilog_const(const char *value, bool allow_s } else if (allow_string) { c = RTLIL::Const(val); } else { - c = mkconst_str(val); + c = RTLIL::Const(val); log_warning("encoding value '%s' as string.\n", value); } if (is_signed) @@ -450,6 +433,19 @@ void VerificImporter::import_attributes(dict &att auto type_range = nl->GetTypeRange(obj->Name()); if (!type_range) return; + if (type_range->IsTypeScalar()) { + const long long bottom_bound = type_range->GetScalarRangeLeftBound(); + const long long top_bound = type_range->GetScalarRangeRightBound(); + const unsigned bit_width = type_range->NumElements(); + RTLIL::Const bottom_const(bottom_bound, bit_width); + RTLIL::Const top_const(top_bound, bit_width); + if (bottom_bound < 0 || top_bound < 0) { + bottom_const.flags |= RTLIL::CONST_FLAG_SIGNED; + top_const.flags |= RTLIL::CONST_FLAG_SIGNED; + } + attributes.emplace(ID(bottom_bound), bottom_const); + attributes.emplace(ID(top_bound), top_const); + } if (!type_range->IsTypeEnum()) return; #ifdef VERIFIC_VHDL_SUPPORT @@ -1621,7 +1617,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma if (*ascii_initdata == 0) break; if (*ascii_initdata == '0' || *ascii_initdata == '1') { - initval[bit_idx] = (*ascii_initdata == '0') ? State::S0 : State::S1; + initval.bits()[bit_idx] = (*ascii_initdata == '0') ? State::S0 : State::S1; initval_valid = true; } ascii_initdata++; @@ -1743,9 +1739,9 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma if (init_nets.count(net)) { if (init_nets.at(net) == '0') - initval.bits.at(bitidx) = State::S0; + initval.bits().at(bitidx) = State::S0; if (init_nets.at(net) == '1') - initval.bits.at(bitidx) = State::S1; + initval.bits().at(bitidx) = State::S1; initval_valid = true; init_nets.erase(net); } @@ -1819,12 +1815,12 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma initval = bit.wire->attributes.at(ID::init); while (GetSize(initval) < GetSize(bit.wire)) - initval.bits.push_back(State::Sx); + initval.bits().push_back(State::Sx); if (it.second == '0') - initval.bits.at(bit.offset) = State::S0; + initval.bits().at(bit.offset) = State::S0; if (it.second == '1') - initval.bits.at(bit.offset) = State::S1; + initval.bits().at(bit.offset) = State::S1; bit.wire->attributes[ID::init] = initval; } @@ -2011,7 +2007,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma } Const qx_init = Const(State::S1, width); - qx_init.bits.resize(2 * width, State::S0); + qx_init.bits().resize(2 * width, State::S0); clocking.addDff(new_verific_id(inst), sig_dx, sig_qx, qx_init); module->addXnor(new_verific_id(inst), sig_dx, sig_qx, sig_ox); @@ -2129,13 +2125,12 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma if (verific_verbose) log(" assert condition %s.\n", log_signal(cond)); - const char *assume_attr = nullptr; // inst->GetAttValue("assume"); - - Cell *cell = nullptr; - if (assume_attr != nullptr && !strcmp(assume_attr, "1")) - cell = module->addAssume(new_verific_id(inst), cond, State::S1); - else - cell = module->addAssert(new_verific_id(inst), cond, State::S1); + Cell *cell = module->addAssert(new_verific_id(inst), cond, State::S1); + // Initialize FF feeding condition to 1, in case it is not + // used by rest of design logic, to prevent failing on + // initial uninitialized state + if (cond.is_wire() && !cond.wire->name.isPublic()) + cond.wire->attributes[ID::init] = Const(1,1); import_attributes(cell->attributes, inst); continue; @@ -2282,7 +2277,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma continue; if (non_ff_bits.count(SigBit(wire, i))) - initval[i] = State::Sx; + initval.bits()[i] = State::Sx; } if (wire->port_input) { @@ -2469,7 +2464,7 @@ Cell *VerificClocking::addDff(IdString name, SigSpec sig_d, SigSpec sig_q, Const if (c.wire && c.wire->attributes.count(ID::init)) { Const val = c.wire->attributes.at(ID::init); for (int i = 0; i < GetSize(c); i++) - initval[offset+i] = val[c.offset+i]; + initval.bits()[offset+i] = val[c.offset+i]; } offset += GetSize(c); } @@ -2540,7 +2535,7 @@ Cell *VerificClocking::addAldff(IdString name, RTLIL::SigSpec sig_aload, RTLIL:: if (c.wire && c.wire->attributes.count(ID::init)) { Const val = c.wire->attributes.at(ID::init); for (int i = 0; i < GetSize(c); i++) - initval[offset+i] = val[c.offset+i]; + initval.bits()[offset+i] = val[c.offset+i]; } offset += GetSize(c); } diff --git a/frontends/verific/verificsva.cc b/frontends/verific/verificsva.cc index b219c0165..ef8247e83 100644 --- a/frontends/verific/verificsva.cc +++ b/frontends/verific/verificsva.cc @@ -575,7 +575,7 @@ struct SvaFsm if (delta_pos >= 0 && i_within_j && j_within_i) { did_something = true; - values[i][delta_pos] = State::Sa; + values[i].bits()[delta_pos] = State::Sa; values[j] = values.back(); values.pop_back(); goto next_pair; diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index 15a04eb28..fe86626b8 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -464,6 +464,7 @@ static const AstNode *addAsgnBinopStmt(dict *attr, AstNode * %% input: { + (void)frontend_verilog_yynerrs; ast_stack.clear(); ast_stack.push_back(current_ast); } design { @@ -3506,6 +3507,12 @@ basic_expr: $$ = new AstNode(AST_CAST_SIZE, $1, $4); SET_AST_NODE_LOC($$, @1, @4); } | + typedef_base_type OP_CAST '(' expr ')' { + if (!sv_mode) + frontend_verilog_yyerror("Static cast is only supported in SystemVerilog mode."); + $$ = new AstNode(AST_CAST_SIZE, $1, $4); + SET_AST_NODE_LOC($$, @1, @4); + } | '(' expr '=' expr ')' { ensureAsgnExprAllowed(); AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, $4); diff --git a/kernel/bitpattern.h b/kernel/bitpattern.h index 7a8eb39f9..c1ceac14c 100644 --- a/kernel/bitpattern.h +++ b/kernel/bitpattern.h @@ -80,7 +80,7 @@ struct BitPatternPool bits_t sig2bits(RTLIL::SigSpec sig) { bits_t bits; - bits.bitdata = sig.as_const().bits; + bits.bitdata = sig.as_const().bits(); for (auto &b : bits.bitdata) if (b > RTLIL::State::S1) b = RTLIL::State::Sa; diff --git a/kernel/calc.cc b/kernel/calc.cc index 9b02a6e30..172b6a905 100644 --- a/kernel/calc.cc +++ b/kernel/calc.cc @@ -30,13 +30,13 @@ static void extend_u0(RTLIL::Const &arg, int width, bool is_signed) { RTLIL::State padding = RTLIL::State::S0; - if (arg.bits.size() > 0 && is_signed) - padding = arg.bits.back(); + if (arg.size() > 0 && is_signed) + padding = arg.back(); - while (int(arg.bits.size()) < width) - arg.bits.push_back(padding); + while (int(arg.size()) < width) + arg.bits().push_back(padding); - arg.bits.resize(width); + arg.bits().resize(width); } static BigInteger const2big(const RTLIL::Const &val, bool as_signed, int &undef_bit_pos) @@ -45,17 +45,17 @@ static BigInteger const2big(const RTLIL::Const &val, bool as_signed, int &undef_ BigInteger::Sign sign = BigInteger::positive; State inv_sign_bit = RTLIL::State::S1; - size_t num_bits = val.bits.size(); + size_t num_bits = val.size(); - if (as_signed && num_bits && val.bits[num_bits-1] == RTLIL::State::S1) { + if (as_signed && num_bits && val[num_bits-1] == RTLIL::State::S1) { inv_sign_bit = RTLIL::State::S0; sign = BigInteger::negative; num_bits--; } for (size_t i = 0; i < num_bits; i++) - if (val.bits[i] == RTLIL::State::S0 || val.bits[i] == RTLIL::State::S1) - mag.setBit(i, val.bits[i] == inv_sign_bit); + if (val[i] == RTLIL::State::S0 || val[i] == RTLIL::State::S1) + mag.setBit(i, val[i] == inv_sign_bit); else if (undef_bit_pos < 0) undef_bit_pos = i; @@ -79,19 +79,19 @@ static RTLIL::Const big2const(const BigInteger &val, int result_len, int undef_b { mag--; for (int i = 0; i < result_len; i++) - result.bits[i] = mag.getBit(i) ? RTLIL::State::S0 : RTLIL::State::S1; + result.bits()[i] = mag.getBit(i) ? RTLIL::State::S0 : RTLIL::State::S1; } else { for (int i = 0; i < result_len; i++) - result.bits[i] = mag.getBit(i) ? RTLIL::State::S1 : RTLIL::State::S0; + result.bits()[i] = mag.getBit(i) ? RTLIL::State::S1 : RTLIL::State::S0; } } #if 0 if (undef_bit_pos >= 0) for (int i = undef_bit_pos; i < result_len; i++) - result.bits[i] = RTLIL::State::Sx; + result[i] = RTLIL::State::Sx; #endif return result; @@ -132,19 +132,19 @@ static RTLIL::State logic_xnor(RTLIL::State a, RTLIL::State b) RTLIL::Const RTLIL::const_not(const RTLIL::Const &arg1, const RTLIL::Const&, bool signed1, bool, int result_len) { if (result_len < 0) - result_len = arg1.bits.size(); + result_len = arg1.size(); RTLIL::Const arg1_ext = arg1; extend_u0(arg1_ext, result_len, signed1); RTLIL::Const result(RTLIL::State::Sx, result_len); for (size_t i = 0; i < size_t(result_len); i++) { - if (i >= arg1_ext.bits.size()) - result.bits[i] = RTLIL::State::S0; - else if (arg1_ext.bits[i] == RTLIL::State::S0) - result.bits[i] = RTLIL::State::S1; - else if (arg1_ext.bits[i] == RTLIL::State::S1) - result.bits[i] = RTLIL::State::S0; + if (i >= arg1_ext.size()) + result.bits()[i] = RTLIL::State::S0; + else if (arg1_ext.bits()[i] == RTLIL::State::S0) + result.bits()[i] = RTLIL::State::S1; + else if (arg1_ext.bits()[i] == RTLIL::State::S1) + result.bits()[i] = RTLIL::State::S0; } return result; @@ -154,16 +154,16 @@ static RTLIL::Const logic_wrapper(RTLIL::State(*logic_func)(RTLIL::State, RTLIL: RTLIL::Const arg1, RTLIL::Const arg2, bool signed1, bool signed2, int result_len = -1) { if (result_len < 0) - result_len = max(arg1.bits.size(), arg2.bits.size()); + result_len = max(arg1.size(), arg2.size()); extend_u0(arg1, result_len, signed1); extend_u0(arg2, result_len, signed2); RTLIL::Const result(RTLIL::State::Sx, result_len); for (size_t i = 0; i < size_t(result_len); i++) { - RTLIL::State a = i < arg1.bits.size() ? arg1.bits[i] : RTLIL::State::S0; - RTLIL::State b = i < arg2.bits.size() ? arg2.bits[i] : RTLIL::State::S0; - result.bits[i] = logic_func(a, b); + RTLIL::State a = i < arg1.size() ? arg1.bits()[i] : RTLIL::State::S0; + RTLIL::State b = i < arg2.size() ? arg2.bits()[i] : RTLIL::State::S0; + result.bits()[i] = logic_func(a, b); } return result; @@ -193,12 +193,12 @@ static RTLIL::Const logic_reduce_wrapper(RTLIL::State initial, RTLIL::State(*log { RTLIL::State temp = initial; - for (size_t i = 0; i < arg1.bits.size(); i++) - temp = logic_func(temp, arg1.bits[i]); + for (size_t i = 0; i < arg1.size(); i++) + temp = logic_func(temp, arg1[i]); RTLIL::Const result(temp); - while (int(result.bits.size()) < result_len) - result.bits.push_back(RTLIL::State::S0); + while (int(result.size()) < result_len) + result.bits().push_back(RTLIL::State::S0); return result; } @@ -220,11 +220,11 @@ RTLIL::Const RTLIL::const_reduce_xor(const RTLIL::Const &arg1, const RTLIL::Cons RTLIL::Const RTLIL::const_reduce_xnor(const RTLIL::Const &arg1, const RTLIL::Const&, bool, bool, int result_len) { RTLIL::Const buffer = logic_reduce_wrapper(RTLIL::State::S0, logic_xor, arg1, result_len); - if (!buffer.bits.empty()) { - if (buffer.bits.front() == RTLIL::State::S0) - buffer.bits.front() = RTLIL::State::S1; - else if (buffer.bits.front() == RTLIL::State::S1) - buffer.bits.front() = RTLIL::State::S0; + if (!buffer.empty()) { + if (buffer.front() == RTLIL::State::S0) + buffer.bits().front() = RTLIL::State::S1; + else if (buffer.front() == RTLIL::State::S1) + buffer.bits().front() = RTLIL::State::S0; } return buffer; } @@ -240,8 +240,8 @@ RTLIL::Const RTLIL::const_logic_not(const RTLIL::Const &arg1, const RTLIL::Const BigInteger a = const2big(arg1, signed1, undef_bit_pos_a); RTLIL::Const result(a.isZero() ? undef_bit_pos_a >= 0 ? RTLIL::State::Sx : RTLIL::State::S1 : RTLIL::State::S0); - while (int(result.bits.size()) < result_len) - result.bits.push_back(RTLIL::State::S0); + while (int(result.size()) < result_len) + result.bits().push_back(RTLIL::State::S0); return result; } @@ -255,8 +255,8 @@ RTLIL::Const RTLIL::const_logic_and(const RTLIL::Const &arg1, const RTLIL::Const RTLIL::State bit_b = b.isZero() ? undef_bit_pos_b >= 0 ? RTLIL::State::Sx : RTLIL::State::S0 : RTLIL::State::S1; RTLIL::Const result(logic_and(bit_a, bit_b)); - while (int(result.bits.size()) < result_len) - result.bits.push_back(RTLIL::State::S0); + while (int(result.size()) < result_len) + result.bits().push_back(RTLIL::State::S0); return result; } @@ -270,8 +270,8 @@ RTLIL::Const RTLIL::const_logic_or(const RTLIL::Const &arg1, const RTLIL::Const RTLIL::State bit_b = b.isZero() ? undef_bit_pos_b >= 0 ? RTLIL::State::Sx : RTLIL::State::S0 : RTLIL::State::S1; RTLIL::Const result(logic_or(bit_a, bit_b)); - while (int(result.bits.size()) < result_len) - result.bits.push_back(RTLIL::State::S0); + while (int(result.size()) < result_len) + result.bits().push_back(RTLIL::State::S0); return result; } @@ -286,7 +286,7 @@ static RTLIL::Const const_shift_worker(const RTLIL::Const &arg1, const RTLIL::Co BigInteger offset = const2big(arg2, signed2, undef_bit_pos) * direction; if (result_len < 0) - result_len = arg1.bits.size(); + result_len = arg1.size(); RTLIL::Const result(RTLIL::State::Sx, result_len); if (undef_bit_pos >= 0) @@ -295,11 +295,11 @@ static RTLIL::Const const_shift_worker(const RTLIL::Const &arg1, const RTLIL::Co for (int i = 0; i < result_len; i++) { BigInteger pos = BigInteger(i) + offset; if (pos < 0) - result.bits[i] = vacant_bits; - else if (pos >= BigInteger(int(arg1.bits.size()))) - result.bits[i] = sign_ext ? arg1.bits.back() : vacant_bits; + result.bits()[i] = vacant_bits; + else if (pos >= BigInteger(int(arg1.size()))) + result.bits()[i] = sign_ext ? arg1.back() : vacant_bits; else - result.bits[i] = arg1.bits[pos.toInt()]; + result.bits()[i] = arg1[pos.toInt()]; } return result; @@ -347,8 +347,8 @@ RTLIL::Const RTLIL::const_lt(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool y = const2big(arg1, signed1, undef_bit_pos) < const2big(arg2, signed2, undef_bit_pos); RTLIL::Const result(undef_bit_pos >= 0 ? RTLIL::State::Sx : y ? RTLIL::State::S1 : RTLIL::State::S0); - while (int(result.bits.size()) < result_len) - result.bits.push_back(RTLIL::State::S0); + while (int(result.size()) < result_len) + result.bits().push_back(RTLIL::State::S0); return result; } @@ -358,8 +358,8 @@ RTLIL::Const RTLIL::const_le(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool y = const2big(arg1, signed1, undef_bit_pos) <= const2big(arg2, signed2, undef_bit_pos); RTLIL::Const result(undef_bit_pos >= 0 ? RTLIL::State::Sx : y ? RTLIL::State::S1 : RTLIL::State::S0); - while (int(result.bits.size()) < result_len) - result.bits.push_back(RTLIL::State::S0); + while (int(result.size()) < result_len) + result.bits().push_back(RTLIL::State::S0); return result; } @@ -369,31 +369,31 @@ RTLIL::Const RTLIL::const_eq(const RTLIL::Const &arg1, const RTLIL::Const &arg2, RTLIL::Const arg2_ext = arg2; RTLIL::Const result(RTLIL::State::S0, result_len); - int width = max(arg1_ext.bits.size(), arg2_ext.bits.size()); + int width = max(arg1_ext.size(), arg2_ext.size()); extend_u0(arg1_ext, width, signed1 && signed2); extend_u0(arg2_ext, width, signed1 && signed2); RTLIL::State matched_status = RTLIL::State::S1; - for (size_t i = 0; i < arg1_ext.bits.size(); i++) { - if (arg1_ext.bits.at(i) == RTLIL::State::S0 && arg2_ext.bits.at(i) == RTLIL::State::S1) + for (size_t i = 0; i < arg1_ext.size(); i++) { + if (arg1_ext.at(i) == RTLIL::State::S0 && arg2_ext.at(i) == RTLIL::State::S1) return result; - if (arg1_ext.bits.at(i) == RTLIL::State::S1 && arg2_ext.bits.at(i) == RTLIL::State::S0) + if (arg1_ext.at(i) == RTLIL::State::S1 && arg2_ext.at(i) == RTLIL::State::S0) return result; - if (arg1_ext.bits.at(i) > RTLIL::State::S1 || arg2_ext.bits.at(i) > RTLIL::State::S1) + if (arg1_ext.at(i) > RTLIL::State::S1 || arg2_ext.at(i) > RTLIL::State::S1) matched_status = RTLIL::State::Sx; } - result.bits.front() = matched_status; + result.bits().front() = matched_status; return result; } RTLIL::Const RTLIL::const_ne(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) { RTLIL::Const result = RTLIL::const_eq(arg1, arg2, signed1, signed2, result_len); - if (result.bits.front() == RTLIL::State::S0) - result.bits.front() = RTLIL::State::S1; - else if (result.bits.front() == RTLIL::State::S1) - result.bits.front() = RTLIL::State::S0; + if (result.front() == RTLIL::State::S0) + result.bits().front() = RTLIL::State::S1; + else if (result.front() == RTLIL::State::S1) + result.bits().front() = RTLIL::State::S0; return result; } @@ -403,26 +403,26 @@ RTLIL::Const RTLIL::const_eqx(const RTLIL::Const &arg1, const RTLIL::Const &arg2 RTLIL::Const arg2_ext = arg2; RTLIL::Const result(RTLIL::State::S0, result_len); - int width = max(arg1_ext.bits.size(), arg2_ext.bits.size()); + int width = max(arg1_ext.size(), arg2_ext.size()); extend_u0(arg1_ext, width, signed1 && signed2); extend_u0(arg2_ext, width, signed1 && signed2); - for (size_t i = 0; i < arg1_ext.bits.size(); i++) { - if (arg1_ext.bits.at(i) != arg2_ext.bits.at(i)) + for (size_t i = 0; i < arg1_ext.size(); i++) { + if (arg1_ext.at(i) != arg2_ext.at(i)) return result; } - result.bits.front() = RTLIL::State::S1; + result.bits().front() = RTLIL::State::S1; return result; } RTLIL::Const RTLIL::const_nex(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) { RTLIL::Const result = RTLIL::const_eqx(arg1, arg2, signed1, signed2, result_len); - if (result.bits.front() == RTLIL::State::S0) - result.bits.front() = RTLIL::State::S1; - else if (result.bits.front() == RTLIL::State::S1) - result.bits.front() = RTLIL::State::S0; + if (result.front() == RTLIL::State::S0) + result.bits().front() = RTLIL::State::S1; + else if (result.front() == RTLIL::State::S1) + result.bits().front() = RTLIL::State::S0; return result; } @@ -432,8 +432,8 @@ RTLIL::Const RTLIL::const_ge(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool y = const2big(arg1, signed1, undef_bit_pos) >= const2big(arg2, signed2, undef_bit_pos); RTLIL::Const result(undef_bit_pos >= 0 ? RTLIL::State::Sx : y ? RTLIL::State::S1 : RTLIL::State::S0); - while (int(result.bits.size()) < result_len) - result.bits.push_back(RTLIL::State::S0); + while (int(result.size()) < result_len) + result.bits().push_back(RTLIL::State::S0); return result; } @@ -443,8 +443,8 @@ RTLIL::Const RTLIL::const_gt(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool y = const2big(arg1, signed1, undef_bit_pos) > const2big(arg2, signed2, undef_bit_pos); RTLIL::Const result(undef_bit_pos >= 0 ? RTLIL::State::Sx : y ? RTLIL::State::S1 : RTLIL::State::S0); - while (int(result.bits.size()) < result_len) - result.bits.push_back(RTLIL::State::S0); + while (int(result.size()) < result_len) + result.bits().push_back(RTLIL::State::S0); return result; } @@ -452,21 +452,21 @@ RTLIL::Const RTLIL::const_add(const RTLIL::Const &arg1, const RTLIL::Const &arg2 { int undef_bit_pos = -1; BigInteger y = const2big(arg1, signed1, undef_bit_pos) + const2big(arg2, signed2, undef_bit_pos); - return big2const(y, result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), undef_bit_pos); + return big2const(y, result_len >= 0 ? result_len : max(arg1.size(), arg2.size()), undef_bit_pos); } RTLIL::Const RTLIL::const_sub(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) { int undef_bit_pos = -1; BigInteger y = const2big(arg1, signed1, undef_bit_pos) - const2big(arg2, signed2, undef_bit_pos); - return big2const(y, result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), undef_bit_pos); + return big2const(y, result_len >= 0 ? result_len : max(arg1.size(), arg2.size()), undef_bit_pos); } RTLIL::Const RTLIL::const_mul(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) { int undef_bit_pos = -1; BigInteger y = const2big(arg1, signed1, undef_bit_pos) * const2big(arg2, signed2, undef_bit_pos); - return big2const(y, result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), min(undef_bit_pos, 0)); + return big2const(y, result_len >= 0 ? result_len : max(arg1.size(), arg2.size()), min(undef_bit_pos, 0)); } // truncating division @@ -480,7 +480,7 @@ RTLIL::Const RTLIL::const_div(const RTLIL::Const &arg1, const RTLIL::Const &arg2 bool result_neg = (a.getSign() == BigInteger::negative) != (b.getSign() == BigInteger::negative); a = a.getSign() == BigInteger::negative ? -a : a; b = b.getSign() == BigInteger::negative ? -b : b; - return big2const(result_neg ? -(a / b) : (a / b), result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), min(undef_bit_pos, 0)); + return big2const(result_neg ? -(a / b) : (a / b), result_len >= 0 ? result_len : max(arg1.size(), arg2.size()), min(undef_bit_pos, 0)); } // truncating modulo @@ -494,7 +494,7 @@ RTLIL::Const RTLIL::const_mod(const RTLIL::Const &arg1, const RTLIL::Const &arg2 bool result_neg = a.getSign() == BigInteger::negative; a = a.getSign() == BigInteger::negative ? -a : a; b = b.getSign() == BigInteger::negative ? -b : b; - return big2const(result_neg ? -(a % b) : (a % b), result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), min(undef_bit_pos, 0)); + return big2const(result_neg ? -(a % b) : (a % b), result_len >= 0 ? result_len : max(arg1.size(), arg2.size()), min(undef_bit_pos, 0)); } RTLIL::Const RTLIL::const_divfloor(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) @@ -516,7 +516,7 @@ RTLIL::Const RTLIL::const_divfloor(const RTLIL::Const &arg1, const RTLIL::Const // bigint division with negative numbers is wonky, make sure we only negate at the very end result = -((a + b - 1) / b); } - return big2const(result, result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), min(undef_bit_pos, 0)); + return big2const(result, result_len >= 0 ? result_len : max(arg1.size(), arg2.size()), min(undef_bit_pos, 0)); } RTLIL::Const RTLIL::const_modfloor(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) @@ -539,7 +539,7 @@ RTLIL::Const RTLIL::const_modfloor(const RTLIL::Const &arg1, const RTLIL::Const } else { modulo = b_sign == BigInteger::negative ? truncated - b : truncated + b; } - return big2const(modulo, result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), min(undef_bit_pos, 0)); + return big2const(modulo, result_len >= 0 ? result_len : max(arg1.size(), arg2.size()), min(undef_bit_pos, 0)); } RTLIL::Const RTLIL::const_pow(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) @@ -590,7 +590,7 @@ RTLIL::Const RTLIL::const_pow(const RTLIL::Const &arg1, const RTLIL::Const &arg2 y *= -1; } - return big2const(y, result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), min(undef_bit_pos, 0)); + return big2const(y, result_len >= 0 ? result_len : max(arg1.size(), arg2.size()), min(undef_bit_pos, 0)); } RTLIL::Const RTLIL::const_pos(const RTLIL::Const &arg1, const RTLIL::Const&, bool signed1, bool, int result_len) @@ -601,6 +601,14 @@ RTLIL::Const RTLIL::const_pos(const RTLIL::Const &arg1, const RTLIL::Const&, boo return arg1_ext; } +RTLIL::Const RTLIL::const_buf(const RTLIL::Const &arg1, const RTLIL::Const&, bool signed1, bool, int result_len) +{ + RTLIL::Const arg1_ext = arg1; + extend_u0(arg1_ext, result_len, signed1); + + return arg1_ext; +} + RTLIL::Const RTLIL::const_neg(const RTLIL::Const &arg1, const RTLIL::Const&, bool signed1, bool, int result_len) { RTLIL::Const arg1_ext = arg1; @@ -620,7 +628,7 @@ RTLIL::Const RTLIL::const_mux(const RTLIL::Const &arg1, const RTLIL::Const &arg2 RTLIL::Const ret = arg1; for (int i = 0; i < ret.size(); i++) if (ret[i] != arg2[i]) - ret[i] = State::Sx; + ret.bits()[i] = State::Sx; return ret; } @@ -634,18 +642,18 @@ RTLIL::Const RTLIL::const_pmux(const RTLIL::Const &arg1, const RTLIL::Const &arg for (int i = 0; i < arg3.size(); i++) if (arg3[i] == State::S1) - return RTLIL::Const(std::vector(arg2.bits.begin() + i*arg1.bits.size(), arg2.bits.begin() + (i+1)*arg1.bits.size())); + return RTLIL::Const(std::vector(arg2.begin() + i*arg1.size(), arg2.begin() + (i+1)*arg1.size())); log_abort(); // unreachable } RTLIL::Const RTLIL::const_bmux(const RTLIL::Const &arg1, const RTLIL::Const &arg2) { - std::vector t = arg1.bits; + std::vector t = arg1.to_bits(); for (int i = GetSize(arg2)-1; i >= 0; i--) { - RTLIL::State sel = arg2.bits.at(i); + RTLIL::State sel = arg2.at(i); std::vector new_t; if (sel == State::S0) new_t = std::vector(t.begin(), t.begin() + GetSize(t)/2); @@ -681,10 +689,10 @@ RTLIL::Const RTLIL::const_demux(const RTLIL::Const &arg1, const RTLIL::Const &ar res.push_back(State::S0); } else if (x) { for (int j = 0; j < width; j++) - res.push_back(arg1.bits[j] == State::S0 ? State::S0 : State::Sx); + res.push_back(arg1[j] == State::S0 ? State::S0 : State::Sx); } else { for (int j = 0; j < width; j++) - res.push_back(arg1.bits[j]); + res.push_back(arg1[j]); } } return res; @@ -695,7 +703,7 @@ RTLIL::Const RTLIL::const_bweqx(const RTLIL::Const &arg1, const RTLIL::Const &ar log_assert(arg2.size() == arg1.size()); RTLIL::Const result(RTLIL::State::S0, arg1.size()); for (int i = 0; i < arg1.size(); i++) - result[i] = arg1[i] == arg2[i] ? State::S1 : State::S0; + result.bits()[i] = arg1[i] == arg2[i] ? State::S1 : State::S0; return result; } @@ -707,7 +715,7 @@ RTLIL::Const RTLIL::const_bwmux(const RTLIL::Const &arg1, const RTLIL::Const &ar RTLIL::Const result(RTLIL::State::Sx, arg1.size()); for (int i = 0; i < arg1.size(); i++) { if (arg3[i] != State::Sx || arg1[i] == arg2[i]) - result[i] = arg3[i] == State::S1 ? arg2[i] : arg1[i]; + result.bits()[i] = arg3[i] == State::S1 ? arg2[i] : arg1[i]; } return result; diff --git a/kernel/cellaigs.cc b/kernel/cellaigs.cc index 5dda4503f..de0a49394 100644 --- a/kernel/cellaigs.cc +++ b/kernel/cellaigs.cc @@ -290,7 +290,7 @@ Aig::Aig(Cell *cell) } } - if (cell->type.in(ID($not), ID($_NOT_), ID($pos), ID($_BUF_))) + if (cell->type.in(ID($not), ID($_NOT_), ID($pos), ID($buf), ID($_BUF_))) { for (int i = 0; i < GetSize(cell->getPort(ID::Y)); i++) { int A = mk.inport(ID::A, i); diff --git a/kernel/celledges.cc b/kernel/celledges.cc index 09cdfd55b..bad7124d9 100644 --- a/kernel/celledges.cc +++ b/kernel/celledges.cc @@ -24,7 +24,7 @@ PRIVATE_NAMESPACE_BEGIN void bitwise_unary_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell) { - bool is_signed = cell->getParam(ID::A_SIGNED).as_bool(); + bool is_signed = (cell->type != ID($buf)) && cell->getParam(ID::A_SIGNED).as_bool(); int a_width = GetSize(cell->getPort(ID::A)); int y_width = GetSize(cell->getPort(ID::Y)); @@ -392,7 +392,7 @@ PRIVATE_NAMESPACE_END bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL::Cell *cell) { - if (cell->type.in(ID($not), ID($pos))) { + if (cell->type.in(ID($not), ID($pos), ID($buf))) { bitwise_unary_op(this, cell); return true; } diff --git a/kernel/celltypes.h b/kernel/celltypes.h index fde6624e1..6aebabc19 100644 --- a/kernel/celltypes.h +++ b/kernel/celltypes.h @@ -114,7 +114,7 @@ struct CellTypes void setup_internals_eval() { std::vector unary_ops = { - ID($not), ID($pos), ID($neg), + ID($not), ID($pos), ID($buf), ID($neg), ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool), ID($logic_not), ID($slice), ID($lut), ID($sop) }; @@ -325,7 +325,7 @@ struct CellTypes static RTLIL::Const eval_not(RTLIL::Const v) { - for (auto &bit : v.bits) + for (auto &bit : v.bits()) if (bit == State::S0) bit = State::S1; else if (bit == State::S1) bit = State::S0; return v; @@ -339,7 +339,7 @@ struct CellTypes type = ID($shl); if (type != ID($sshr) && type != ID($sshl) && type != ID($shr) && type != ID($shl) && type != ID($shift) && type != ID($shiftx) && - type != ID($pos) && type != ID($neg) && type != ID($not)) { + type != ID($pos) && type != ID($buf) && type != ID($neg) && type != ID($not)) { if (!signed1 || !signed2) signed1 = false, signed2 = false; } @@ -384,7 +384,7 @@ struct CellTypes HANDLE_CELL_TYPE(neg) #undef HANDLE_CELL_TYPE - if (type == ID($_BUF_)) + if (type.in(ID($_BUF_), ID($buf))) return arg1; if (type == ID($_NOT_)) return eval_not(arg1); @@ -419,13 +419,13 @@ struct CellTypes RTLIL::Const ret; int width = cell->parameters.at(ID::Y_WIDTH).as_int(); int offset = cell->parameters.at(ID::OFFSET).as_int(); - ret.bits.insert(ret.bits.end(), arg1.bits.begin()+offset, arg1.bits.begin()+offset+width); + ret.bits().insert(ret.bits().end(), arg1.begin()+offset, arg1.begin()+offset+width); return ret; } if (cell->type == ID($concat)) { RTLIL::Const ret = arg1; - ret.bits.insert(ret.bits.end(), arg2.bits.begin(), arg2.bits.end()); + ret.bits().insert(ret.bits().end(), arg2.begin(), arg2.end()); return ret; } @@ -448,7 +448,7 @@ struct CellTypes { int width = cell->parameters.at(ID::WIDTH).as_int(); - std::vector t = cell->parameters.at(ID::LUT).bits; + std::vector t = cell->parameters.at(ID::LUT).to_bits(); while (GetSize(t) < (1 << width)) t.push_back(State::S0); t.resize(1 << width); @@ -460,7 +460,7 @@ struct CellTypes { int width = cell->parameters.at(ID::WIDTH).as_int(); int depth = cell->parameters.at(ID::DEPTH).as_int(); - std::vector t = cell->parameters.at(ID::TABLE).bits; + std::vector t = cell->parameters.at(ID::TABLE).to_bits(); while (GetSize(t) < width*depth*2) t.push_back(State::S0); @@ -473,7 +473,7 @@ struct CellTypes bool match_x = true; for (int j = 0; j < width; j++) { - RTLIL::State a = arg1.bits.at(j); + RTLIL::State a = arg1.at(j); if (t.at(2*width*i + 2*j + 0) == State::S1) { if (a == State::S1) match_x = false; if (a != State::S0) match = false; @@ -513,7 +513,7 @@ struct CellTypes if (cell->type == ID($_OAI3_)) return eval_not(const_and(const_or(arg1, arg2, false, false, 1), arg3, false, false, 1)); - log_assert(arg3.bits.size() == 0); + log_assert(arg3.size() == 0); return eval(cell, arg1, arg2, errp); } @@ -524,7 +524,7 @@ struct CellTypes if (cell->type == ID($_OAI4_)) return eval_not(const_and(const_or(arg1, arg2, false, false, 1), const_or(arg3, arg4, false, false, 1), false, false, 1)); - log_assert(arg4.bits.size() == 0); + log_assert(arg4.size() == 0); return eval(cell, arg1, arg2, arg3, errp); } }; diff --git a/kernel/compute_graph.h b/kernel/compute_graph.h new file mode 100644 index 000000000..aeba17f8c --- /dev/null +++ b/kernel/compute_graph.h @@ -0,0 +1,403 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2024 Jannis Harder + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef COMPUTE_GRAPH_H +#define COMPUTE_GRAPH_H + +#include +#include "kernel/yosys.h" + +YOSYS_NAMESPACE_BEGIN + +template< + typename Fn, // Function type (deduplicated across whole graph) + typename Attr = std::tuple<>, // Call attributes (present in every node) + typename SparseAttr = std::tuple<>, // Sparse call attributes (optional per node) + typename Key = std::tuple<> // Stable keys to refer to nodes +> +struct ComputeGraph +{ + struct Ref; +private: + + // Functions are deduplicated by assigning unique ids + idict functions; + + struct Node { + int fn_index; + int arg_offset; + int arg_count; + Attr attr; + + Node(int fn_index, Attr &&attr, int arg_offset, int arg_count = 0) + : fn_index(fn_index), arg_offset(arg_offset), arg_count(arg_count), attr(std::move(attr)) {} + + Node(int fn_index, Attr const &attr, int arg_offset, int arg_count = 0) + : fn_index(fn_index), arg_offset(arg_offset), arg_count(arg_count), attr(attr) {} + }; + + + std::vector nodes; + std::vector args; + dict keys_; + dict sparse_attrs; + +public: + template + struct BaseRef + { + protected: + friend struct ComputeGraph; + Graph *graph_; + int index_; + BaseRef(Graph *graph, int index) : graph_(graph), index_(index) { + log_assert(index_ >= 0); + check(); + } + + void check() const { log_assert(index_ < graph_->size()); } + + Node const &deref() const { check(); return graph_->nodes[index_]; } + + public: + ComputeGraph const &graph() const { return graph_; } + int index() const { return index_; } + + int size() const { return deref().arg_count; } + + BaseRef arg(int n) const + { + Node const &node = deref(); + log_assert(n >= 0 && n < node.arg_count); + return BaseRef(graph_, graph_->args[node.arg_offset + n]); + } + + std::vector::const_iterator arg_indices_cbegin() const + { + Node const &node = deref(); + return graph_->args.cbegin() + node.arg_offset; + } + + std::vector::const_iterator arg_indices_cend() const + { + Node const &node = deref(); + return graph_->args.cbegin() + node.arg_offset + node.arg_count; + } + + Fn const &function() const { return graph_->functions[deref().fn_index]; } + Attr const &attr() const { return deref().attr; } + + bool has_sparse_attr() const { return graph_->sparse_attrs.count(index_); } + + SparseAttr const &sparse_attr() const + { + auto found = graph_->sparse_attrs.find(index_); + log_assert(found != graph_->sparse_attrs.end()); + return found->second; + } + }; + + using ConstRef = BaseRef; + + struct Ref : public BaseRef + { + private: + friend struct ComputeGraph; + Ref(ComputeGraph *graph, int index) : BaseRef(graph, index) {} + Node &deref() const { this->check(); return this->graph_->nodes[this->index_]; } + + public: + Ref(BaseRef ref) : Ref(ref.graph_, ref.index_) {} + + void set_function(Fn const &function) const + { + deref().fn_index = this->graph_->functions(function); + } + + Attr &attr() const { return deref().attr; } + + void append_arg(ConstRef arg) const + { + log_assert(arg.graph_ == this->graph_); + append_arg(arg.index()); + } + + void append_arg(int arg) const + { + log_assert(arg >= 0 && arg < this->graph_->size()); + Node &node = deref(); + if (node.arg_offset + node.arg_count != GetSize(this->graph_->args)) + move_args(node); + this->graph_->args.push_back(arg); + node.arg_count++; + } + + operator ConstRef() const + { + return ConstRef(this->graph_, this->index_); + } + + SparseAttr &sparse_attr() const + { + return this->graph_->sparse_attrs[this->index_]; + } + + void clear_sparse_attr() const + { + this->graph_->sparse_attrs.erase(this->index_); + } + + void assign_key(Key const &key) const + { + this->graph_->keys_.emplace(key, this->index_); + } + + private: + void move_args(Node &node) const + { + auto &args = this->graph_->args; + int old_offset = node.arg_offset; + node.arg_offset = GetSize(args); + for (int i = 0; i != node.arg_count; ++i) + args.push_back(args[old_offset + i]); + } + + }; + + bool has_key(Key const &key) const + { + return keys_.count(key); + } + + dict const &keys() const + { + return keys_; + } + + ConstRef operator()(Key const &key) const + { + auto it = keys_.find(key); + log_assert(it != keys_.end()); + return (*this)[it->second]; + } + + Ref operator()(Key const &key) + { + auto it = keys_.find(key); + log_assert(it != keys_.end()); + return (*this)[it->second]; + } + + int size() const { return GetSize(nodes); } + + ConstRef operator[](int index) const { return ConstRef(this, index); } + Ref operator[](int index) { return Ref(this, index); } + + Ref add(Fn const &function, Attr &&attr) + { + int index = GetSize(nodes); + int fn_index = functions(function); + nodes.emplace_back(fn_index, std::move(attr), GetSize(args)); + return Ref(this, index); + } + + Ref add(Fn const &function, Attr const &attr) + { + int index = GetSize(nodes); + int fn_index = functions(function); + nodes.emplace_back(fn_index, attr, GetSize(args)); + return Ref(this, index); + } + + template + Ref add(Fn const &function, Attr const &attr, T &&args) + { + Ref added = add(function, attr); + for (auto arg : args) + added.append_arg(arg); + return added; + } + + template + Ref add(Fn const &function, Attr &&attr, T &&args) + { + Ref added = add(function, std::move(attr)); + for (auto arg : args) + added.append_arg(arg); + return added; + } + + Ref add(Fn const &function, Attr const &attr, std::initializer_list args) + { + Ref added = add(function, attr); + for (auto arg : args) + added.append_arg(arg); + return added; + } + + Ref add(Fn const &function, Attr &&attr, std::initializer_list args) + { + Ref added = add(function, std::move(attr)); + for (auto arg : args) + added.append_arg(arg); + return added; + } + + template + Ref add(Fn const &function, Attr const &attr, T begin, T end) + { + Ref added = add(function, attr); + for (; begin != end; ++begin) + added.append_arg(*begin); + return added; + } + + void compact_args() + { + std::vector new_args; + for (auto &node : nodes) + { + int new_offset = GetSize(new_args); + for (int i = 0; i < node.arg_count; i++) + new_args.push_back(args[node.arg_offset + i]); + node.arg_offset = new_offset; + } + std::swap(args, new_args); + } + + void permute(std::vector const &perm) + { + log_assert(perm.size() <= nodes.size()); + std::vector inv_perm; + inv_perm.resize(nodes.size(), -1); + for (int i = 0; i < GetSize(perm); ++i) + { + int j = perm[i]; + log_assert(j >= 0 && j < GetSize(nodes)); + log_assert(inv_perm[j] == -1); + inv_perm[j] = i; + } + permute(perm, inv_perm); + } + + void permute(std::vector const &perm, std::vector const &inv_perm) + { + log_assert(inv_perm.size() == nodes.size()); + std::vector new_nodes; + new_nodes.reserve(perm.size()); + dict new_sparse_attrs; + for (int i : perm) + { + int j = GetSize(new_nodes); + new_nodes.emplace_back(std::move(nodes[i])); + auto found = sparse_attrs.find(i); + if (found != sparse_attrs.end()) + new_sparse_attrs.emplace(j, std::move(found->second)); + } + + std::swap(nodes, new_nodes); + std::swap(sparse_attrs, new_sparse_attrs); + + compact_args(); + for (int &arg : args) + { + log_assert(arg < GetSize(inv_perm)); + log_assert(inv_perm[arg] >= 0); + arg = inv_perm[arg]; + } + + for (auto &key : keys_) + { + log_assert(key.second < GetSize(inv_perm)); + log_assert(inv_perm[key.second] >= 0); + key.second = inv_perm[key.second]; + } + } + + struct SccAdaptor + { + private: + ComputeGraph const &graph_; + std::vector indices_; + public: + SccAdaptor(ComputeGraph const &graph) : graph_(graph) + { + indices_.resize(graph.size(), -1); + } + + + typedef int node_type; + + struct node_enumerator { + private: + friend struct SccAdaptor; + int current, end; + node_enumerator(int current, int end) : current(current), end(end) {} + + public: + + bool finished() const { return current == end; } + node_type next() { + log_assert(!finished()); + node_type result = current; + ++current; + return result; + } + }; + + node_enumerator enumerate_nodes() { + return node_enumerator(0, GetSize(indices_)); + } + + + struct successor_enumerator { + private: + friend struct SccAdaptor; + std::vector::const_iterator current, end; + successor_enumerator(std::vector::const_iterator current, std::vector::const_iterator end) : + current(current), end(end) {} + + public: + bool finished() const { return current == end; } + node_type next() { + log_assert(!finished()); + node_type result = *current; + ++current; + return result; + } + }; + + successor_enumerator enumerate_successors(int index) const { + auto const &ref = graph_[index]; + return successor_enumerator(ref.arg_indices_cbegin(), ref.arg_indices_cend()); + } + + int &dfs_index(node_type const &node) { return indices_[node]; } + + std::vector const &dfs_indices() { return indices_; } + }; + +}; + + + +YOSYS_NAMESPACE_END + + +#endif diff --git a/kernel/consteval.h b/kernel/consteval.h index 4c0c26049..73d05f0b3 100644 --- a/kernel/consteval.h +++ b/kernel/consteval.h @@ -76,7 +76,7 @@ struct ConstEval #ifndef NDEBUG RTLIL::SigSpec current_val = values_map(sig); for (int i = 0; i < GetSize(current_val); i++) - log_assert(current_val[i].wire != NULL || current_val[i] == value.bits[i]); + log_assert(current_val[i].wire != NULL || current_val[i] == value[i]); #endif values_map.add(sig, RTLIL::SigSpec(value)); } @@ -115,7 +115,7 @@ struct ConstEval for (int i = 0; i < GetSize(coval); i++) { carry = (sig_g[i] == State::S1) || (sig_p[i] == RTLIL::S1 && carry); - coval.bits[i] = carry ? State::S1 : State::S0; + coval.bits()[i] = carry ? State::S1 : State::S0; } set(sig_co, coval); @@ -153,7 +153,7 @@ struct ConstEval for (int i = 0; i < sig_s.size(); i++) { - RTLIL::State s_bit = sig_s.extract(i, 1).as_const().bits.at(0); + RTLIL::State s_bit = sig_s.extract(i, 1).as_const().at(0); RTLIL::SigSpec b_slice = sig_b.extract(sig_y.size()*i, sig_y.size()); if (s_bit == RTLIL::State::Sx || s_bit == RTLIL::State::S1) @@ -180,10 +180,10 @@ struct ConstEval if (y_values.size() > 1) { - std::vector master_bits = y_values.at(0).bits; + std::vector master_bits = y_values.at(0).to_bits(); for (size_t i = 1; i < y_values.size(); i++) { - std::vector &slave_bits = y_values.at(i).bits; + std::vector slave_bits = y_values.at(i).to_bits(); log_assert(master_bits.size() == slave_bits.size()); for (size_t j = 0; j < master_bits.size(); j++) if (master_bits[j] != slave_bits[j]) @@ -248,8 +248,8 @@ struct ConstEval RTLIL::Const val_x = const_or(t2, t3, false, false, width); for (int i = 0; i < GetSize(val_y); i++) - if (val_y.bits[i] == RTLIL::Sx) - val_x.bits[i] = RTLIL::Sx; + if (val_y[i] == RTLIL::Sx) + val_x.bits()[i] = RTLIL::Sx; set(sig_y, val_y); set(sig_x, val_x); diff --git a/kernel/constids.inc b/kernel/constids.inc index 00db94af4..3052afb49 100644 --- a/kernel/constids.inc +++ b/kernel/constids.inc @@ -43,6 +43,7 @@ X(CE_OVER_SRST) X(CFG_ABITS) X(CFG_DBITS) X(CFG_INIT) +X(chain) X(CI) X(CLK) X(clkbuf_driver) diff --git a/kernel/driver.cc b/kernel/driver.cc index 1e4cd0052..0c0dc9023 100644 --- a/kernel/driver.cc +++ b/kernel/driver.cc @@ -19,6 +19,8 @@ #include "kernel/yosys.h" #include "libs/sha1/sha1.h" +#include "libs/cxxopts/include/cxxopts.hpp" +#include #ifdef YOSYS_ENABLE_READLINE # include @@ -55,55 +57,6 @@ USING_YOSYS_NAMESPACE -char *optarg; -int optind = 1, optcur = 1, optopt = 0; -int getopt(int argc, char **argv, const char *optstring) -{ - if (optind >= argc) - return -1; - - if (argv[optind][0] != '-' || argv[optind][1] == 0) { - optopt = 1; - optarg = argv[optind++]; - return optopt; - } - - bool takes_arg = false; - optopt = argv[optind][optcur]; - - if (optopt == '-') { - ++optind; - return -1; - } - - for (int i = 0; optstring[i]; i++) - if (optopt == optstring[i] && optstring[i + 1] == ':') - takes_arg = true; - - if (!takes_arg) { - if (argv[optind][++optcur] == 0) - optind++, optcur = 1; - return optopt; - } - - if (argv[optind][++optcur]) { - optarg = argv[optind++] + optcur; - optcur = 1; - return optopt; - } - - if (++optind >= argc) { - fprintf(stderr, "%s: option '-%c' expects an argument\n", argv[0], optopt); - optopt = '?'; - return optopt; - } - - optarg = argv[optind]; - optind++, optcur = 1; - - return optopt; -} - #ifdef EMSCRIPTEN # include # include @@ -235,12 +188,14 @@ int main(int argc, char **argv) std::vector passes_commands; std::vector frontend_files; std::vector plugin_filenames; + std::vector special_args; std::string output_filename = ""; std::string scriptfile = ""; std::string depsfile = ""; std::string topmodule = ""; std::string perffile = ""; bool scriptfile_tcl = false; + bool scriptfile_python = false; bool print_banner = true; bool print_stats = true; bool call_abort = false; @@ -250,292 +205,243 @@ int main(int argc, char **argv) bool mode_v = false; bool mode_q = false; - if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "-help") || !strcmp(argv[1], "--help"))) - { - printf("\n"); - printf("Usage: %s [options] [ [..]]\n", argv[0]); - printf("\n"); - printf(" -Q\n"); - printf(" suppress printing of banner (copyright, disclaimer, version)\n"); - printf("\n"); - printf(" -T\n"); - printf(" suppress printing of footer (log hash, version, timing statistics)\n"); - printf("\n"); - printf(" -q\n"); - printf(" quiet operation. only write warnings and error messages to console\n"); - printf(" use this option twice to also quiet warning messages\n"); - printf("\n"); - printf(" -v \n"); - printf(" print log headers up to level to the console. (this\n"); - printf(" implies -q for everything except the 'End of script.' message.)\n"); - printf("\n"); - printf(" -t\n"); - printf(" annotate all log messages with a time stamp\n"); - printf("\n"); - printf(" -d\n"); - printf(" print more detailed timing stats at exit\n"); - printf("\n"); - printf(" -l logfile\n"); - printf(" write log messages to the specified file\n"); - printf("\n"); - printf(" -L logfile\n"); - printf(" like -l but open log file in line buffered mode\n"); - printf("\n"); - printf(" -o outfile\n"); - printf(" write the design to the specified file on exit\n"); - printf("\n"); - printf(" -b backend\n"); - printf(" use this backend for the output file specified on the command line\n"); - printf("\n"); - printf(" -f frontend\n"); - printf(" use the specified frontend for the input files on the command line\n"); - printf("\n"); - printf(" -H\n"); - printf(" print the command list\n"); - printf("\n"); - printf(" -h command\n"); - printf(" print the help message for the specified command\n"); - printf("\n"); - printf(" -s scriptfile\n"); - printf(" execute the commands in the script file\n"); + cxxopts::Options options(argv[0], "Yosys Open SYnthesis Suite"); + options.set_width(SIZE_MAX); + + options.add_options("operation") + ("b,backend", "use for the output file specified on the command line", + cxxopts::value(), "") + ("f,frontend", "use for the input files on the command line", + cxxopts::value(), "") + ("s,scriptfile", "execute the commands in ", + cxxopts::value(), "") #ifdef YOSYS_ENABLE_TCL - printf("\n"); - printf(" -c tcl_scriptfile\n"); - printf(" execute the commands in the tcl script file (see 'help tcl' for details)\n"); - printf("\n"); - printf(" -C\n"); - printf(" enters TCL interatcive shell mode\n"); -#endif - printf("\n"); - printf(" -p command\n"); - printf(" execute the commands (to chain commands, separate them with semicolon + whitespace: 'cmd1; cmd2')\n"); - printf("\n"); - printf(" -m module_file\n"); - printf(" load the specified module (aka plugin)\n"); - printf("\n"); - printf(" -X\n"); - printf(" enable tracing of core data structure changes. for debugging\n"); - printf("\n"); - printf(" -M\n"); - printf(" will slightly randomize allocated pointer addresses. for debugging\n"); - printf("\n"); - printf(" -A\n"); - printf(" will call abort() at the end of the script. for debugging\n"); - printf("\n"); - printf(" -r \n"); - printf(" elaborate command line arguments using the specified top module\n"); - printf("\n"); - printf(" -D [=]\n"); - printf(" set the specified Verilog define (via \"read -define\")\n"); - printf("\n"); - printf(" -P [:]\n"); - printf(" dump the design when printing the specified log header to a file.\n"); - printf(" yosys_dump_.il is used as filename if none is specified.\n"); - printf(" Use 'ALL' as to dump at every header.\n"); - printf("\n"); - printf(" -W regex\n"); - printf(" print a warning for all log messages matching the regex.\n"); - printf("\n"); - printf(" -w regex\n"); - printf(" if a warning message matches the regex, it is printed as regular\n"); - printf(" message instead.\n"); - printf("\n"); - printf(" -e regex\n"); - printf(" if a warning message matches the regex, it is printed as error\n"); - printf(" message instead and the tool terminates with a nonzero return code.\n"); - printf("\n"); - printf(" -E \n"); - printf(" write a Makefile dependencies file with in- and output file names\n"); - printf("\n"); - printf(" -x \n"); - printf(" do not print warnings for the specified experimental feature\n"); - printf("\n"); - printf(" -g\n"); - printf(" globally enable debug log messages\n"); - printf("\n"); - printf(" -V\n"); - printf(" print version information and exit\n"); - printf("\n"); - printf("The option -S is a shortcut for calling the \"synth\" command, a default\n"); - printf("script for transforming the Verilog input to a gate-level netlist. For example:\n"); - printf("\n"); - printf(" yosys -o output.blif -S input.v\n"); - printf("\n"); - printf("For more complex synthesis jobs it is recommended to use the read_* and write_*\n"); - printf("commands in a script file instead of specifying input and output files on the\n"); - printf("command line.\n"); - printf("\n"); - printf("When no commands, script files or input files are specified on the command\n"); - printf("line, yosys automatically enters the interactive command mode. Use the 'help'\n"); - printf("command to get information on the individual commands.\n"); - printf("\n"); + ("c,tcl-scriptfile", "execute the commands in the TCL (see 'help tcl' for details)", + cxxopts::value(),"") + ("C,tcl-interactive", "enters TCL interactive shell mode") +#endif // YOSYS_ENABLE_TCL +#ifdef WITH_PYTHON + ("y,py-scriptfile", "execute the Python