pax_global_header00006660000000000000000000000064144755427630014533gustar00rootroot0000000000000052 comment=2584903a0605cc7ffdfc1996254ccc1f548403f2 yosys-yosys-0.33/000077500000000000000000000000001447554276300140325ustar00rootroot00000000000000yosys-yosys-0.33/.clang-format000066400000000000000000000004251447554276300164060ustar00rootroot00000000000000# Default Linux style BasedOnStyle: LLVM IndentWidth: 8 UseTab: Always BreakBeforeBraces: Linux AllowShortIfStatementsOnASingleLine: false IndentCaseLabels: false # From guidelines/CodingStyle TabWidth: 8 ContinuationIndentWidth: 2 ColumnLimit: 150 # BreakBeforeBraces: Linux yosys-yosys-0.33/.dockerignore000066400000000000000000000002071447554276300165050ustar00rootroot00000000000000.editorconfig .gitignore .gitmodules .github .git Dockerfile README.md manual guidelines CodeOfConduct .travis .travis.yml yosys-yosys-0.33/.editorconfig000066400000000000000000000003021447554276300165020ustar00rootroot00000000000000root = true [*] indent_style = tab indent_size = tab trim_trailing_whitespace = true insert_final_newline = true [abc/**] indent_style = space indent_size = 2 trim_trailing_whitespace = false yosys-yosys-0.33/.gitattributes000066400000000000000000000000671447554276300167300ustar00rootroot00000000000000*.v linguist-language=Verilog /.gitcommit export-subst yosys-yosys-0.33/.gitcommit000066400000000000000000000000141447554276300160220ustar00rootroot000000000000002584903a060 yosys-yosys-0.33/.github/000077500000000000000000000000001447554276300153725ustar00rootroot00000000000000yosys-yosys-0.33/.github/ISSUE_TEMPLATE/000077500000000000000000000000001447554276300175555ustar00rootroot00000000000000yosys-yosys-0.33/.github/ISSUE_TEMPLATE/bug_report.yml000066400000000000000000000043551447554276300224570ustar00rootroot00000000000000name: Bug Report description: Report an issue or regression with Yosys labels: ["pending-verification"] body: - type: markdown attributes: value: > If you have a general question, please ask it in the [Discussions](https://github.com/YosysHQ/yosys/discussions) area or join our [IRC Channel](https://web.libera.chat/#yosys) or [Community Slack](https://join.slack.com/t/yosyshq/shared_invite/zt-1aopkns2q-EiQ97BeQDt_pwvE41sGSuA). If you have a feature request, please fill out the appropriate issue form, this form is for bugs and/or regressions. Please contact [YosysHQ GmbH](https://www.yosyshq.com/) if you need commercial support for Yosys. - type: input id: yosys_version attributes: label: Version description: "The version of yosys this bug was encountered on." placeholder: "The output of `yosys --version`" validations: required: true - type: dropdown id: os attributes: label: On which OS did this happen? options: - Linux - macOS - Windows - BSD multiple: true validations: required: true - type: markdown attributes: value: > When providing steps to reproduce the issue, please ensure that the issue is reproducible in the current git master of Yosys. Also ensure to provide all necessary source files needed. Please see [https://stackoverflow.com/help/mcve](https://stackoverflow.com/help/mcve) for information on how to create a Minimal, Complete, and Verifiable Example (MCVE). - type: textarea id: reproduction_steps attributes: label: Reproduction Steps description: "Please provide clear and concise steps to reproduce the issue." validations: required: true - type: textarea id: expected_behavior attributes: label: Expected Behavior description: "Please describe the behavior you would have expected from the tool." validations: required: true - type: textarea id: actual_behavior attributes: label: Actual Behavior description: "Please describe how the behavior you see differs from the expected behavior." validations: required: true yosys-yosys-0.33/.github/ISSUE_TEMPLATE/config.yml000066400000000000000000000006311447554276300215450ustar00rootroot00000000000000contact_links: - name: Discussions url: https://github.com/YosysHQ/yosys/discussions about: "Have a question? Ask it on our discussions page!" - name: Community Slack url: https://join.slack.com/t/yosyshq/shared_invite/zt-1aopkns2q-EiQ97BeQDt_pwvE41sGSuA about: "Yosys Community Slack" - name: IRC Channel url: https://web.libera.chat/#yosys about: "#yosys on irc.libera.chat" yosys-yosys-0.33/.github/ISSUE_TEMPLATE/feature_request.yml000066400000000000000000000016321447554276300235050ustar00rootroot00000000000000name: Feature Request description: "Submit a feature request for Yosys" labels: ["feature-request"] body: - type: markdown attributes: value: > If you have a general question, please ask it in the [Discussions](https://github.com/YosysHQ/yosys/discussions) area or join our [IRC Channel](https://web.libera.chat/#yosys) or [Community Slack](https://join.slack.com/t/yosyshq/shared_invite/zt-1aopkns2q-EiQ97BeQDt_pwvE41sGSuA). If you have a bug report, please fill out the appropriate issue form, this form is for feature requests. Please contact [YosysHQ GmbH](https://www.yosyshq.com/) if you need commercial support or work done for Yosys. - type: textarea id: feature_description attributes: label: Feature Description description: "A clear and detailed description of the feature." validations: required: true yosys-yosys-0.33/.github/workflows/000077500000000000000000000000001447554276300174275ustar00rootroot00000000000000yosys-yosys-0.33/.github/workflows/codeql.yml000066400000000000000000000011401447554276300214150ustar00rootroot00000000000000name: "CodeQL" on: workflow_dispatch: schedule: - cron: '0 3 * * *' jobs: analyze: name: Analyze runs-on: ubuntu-latest steps: - name: Install deps run: sudo apt-get install bison flex libreadline-dev tcl-dev libffi-dev - name: Checkout repository uses: actions/checkout@v3 - name: Initialize CodeQL uses: github/codeql-action/init@v2 with: languages: cpp queries: security-extended,security-and-quality - name: Build run: make yosys -j6 - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 yosys-yosys-0.33/.github/workflows/emcc.yml000066400000000000000000000006021447554276300210570ustar00rootroot00000000000000name: Emscripten Build on: [push, pull_request] jobs: emcc: runs-on: ubuntu-latest steps: - uses: mymindstorm/setup-emsdk@v11 - uses: actions/checkout@v3 - name: Build run: | make config-emcc make YOSYS_VER=latest - uses: actions/upload-artifact@v3 with: name: yosysjs path: yosysjs-latest.zip yosys-yosys-0.33/.github/workflows/test-linux.yml000066400000000000000000000067631447554276300223020ustar00rootroot00000000000000name: Build and run tests (Linux) on: [push, pull_request] jobs: test-linux: runs-on: ${{ matrix.os.id }} strategy: matrix: os: - { id: ubuntu-20.04, name: focal } compiler: - 'clang-12' - 'gcc-11' cpp_std: - 'c++11' - 'c++14' - 'c++17' - 'c++20' include: # Limit the older compilers to C++11 mode - os: { id: ubuntu-20.04, name: focal } compiler: 'clang-11' cpp_std: 'c++11' - os: { id: ubuntu-20.04, name: focal } compiler: 'gcc-10' cpp_std: 'c++11' fail-fast: false steps: - name: Install Dependencies shell: bash run: | sudo apt-get update sudo apt-get install gperf build-essential bison flex libreadline-dev gawk tcl-dev libffi-dev git graphviz xdot pkg-config python python3 libboost-system-dev libboost-python-dev libboost-filesystem-dev zlib1g-dev - name: Setup GCC if: startsWith(matrix.compiler, 'gcc') shell: bash run: | CXX=${CC/#gcc/g++} sudo apt-add-repository ppa:ubuntu-toolchain-r/test sudo apt-get update sudo apt-get install $CC $CXX echo "CC=$CC" >> $GITHUB_ENV echo "CXX=$CXX" >> $GITHUB_ENV env: CC: ${{ matrix.compiler }} - name: Setup Clang if: startsWith(matrix.compiler, 'clang') shell: bash run: | wget https://apt.llvm.org/llvm-snapshot.gpg.key sudo apt-key add llvm-snapshot.gpg.key rm llvm-snapshot.gpg.key sudo apt-add-repository "deb https://apt.llvm.org/${{ matrix.os.name }}/ llvm-toolchain-${{ matrix.os.name }} main" sudo apt-get update CXX=${CC/#clang/clang++} sudo apt-get install $CC $CXX echo "CC=$CC" >> $GITHUB_ENV echo "CXX=$CXX" >> $GITHUB_ENV env: CC: ${{ matrix.compiler }} - name: Runtime environment shell: bash env: WORKSPACE: ${{ github.workspace }} run: | echo "GITHUB_WORKSPACE=`pwd`" >> $GITHUB_ENV echo "$GITHUB_WORKSPACE/.local/bin" >> $GITHUB_PATH echo "procs=$(nproc)" >> $GITHUB_ENV - name: Tool versions shell: bash run: | $CC --version $CXX --version - name: Checkout Yosys uses: actions/checkout@v3 - name: Get iverilog shell: bash run: | git clone https://github.com/steveicarus/iverilog.git - name: Cache iverilog id: cache-iverilog uses: actions/cache@v3 with: path: .local/ key: ${{ matrix.os.id }}-${{ hashFiles('iverilog/.git/refs/heads/master') }} - name: Build iverilog if: steps.cache-iverilog.outputs.cache-hit != 'true' shell: bash run: | mkdir -p $GITHUB_WORKSPACE/.local/ cd iverilog autoconf CC=gcc CXX=g++ ./configure --prefix=$GITHUB_WORKSPACE/.local make -j${{ env.procs }} make install - name: Build yosys shell: bash run: | make config-${CC%%-*} make -j${{ env.procs }} CCXXSTD=${{ matrix.cpp_std }} CC=$CC CXX=$CC LD=$CC - name: Run tests if: (matrix.cpp_std == 'c++11') && (matrix.compiler == 'gcc-11') shell: bash run: | make -j${{ env.procs }} test CXXSTD=${{ matrix.cpp_std }} CC=$CC CXX=$CC LD=$CC yosys-yosys-0.33/.github/workflows/test-macos.yml000066400000000000000000000037371447554276300222430ustar00rootroot00000000000000name: Build and run tests (macOS) on: [push, pull_request] jobs: test-macos: runs-on: ${{ matrix.os.id }} strategy: matrix: os: - { id: macos-11, name: 'Big Sur' } cpp_std: - 'c++11' - 'c++17' fail-fast: false steps: - name: Install Dependencies run: | brew install bison flex gawk libffi pkg-config bash - name: Runtime environment shell: bash env: WORKSPACE: ${{ github.workspace }} run: | echo "GITHUB_WORKSPACE=`pwd`" >> $GITHUB_ENV echo "$GITHUB_WORKSPACE/.local/bin" >> $GITHUB_PATH echo "$(brew --prefix bison)/bin" >> $GITHUB_PATH echo "$(brew --prefix flex)/bin" >> $GITHUB_PATH echo "procs=$(sysctl -n hw.ncpu)" >> $GITHUB_ENV - name: Tool versions shell: bash run: | cc --version - name: Checkout Yosys uses: actions/checkout@v3 - name: Get iverilog shell: bash run: | git clone https://github.com/steveicarus/iverilog.git - name: Cache iverilog id: cache-iverilog uses: actions/cache@v3 with: path: .local/ key: ${{ matrix.os.id }}-${{ hashFiles('iverilog/.git/refs/heads/master') }} - name: Build iverilog if: steps.cache-iverilog.outputs.cache-hit != 'true' shell: bash run: | mkdir -p $GITHUB_WORKSPACE/.local/ cd iverilog autoconf CC=gcc CXX=g++ ./configure --prefix=$GITHUB_WORKSPACE/.local/ make -j${{ env.procs }} make install - name: Build yosys shell: bash run: | make config-clang make -j${{ env.procs }} CXXSTD=${{ matrix.cpp_std }} CC=cc CXX=cc LD=cc - name: Run tests if: matrix.cpp_std == 'c++11' shell: bash run: | make -j${{ env.procs }} test CXXSTD=${{ matrix.cpp_std }} CC=cc CXX=cc LD=cc yosys-yosys-0.33/.github/workflows/version.yml000066400000000000000000000022531447554276300216410ustar00rootroot00000000000000name: Bump version on: workflow_dispatch: schedule: - cron: '0 0 * * *' jobs: bump-version: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 with: fetch-depth: 0 - name: Take last commit id: log run: echo "message=$(git log --no-merges -1 --oneline)" >> $GITHUB_OUTPUT - name: Take repository id: repo run: echo "message=$GITHUB_REPOSITORY" >> $GITHUB_OUTPUT - name: Bump version if: "!contains(steps.log.outputs.message, 'Bump version') && contains(steps.repo.outputs.message, 'YosysHQ/yosys')" run: | make bumpversion git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" git config --local user.name "github-actions[bot]" git add Makefile git commit -m "Bump version" - name: Push changes # push the output folder to your repo if: "!contains(steps.log.outputs.message, 'Bump version') && contains(steps.repo.outputs.message, 'YosysHQ/yosys')" uses: ad-m/github-push-action@master with: github_token: ${{ secrets.GITHUB_TOKEN }} yosys-yosys-0.33/.github/workflows/vs.yml000066400000000000000000000015161447554276300206050ustar00rootroot00000000000000name: Visual Studio Build on: [push, pull_request] jobs: yosys-vcxsrc: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Build run: make vcxsrc YOSYS_VER=latest - uses: actions/upload-artifact@v3 with: name: vcxsrc path: yosys-win32-vcxsrc-latest.zip build: runs-on: windows-2019 needs: yosys-vcxsrc steps: - uses: actions/download-artifact@v3 with: name: vcxsrc path: . - name: unzip run: unzip yosys-win32-vcxsrc-latest.zip - name: setup-msbuild uses: microsoft/setup-msbuild@v1 - name: MSBuild working-directory: yosys-win32-vcxsrc-latest run: msbuild YosysVS.sln /p:PlatformToolset=v142 /p:Configuration=Release /p:WindowsTargetPlatformVersion=10.0.17763.0 yosys-yosys-0.33/.gitignore000066400000000000000000000012261447554276300160230ustar00rootroot00000000000000*.o *.d .*.swp *.gch *.gcda *.gcno __pycache__ /.cproject /.project /.settings /qtcreator.files /qtcreator.includes /qtcreator.config /qtcreator.creator /qtcreator.creator.user /coverage.info /coverage_html /Makefile.conf /abc /viz.js /yosys /yosys.exe /yosys.js /yosys.wasm /yosys-abc /yosys-abc.exe /yosys-config /yosys-smtbmc /yosys-smtbmc.exe /yosys-smtbmc-script.py /yosys-witness /yosys-witness.exe /yosys-witness-script.py /yosys-filterlib /yosys-filterlib.exe /kernel/*.pyh /kernel/python_wrappers.cc /kernel/version_*.cc /share /yosys-win32-mxebin-* /yosys-win32-vcxsrc-* /yosysjs-* /libyosys.so /tests/unit/bintest/ /tests/unit/objtest/ /tests/ystests yosys-yosys-0.33/.mailmap000066400000000000000000000006431447554276300154560ustar00rootroot00000000000000Marcelina Kościelnicka Marcelina Kościelnicka Marcelina Kościelnicka Claire Xenia Wolf Claire Xenia Wolf Claire Xenia Wolf Claire Xenia Wolf yosys-yosys-0.33/Brewfile000066400000000000000000000002521447554276300155130ustar00rootroot00000000000000brew "bison" brew "flex" brew "gawk" brew "libffi" brew "git" brew "graphviz" brew "pkg-config" brew "python3" brew "tcl-tk" brew "xdot" brew "bash" brew 'boost-python3' yosys-yosys-0.33/CHANGELOG000066400000000000000000001431271447554276300152540ustar00rootroot00000000000000 List of major changes and improvements between releases ======================================================= Yosys 0.32 .. Yosys 0.33 -------------------------- * Various - Added "$print" cell, produced by "$display" and "$write" Verilog tasks. - Added "$print" cell handling in CXXRTL. * Lattice FPGA support - Added generic "synth_lattice" pass (for now MachXO2/XO3/XO3D) - Removed "synth_machxo2" pass - Pass "ecp5_gsr" renamed to "lattice_gsr" - "synth_machxo2" equivalent is "synth_lattice -family xo2" Yosys 0.31 .. Yosys 0.32 -------------------------- * Verific support - Added sub option "-lib" to reading commands for VHDL and SystemVerilog, that will later import all units/modules from marked files as blackboxes. * Various - Added support for $lt, $le, $gt, $ge to the code generating AIGs. Yosys 0.30 .. Yosys 0.31 -------------------------- * New commands and options - Added option "-lsbidx" to "write_edif" pass. * Various - Added support for $divfloor operator to cxxrtl backend. - dfflegalize: allow setting mince and minsrst args from scratchpad. Yosys 0.29 .. Yosys 0.30 -------------------------- * New commands and options - Added "recover_names" pass to recover names post-mapping. * Gowin support - Added remaining primitives blackboxes. * Various - "show -colorattr" will now color the cells, wires, and connection arrows. - "show -viewer none" will not execute viewer. Yosys 0.28 .. Yosys 0.29 -------------------------- * New commands and options - Added "synthprop" pass for synthesizable properties. * Verific support - Handle conditions on clocked concurrent assertions in unclocked procedural contexts. * Verilog - Fix const eval of unbased unsized constants. - Handling of attributes for struct / union variables. Yosys 0.27 .. Yosys 0.28 -------------------------- * Verilog - Out of bounds checking for struct/union members. * Verific support - Fix enum_values support and signed attribute values. * ECP5 support - Added "synth_ecp5 -iopad" * MachXO2 support - Added "synth_machxo2 -ccu2" Yosys 0.26 .. Yosys 0.27 -------------------------- * New commands and options - Added option "-make_assert" to "equiv_make" pass. - Added option "-coverenable" to "chformal" pass. * Verilog - Resolve package types in interfaces. - Handle range offsets in packed arrays within packed structs. - Support for data and array queries on struct/union item expressions. * GateMate support - Enable register initialization. Yosys 0.25 .. Yosys 0.26 -------------------------- * New commands and options - Added "bwmuxmap" pass to replace $bwmux cells with equivalent logic. - Added "xprop" experimental pass for formal x propagation. - Added "splitcells" pass to split up multi-bit cells. - Added "viz" pass to visualize data flow graph. - Added option "-make_cover" to "miter" pass. - Added option "-noparallelcase" to "write_verilog" pass. - Added option "-chain" to "insbuf" pass. - Added options "-hierarchy" and "-assume" to "formalff" pass. - Added options "-append" and "-summary" to "sim" pass. - Added option "-ywmap" to "write_btor" pass. - Added option "-ignore-self-reset" to "fsm_detect" pass. * Verilog - Support for struct members of union type. - Support for struct member package types. * Various - Added Yosys witness (.yw) cosimulation. - GCC 4.8 is deprecated, compiler with full C++11 support is required. Yosys 0.24 .. Yosys 0.25 -------------------------- * Verific support - Respect "noblackbox" attribute for modules. * Various - Documentation is hosted at https://yosyshq.readthedocs.io/projects/yosys/en/latest/ Yosys 0.23 .. Yosys 0.24 -------------------------- * New commands and options - Added option "-set-def-formal" to "sat" pass. - Added option "-s" to "tee" command. * Verilog - Support for module-scoped identifiers referring to tasks and functions. - Support for arrays with swapped ranges within structs. * Verific support - Support for importing verilog configurations per name. - "verific -set-XXXXX" commands are now able to set severity to all messages of certain type (errors, warnings, infos and comments) * Various - TCL shell support (use "yosys -C") - Added FABulous eFPGA frontend Yosys 0.22 .. Yosys 0.23 -------------------------- * New commands and options - Added option "-cross" to "miter" pass. - Added option "-nocheck" to "equiv_opt" pass. * Formal Verification - yosys-smtbmc: Added "--detect-loops" option for checking if states are unique in temporal induction counter examples. * Verific support - Added support for reading Liberty files using Verific library. (Optinally enabled with ENABLE_VERIFIC_LIBERTY) - Added option "-cells" to "verific -import" enabling import of all cells from verific design. * Various - MinGW build (Windows) plugin support. - Added YOSYS_ABORT_ON_LOG_ERROR environment variable for debugging. Setting it to 1 causes abort() to be called when Yosys terminates with an error message. Yosys 0.21 .. Yosys 0.22 -------------------------- * Verific support - Added support for here-document for "verific" command (for reading source files). - Added support for reading EDIF files using Verific library. (Optinally enabled with ENABLE_VERIFIC_EDIF) * Various - Added tech specific utilization to "stat" json. Yosys 0.20 .. Yosys 0.21 -------------------------- * New commands and options - Added "formalff" pass - transforms FFs for formal verification - Added option "-formal" to "memory_map" pass - Added option "-witness" to "rename" - give public names to all signals present in yosys witness traces - Added option "-hdlname" to "sim" pass - preserves hiearachy when writing simulation output for a flattened design - Addded option "-scramble-name" to "rename" pass * Formal Verification - Added $anyinit cell to directly represent FFs with an unconstrained initialization value. These can be generated by the new formalff pass. - New JSON based yosys witness format for formal verification traces. - yosys-smtbmc: Reading and writing of yosys witness traces. - write_smt2: Emit inline metadata to support yosys witness trace. - yosys-witness is a new tool to inspect and convert yosys witness traces. - write_aiger: Option to write a map file for yosys witness trace conversion. - yosys-witness: Conversion from and to AIGER witness traces. * Verific support - Filename re-writing support for "verific" pass. * Various - ABC performance improvements - Filename re-writing added for "show -lib". * SmartFusion2 support - Added $alu support - Added SYSRESET and XTLOSC cells - Compatible now with LiberoSoc flow Yosys 0.19 .. Yosys 0.20 -------------------------- * New commands and options - Added option "-wb" to "read_liberty" pass * Various - Added support for $modfloor operator to cxxrtl backend - Support build on OpenBSD - Fixed smt2 backend use of $shift/$shiftx with negative shift amounts, which affects bit/part-select assignments with a dynamic index. Shift operators were not affected. * Verific support - Proper import of port ranges into Yosys, may result in reversed bit-order of top-level ports for some synthesis flows. Yosys 0.18 .. Yosys 0.19 -------------------------- * New commands and options - Added option "-rom-only" to "memory_libmap" pass - Added option "-smtcheck" to "hierarchy" pass - Added option "-keepdc" to "memory_libmap" pass - Added option "-suffix" to "rename" pass - Added "gatemate_foldinv" pass * Formal Verification - Added support for $pos cell in btor backend - Added the "smtlib2_module" and "smtlib2_comb_expr" attributes * GateMate support - Added LUT tree mapping * Verific support - Added option "-pp" to "verific -import" Yosys 0.17 .. Yosys 0.18 -------------------------- * Various - Migrated most flows to use memory_libmap based memory inference * New commands and options - Added "memory_libmap" pass - Added "memory_bmux2rom" pass - converts muxes to ROMs - Added "memory_dff -no-rw-check" - Added "opt_ffinv" pass - push inverters through FFs - Added "proc_rom" pass - convert switches to ROMs - Added "proc -norom" option - will omit the proc_rom pass - Added option "-no-rw-check" to synth passes - Added "synth_ice40 -spram" option for automatic inference of SB_SPRAM256KA - Added options "-nobram" and "-nolutram" to synth_machxo2 pass * Formal Verification - Fixed the signedness of $past's return value to be the same as the argument's instead of always unsigned. * Verilog - Fixed an issue where simplifying case statements by removing unreachable cases could result in the wrong signedness being used for comparison with the remaining cases - Fixed size and signedness computation for expressions containing array querying functions - Fixed size and signedness computation of functions used in ternary expressions or case item expressions * Verific support - Proper file location for readmem commands - Added "-vlog-libext" option to specify search extension for libraries Yosys 0.16 .. Yosys 0.17 -------------------------- * New commands and options - Added "write_jny" ( JSON netlist metadata format ) - Added "tribuf -formal" * SystemVerilog - Fixed automatic `nosync` inference for local variables in `always_comb` procedures not applying to nested blocks and blocks in functions Yosys 0.15 .. Yosys 0.16 -------------------------- * Various - Added BTOR2 witness file co-simulation. - Simulation calls external vcd2fst for VCD conversion. - Added fst2tb pass - generates testbench for the circuit using the given top-level module and simulus signal from FST file. - yosys-smtbmc: Option to keep going after failed assertions in BMC mode * Verific support - Import modules in alphabetic (reproducable) order. Yosys 0.14 .. Yosys 0.15 -------------------------- * Various - clk2fflogic: nice names for autogenerated signals - simulation include support for all flip-flop types. - Added AIGER witness file co-simulation. * Verilog - Fixed evaluation of constant functions with variables or arguments with reversed dimensions - Fixed elaboration of dynamic range assignments where the vector is reversed or is not zero-indexed - Added frontend support for time scale delay values (e.g., `#1ns`) * SystemVerilog - Added support for accessing whole sub-structures in expressions * New commands and options - Added glift command, used to create gate-level information flow tracking (GLIFT) models by the "constructive mapping" approach * Verific support - Ability to override default parser mode for verific -f command. Yosys 0.13 .. Yosys 0.14 -------------------------- * Various - Added $bmux and $demux cells and related optimization patterns. * New commands and options - Added "bmuxmap" and "dmuxmap" passes - Added "-fst" option to "sim" pass for writing FST files - Added "-r", "-scope", "-start", "-stop", "-at", "-sim", "-sim-gate", "-sim-gold" options to "sim" pass for co-simulation * Anlogic support - Added support for BRAMs Yosys 0.12 .. Yosys 0.13 -------------------------- * Various - Use "read" command to parse HDL files from Yosys command-line - Added "yosys -r " command line option - write_verilog: dump zero width sigspecs correctly * SystemVerilog - Fixed regression preventing the use array querying functions in case expressions and case item expressions - Fixed static size casts inadvertently limiting the result width of binary operations - Fixed static size casts ignoring expression signedness - Fixed static size casts not extending unbased unsized literals - Added automatic `nosync` inference for local variables in `always_comb` procedures which are always assigned before they are used to avoid errant latch inference * New commands and options - Added "clean_zerowidth" pass * Verific support - Add YOSYS to the implicitly defined verilog macros in verific Yosys 0.11 .. Yosys 0.12 -------------------------- * Various - Added iopadmap native support for negative-polarity output enable - ABC update * SystemVerilog - Support parameters using struct as a wiretype * New commands and options - Added "-genlib" option to "abc" pass - Added "sta" very crude static timing analysis pass * Verific support - Fixed memory block size in import * New back-ends - Added support for GateMate FPGA from Cologne Chip AG * Intel ALM support - Added preliminary Arria V support Yosys 0.10 .. Yosys 0.11 -------------------------- * Various - Added $aldff and $aldffe (flip-flops with async load) cells * SystemVerilog - Fixed an issue which prevented writing directly to a memory word via a connection to an output port - Fixed an issue which prevented unbased unsized literals (e.g., `'1`) from filling the width of a cell input - Fixed an issue where connecting a slice covering the entirety of a signed signal to a cell input would cause a failed assertion * Verific support - Importer support for {PRIM,WIDE_OPER}_DFF - Importer support for PRIM_BUFIF1 - Option to use Verific without VHDL support - Importer support for {PRIM,WIDE_OPER}_DLATCH{,RS} - Added -cfg option for getting/setting Verific runtime flags Yosys 0.9 .. Yosys 0.10 -------------------------- * Various - Added automatic gzip decompression for frontends - Added $_NMUX_ cell type - Added automatic gzip compression (based on filename extension) for backends - Improve attribute and parameter encoding in JSON to avoid ambiguities between bit vectors and strings containing [01xz]* - Improvements in pmgen: subpattern and recursive matches - Support explicit FIRRTL properties - Improvements in pmgen: slices, choices, define, generate - Added "_TECHMAP_WIREINIT_*_" parameter and "_TECHMAP_REMOVEINIT_*_" wire for "techmap" pass - Added +/mul2dsp.v for decomposing wide multipliers to custom-sized ones - Added new frontend: rpc - Added --version and -version as aliases for -V - Improve yosys-smtbmc "solver not found" handling - Improved support of $readmem[hb] Memory Content File inclusion - Added CXXRTL backend - Use YosysHQ/abc instead of upstream berkeley-abc/abc - Added WASI platform support. - Added extmodule support to firrtl backend - Added $divfloor and $modfloor cells - Added $adffe, $dffsre, $sdff, $sdffe, $sdffce, $adlatch cells - Added "_TECHMAP_CELLNAME_" parameter for "techmap" pass - Added firrtl backend support for generic parameters in blackbox components - Added $meminit_v2 cells (with support for write mask) - Added $mem_v2, $memrd_v2, $memwr_v2, with the following features: - write priority masks, per write/write port pair - transparency and undefined collision behavior masks, per read/write port pair - read port reset and initialization - wide ports (accessing a naturally aligned power-of-two number of memory cells) * New commands and options - Added "write_xaiger" backend - Added "read_xaiger" - Added "abc9" pass for timing-aware techmapping (experimental, FPGA only) - Added "synth -abc9" (experimental) - Added "script -scriptwire" - Added "clkbufmap" pass - Added "extractinv" pass and "invertible_pin" attribute - Added "proc_clean -quiet" - Added "proc_prune" pass - Added "stat -tech cmos" - Added "opt_share" pass, run as part of "opt -full" - Added "-match-init" option to "dff2dffs" pass - Added "equiv_opt -multiclock" - Added "techmap_autopurge" support to techmap - Added "add -mod " - Added "paramap" pass - Added "portlist" command - Added "check -mapped" - Added "check -allow-tbuf" - Added "autoname" pass - Added "write_verilog -extmem" - Added "opt_mem" pass - Added "scratchpad" pass - Added "fminit" pass - Added "opt_lut_ins" pass - Added "logger" pass - Added "show -nobg" - Added "exec" command - Added "design -delete" - Added "design -push-copy" - Added "qbfsat" command - Added "select -unset" - Added "dfflegalize" pass - Removed "opt_expr -clkinv" option, made it the default - Added "proc -nomux - Merged "dffsr2dff", "opt_rmdff", "dff2dffe", "dff2dffs", "peepopt.dffmux" passes into a new "opt_dff" pass * SystemVerilog - Added checking of always block types (always_comb, always_latch and always_ff) - Added support for wildcard port connections (.*) - Added support for enum typedefs - Added support for structs and packed unions. - Allow constant function calls in for loops and generate if and case - Added support for static cast - Added support for logic typed parameters - Fixed generate scoping issues - Added support for real-valued parameters - Allow localparams in constant functions - Module name scope support - Support recursive functions using ternary expressions - Extended support for integer types - Support for parameters without default values - Allow globals in one file to depend on globals in another - Added support for: *=, /=, %=, <<=, >>=, <<<=, >>>= - Added support for parsing the 'bind' construct - support declaration in procedural for initialization - support declaration in generate for initialization - Support wand and wor of data types * Verific support - Added "verific -L" - Add Verific SVA support for "always" properties - Add Verific support for SVA nexttime properties - Improve handling of verific primitives in "verific -import -V" mode - Import attributes for wires - Support VHDL enums - Added support for command files * New back-ends - Added initial EFINIX support - Added Intel ALM: alternative synthesis for Intel FPGAs - Added initial Nexus support - Added initial MachXO2 support - Added initial QuickLogic PolarPro 3 support * ECP5 support - Renamed labels/options in synth_ecp5 (e.g. dram -> map_lutram; -nodram -> -nolutram) - Added "synth_ecp5 -abc9" (experimental) - Added "synth_ecp5 -nowidelut" - "synth_ecp5" to now infer DSP blocks (-nodsp to disable, experimental) * iCE40 support - Added "synth_ice40 -abc9" (experimental) - Added "synth_ice40 -device" - Renamed labels/options in synth_ice40 (e.g. dram -> map_lutram; -nodram -> -nolutram) - Added "ice40_wrapcarry" to encapsulate SB_LUT+SB_CARRY pairs for techmapping - Removed "ice40_unlut" - Added "ice40_dsp" for Lattice iCE40 DSP packing - "synth_ice40 -dsp" to infer DSP blocks * Xilinx support - Added "synth_xilinx -abc9" (experimental) - Added "synth_xilinx -nocarry" - Added "synth_xilinx -nowidelut" - "synth_xilinx" to now infer wide multiplexers (-widemux to enable) - Renamed labels/options in synth_xilinx (e.g. dram -> map_lutram; -nodram -> -nolutram) - Added "synth_xilinx -family xc6s" for Spartan 6 support (experimental) - Added "synth_xilinx -ise" (experimental) - Added "synth_xilinx -iopad" - "synth_xilinx" now automatically inserts clock buffers (add -noclkbuf to disable) - Added "xilinx_srl" for Xilinx shift register extraction - Removed "shregmap -tech xilinx" (superseded by "xilinx_srl") - Added "xilinx_dsp" for Xilinx DSP packing - "synth_xilinx" to now infer DSP blocks (-nodsp to disable) - Added latch support to synth_xilinx - Added support for flip-flops with synchronous reset to synth_xilinx - Added support for flip-flops with reset and enable to synth_xilinx - Added "xilinx_dffopt" pass - Added "synth_xilinx -dff" * Intel support - Renamed labels in synth_intel (e.g. bram -> map_bram) - synth_intel: cyclone10 -> cyclone10lp, a10gx -> arria10gx - Added "intel_alm -abc9" (experimental) * CoolRunner2 support - Separate and improve buffer cell insertion pass - Use extract_counter to optimize counters Yosys 0.8 .. Yosys 0.9 ---------------------- * Various - Many bugfixes and small improvements - Added support for SystemVerilog interfaces and modports - Added "write_edif -attrprop" - Added "opt_lut" pass - Added "gate2lut.v" techmap rule - Added "rename -src" - Added "equiv_opt" pass - Added "flowmap" LUT mapping pass - Added "rename -wire" to rename cells based on the wires they drive - Added "bugpoint" for creating minimised testcases - Added "write_edif -gndvccy" - "write_verilog" to escape Verilog keywords - Fixed sign handling of real constants - "write_verilog" to write initial statement for initial flop state - Added pmgen pattern matcher generator - Fixed opt_rmdff handling of $_DFFSR_???_ and $_DLATCHSR_???_ - Added "setundef -params" to replace undefined cell parameters - Renamed "yosys -D" to "yosys -U", added "yosys -D" to set Verilog defines - Fixed handling of defparam when default_nettype is none - Fixed "wreduce" flipflop handling - Fixed FIRRTL to Verilog process instance subfield assignment - Added "write_verilog -siminit" - Several fixes and improvements for mem2reg memories - Fixed handling of task output ports in clocked always blocks - Improved handling of and-with-1 and or-with-0 in "opt_expr" - Added "read_aiger" frontend - Added "mutate" pass - Added "hdlname" attribute - Added "rename -output" - Added "read_ilang -lib" - Improved "proc" full_case detection and handling - Added "whitebox" and "lib_whitebox" attributes - Added "read_verilog -nowb", "flatten -wb" and "wbflip" - Added Python bindings and support for Python plug-ins - Added "pmux2shiftx" - Added log_debug framework for reduced default verbosity - Improved "opt_expr" and "opt_clean" handling of (partially) undriven and/or unused wires - Added "peepopt" peephole optimisation pass using pmgen - Added approximate support for SystemVerilog "var" keyword - Added parsing of "specify" blocks into $specrule and $specify[23] - Added support for attributes on parameters and localparams - Added support for parsing attributes on port connections - Added "wreduce -keepdc" - Added support for optimising $dffe and $_DFFE_* cells in "opt_rmdff" - Added Verilog wand/wor wire type support - Added support for elaboration system tasks - Added "muxcover -mux{4,8,16}=" - Added "muxcover -dmux=" - Added "muxcover -nopartial" - Added "muxpack" pass - Added "pmux2shiftx -norange" - Added support for "~" in filename parsing - Added "read_verilog -pwires" feature to turn parameters into wires - Fixed sign extension of unsized constants with 'bx and 'bz MSB - Fixed genvar to be a signed type - Added support for attributes on case rules - Added "upto" and "offset" to JSON frontend and backend - Several liberty file parser improvements - Fixed handling of more complex BRAM patterns - Add "write_aiger -I -O -B" * Formal Verification - Added $changed support to read_verilog - Added "read_verilog -noassert -noassume -assert-assumes" - Added btor ops for $mul, $div, $mod and $concat - Added yosys-smtbmc support for btor witnesses - Added "supercover" pass - Fixed $global_clock handling vs autowire - Added $dffsr support to "async2sync" - Added "fmcombine" pass - Added memory init support in "write_btor" - Added "cutpoint" pass - Changed "ne" to "neq" in btor2 output - Added support for SVA "final" keyword - Added "fmcombine -initeq -anyeq" - Added timescale and generated-by header to yosys-smtbmc vcd output - Improved BTOR2 handling of undriven wires * Verific support - Enabled Verific flags vhdl_support_variable_slice and veri_elaborate_top_level_modules_having_interface_ports - Improved support for asymmetric memories - Added "verific -chparam" - Fixed "verific -extnets" for more complex situations - Added "read -verific" and "read -noverific" - Added "hierarchy -chparam" * New back-ends - Added initial Anlogic support - Added initial SmartFusion2 and IGLOO2 support * ECP5 support - Added "synth_ecp5 -nowidelut" - Added BRAM inference support to "synth_ecp5" - Added support for transforming Diamond IO and flipflop primitives * iCE40 support - Added "ice40_unlut" pass - Added "synth_ice40 -relut" - Added "synth_ice40 -noabc" - Added "synth_ice40 -dffe_min_ce_use" - Added DSP inference support using pmgen - Added support for initialising BRAM primitives from a file - Added iCE40 Ultra RGB LED driver cells * Xilinx support - Use "write_edif -pvector bra" for Xilinx EDIF files - Fixes for VPR place and route support with "synth_xilinx" - Added more cell simulation models - Added "synth_xilinx -family" - Added "stat -tech xilinx" to estimate logic cell usage - Added "synth_xilinx -nocarry" - Added "synth_xilinx -nowidelut" - "synth_xilinx" to now infer hard shift registers (-nosrl to disable) - Added support for mapping RAM32X1D Yosys 0.7 .. Yosys 0.8 ---------------------- * Various - Many bugfixes and small improvements - Strip debug symbols from installed binary - Replace -ignore_redef with -[no]overwrite in front-ends - Added write_verilog hex dump support, add -nohex option - Added "write_verilog -decimal" - Added "scc -set_attr" - Added "verilog_defines" command - Remember defines from one read_verilog to next - Added support for hierarchical defparam - Added FIRRTL back-end - Improved ABC default scripts - Added "design -reset-vlog" - Added "yosys -W regex", "yosys -w regex", and "yosys -e regex" - Added Verilog $rtoi and $itor support - Added "check -initdrv" - Added "read_blif -wideports" - Added support for SystemVerilog "++" and "--" operators - Added support for SystemVerilog unique, unique0, and priority case - Added "write_edif" options for edif "flavors" - Added support for resetall compiler directive - Added simple C beck-end (bitwise combinatorical only atm) - Added $_ANDNOT_ and $_ORNOT_ cell types - Added cell library aliases to "abc -g" - Added "setundef -anyseq" - Added "chtype" command - Added "design -import" - Added "write_table" command - Added "read_json" command - Added "sim" command - Added "extract_fa" and "extract_reduce" commands - Added "extract_counter" command - Added "opt_demorgan" command - Added support for $size and $bits SystemVerilog functions - Added "blackbox" command - Added "ltp" command - Added support for editline as replacement for readline - Added warnings for driver-driver conflicts between FFs (and other cells) and constants - Added "yosys -E" for creating Makefile dependencies files - Added "synth -noshare" - Added "memory_nordff" - Added "setundef -undef -expose -anyconst" - Added "expose -input" - Added specify/specparam parser support (simply ignore them) - Added "write_blif -inames -iattr" - Added "hierarchy -simcheck" - Added an option to statically link abc into yosys - Added protobuf back-end - Added BLIF parsing support for .conn and .cname - Added read_verilog error checking for reg/wire/logic misuse - Added "make coverage" and ENABLE_GCOV build option * Changes in Yosys APIs - Added ConstEval defaultval feature - Added {get,set}_src_attribute() methods on RTLIL::AttrObject - Added SigSpec::is_fully_ones() and Const::is_fully_ones() - Added log_file_warning() and log_file_error() functions * Formal Verification - Added "write_aiger" - Added "yosys-smtbmc --aig" - Added "always " to .smtc format - Added $cover cell type and support for cover properties - Added $fair/$live cell type and support for liveness properties - Added smtbmc support for memory vcd dumping - Added "chformal" command - Added "write_smt2 -stbv" and "write_smt2 -stdt" - Fix equiv_simple, old behavior now available with "equiv_simple -short" - Change to Yices2 as default SMT solver (it is GPL now) - Added "yosys-smtbmc --presat" (now default in SymbiYosys) - Added "yosys-smtbmc --smtc-init --smtc-top --noinit" - Added a brand new "write_btor" command for BTOR2 - Added clk2fflogic memory support and other improvements - Added "async memory write" support to write_smt2 - Simulate clock toggling in yosys-smtbmc VCD output - Added $allseq/$allconst cells for EA-solving - Make -nordff the default in "prep" - Added (* gclk *) attribute - Added "async2sync" pass for single-clock designs with async resets * Verific support - Many improvements in Verific front-end - Added proper handling of concurent SVA properties - Map "const" and "rand const" to $anyseq/$anyconst - Added "verific -import -flatten" and "verific -import -extnets" - Added "verific -vlog-incdir -vlog-define -vlog-libdir" - Remove PSL support (because PSL has been removed in upstream Verific) - Improve integration with "hierarchy" command design elaboration - Added YOSYS_NOVERIFIC for running non-verific test cases with verific bin - Added simpilied "read" command that automatically uses verific if available - Added "verific -set- .." - Added "verific -work " * New back-ends - Added initial Coolrunner-II support - Added initial eASIC support - Added initial ECP5 support * GreenPAK Support - Added support for GP_DLATCH, GP_SPI, GP_DCMx, GP_COUNTx, etc. * iCE40 Support - Add "synth_ice40 -vpr" - Add "synth_ice40 -nodffe" - Add "synth_ice40 -json" - Add Support for UltraPlus cells * MAX10 and Cyclone IV Support - Added initial version of metacommand "synth_intel". - Improved write_verilog command to produce VQM netlist for Quartus Prime. - Added support for MAX10 FPGA family synthesis. - Added support for Cyclone IV family synthesis. - Added example of implementation for DE2i-150 board. - Added example of implementation for MAX10 development kit. - Added LFSR example from Asic World. - Added "dffinit -highlow" for mapping to Intel primitives Yosys 0.6 .. Yosys 0.7 ---------------------- * Various - Added "yosys -D" feature - Added support for installed plugins in $(DATDIR)/plugins/ - Renamed opt_const to opt_expr - Renamed opt_share to opt_merge - Added "prep -flatten" and "synth -flatten" - Added "prep -auto-top" and "synth -auto-top" - Using "mfs" and "lutpack" in ABC lut mapping - Support for abstract modules in chparam - Cleanup abstract modules at end of "hierarchy -top" - Added tristate buffer support to iopadmap - Added opt_expr support for div/mod by power-of-two - Added "select -assert-min -assert-max " - Added "attrmvcp" pass - Added "attrmap" command - Added "tee +INT -INT" - Added "zinit" pass - Added "setparam -type" - Added "shregmap" pass - Added "setundef -init" - Added "nlutmap -assert" - Added $sop cell type and "abc -sop -I -P " - Added "dc2" to default ABC scripts - Added "deminout" - Added "insbuf" command - Added "prep -nomem" - Added "opt_rmdff -keepdc" - Added "prep -nokeepdc" - Added initial version of "synth_gowin" - Added "fsm_expand -full" - Added support for fsm_encoding="user" - Many improvements in GreenPAK4 support - Added black box modules for all Xilinx 7-series lib cells - Added synth_ice40 support for latches via logic loops - Fixed ice40_opt lut unmapping, added "ice40_opt -unlut" * Build System - Added ABCEXTERNAL and ABCURL make variables - Added BINDIR, LIBDIR, and DATDIR make variables - Added PKG_CONFIG make variable - Added SEED make variable (for "make test") - Added YOSYS_VER_STR make variable - Updated min GCC requirement to GCC 4.8 - Updated required Bison version to Bison 3.x * Internal APIs - Added ast.h to exported headers - Added ScriptPass helper class for script-like passes - Added CellEdgesDatabase API * Front-ends and Back-ends - Added filename glob support to all front-ends - Added avail (black-box) module params to ilang format - Added $display %m support - Added support for $stop Verilog system task - Added support for SystemVerilog packages - Fixed procedural assignments to non-unique lvalues, e.g. {y,y} = {a,b} - Added support for "active high" and "active low" latches in read_blif and write_blif - Use init value "2" for all uninitialized FFs in BLIF back-end - Added "read_blif -sop" - Added "write_blif -noalias" - Added various write_blif options for VTR support - write_json: also write module attributes. - Added "write_verilog -nodec -nostr -defparam" - Added "read_verilog -norestrict -assume-asserts" - Added support for bus interfaces to "read_liberty -lib" - Added liberty parser support for types within cell decls - Added "write_verilog -renameprefix -v" - Added "write_edif -nogndvcc" * Formal Verification - Support for hierarchical designs in smt2 back-end - Yosys-smtbmc: Support for hierarchical VCD dumping - Added $initstate cell type and vlog function - Added $anyconst and $anyseq cell types and vlog functions - Added printing of code loc of failed asserts to yosys-smtbmc - Added memory_memx pass, "memory -memx", and "prep -memx" - Added "proc_mux -ifx" - Added "yosys-smtbmc -g" - Deprecated "write_smt2 -regs" (by default on now) - Made "write_smt2 -bv -mem" default, added "write_smt2 -nobv -nomem" - Added support for memories to smtio.py - Added "yosys-smtbmc --dump-vlogtb" - Added "yosys-smtbmc --smtc --dump-smtc" - Added "yosys-smtbmc --dump-all" - Added assertpmux command - Added "yosys-smtbmc --unroll" - Added $past, $stable, $rose, $fell SVA functions - Added "yosys-smtbmc --noinfo and --dummy" - Added "yosys-smtbmc --noincr" - Added "yosys-smtbmc --cex " - Added $ff and $_FF_ cell types - Added $global_clock verilog syntax support for creating $ff cells - Added clk2fflogic Yosys 0.5 .. Yosys 0.6 ---------------------- * Various - Added Contributor Covenant Code of Conduct - Various improvements in dict<> and pool<> - Added hashlib::mfp and refactored SigMap - Improved support for reals as module parameters - Various improvements in SMT2 back-end - Added "keep_hierarchy" attribute - Verilog front-end: define `BLACKBOX in -lib mode - Added API for converting internal cells to AIGs - Added ENABLE_LIBYOSYS Makefile option - Removed "techmap -share_map" (use "-map +/filename" instead) - Switched all Python scripts to Python 3 - Added support for $display()/$write() and $finish() to Verilog front-end - Added "yosys-smtbmc" formal verification flow - Added options for clang sanitizers to Makefile * New commands and options - Added "scc -expect -nofeedback" - Added "proc_dlatch" - Added "check" - Added "select %xe %cie %coe %M %C %R" - Added "sat -dump_json" (WaveJSON format) - Added "sat -tempinduct-baseonly -tempinduct-inductonly" - Added "sat -stepsize" and "sat -tempinduct-step" - Added "sat -show-regs -show-public -show-all" - Added "write_json" (Native Yosys JSON format) - Added "write_blif -attr" - Added "dffinit" - Added "chparam" - Added "muxcover" - Added "pmuxtree" - Added memory_bram "make_outreg" feature - Added "splice -wires" - Added "dff2dffe -direct-match" - Added simplemap $lut support - Added "read_blif" - Added "opt_share -share_all" - Added "aigmap" - Added "write_smt2 -mem -regs -wires" - Added "memory -nordff" - Added "write_smv" - Added "synth -nordff -noalumacc" - Added "rename -top new_name" - Added "opt_const -clkinv" - Added "synth -nofsm" - Added "miter -assert" - Added "read_verilog -noautowire" - Added "read_verilog -nodpi" - Added "tribuf" - Added "lut2mux" - Added "nlutmap" - Added "qwp" - Added "test_cell -noeval" - Added "edgetypes" - Added "equiv_struct" - Added "equiv_purge" - Added "equiv_mark" - Added "equiv_add -try -cell" - Added "singleton" - Added "abc -g -luts" - Added "torder" - Added "write_blif -cname" - Added "submod -copy" - Added "dffsr2dff" - Added "stat -liberty" * Synthesis metacommands - Various improvements in synth_xilinx - Added synth_ice40 and synth_greenpak4 - Added "prep" metacommand for "synthesis lite" * Cell library changes - Added cell types to "help" system - Added $meminit cell type - Added $assume cell type - Added $_MUX4_, $_MUX8_, and $_MUX16_ cells - Added $tribuf and $_TBUF_ cell types - Added read-enable to memory model * YosysJS - Various improvements in emscripten build - Added alternative webworker-based JS API - Added a few example applications Yosys 0.4 .. Yosys 0.5 ---------------------- * API changes - Added log_warning() - Added eval_select_args() and eval_select_op() - Added cell->known(), cell->input(portname), cell->output(portname) - Skip blackbox modules in design->selected_modules() - Replaced std::map<> and std::set<> with dict<> and pool<> - New SigSpec::extend() is what used to be SigSpec::extend_u0() - Added YS_OVERRIDE, YS_FINAL, YS_ATTRIBUTE, YS_NORETURN * Cell library changes - Added flip-flops with enable ($dffe etc.) - Added $equiv cells for equivalence checking framework * Various - Updated ABC to hg rev 61ad5f908c03 - Added clock domain partitioning to ABC pass - Improved plugin building (see "yosys-config --build") - Added ENABLE_NDEBUG Makefile flag for high-performance builds - Added "yosys -d", "yosys -L" and other driver improvements - Added support for multi-bit (array) cell ports to "write_edif" - Now printing most output to stdout, not stderr - Added "onehot" attribute (set by "fsm_map") - Various performance improvements - Vastly improved Xilinx flow - Added "make unsintall" * Equivalence checking - Added equivalence checking commands: equiv_make equiv_simple equiv_status equiv_induct equiv_miter equiv_add equiv_remove * Block RAM support: - Added "memory_bram" command - Added BRAM support to Xilinx flow * Other New Commands and Options - Added "dff2dffe" - Added "fsm -encfile" - Added "dfflibmap -prepare" - Added "write_blid -unbuf -undef -blackbox" - Added "write_smt2" for writing SMT-LIBv2 files - Added "test_cell -w -muxdiv" - Added "select -read" Yosys 0.3.0 .. Yosys 0.4 ------------------------ * Platform Support - Added support for mxe-based cross-builds for win32 - Added sourcecode-export as VisualStudio project - Added experimental EMCC (JavaScript) support * Verilog Frontend - Added -sv option for SystemVerilog (and automatic *.sv file support) - Added support for real-valued constants and constant expressions - Added support for non-standard "via_celltype" attribute on task/func - Added support for non-standard "module mod_name(...);" syntax - Added support for non-standard """ macro bodies - Added support for array with more than one dimension - Added support for $readmemh and $readmemb - Added support for DPI functions * Changes in internal cell library - Added $shift and $shiftx cell types - Added $alu, $lcu, $fa and $macc cell types - Removed $bu0 and $safe_pmux cell types - $mem/$memwr WR_EN input is now a per-data-bit enable signal - Added $_NAND_ $_NOR_ $_XNOR_ $_AOI3_ $_OAI3_ $_AOI4_ $_OAI4_ - Renamed ports of $lut cells (from I->O to A->Y) - Renamed $_INV_ to $_NOT_ * Changes for simple synthesis flows - There is now a "synth" command with a recommended default script - Many improvements in synthesis of arithmetic functions to gates - Multipliers and adders with many operands are using carry-save adder trees - Remaining adders are now implemented using Brent-Kung carry look-ahead adders - Various new high-level optimizations on RTL netlist - Various improvements in FSM optimization - Updated ABC to hg 5b5af75f1dda (from 2014-11-07) * Changes in internal APIs and RTLIL - Added log_id() and log_cell() helper functions - Added function-like cell creation helpers - Added GetSize() function (like .size() but with int) - Major refactoring of RTLIL::Module and related classes - Major refactoring of RTLIL::SigSpec and related classes - Now RTLIL::IdString is essentially an int - Added macros for code coverage counters - Added some Makefile magic for pretty make logs - Added "kernel/yosys.h" with all the core definitions - Changed a lot of code from FILE* to c++ streams - Added RTLIL::Monitor API and "trace" command - Added "Yosys" C++ namespace * Changes relevant to SAT solving - Added ezSAT::keep_cnf() and ezSAT::non_incremental() - Added native ezSAT support for vector shift ops - Updated MiniSAT to git 37dc6c67e2 (from 2013-09-25) * New commands (or large improvements to commands) - Added "synth" command with default script - Added "share" (finally some real resource sharing) - Added "memory_share" (reduce number of ports on memories) - Added "wreduce" and "alumacc" commands - Added "opt -keepdc -fine -full -fast" - Added some "test_*" commands * Various other changes - Added %D and %c select operators - Added support for labels in yosys scripts - Added support for here-documents in yosys scripts - Support "+/" prefix for files from proc_share_dir - Added "autoidx" statement to ilang language - Switched from "yosys-svgviewer" to "xdot" - Renamed "stdcells.v" to "techmap.v" - Various bug fixes and small improvements - Improved welcome and bye messages Yosys 0.2.0 .. Yosys 0.3.0 -------------------------- * Driver program and overall behavior: - Added "design -push" and "design -pop" - Added "tee" command for redirecting log output * Changes in the internal cell library: - Added $dlatchsr and $_DLATCHSR_???_ cell types * Improvements in Verilog frontend: - Improved support for const functions (case, always, repeat) - The generate..endgenerate keywords are now optional - Added support for arrays of module instances - Added support for "`default_nettype" directive - Added support for "`line" directive * Other front- and back-ends: - Various changes to "write_blif" options - Various improvements in EDIF backend - Added "vhdl2verilog" pseudo-front-end - Added "verific" pseudo-front-end * Improvements in technology mapping: - Added support for recursive techmap - Added CONSTMSK and CONSTVAL features to techmap - Added _TECHMAP_CONNMAP_*_ feature to techmap - Added _TECHMAP_REPLACE_ feature to techmap - Added "connwrappers" command for wrap-extract-unwrap method - Added "extract -map %" feature - Added "extract -ignore_param ..." and "extract -ignore_parameters" - Added "techmap -max_iter" option * Improvements to "eval" and "sat" framework: - Now include a copy of Minisat (with build fixes applied) - Switched to Minisat::SimpSolver as SAT back-end - Added "sat -dump_vcd" feature - Added "sat -dump_cnf" feature - Added "sat -initsteps " feature - Added "freduce -stop " feature - Added "freduce -dump " feature * Integration with ABC: - Updated ABC rev to 7600ffb9340c * Improvements in the internal APIs: - Added RTLIL::Module::add... helper methods - Various build fixes for OSX (Darwin) and OpenBSD Yosys 0.1.0 .. Yosys 0.2.0 -------------------------- * Changes to the driver program: - Added "yosys -h" and "yosys -H" - Added support for backslash line continuation in scripts - Added support for #-comments in same line as command - Added "echo" and "log" commands * Improvements in Verilog frontend: - Added support for local registers in named blocks - Added support for "case" in "generate" blocks - Added support for $clog2 system function - Added support for basic SystemVerilog assert statements - Added preprocessor support for macro arguments - Added preprocessor support for `elsif statement - Added "verilog_defaults" command - Added read_verilog -icells option - Added support for constant sizes from parameters - Added "read_verilog -setattr" - Added support for function returning 'integer' - Added limited support for function calls in parameter values - Added "read_verilog -defer" to suppress evaluation of modules with default parameters * Other front- and back-ends: - Added BTOR backend - Added Liberty frontend * Improvements in technology mapping: - The "dfflibmap" command now strongly prefers solutions with no inverters in clock paths - The "dfflibmap" command now prefers cells with smaller area - Added support for multiple -map options to techmap - Added "dfflibmap" support for //-comments in liberty files - Added "memory_unpack" command to revert "memory_collect" - Added standard techmap rule "techmap -share_map pmux2mux.v" - Added "iopadmap -bits" - Added "setundef" command - Added "hilomap" command * Changes in the internal cell library: - Major rewrite of simlib.v for better compatibility with other tools - Added PRIORITY parameter to $memwr cells - Added TRANSPARENT parameter to $memrd cells - Added RD_TRANSPARENT parameter to $mem cells - Added $bu0 cell (always 0-extend, even undef MSB) - Added $assert cell type - Added $slice and $concat cell types * Integration with ABC: - Updated ABC to hg rev 2058c8ccea68 - Tighter integration of ABC build with Yosys build. The make targets 'make abc' and 'make install-abc' are now obsolete. - Added support for passing FFs from one clock domain through ABC - Now always use BLIF as exchange format with ABC - Added support for "abc -script +" - Improved standard ABC recipe - Added support for "keep" attribute to abc command - Added "abc -dff / -clk / -keepff" options * Improvements to "eval" and "sat" framework: - Added support for "0" and "~0" in right-hand side -set expressions - Added "eval -set-undef" and "eval -table" - Added "sat -set-init" and "sat -set-init-*" for sequential problems - Added undef support to SAT solver, incl. various new "sat" options - Added correct support for === and !== for "eval" and "sat" - Added "sat -tempinduct" (default -seq is now non-induction sequential) - Added "sat -prove-asserts" - Complete rewrite of the 'freduce' command - Added "miter" command - Added "sat -show-inputs" and "sat -show-outputs" - Added "sat -ignore_unknown_cells" (now produce an error by default) - Added "sat -falsify" - Now "sat -verify" and "sat -falsify" can also be used without "-prove" - Added "expose" command - Added support for @ to sat and eval signal expressions * Changes in the 'make test' framework and auxiliary test tools: - Added autotest.sh -p and -f options - Replaced autotest.sh ISIM support with XSIM support - Added test cases for SAT framework * Added "abbreviated IDs": - Now $$foo can be abbreviated as $foo. - Usually this last part is a unique id (from RTLIL::autoidx) - This abbreviated IDs are now also used in "show" output * Other changes to selection framework: - Now */ is optional in */: expressions - Added "select -assert-none" and "select -assert-any" - Added support for matching modules by attribute (A:) - Added "select -none" - Added support for r: pattern for matching cell parameters - Added support for !=, <, <=, >=, > for attribute and parameter matching - Added support for %s for selecting sub-modules - Added support for %m for expanding selections to whole modules - Added support for i:*, o:* and x:* pattern for selecting module ports - Added support for s: pattern for matching wire width - Added support for %a operation to select wire aliases * Various other changes to commands and options: - The "ls" command now supports wildcards - Added "show -pause" and "show -format dot" - Added "show -color" support for cells - Added "show -label" and "show -notitle" - Added "dump -m" and "dump -n" - Added "history" command - Added "rename -hide" - Added "connect" command - Added "splitnets -driver" - Added "opt_const -mux_undef" - Added "opt_const -mux_bool" - Added "opt_const -undriven" - Added "opt -mux_undef -mux_bool -undriven -purge" - Added "hierarchy -libdir" - Added "hierarchy -purge_lib" (by default now do not remove lib cells) - Added "delete" command - Added "dump -append" - Added "setattr" and "setparam" commands - Added "design -stash/-copy-from/-copy-to" - Added "copy" command - Added "splice" command yosys-yosys-0.33/CODEOWNERS000066400000000000000000000031011447554276300154200ustar00rootroot00000000000000## CODE NOTIFICATIONS # Register yourself here to be notified about modifications # for any files you have an interest in/know your way around. # Each line is a file pattern followed by one or more users. # Both github usernames and email addresses are supported. # Order is important; the last matching pattern takes the most # precedence. Previous matches will not be applied. # PATH (can use glob) USERNAME(S) passes/cmds/scratchpad.cc @nakengelhardt frontends/rpc/ @whitequark backends/cxxrtl/ @whitequark passes/cmds/bugpoint.cc @whitequark passes/techmap/flowmap.cc @whitequark passes/opt/opt_lut.cc @whitequark passes/techmap/abc9*.cc @eddiehung @Ravenslofty backends/aiger/xaiger.cc @eddiehung ## External Contributors # Only users with write permission to the repository get review # requests automatically, but we add information for other # contributors here too, so we know who to ask to take a look. # These still override previous lines, so be careful not to # accidentally disable any of the above rules. frontends/verilog/ @zachjs frontends/ast/ @zachjs techlibs/intel_alm/ @Ravenslofty techlibs/gowin/ @pepijndevos techlibs/gatemate/ @pu-cc # pyosys misc/*.py @btut backends/firrtl @ucbjrl @azidar passes/sat/qbfsat.cc @boqwxp passes/sat/qbfsat.h @boqwxp passes/cmds/exec.cc @boqwxp passes/cmds/glift.cc @boqwxp passes/cmds/printattrs.cc @boqwxp yosys-yosys-0.33/COPYING000066400000000000000000000014111447554276300150620ustar00rootroot00000000000000ISC License Copyright (C) 2012 - 2020 Claire Xenia Wolf Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. yosys-yosys-0.33/Dockerfile000066400000000000000000000017051447554276300160270ustar00rootroot00000000000000ARG IMAGE="python:3-slim-buster" #--- FROM $IMAGE AS base RUN apt-get update -qq \ && DEBIAN_FRONTEND=noninteractive apt-get -y install --no-install-recommends \ ca-certificates \ clang \ curl \ libffi-dev \ libreadline-dev \ tcl-dev \ graphviz \ xdot \ && apt-get autoclean && apt-get clean && apt-get -y autoremove \ && update-ca-certificates \ && rm -rf /var/lib/apt/lists #--- FROM base AS build RUN apt-get update -qq \ && DEBIAN_FRONTEND=noninteractive apt-get -y install --no-install-recommends \ bison \ flex \ gawk \ gcc \ git \ iverilog \ pkg-config \ && apt-get autoclean && apt-get clean && apt-get -y autoremove \ && rm -rf /var/lib/apt/lists COPY . /yosys ENV PREFIX /opt/yosys RUN cd /yosys \ && make \ && make install \ && make test #--- FROM base COPY --from=build /opt/yosys /opt/yosys ENV PATH /opt/yosys/bin:$PATH RUN useradd -m yosys USER yosys CMD ["yosys"] yosys-yosys-0.33/Makefile000066400000000000000000001132551447554276300155010ustar00rootroot00000000000000 CONFIG := clang # CONFIG := gcc # CONFIG := afl-gcc # CONFIG := emcc # CONFIG := wasi # CONFIG := mxe # CONFIG := msys2-32 # CONFIG := msys2-64 # features (the more the better) ENABLE_TCL := 1 ENABLE_ABC := 1 ENABLE_GLOB := 1 ENABLE_PLUGINS := 1 ENABLE_READLINE := 1 ENABLE_EDITLINE := 0 ENABLE_GHDL := 0 ENABLE_VERIFIC := 0 ENABLE_VERIFIC_EDIF := 0 ENABLE_VERIFIC_LIBERTY := 0 DISABLE_VERIFIC_EXTENSIONS := 0 DISABLE_VERIFIC_VHDL := 0 ENABLE_COVER := 1 ENABLE_LIBYOSYS := 0 ENABLE_ZLIB := 1 # python wrappers ENABLE_PYOSYS := 0 # other configuration flags ENABLE_GCOV := 0 ENABLE_GPROF := 0 ENABLE_DEBUG := 0 ENABLE_NDEBUG := 0 ENABLE_CCACHE := 0 # sccache is not always a drop-in replacement for ccache in practice ENABLE_SCCACHE := 0 LINK_CURSES := 0 LINK_TERMCAP := 0 LINK_ABC := 0 # Needed for environments that can't run executables (i.e. emscripten, wasm) DISABLE_SPAWN := 0 # Needed for environments that don't have proper thread support (i.e. emscripten, wasm--for now) DISABLE_ABC_THREADS := 0 # clang sanitizers SANITIZER = # SANITIZER = address # SANITIZER = memory # SANITIZER = undefined # SANITIZER = cfi PROGRAM_PREFIX := OS := $(shell uname -s) PREFIX ?= /usr/local INSTALL_SUDO := ifneq ($(filter MINGW%,$(OS)),) OS := MINGW endif ifneq ($(wildcard Makefile.conf),) include Makefile.conf endif ifeq ($(ENABLE_PYOSYS),1) ENABLE_LIBYOSYS := 1 endif BINDIR := $(PREFIX)/bin LIBDIR := $(PREFIX)/lib/$(PROGRAM_PREFIX)yosys DATDIR := $(PREFIX)/share/$(PROGRAM_PREFIX)yosys EXE = OBJS = GENFILES = EXTRA_OBJS = EXTRA_TARGETS = TARGETS = $(PROGRAM_PREFIX)yosys$(EXE) $(PROGRAM_PREFIX)yosys-config PRETTY = 1 SMALL = 0 # Unit test UNITESTPATH := tests/unit all: top-all YOSYS_SRC := $(dir $(firstword $(MAKEFILE_LIST))) VPATH := $(YOSYS_SRC) CXXSTD ?= c++11 CXXFLAGS := $(CXXFLAGS) -Wall -Wextra -ggdb -I. -I"$(YOSYS_SRC)" -MD -MP -D_YOSYS_ -fPIC -I$(PREFIX)/include LDLIBS := $(LDLIBS) -lstdc++ -lm PLUGIN_LDFLAGS := PLUGIN_LDLIBS := EXE_LDFLAGS := ifeq ($(OS), MINGW) EXE_LDFLAGS := -Wl,--export-all-symbols -Wl,--out-implib,libyosys_exe.a PLUGIN_LDFLAGS += -L"$(LIBDIR)" PLUGIN_LDLIBS := -lyosys_exe endif PKG_CONFIG ?= pkg-config SED ?= sed BISON ?= bison STRIP ?= strip AWK ?= awk ifeq ($(OS), Darwin) PLUGIN_LDFLAGS += -undefined dynamic_lookup # homebrew search paths ifneq ($(shell :; command -v brew),) BREW_PREFIX := $(shell brew --prefix)/opt $(info $$BREW_PREFIX is [${BREW_PREFIX}]) ifeq ($(ENABLE_PYOSYS),1) CXXFLAGS += -I$(BREW_PREFIX)/boost/include/boost LDFLAGS += -L$(BREW_PREFIX)/boost/lib endif CXXFLAGS += -I$(BREW_PREFIX)/readline/include LDFLAGS += -L$(BREW_PREFIX)/readline/lib PKG_CONFIG_PATH := $(BREW_PREFIX)/libffi/lib/pkgconfig:$(PKG_CONFIG_PATH) PKG_CONFIG_PATH := $(BREW_PREFIX)/tcl-tk/lib/pkgconfig:$(PKG_CONFIG_PATH) export PATH := $(BREW_PREFIX)/bison/bin:$(BREW_PREFIX)/gettext/bin:$(BREW_PREFIX)/flex/bin:$(PATH) # macports search paths else ifneq ($(shell :; command -v port),) PORT_PREFIX := $(patsubst %/bin/port,%,$(shell :; command -v port)) CXXFLAGS += -I$(PORT_PREFIX)/include LDFLAGS += -L$(PORT_PREFIX)/lib PKG_CONFIG_PATH := $(PORT_PREFIX)/lib/pkgconfig:$(PKG_CONFIG_PATH) export PATH := $(PORT_PREFIX)/bin:$(PATH) endif else LDFLAGS += -rdynamic ifneq ($(OS), OpenBSD) LDLIBS += -lrt endif endif YOSYS_VER := 0.33 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo # will have this file in its unexpanded form tough, in which case we fall # back to calling git directly. TARBALL_GIT_REV := $(shell cat $(YOSYS_SRC)/.gitcommit) ifeq ($(TARBALL_GIT_REV),$$Format:%h$$) GIT_REV := $(shell GIT_DIR=$(YOSYS_SRC)/.git git rev-parse --short=9 HEAD || echo UNKNOWN) else GIT_REV := $(TARBALL_GIT_REV) endif OBJS = kernel/version_$(GIT_REV).o bumpversion: # sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline fbab08a.. | wc -l`/;" Makefile # set 'ABCREV = default' to use abc/ as it is # # Note: If you do ABC development, make sure that 'abc' in this directory # is just a symlink to your actual ABC working directory, as 'make mrproper' # will remove the 'abc' directory and you do not want to accidentally # delete your work on ABC.. ABCREV = bb64142 ABCPULL = 1 ABCURL ?= https://github.com/YosysHQ/abc ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 ABC_USE_NAMESPACE=abc VERBOSE=$(Q) # set ABCEXTERNAL = to use an external ABC instance # Note: The in-tree ABC (yosys-abc) will not be installed when ABCEXTERNAL is set. ABCEXTERNAL ?= define newline endef ifneq ($(wildcard Makefile.conf),) # don't echo Makefile.conf contents when invoked to print source versions ifeq ($(findstring echo-,$(MAKECMDGOALS)),) $(info $(subst $$--$$,$(newline),$(shell sed 's,^,[Makefile.conf] ,; s,$$,$$--$$,;' < Makefile.conf | tr -d '\n' | sed 's,\$$--\$$$$,,'))) endif include Makefile.conf endif PYTHON_EXECUTABLE := $(shell if python3 -c ""; then echo "python3"; else echo "python"; fi) ifeq ($(ENABLE_PYOSYS),1) PYTHON_VERSION_TESTCODE := "import sys;t='{v[0]}.{v[1]}'.format(v=list(sys.version_info[:2]));print(t)" PYTHON_VERSION := $(shell $(PYTHON_EXECUTABLE) -c ""$(PYTHON_VERSION_TESTCODE)"") PYTHON_MAJOR_VERSION := $(shell echo $(PYTHON_VERSION) | cut -f1 -d.) ENABLE_PYTHON_CONFIG_EMBED ?= $(shell $(PYTHON_EXECUTABLE)-config --embed --libs > /dev/null && echo 1) ifeq ($(ENABLE_PYTHON_CONFIG_EMBED),1) PYTHON_CONFIG := $(PYTHON_EXECUTABLE)-config --embed else PYTHON_CONFIG := $(PYTHON_EXECUTABLE)-config endif PYTHON_DESTDIR := $(shell $(PYTHON_EXECUTABLE) -c "import site; print(site.getsitepackages()[-1]);") # Reload Makefile.conf to override python specific variables if defined ifneq ($(wildcard Makefile.conf),) include Makefile.conf endif endif ABC_ARCHFLAGS = "" ifeq ($(OS), OpenBSD) ABC_ARCHFLAGS += "-DABC_NO_RLIMIT" endif ifeq ($(CONFIG),clang) CXX = clang LD = clang++ CXXFLAGS += -std=$(CXXSTD) -Os ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -Wno-c++11-narrowing $(ABC_ARCHFLAGS)" ifneq ($(SANITIZER),) $(info [Clang Sanitizer] $(SANITIZER)) CXXFLAGS += -g -O1 -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=$(SANITIZER) LDFLAGS += -g -fsanitize=$(SANITIZER) ifneq ($(findstring address,$(SANITIZER)),) ENABLE_COVER := 0 endif ifneq ($(findstring memory,$(SANITIZER)),) CXXFLAGS += -fPIE -fsanitize-memory-track-origins LDFLAGS += -fPIE -fsanitize-memory-track-origins endif ifneq ($(findstring cfi,$(SANITIZER)),) CXXFLAGS += -flto LDFLAGS += -flto endif endif else ifeq ($(CONFIG),gcc) CXX = gcc LD = gcc CXXFLAGS += -std=$(CXXSTD) -Os ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H $(ABC_ARCHFLAGS)" else ifeq ($(CONFIG),gcc-static) LD = $(CXX) LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -static LDLIBS := $(filter-out -lrt,$(LDLIBS)) CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS)) CXXFLAGS += -std=$(CXXSTD) -Os ABCMKARGS = CC="$(CC)" CXX="$(CXX)" LD="$(LD)" ABC_USE_LIBSTDCXX=1 LIBS="-lm -lpthread -static" OPTFLAGS="-O" \ ARCHFLAGS="-DABC_USE_STDINT_H -DABC_NO_DYNAMIC_LINKING=1 -Wno-unused-but-set-variable $(ARCHFLAGS)" ABC_USE_NO_READLINE=1 ifeq ($(DISABLE_ABC_THREADS),1) ABCMKARGS += "ABC_USE_NO_PTHREADS=1" endif else ifeq ($(CONFIG),afl-gcc) CXX = AFL_QUIET=1 AFL_HARDEN=1 afl-gcc LD = AFL_QUIET=1 AFL_HARDEN=1 afl-gcc CXXFLAGS += -std=$(CXXSTD) -Os ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H" else ifeq ($(CONFIG),cygwin) CXX = gcc LD = gcc CXXFLAGS += -std=gnu++11 -Os ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H" else ifeq ($(CONFIG),emcc) CXX = emcc LD = emcc CXXFLAGS := -std=$(CXXSTD) $(filter-out -fPIC -ggdb,$(CXXFLAGS)) ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DABC_MEMALIGN=8 -Wno-c++11-narrowing" EMCC_CXXFLAGS := -Os -Wno-warn-absolute-paths EMCC_LDFLAGS := --memory-init-file 0 --embed-file share EMCC_LDFLAGS += -s NO_EXIT_RUNTIME=1 EMCC_LDFLAGS += -s EXPORTED_FUNCTIONS="['_main','_run','_prompt','_errmsg','_memset']" EMCC_LDFLAGS += -s TOTAL_MEMORY=134217728 EMCC_LDFLAGS += -s EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]' # https://github.com/kripken/emscripten/blob/master/src/settings.js CXXFLAGS += $(EMCC_CXXFLAGS) LDFLAGS += $(EMCC_LDFLAGS) LDLIBS = EXE = .js DISABLE_SPAWN := 1 TARGETS := $(filter-out $(PROGRAM_PREFIX)yosys-config,$(TARGETS)) EXTRA_TARGETS += yosysjs-$(YOSYS_VER).zip ifeq ($(ENABLE_ABC),1) LINK_ABC := 1 DISABLE_ABC_THREADS := 1 endif viz.js: wget -O viz.js.part https://github.com/mdaines/viz.js/releases/download/0.0.3/viz.js mv viz.js.part viz.js yosysjs-$(YOSYS_VER).zip: yosys.js viz.js misc/yosysjs/* rm -rf yosysjs-$(YOSYS_VER) yosysjs-$(YOSYS_VER).zip mkdir -p yosysjs-$(YOSYS_VER) cp viz.js misc/yosysjs/* yosys.js yosys.wasm yosysjs-$(YOSYS_VER)/ zip -r yosysjs-$(YOSYS_VER).zip yosysjs-$(YOSYS_VER) yosys.html: misc/yosys.html $(P) cp misc/yosys.html yosys.html else ifeq ($(CONFIG),wasi) ifeq ($(WASI_SDK),) CXX = clang LD = clang++ AR = llvm-ar RANLIB = llvm-ranlib WASIFLAGS := -target wasm32-wasi --sysroot $(WASI_SYSROOT) $(WASIFLAGS) else CXX = $(WASI_SDK)/bin/clang LD = $(WASI_SDK)/bin/clang++ AR = $(WASI_SDK)/bin/ar RANLIB = $(WASI_SDK)/bin/ranlib WASIFLAGS := --sysroot $(WASI_SDK)/share/wasi-sysroot $(WASIFLAGS) endif CXXFLAGS := $(WASIFLAGS) -std=$(CXXSTD) -Os $(filter-out -fPIC,$(CXXFLAGS)) LDFLAGS := $(WASIFLAGS) -Wl,-z,stack-size=1048576 $(filter-out -rdynamic,$(LDFLAGS)) LDLIBS := $(filter-out -lrt,$(LDLIBS)) ABCMKARGS += AR="$(AR)" RANLIB="$(RANLIB)" ABCMKARGS += ARCHFLAGS="$(WASIFLAGS) -DABC_USE_STDINT_H -DABC_NO_DYNAMIC_LINKING -DABC_NO_RLIMIT -Wno-c++11-narrowing" ABCMKARGS += OPTFLAGS="-Os" EXE = .wasm DISABLE_SPAWN := 1 ifeq ($(ENABLE_ABC),1) LINK_ABC := 1 DISABLE_ABC_THREADS := 1 endif else ifeq ($(CONFIG),mxe) PKG_CONFIG = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-pkg-config CXX = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-g++ LD = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-g++ CXXFLAGS += -std=$(CXXSTD) -Os -D_POSIX_SOURCE -DYOSYS_MXE_HACKS -Wno-attributes CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS)) LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -s LDLIBS := $(filter-out -lrt,$(LDLIBS)) ABCMKARGS += ARCHFLAGS="-DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -fpermissive -w" # TODO: Try to solve pthread linking issue in more appropriate way ABCMKARGS += LIBS="lib/x86/pthreadVC2.lib -s" LDFLAGS="-Wl,--allow-multiple-definition" ABC_USE_NO_READLINE=1 CC="/usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-gcc" EXE = .exe else ifeq ($(CONFIG),msys2-32) CXX = i686-w64-mingw32-g++ LD = i686-w64-mingw32-g++ CXXFLAGS += -std=$(CXXSTD) -Os -D_POSIX_SOURCE -DYOSYS_WIN32_UNIX_DIR CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS)) LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -s LDLIBS := $(filter-out -lrt,$(LDLIBS)) ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -fpermissive -w" ABCMKARGS += LIBS="-lpthread -s" ABC_USE_NO_READLINE=0 CC="i686-w64-mingw32-gcc" CXX="$(CXX)" EXE = .exe else ifeq ($(CONFIG),msys2-64) CXX = x86_64-w64-mingw32-g++ LD = x86_64-w64-mingw32-g++ CXXFLAGS += -std=$(CXXSTD) -Os -D_POSIX_SOURCE -DYOSYS_WIN32_UNIX_DIR CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS)) LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -s LDLIBS := $(filter-out -lrt,$(LDLIBS)) ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -fpermissive -w" ABCMKARGS += LIBS="-lpthread -s" ABC_USE_NO_READLINE=0 CC="x86_64-w64-mingw32-gcc" CXX="$(CXX)" EXE = .exe else ifneq ($(CONFIG),none) $(error Invalid CONFIG setting '$(CONFIG)'. Valid values: clang, gcc, emcc, mxe, msys2-32, msys2-64) endif ifeq ($(ENABLE_LIBYOSYS),1) TARGETS += libyosys.so endif ifeq ($(ENABLE_PYOSYS),1) # Detect name of boost_python library. Some distros use boost_python-py, other boost_python, some only use the major version number, some a concatenation of major and minor version numbers CHECK_BOOST_PYTHON = (echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_CONFIG) --ldflags) -l$(1) - > /dev/null 2>&1 && echo "-l$(1)") BOOST_PYTHON_LIB ?= $(shell \ $(call CHECK_BOOST_PYTHON,boost_python-py$(subst .,,$(PYTHON_VERSION))) || \ $(call CHECK_BOOST_PYTHON,boost_python-py$(PYTHON_MAJOR_VERSION)) || \ $(call CHECK_BOOST_PYTHON,boost_python$(subst .,,$(PYTHON_VERSION))) || \ $(call CHECK_BOOST_PYTHON,boost_python$(PYTHON_MAJOR_VERSION)) \ ) ifeq ($(BOOST_PYTHON_LIB),) $(error BOOST_PYTHON_LIB could not be detected. Please define manually) endif LDLIBS += $(shell $(PYTHON_CONFIG) --libs) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem # python-config --ldflags includes LDLIBS for some reason LDFLAGS += $(filter-out -l%,$(shell $(PYTHON_CONFIG) --ldflags)) CXXFLAGS += $(shell $(PYTHON_CONFIG) --includes) -DWITH_PYTHON PY_WRAPPER_FILE = kernel/python_wrappers OBJS += $(PY_WRAPPER_FILE).o PY_GEN_SCRIPT= py_wrap_generator PY_WRAP_INCLUDES := $(shell python$(PYTHON_VERSION) -c "from misc import $(PY_GEN_SCRIPT); $(PY_GEN_SCRIPT).print_includes()") endif # ENABLE_PYOSYS ifeq ($(ENABLE_READLINE),1) CXXFLAGS += -DYOSYS_ENABLE_READLINE ifeq ($(OS), $(filter $(OS),FreeBSD OpenBSD NetBSD)) CXXFLAGS += -I/usr/local/include endif LDLIBS += -lreadline ifeq ($(LINK_CURSES),1) LDLIBS += -lcurses ABCMKARGS += "ABC_READLINE_LIBRARIES=-lcurses -lreadline" endif ifeq ($(LINK_TERMCAP),1) LDLIBS += -ltermcap ABCMKARGS += "ABC_READLINE_LIBRARIES=-lreadline -ltermcap" endif ifeq ($(CONFIG),mxe) LDLIBS += -ltermcap endif else ifeq ($(ENABLE_EDITLINE),1) CXXFLAGS += -DYOSYS_ENABLE_EDITLINE LDLIBS += -ledit -ltinfo -lbsd else ABCMKARGS += "ABC_USE_NO_READLINE=1" endif endif ifeq ($(DISABLE_ABC_THREADS),1) ABCMKARGS += "ABC_USE_NO_PTHREADS=1" endif ifeq ($(DISABLE_SPAWN),1) CXXFLAGS += -DYOSYS_DISABLE_SPAWN endif ifeq ($(ENABLE_PLUGINS),1) CXXFLAGS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --cflags libffi) -DYOSYS_ENABLE_PLUGINS ifeq ($(OS), MINGW) CXXFLAGS += -Ilibs/dlfcn-win32 endif LDLIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --libs libffi || echo -lffi) ifneq ($(OS), $(filter $(OS),FreeBSD OpenBSD NetBSD MINGW)) LDLIBS += -ldl endif endif ifeq ($(ENABLE_GLOB),1) CXXFLAGS += -DYOSYS_ENABLE_GLOB endif ifeq ($(ENABLE_ZLIB),1) CXXFLAGS += -DYOSYS_ENABLE_ZLIB LDLIBS += -lz endif ifeq ($(ENABLE_TCL),1) TCL_VERSION ?= tcl$(shell bash -c "tclsh <(echo 'puts [info tclversion]')") ifeq ($(OS), $(filter $(OS),FreeBSD OpenBSD NetBSD)) # BSDs usually use tcl8.6, but the lib is named "libtcl86" TCL_INCLUDE ?= /usr/local/include/$(TCL_VERSION) TCL_LIBS ?= -l$(subst .,,$(TCL_VERSION)) else TCL_INCLUDE ?= /usr/include/$(TCL_VERSION) TCL_LIBS ?= -l$(TCL_VERSION) endif ifeq ($(CONFIG),mxe) CXXFLAGS += -DYOSYS_ENABLE_TCL LDLIBS += -ltcl86 -lwsock32 -lws2_32 -lnetapi32 -lz -luserenv else CXXFLAGS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --cflags tcl || echo -I$(TCL_INCLUDE)) -DYOSYS_ENABLE_TCL LDLIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --libs tcl || echo $(TCL_LIBS)) endif endif ifeq ($(ENABLE_GCOV),1) CXXFLAGS += --coverage LDFLAGS += --coverage endif ifeq ($(ENABLE_GPROF),1) CXXFLAGS += -pg LDFLAGS += -pg endif ifeq ($(ENABLE_NDEBUG),1) CXXFLAGS := -O3 -DNDEBUG $(filter-out -Os -ggdb,$(CXXFLAGS)) endif ifeq ($(ENABLE_DEBUG),1) ifeq ($(CONFIG),clang) CXXFLAGS := -O0 -DDEBUG $(filter-out -Os,$(CXXFLAGS)) else CXXFLAGS := -Og -DDEBUG $(filter-out -Os,$(CXXFLAGS)) endif endif ifeq ($(ENABLE_ABC),1) CXXFLAGS += -DYOSYS_ENABLE_ABC ifeq ($(LINK_ABC),1) CXXFLAGS += -DYOSYS_LINK_ABC ifeq ($(DISABLE_ABC_THREADS),0) LDLIBS += -lpthread endif else ifeq ($(ABCEXTERNAL),) TARGETS += $(PROGRAM_PREFIX)yosys-abc$(EXE) endif endif endif ifeq ($(ENABLE_GHDL),1) GHDL_PREFIX ?= $(PREFIX) GHDL_INCLUDE_DIR ?= $(GHDL_PREFIX)/include GHDL_LIB_DIR ?= $(GHDL_PREFIX)/lib CXXFLAGS += -I$(GHDL_INCLUDE_DIR) -DYOSYS_ENABLE_GHDL LDLIBS += $(GHDL_LIB_DIR)/libghdl.a $(file <$(GHDL_LIB_DIR)/libghdl.link) endif LDLIBS_VERIFIC = ifeq ($(ENABLE_VERIFIC),1) VERIFIC_DIR ?= /usr/local/src/verific_lib VERIFIC_COMPONENTS ?= verilog database util containers hier_tree ifneq ($(DISABLE_VERIFIC_VHDL),1) VERIFIC_COMPONENTS += vhdl CXXFLAGS += -DVERIFIC_VHDL_SUPPORT else ifneq ($(wildcard $(VERIFIC_DIR)/vhdl),) VERIFIC_COMPONENTS += vhdl endif endif ifeq ($(ENABLE_VERIFIC_EDIF),1) VERIFIC_COMPONENTS += edif CXXFLAGS += -DVERIFIC_EDIF_SUPPORT endif ifeq ($(ENABLE_VERIFIC_LIBERTY),1) VERIFIC_COMPONENTS += synlib CXXFLAGS += -DVERIFIC_LIBERTY_SUPPORT endif ifneq ($(DISABLE_VERIFIC_EXTENSIONS),1) VERIFIC_COMPONENTS += extensions CXXFLAGS += -DYOSYSHQ_VERIFIC_EXTENSIONS endif CXXFLAGS += $(patsubst %,-I$(VERIFIC_DIR)/%,$(VERIFIC_COMPONENTS)) -DYOSYS_ENABLE_VERIFIC ifeq ($(OS), Darwin) LDLIBS_VERIFIC += $(foreach comp,$(patsubst %,$(VERIFIC_DIR)/%/*-mac.a,$(VERIFIC_COMPONENTS)),-Wl,-force_load $(comp)) -lz else LDLIBS_VERIFIC += -Wl,--whole-archive $(patsubst %,$(VERIFIC_DIR)/%/*-linux.a,$(VERIFIC_COMPONENTS)) -Wl,--no-whole-archive -lz endif endif ifeq ($(ENABLE_COVER),1) CXXFLAGS += -DYOSYS_ENABLE_COVER endif ifeq ($(ENABLE_CCACHE),1) CXX := ccache $(CXX) else ifeq ($(ENABLE_SCCACHE),1) CXX := sccache $(CXX) endif endif define add_share_file EXTRA_TARGETS += $(subst //,/,$(1)/$(notdir $(2))) $(subst //,/,$(1)/$(notdir $(2))): $(2) $$(P) mkdir -p $(1) $$(Q) cp "$(YOSYS_SRC)"/$(2) $(subst //,/,$(1)/$(notdir $(2))) endef define add_gen_share_file EXTRA_TARGETS += $(subst //,/,$(1)/$(notdir $(2))) $(subst //,/,$(1)/$(notdir $(2))): $(2) $$(P) mkdir -p $(1) $$(Q) cp $(2) $(subst //,/,$(1)/$(notdir $(2))) endef define add_include_file $(eval $(call add_share_file,$(dir share/include/$(1)),$(1))) endef define add_extra_objs EXTRA_OBJS += $(1) .SECONDARY: $(1) endef ifeq ($(PRETTY), 1) P_STATUS = 0 P_OFFSET = 0 P_UPDATE = $(eval P_STATUS=$(shell echo $(OBJS) $(PROGRAM_PREFIX)yosys$(EXE) | $(AWK) 'BEGIN { RS = " "; I = $(P_STATUS)+0; } $$1 == "$@" && NR > I { I = NR; } END { print I; }')) P_SHOW = [$(shell $(AWK) "BEGIN { N=$(words $(OBJS) $(PROGRAM_PREFIX)yosys$(EXE)); printf \"%3d\", $(P_OFFSET)+90*$(P_STATUS)/N; exit; }")%] P = @echo "$(if $(findstring $@,$(TARGETS) $(EXTRA_TARGETS)),$(eval P_OFFSET = 10))$(call P_UPDATE)$(call P_SHOW) Building $@"; Q = @ S = -s else P_SHOW = -> P = Q = S = endif $(eval $(call add_include_file,kernel/yosys.h)) $(eval $(call add_include_file,kernel/hashlib.h)) $(eval $(call add_include_file,kernel/log.h)) $(eval $(call add_include_file,kernel/rtlil.h)) $(eval $(call add_include_file,kernel/binding.h)) $(eval $(call add_include_file,kernel/register.h)) $(eval $(call add_include_file,kernel/cellaigs.h)) $(eval $(call add_include_file,kernel/celltypes.h)) $(eval $(call add_include_file,kernel/celledges.h)) $(eval $(call add_include_file,kernel/consteval.h)) $(eval $(call add_include_file,kernel/constids.inc)) $(eval $(call add_include_file,kernel/sigtools.h)) $(eval $(call add_include_file,kernel/modtools.h)) $(eval $(call add_include_file,kernel/macc.h)) $(eval $(call add_include_file,kernel/utils.h)) $(eval $(call add_include_file,kernel/satgen.h)) $(eval $(call add_include_file,kernel/qcsat.h)) $(eval $(call add_include_file,kernel/ff.h)) $(eval $(call add_include_file,kernel/ffinit.h)) ifeq ($(ENABLE_ZLIB),1) $(eval $(call add_include_file,kernel/fstdata.h)) endif $(eval $(call add_include_file,kernel/mem.h)) $(eval $(call add_include_file,kernel/yw.h)) $(eval $(call add_include_file,kernel/json.h)) $(eval $(call add_include_file,libs/ezsat/ezsat.h)) $(eval $(call add_include_file,libs/ezsat/ezminisat.h)) ifeq ($(ENABLE_ZLIB),1) $(eval $(call add_include_file,libs/fst/fstapi.h)) endif $(eval $(call add_include_file,libs/sha1/sha1.h)) $(eval $(call add_include_file,libs/json11/json11.hpp)) $(eval $(call add_include_file,passes/fsm/fsmdata.h)) $(eval $(call add_include_file,frontends/ast/ast.h)) $(eval $(call add_include_file,frontends/ast/ast_binding.h)) $(eval $(call add_include_file,frontends/blif/blifparse.h)) $(eval $(call add_include_file,backends/rtlil/rtlil_backend.h)) $(eval $(call add_include_file,backends/cxxrtl/cxxrtl.h)) $(eval $(call add_include_file,backends/cxxrtl/cxxrtl_vcd.h)) $(eval $(call add_include_file,backends/cxxrtl/cxxrtl_capi.cc)) $(eval $(call add_include_file,backends/cxxrtl/cxxrtl_capi.h)) $(eval $(call add_include_file,backends/cxxrtl/cxxrtl_vcd_capi.cc)) $(eval $(call add_include_file,backends/cxxrtl/cxxrtl_vcd_capi.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/satgen.o kernel/qcsat.o kernel/mem.o kernel/ffmerge.o kernel/ff.o kernel/yw.o kernel/json.o kernel/fmt.o ifeq ($(ENABLE_ZLIB),1) OBJS += kernel/fstdata.o endif ifeq ($(ENABLE_PLUGINS),1) ifeq ($(OS), MINGW) OBJS += libs/dlfcn-win32/dlfcn.o endif endif kernel/log.o: CXXFLAGS += -DYOSYS_SRC='"$(YOSYS_SRC)"' kernel/yosys.o: CXXFLAGS += -DYOSYS_DATDIR='"$(DATDIR)"' -DYOSYS_PROGRAM_PREFIX='"$(PROGRAM_PREFIX)"' ifeq ($(ENABLE_ABC),1) ifneq ($(ABCEXTERNAL),) kernel/yosys.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"' endif endif OBJS += libs/bigint/BigIntegerAlgorithms.o libs/bigint/BigInteger.o libs/bigint/BigIntegerUtils.o OBJS += libs/bigint/BigUnsigned.o libs/bigint/BigUnsignedInABase.o OBJS += libs/sha1/sha1.o ifneq ($(SMALL),1) OBJS += libs/json11/json11.o OBJS += libs/subcircuit/subcircuit.o OBJS += libs/ezsat/ezsat.o OBJS += libs/ezsat/ezminisat.o OBJS += libs/minisat/Options.o OBJS += libs/minisat/SimpSolver.o OBJS += libs/minisat/Solver.o OBJS += libs/minisat/System.o ifeq ($(ENABLE_ZLIB),1) OBJS += libs/fst/fstapi.o OBJS += libs/fst/fastlz.o OBJS += libs/fst/lz4.o endif include $(YOSYS_SRC)/frontends/*/Makefile.inc include $(YOSYS_SRC)/passes/*/Makefile.inc include $(YOSYS_SRC)/backends/*/Makefile.inc include $(YOSYS_SRC)/techlibs/*/Makefile.inc else include $(YOSYS_SRC)/frontends/verilog/Makefile.inc include $(YOSYS_SRC)/frontends/rtlil/Makefile.inc include $(YOSYS_SRC)/frontends/ast/Makefile.inc include $(YOSYS_SRC)/frontends/blif/Makefile.inc OBJS += passes/hierarchy/hierarchy.o OBJS += passes/cmds/select.o OBJS += passes/cmds/show.o OBJS += passes/cmds/stat.o OBJS += passes/cmds/cover.o OBJS += passes/cmds/design.o OBJS += passes/cmds/plugin.o include $(YOSYS_SRC)/passes/proc/Makefile.inc include $(YOSYS_SRC)/passes/opt/Makefile.inc include $(YOSYS_SRC)/passes/techmap/Makefile.inc include $(YOSYS_SRC)/backends/verilog/Makefile.inc include $(YOSYS_SRC)/backends/rtlil/Makefile.inc include $(YOSYS_SRC)/techlibs/common/Makefile.inc endif ifeq ($(LINK_ABC),1) OBJS += $(PROGRAM_PREFIX)yosys-libabc.a endif # prevent the CXXFLAGS set by this Makefile from reaching abc/Makefile, # especially the -MD flag which will break the build when CXX is clang unexport CXXFLAGS top-all: $(TARGETS) $(EXTRA_TARGETS) @echo "" @echo " Build successful." @echo "" ifeq ($(CONFIG),emcc) yosys.js: $(filter-out yosysjs-$(YOSYS_VER).zip,$(EXTRA_TARGETS)) endif $(PROGRAM_PREFIX)yosys$(EXE): $(OBJS) $(P) $(LD) -o $(PROGRAM_PREFIX)yosys$(EXE) $(EXE_LDFLAGS) $(LDFLAGS) $(OBJS) $(LDLIBS) $(LDLIBS_VERIFIC) libyosys.so: $(filter-out kernel/driver.o,$(OBJS)) ifeq ($(OS), Darwin) $(P) $(LD) -o libyosys.so -shared -Wl,-install_name,$(LIBDIR)/libyosys.so $(LDFLAGS) $^ $(LDLIBS) $(LDLIBS_VERIFIC) else $(P) $(LD) -o libyosys.so -shared -Wl,-soname,$(LIBDIR)/libyosys.so $(LDFLAGS) $^ $(LDLIBS) $(LDLIBS_VERIFIC) endif %.o: %.cc $(Q) mkdir -p $(dir $@) $(P) $(CXX) -o $@ -c $(CPPFLAGS) $(CXXFLAGS) $< %.pyh: %.h $(Q) mkdir -p $(dir $@) $(P) cat $< | grep -E -v "#[ ]*(include|error)" | $(LD) $(CXXFLAGS) -x c++ -o $@ -E -P - ifeq ($(ENABLE_PYOSYS),1) $(PY_WRAPPER_FILE).cc: misc/$(PY_GEN_SCRIPT).py $(PY_WRAP_INCLUDES) $(Q) mkdir -p $(dir $@) $(P) python$(PYTHON_VERSION) -c "from misc import $(PY_GEN_SCRIPT); $(PY_GEN_SCRIPT).gen_wrappers(\"$(PY_WRAPPER_FILE).cc\")" endif %.o: %.cpp $(Q) mkdir -p $(dir $@) $(P) $(CXX) -o $@ -c $(CPPFLAGS) $(CXXFLAGS) $< YOSYS_VER_STR := Yosys $(YOSYS_VER) (git sha1 $(GIT_REV), $(notdir $(CXX)) $(shell \ $(CXX) --version | tr ' ()' '\n' | grep '^[0-9]' | head -n1) $(filter -f% -m% -O% -DNDEBUG,$(CXXFLAGS))) kernel/version_$(GIT_REV).cc: $(YOSYS_SRC)/Makefile $(P) rm -f kernel/version_*.o kernel/version_*.d kernel/version_*.cc $(Q) mkdir -p kernel && echo "namespace Yosys { extern const char *yosys_version_str; const char *yosys_version_str=\"$(YOSYS_VER_STR)\"; }" > kernel/version_$(GIT_REV).cc ifeq ($(ENABLE_VERIFIC),1) CXXFLAGS_NOVERIFIC = $(foreach v,$(CXXFLAGS),$(if $(findstring $(VERIFIC_DIR),$(v)),,$(v))) LDLIBS_NOVERIFIC = $(foreach v,$(LDLIBS),$(if $(findstring $(VERIFIC_DIR),$(v)),,$(v))) else CXXFLAGS_NOVERIFIC = $(CXXFLAGS) LDLIBS_NOVERIFIC = $(LDLIBS) endif $(PROGRAM_PREFIX)yosys-config: misc/yosys-config.in $(P) $(SED) -e 's#@CXXFLAGS@#$(subst -Ilibs/dlfcn-win32,,$(subst -I. -I"$(YOSYS_SRC)",-I"$(DATDIR)/include",$(strip $(CXXFLAGS_NOVERIFIC))))#;' \ -e 's#@CXX@#$(strip $(CXX))#;' -e 's#@LDFLAGS@#$(strip $(LDFLAGS) $(PLUGIN_LDFLAGS))#;' -e 's#@LDLIBS@#$(strip $(LDLIBS_NOVERIFIC) $(PLUGIN_LDLIBS))#;' \ -e 's#@BINDIR@#$(strip $(BINDIR))#;' -e 's#@DATDIR@#$(strip $(DATDIR))#;' < $< > $(PROGRAM_PREFIX)yosys-config $(Q) chmod +x $(PROGRAM_PREFIX)yosys-config abc/abc-$(ABCREV)$(EXE) abc/libabc-$(ABCREV).a: $(P) ifneq ($(ABCREV),default) $(Q) if test -d abc/.hg; then \ echo 'REEBE: NOP qverpgbel vf n ut jbexvat pbcl! Erzbir nop/ naq er-eha "znxr".' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; false; \ fi $(Q) if test -d abc && test -d abc/.git && ! git -C abc diff-index --quiet HEAD; then \ echo 'REEBE: NOP pbagnvaf ybpny zbqvsvpngvbaf! Frg NOPERI=qrsnhyg va Lbflf Znxrsvyr!' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; false; \ fi $(Q) if test -d abc && ! test -d abc/.git && ! test "`cat abc/.gitcommit | cut -c1-7`" = "$(ABCREV)"; then \ echo 'REEBE: Qbjaybnqrq NOP irefvbaf qbrf abg zngpu! Qbjaybnq sebz:' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; echo $(ABCURL)/archive/$(ABCREV).tar.gz; false; \ fi # set a variable so the test fails if git fails to run - when comparing outputs directly, empty string would match empty string $(Q) if test -d abc && ! test -d abc/.git && test "`cat abc/.gitcommit | cut -c1-7`" = "$(ABCREV)"; then \ echo "Compiling local copy of ABC"; \ elif ! (cd abc 2> /dev/null && rev="`git rev-parse $(ABCREV)`" && test "`git rev-parse HEAD`" = "$$rev"); then \ test $(ABCPULL) -ne 0 || { echo 'REEBE: NOP abg hc gb qngr naq NOPCHYY frg gb 0 va Znxrsvyr!' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; exit 1; }; \ echo "Pulling ABC from $(ABCURL):"; set -x; \ test -d abc || git clone $(ABCURL) abc; \ cd abc && $(MAKE) DEP= clean && git fetch $(ABCURL) && git checkout $(ABCREV); \ fi endif $(Q) rm -f abc/abc-[0-9a-f]* $(Q) $(MAKE) -C abc $(S) $(ABCMKARGS) $(if $(filter %.a,$@),PROG="abc-$(ABCREV)",PROG="abc-$(ABCREV)$(EXE)") MSG_PREFIX="$(eval P_OFFSET = 5)$(call P_SHOW)$(eval P_OFFSET = 10) ABC: " $(if $(filter %.a,$@),libabc-$(ABCREV).a) ifeq ($(ABCREV),default) .PHONY: abc/abc-$(ABCREV)$(EXE) .PHONY: abc/libabc-$(ABCREV).a endif $(PROGRAM_PREFIX)yosys-abc$(EXE): abc/abc-$(ABCREV)$(EXE) $(P) cp abc/abc-$(ABCREV)$(EXE) $(PROGRAM_PREFIX)yosys-abc$(EXE) $(PROGRAM_PREFIX)yosys-libabc.a: abc/libabc-$(ABCREV).a $(P) cp abc/libabc-$(ABCREV).a $(PROGRAM_PREFIX)yosys-libabc.a ifneq ($(SEED),) SEEDOPT="-S $(SEED)" else SEEDOPT="" endif ifneq ($(ABCEXTERNAL),) ABCOPT="-A $(ABCEXTERNAL)" else ABCOPT="" endif test: $(TARGETS) $(EXTRA_TARGETS) ifeq ($(ENABLE_VERIFIC),1) +cd tests/verific && bash run-test.sh $(SEEDOPT) endif +cd tests/simple && bash run-test.sh $(SEEDOPT) +cd tests/simple_abc9 && bash run-test.sh $(SEEDOPT) +cd tests/hana && bash run-test.sh $(SEEDOPT) +cd tests/asicworld && bash run-test.sh $(SEEDOPT) # +cd tests/realmath && bash run-test.sh $(SEEDOPT) +cd tests/share && bash run-test.sh $(SEEDOPT) +cd tests/opt_share && bash run-test.sh $(SEEDOPT) +cd tests/fsm && bash run-test.sh $(SEEDOPT) +cd tests/techmap && bash run-test.sh +cd tests/memories && bash run-test.sh $(ABCOPT) $(SEEDOPT) +cd tests/memlib && bash run-test.sh $(SEEDOPT) +cd tests/bram && bash run-test.sh $(SEEDOPT) +cd tests/various && bash run-test.sh +cd tests/select && bash run-test.sh +cd tests/sat && bash run-test.sh +cd tests/sim && bash run-test.sh +cd tests/svinterfaces && bash run-test.sh $(SEEDOPT) +cd tests/svtypes && bash run-test.sh $(SEEDOPT) +cd tests/proc && bash run-test.sh +cd tests/blif && bash run-test.sh +cd tests/opt && bash run-test.sh +cd tests/aiger && bash run-test.sh $(ABCOPT) +cd tests/arch && bash run-test.sh +cd tests/arch/ice40 && bash run-test.sh $(SEEDOPT) +cd tests/arch/xilinx && bash run-test.sh $(SEEDOPT) +cd tests/arch/ecp5 && bash run-test.sh $(SEEDOPT) +cd tests/arch/machxo2 && bash run-test.sh $(SEEDOPT) +cd tests/arch/efinix && bash run-test.sh $(SEEDOPT) +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/nexus && bash run-test.sh $(SEEDOPT) +cd tests/arch/quicklogic && bash run-test.sh $(SEEDOPT) +cd tests/arch/gatemate && bash run-test.sh $(SEEDOPT) +cd tests/rpc && bash run-test.sh +cd tests/memfile && bash run-test.sh +cd tests/verilog && bash run-test.sh +cd tests/xprop && bash run-test.sh $(SEEDOPT) +cd tests/fmt && bash run-test.sh @echo "" @echo " Passed \"make test\"." @echo "" VALGRIND ?= valgrind --error-exitcode=1 --leak-check=full --show-reachable=yes --errors-for-leak-kinds=all vgtest: $(TARGETS) $(EXTRA_TARGETS) $(VALGRIND) ./yosys -p 'setattr -mod -unset top; synth' $$( ls tests/simple/*.v | grep -v repwhile.v ) @echo "" @echo " Passed \"make vgtest\"." @echo "" vloghtb: $(TARGETS) $(EXTRA_TARGETS) +cd tests/vloghtb && bash run-test.sh @echo "" @echo " Passed \"make vloghtb\"." @echo "" ystests: $(TARGETS) $(EXTRA_TARGETS) rm -rf tests/ystests git clone https://github.com/YosysHQ/yosys-tests.git tests/ystests +$(MAKE) PATH="$$PWD:$$PATH" -C tests/ystests @echo "" @echo " Finished \"make ystests\"." @echo "" # Unit test unit-test: libyosys.so @$(MAKE) -C $(UNITESTPATH) CXX="$(CXX)" CPPFLAGS="$(CPPFLAGS)" \ CXXFLAGS="$(CXXFLAGS)" LDLIBS="$(LDLIBS)" ROOTPATH="$(CURDIR)" clean-unit-test: @$(MAKE) -C $(UNITESTPATH) clean install: $(TARGETS) $(EXTRA_TARGETS) $(INSTALL_SUDO) mkdir -p $(DESTDIR)$(BINDIR) $(INSTALL_SUDO) cp $(filter-out libyosys.so,$(TARGETS)) $(DESTDIR)$(BINDIR) ifneq ($(filter $(PROGRAM_PREFIX)yosys,$(TARGETS)),) $(INSTALL_SUDO) $(STRIP) -S $(DESTDIR)$(BINDIR)/$(PROGRAM_PREFIX)yosys endif ifneq ($(filter $(PROGRAM_PREFIX)yosys-abc,$(TARGETS)),) $(INSTALL_SUDO) $(STRIP) $(DESTDIR)$(BINDIR)/$(PROGRAM_PREFIX)yosys-abc endif ifneq ($(filter $(PROGRAM_PREFIX)yosys-filterlib,$(TARGETS)),) $(INSTALL_SUDO) $(STRIP) $(DESTDIR)$(BINDIR)/$(PROGRAM_PREFIX)yosys-filterlib endif $(INSTALL_SUDO) mkdir -p $(DESTDIR)$(DATDIR) $(INSTALL_SUDO) cp -r share/. $(DESTDIR)$(DATDIR)/. ifeq ($(ENABLE_LIBYOSYS),1) $(INSTALL_SUDO) mkdir -p $(DESTDIR)$(LIBDIR) $(INSTALL_SUDO) cp libyosys.so $(DESTDIR)$(LIBDIR)/ $(INSTALL_SUDO) $(STRIP) -S $(DESTDIR)$(LIBDIR)/libyosys.so ifeq ($(ENABLE_PYOSYS),1) $(INSTALL_SUDO) mkdir -p $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys $(INSTALL_SUDO) cp libyosys.so $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys/libyosys.so $(INSTALL_SUDO) cp misc/__init__.py $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys/ endif endif ifeq ($(ENABLE_PLUGINS),1) ifeq ($(OS), MINGW) $(INSTALL_SUDO) mkdir -p $(DESTDIR)$(LIBDIR) $(INSTALL_SUDO) cp libyosys_exe.a $(DESTDIR)$(LIBDIR)/ endif endif uninstall: $(INSTALL_SUDO) rm -vf $(addprefix $(DESTDIR)$(BINDIR)/,$(notdir $(TARGETS))) $(INSTALL_SUDO) rm -rvf $(DESTDIR)$(DATDIR) ifeq ($(ENABLE_LIBYOSYS),1) $(INSTALL_SUDO) rm -vf $(DESTDIR)$(LIBDIR)/libyosys.so ifeq ($(ENABLE_PYOSYS),1) $(INSTALL_SUDO) rm -vf $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys/libyosys.so $(INSTALL_SUDO) rm -vf $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys/__init__.py $(INSTALL_SUDO) rmdir $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys endif endif # also others, but so long as it doesn't fail this is enough to know we tried docs/source/cmd/abc.rst: $(TARGETS) $(EXTRA_TARGETS) mkdir -p docs/source/cmd ./$(PROGRAM_PREFIX)yosys -p 'help -write-rst-command-reference-manual' PHONY: docs/gen_images docs/guidelines docs/gen_images: $(Q) $(MAKE) -C docs/images all DOCS_GUIDELINE_FILES := GettingStarted CodingStyle docs/guidelines: $(Q) mkdir -p docs/source/temp $(Q) cp -f $(addprefix guidelines/,$(DOCS_GUIDELINE_FILES)) docs/source/temp DOC_TARGET ?= html docs: docs/source/cmd/abc.rst docs/gen_images docs/guidelines $(Q) $(MAKE) -C docs $(DOC_TARGET) clean: rm -rf share rm -rf kernel/*.pyh rm -f $(OBJS) $(GENFILES) $(TARGETS) $(EXTRA_TARGETS) $(EXTRA_OBJS) $(PY_WRAP_INCLUDES) $(PY_WRAPPER_FILE).cc rm -f kernel/version_*.o kernel/version_*.cc rm -f libs/*/*.d frontends/*/*.d passes/*/*.d backends/*/*.d kernel/*.d techlibs/*/*.d rm -rf tests/asicworld/*.out tests/asicworld/*.log rm -rf tests/hana/*.out tests/hana/*.log rm -rf tests/simple/*.out tests/simple/*.log rm -rf tests/memories/*.out tests/memories/*.log tests/memories/*.dmp rm -rf tests/sat/*.log tests/techmap/*.log tests/various/*.log rm -rf tests/bram/temp tests/fsm/temp tests/realmath/temp tests/share/temp tests/smv/temp tests/various/temp rm -rf vloghtb/Makefile vloghtb/refdat vloghtb/rtl vloghtb/scripts vloghtb/spec vloghtb/check_yosys vloghtb/vloghammer_tb.tar.bz2 vloghtb/temp vloghtb/log_test_* rm -f tests/svinterfaces/*.log_stdout tests/svinterfaces/*.log_stderr tests/svinterfaces/dut_result.txt tests/svinterfaces/reference_result.txt tests/svinterfaces/a.out tests/svinterfaces/*_syn.v tests/svinterfaces/*.diff rm -f tests/tools/cmp_tbdata $(MAKE) -C docs clean $(MAKE) -C docs/images clean rm -rf docs/source/cmd docs/util/__pycache__ clean-abc: $(MAKE) -C abc DEP= clean rm -f $(PROGRAM_PREFIX)yosys-abc$(EXE) $(PROGRAM_PREFIX)yosys-libabc.a abc/abc-[0-9a-f]* abc/libabc-[0-9a-f]*.a mrproper: clean git clean -xdf coverage: ./$(PROGRAM_PREFIX)yosys -qp 'help; help -all' rm -rf coverage.info coverage_html lcov --capture -d . --no-external -o coverage.info genhtml coverage.info --output-directory coverage_html qtcreator: { for file in $(basename $(OBJS)); do \ for prefix in cc y l; do if [ -f $${file}.$${prefix} ]; then echo $$file.$${prefix}; fi; done \ done; find backends frontends kernel libs passes -type f \( -name '*.h' -o -name '*.hh' \); } > qtcreator.files { echo .; find backends frontends kernel libs passes -type f \( -name '*.h' -o -name '*.hh' \) -printf '%h\n' | sort -u; } > qtcreator.includes touch qtcreator.config qtcreator.creator vcxsrc: $(GENFILES) $(EXTRA_TARGETS) rm -rf yosys-win32-vcxsrc-$(YOSYS_VER){,.zip} set -e; for f in `ls $(filter %.cc %.cpp,$(GENFILES)) $(addsuffix .cc,$(basename $(OBJS))) $(addsuffix .cpp,$(basename $(OBJS))) 2> /dev/null`; do \ echo "Analyse: $$f" >&2; cpp -std=c++11 -MM -I. -D_YOSYS_ $$f; done | sed 's,.*:,,; s,//*,/,g; s,/[^/]*/\.\./,/,g; y, \\,\n\n,;' | grep '^[^/]' | sort -u | grep -v kernel/version_ > srcfiles.txt bash misc/create_vcxsrc.sh yosys-win32-vcxsrc $(YOSYS_VER) $(GIT_REV) echo "namespace Yosys { extern const char *yosys_version_str; const char *yosys_version_str=\"Yosys (Version Information Unavailable)\"; }" > kernel/version.cc zip yosys-win32-vcxsrc-$(YOSYS_VER)/genfiles.zip $(GENFILES) kernel/version.cc zip -r yosys-win32-vcxsrc-$(YOSYS_VER).zip yosys-win32-vcxsrc-$(YOSYS_VER)/ rm -f srcfiles.txt kernel/version.cc ifeq ($(CONFIG),mxe) mxebin: $(TARGETS) $(EXTRA_TARGETS) rm -rf yosys-win32-mxebin-$(YOSYS_VER){,.zip} mkdir -p yosys-win32-mxebin-$(YOSYS_VER) cp -r $(PROGRAM_PREFIX)yosys.exe share/ yosys-win32-mxebin-$(YOSYS_VER)/ ifeq ($(ENABLE_ABC),1) cp -r $(PROGRAM_PREFIX)yosys-abc.exe abc/lib/x86/pthreadVC2.dll yosys-win32-mxebin-$(YOSYS_VER)/ endif echo -en 'This is Yosys $(YOSYS_VER) for Win32.\r\n' > yosys-win32-mxebin-$(YOSYS_VER)/readme.txt echo -en 'Documentation at https://yosyshq.net/yosys/.\r\n' >> yosys-win32-mxebin-$(YOSYS_VER)/readme.txt zip -r yosys-win32-mxebin-$(YOSYS_VER).zip yosys-win32-mxebin-$(YOSYS_VER)/ endif config-clean: clean rm -f Makefile.conf config-clang: clean echo 'CONFIG := clang' > Makefile.conf config-gcc: clean echo 'CONFIG := gcc' > Makefile.conf config-gcc-static: clean echo 'CONFIG := gcc-static' > Makefile.conf echo 'ENABLE_PLUGINS := 0' >> Makefile.conf echo 'ENABLE_READLINE := 0' >> Makefile.conf echo 'ENABLE_TCL := 0' >> Makefile.conf config-afl-gcc: clean echo 'CONFIG := afl-gcc' > Makefile.conf config-emcc: clean echo 'CONFIG := emcc' > Makefile.conf echo 'ENABLE_TCL := 0' >> Makefile.conf echo 'ENABLE_ABC := 0' >> Makefile.conf echo 'ENABLE_PLUGINS := 0' >> Makefile.conf echo 'ENABLE_READLINE := 0' >> Makefile.conf echo 'ENABLE_ZLIB := 0' >> Makefile.conf config-wasi: clean echo 'CONFIG := wasi' > Makefile.conf echo 'ENABLE_TCL := 0' >> Makefile.conf echo 'ENABLE_ABC := 0' >> Makefile.conf echo 'ENABLE_PLUGINS := 0' >> Makefile.conf echo 'ENABLE_READLINE := 0' >> Makefile.conf echo 'ENABLE_ZLIB := 0' >> Makefile.conf config-mxe: clean echo 'CONFIG := mxe' > Makefile.conf echo 'ENABLE_PLUGINS := 0' >> Makefile.conf config-msys2-32: clean echo 'CONFIG := msys2-32' > Makefile.conf echo "PREFIX := $(MINGW_PREFIX)" >> Makefile.conf config-msys2-64: clean echo 'CONFIG := msys2-64' > Makefile.conf echo "PREFIX := $(MINGW_PREFIX)" >> Makefile.conf config-cygwin: clean echo 'CONFIG := cygwin' > Makefile.conf config-gcov: clean echo 'CONFIG := gcc' > Makefile.conf echo 'ENABLE_GCOV := 1' >> Makefile.conf echo 'ENABLE_DEBUG := 1' >> Makefile.conf config-gprof: clean echo 'CONFIG := gcc' > Makefile.conf echo 'ENABLE_GPROF := 1' >> Makefile.conf config-sudo: echo "INSTALL_SUDO := sudo" >> Makefile.conf echo-yosys-ver: @echo "$(YOSYS_VER)" echo-git-rev: @echo "$(GIT_REV)" echo-abc-rev: @echo "$(ABCREV)" -include libs/*/*.d -include frontends/*/*.d -include passes/*/*.d -include backends/*/*.d -include kernel/*.d -include techlibs/*/*.d .PHONY: all top-all abc test install install-abc docs clean mrproper qtcreator coverage vcxsrc mxebin .PHONY: config-clean config-clang config-gcc config-gcc-static config-afl-gcc config-gprof config-sudo yosys-yosys-0.33/README.md000066400000000000000000000610571447554276300153220ustar00rootroot00000000000000``` yosys -- Yosys Open SYnthesis Suite Copyright (C) 2012 - 2020 Claire Xenia Wolf Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ``` yosys – Yosys Open SYnthesis Suite =================================== This is a framework for RTL synthesis tools. It currently has extensive Verilog-2005 support and provides a basic set of synthesis algorithms for various application domains. Yosys can be adapted to perform any synthesis job by combining the existing passes (algorithms) using synthesis scripts and adding additional passes as needed by extending the yosys C++ code base. Yosys is free software licensed under the ISC license (a GPL compatible license that is similar in terms to the MIT license or the 2-clause BSD license). Web Site and Other Resources ============================ More information and documentation can be found on the Yosys web site: - https://yosyshq.net/yosys/ The "Documentation" page on the web site contains links to more resources, including a manual that even describes some of the Yosys internals: - https://yosyshq.net/yosys/documentation.html The directory `guidelines` contains additional information for people interested in using the Yosys C++ APIs. Users interested in formal verification might want to use the formal verification front-end for Yosys, SymbiYosys: - https://symbiyosys.readthedocs.io/en/latest/ - https://github.com/YosysHQ/SymbiYosys Installation ============ Yosys is part of the [Tabby CAD Suite](https://www.yosyshq.com/tabby-cad-datasheet) and the [OSS CAD Suite](https://github.com/YosysHQ/oss-cad-suite-build)! The easiest way to use yosys is to install the binary software suite, which contains all required dependencies and related tools. * [Contact YosysHQ](https://www.yosyshq.com/contact) for a [Tabby CAD Suite](https://www.yosyshq.com/tabby-cad-datasheet) Evaluation License and download link * OR go to https://github.com/YosysHQ/oss-cad-suite-build/releases to download the free OSS CAD Suite * Follow the [Install Instructions on GitHub](https://github.com/YosysHQ/oss-cad-suite-build#installation) Make sure to get a Tabby CAD Suite Evaluation License if you need features such as industry-grade SystemVerilog and VHDL parsers! For more information about the difference between Tabby CAD Suite and the OSS CAD Suite, please visit https://www.yosyshq.com/tabby-cad-datasheet Many Linux distributions also provide Yosys binaries, some more up to date than others. Check with your package manager! Building from Source ==================== You need a C++ compiler with C++11 support (up-to-date CLANG or GCC is recommended) and some standard tools such as GNU Flex, GNU Bison, and GNU Make. TCL, readline and libffi are optional (see ``ENABLE_*`` settings in Makefile). Xdot (graphviz) is used by the ``show`` command in yosys to display schematics. For example on Ubuntu Linux 16.04 LTS the following commands will install all prerequisites for building yosys: $ sudo apt-get install build-essential clang bison flex \ libreadline-dev gawk tcl-dev libffi-dev git \ graphviz xdot pkg-config python3 libboost-system-dev \ libboost-python-dev libboost-filesystem-dev zlib1g-dev Similarily, on Mac OS X Homebrew can be used to install dependencies (from within cloned yosys repository): $ brew tap Homebrew/bundle && brew bundle or MacPorts: $ sudo port install bison flex readline gawk libffi \ git graphviz pkgconfig python36 boost zlib tcl On FreeBSD use the following command to install all prerequisites: # pkg install bison flex readline gawk libffi\ git graphviz pkgconf python3 python36 tcl-wrapper boost-libs On FreeBSD system use gmake instead of make. To run tests use: % MAKE=gmake CC=cc gmake test For Cygwin use the following command to install all prerequisites, or select these additional packages: setup-x86_64.exe -q --packages=bison,flex,gcc-core,gcc-g++,git,libffi-devel,libreadline-devel,make,pkg-config,python3,tcl-devel,boost-build,zlib-devel To configure the build system to use a specific compiler, use one of $ make config-clang $ make config-gcc For other compilers and build configurations it might be necessary to make some changes to the config section of the Makefile. $ vi Makefile # ..or.. $ vi Makefile.conf To build Yosys simply type 'make' in this directory. $ make $ sudo make install Note that this also downloads, builds and installs ABC (using yosys-abc as executable name). Tests are located in the tests subdirectory and can be executed using the test target. Note that you need gawk as well as a recent version of iverilog (i.e. build from git). Then, execute tests via: $ make test To use a separate (out-of-tree) build directory, provide a path to the Makefile. $ mkdir build; cd build $ make -f ../Makefile Out-of-tree builds require a clean source tree. Getting Started =============== Yosys can be used with the interactive command shell, with synthesis scripts or with command line arguments. Let's perform a simple synthesis job using the interactive command shell: $ ./yosys yosys> the command ``help`` can be used to print a list of all available commands and ``help `` to print details on the specified command: yosys> help help reading and elaborating the design using the Verilog frontend: yosys> read -sv tests/simple/fiedler-cooley.v yosys> hierarchy -top up3down5 writing the design to the console in the RTLIL format used by Yosys internally: yosys> write_rtlil convert processes (``always`` blocks) to netlist elements and perform some simple optimizations: yosys> proc; opt display design netlist using ``xdot``: yosys> show the same thing using ``gv`` as postscript viewer: yosys> show -format ps -viewer gv translating netlist to gate logic and perform some simple optimizations: yosys> techmap; opt write design netlist to a new Verilog file: yosys> write_verilog synth.v or using a simple synthesis script: $ cat synth.ys read -sv tests/simple/fiedler-cooley.v hierarchy -top up3down5 proc; opt; techmap; opt write_verilog synth.v $ ./yosys synth.ys If ABC is enabled in the Yosys build configuration and a cell library is given in the liberty file ``mycells.lib``, the following synthesis script will synthesize for the given cell library: # read design read -sv tests/simple/fiedler-cooley.v hierarchy -top up3down5 # the high-level stuff proc; fsm; opt; memory; opt # mapping to internal cell library techmap; opt # mapping flip-flops to mycells.lib dfflibmap -liberty mycells.lib # mapping logic to mycells.lib abc -liberty mycells.lib # cleanup clean If you do not have a liberty file but want to test this synthesis script, you can use the file ``examples/cmos/cmos_cells.lib`` from the yosys sources as simple example. Liberty file downloads for and information about free and open ASIC standard cell libraries can be found here: - http://www.vlsitechnology.org/html/libraries.html - http://www.vlsitechnology.org/synopsys/vsclib013.lib The command ``synth`` provides a good default synthesis script (see ``help synth``): read -sv tests/simple/fiedler-cooley.v synth -top up3down5 # mapping to target cells dfflibmap -liberty mycells.lib abc -liberty mycells.lib clean The command ``prep`` provides a good default word-level synthesis script, as used in SMT-based formal verification. Unsupported Verilog-2005 Features ================================= The following Verilog-2005 features are not supported by Yosys and there are currently no plans to add support for them: - Non-synthesizable language features as defined in IEC 62142(E):2005 / IEEE Std. 1364.1(E):2002 - The ``tri``, ``triand`` and ``trior`` net types - The ``config`` and ``disable`` keywords and library map files Verilog Attributes and non-standard features ============================================ - The ``full_case`` attribute on case statements is supported (also the non-standard ``// synopsys full_case`` directive) - The ``parallel_case`` attribute on case statements is supported (also the non-standard ``// synopsys parallel_case`` directive) - The ``// synopsys translate_off`` and ``// synopsys translate_on`` directives are also supported (but the use of ``` `ifdef .. `endif ``` is strongly recommended instead). - The ``nomem2reg`` attribute on modules or arrays prohibits the automatic early conversion of arrays to separate registers. This is potentially dangerous. Usually the front-end has good reasons for converting an array to a list of registers. Prohibiting this step will likely result in incorrect synthesis results. - The ``mem2reg`` attribute on modules or arrays forces the early conversion of arrays to separate registers. - The ``nomeminit`` attribute on modules or arrays prohibits the creation of initialized memories. This effectively puts ``mem2reg`` on all memories that are written to in an ``initial`` block and are not ROMs. - The ``nolatches`` attribute on modules or always-blocks prohibits the generation of logic-loops for latches. Instead all not explicitly assigned values default to x-bits. This does not affect clocked storage elements such as flip-flops. - The ``nosync`` attribute on registers prohibits the generation of a storage element. The register itself will always have all bits set to 'x' (undefined). The variable may only be used as blocking assigned temporary variable within an always block. This is mostly used internally by Yosys to synthesize Verilog functions and access arrays. - The ``nowrshmsk`` attribute on a register prohibits the generation of shift-and-mask type circuits for writing to bit slices of that register. - The ``onehot`` attribute on wires mark them as one-hot state register. This is used for example for memory port sharing and set by the fsm_map pass. - The ``blackbox`` attribute on modules is used to mark empty stub modules that have the same ports as the real thing but do not contain information on the internal configuration. This modules are only used by the synthesis passes to identify input and output ports of cells. The Verilog backend also does not output blackbox modules on default. ``read_verilog``, unless called with ``-noblackbox`` will automatically set the blackbox attribute on any empty module it reads. - The ``noblackbox`` attribute set on an empty module prevents ``read_verilog`` from automatically setting the blackbox attribute on the module. - The ``whitebox`` attribute on modules triggers the same behavior as ``blackbox``, but is for whitebox modules, i.e. library modules that contain a behavioral model of the cell type. - The ``lib_whitebox`` attribute overwrites ``whitebox`` when ``read_verilog`` is run in `-lib` mode. Otherwise it's automatically removed. - The ``dynports`` attribute is used by the Verilog front-end to mark modules that have ports with a width that depends on a parameter. - The ``hdlname`` attribute is used by some passes to document the original (HDL) name of a module when renaming a module. It should contain a single name, or, when describing a hierarchical name in a flattened design, multiple names separated by a single space character. - The ``keep`` attribute on cells and wires is used to mark objects that should never be removed by the optimizer. This is used for example for cells that have hidden connections that are not part of the netlist, such as IO pads. Setting the ``keep`` attribute on a module has the same effect as setting it on all instances of the module. - The ``keep_hierarchy`` attribute on cells and modules keeps the ``flatten`` command from flattening the indicated cells and modules. - The ``init`` attribute on wires is set by the frontend when a register is initialized "FPGA-style" with ``reg foo = val``. It can be used during synthesis to add the necessary reset logic. - The ``top`` attribute on a module marks this module as the top of the design hierarchy. The ``hierarchy`` command sets this attribute when called with ``-top``. Other commands, such as ``flatten`` and various backends use this attribute to determine the top module. - The ``src`` attribute is set on cells and wires created by to the string ``:`` by the HDL front-end and is then carried through the synthesis. When entities are combined, a new |-separated string is created that contains all the string from the original entities. - The ``defaultvalue`` attribute is used to store default values for module inputs. The attribute is attached to the input wire by the HDL front-end when the input is declared with a default value. - The ``parameter`` and ``localparam`` attributes are used to mark wires that represent module parameters or localparams (when the HDL front-end is run in ``-pwires`` mode). - Wires marked with the ``hierconn`` attribute are connected to wires with the same name (format ``cell_name.identifier``) when they are imported from sub-modules by ``flatten``. - The ``clkbuf_driver`` attribute can be set on an output port of a blackbox module to mark it as a clock buffer output, and thus prevent ``clkbufmap`` from inserting another clock buffer on a net driven by such output. - The ``clkbuf_sink`` attribute can be set on an input port of a module to request clock buffer insertion by the ``clkbufmap`` pass. - The ``clkbuf_inv`` attribute can be set on an output port of a module with the value set to the name of an input port of that module. When the ``clkbufmap`` would otherwise insert a clock buffer on this output, it will instead try inserting the clock buffer on the input port (this is used to implement clock inverter cells that clock buffer insertion will "see through"). - The ``clkbuf_inhibit`` is the default attribute to set on a wire to prevent automatic clock buffer insertion by ``clkbufmap``. This behaviour can be overridden by providing a custom selection to ``clkbufmap``. - The ``invertible_pin`` attribute can be set on a port to mark it as invertible via a cell parameter. The name of the inversion parameter is specified as the value of this attribute. The value of the inversion parameter must be of the same width as the port, with 1 indicating an inverted bit and 0 indicating a non-inverted bit. - The ``iopad_external_pin`` attribute on a blackbox module's port marks it as the external-facing pin of an I/O pad, and prevents ``iopadmap`` from inserting another pad cell on it. - The module attribute ``abc9_lut`` is an integer attribute indicating to `abc9` that this module describes a LUT with an area cost of this value, and propagation delays described using `specify` statements. - The module attribute ``abc9_box`` is a boolean specifying a black/white-box definition, with propagation delays described using `specify` statements, for use by `abc9`. - The port attribute ``abc9_carry`` marks the carry-in (if an input port) and carry-out (if output port) ports of a box. This information is necessary for `abc9` to preserve the integrity of carry-chains. Specifying this attribute onto a bus port will affect only its most significant bit. - The module attribute ``abc9_flop`` is a boolean marking the module as a flip-flop. This allows `abc9` to analyse its contents in order to perform sequential synthesis. - The frontend sets attributes ``always_comb``, ``always_latch`` and ``always_ff`` on processes derived from SystemVerilog style always blocks according to the type of the always. These are checked for correctness in ``proc_dlatch``. - The cell attribute ``wildcard_port_conns`` represents wildcard port connections (SystemVerilog ``.*``). These are resolved to concrete connections to matching wires in ``hierarchy``. - In addition to the ``(* ... *)`` attribute syntax, Yosys supports the non-standard ``{* ... *}`` attribute syntax to set default attributes for everything that comes after the ``{* ... *}`` statement. (Reset by adding an empty ``{* *}`` statement.) - In module parameter and port declarations, and cell port and parameter lists, a trailing comma is ignored. This simplifies writing Verilog code generators a bit in some cases. - Modules can be declared with ``module mod_name(...);`` (with three dots instead of a list of module ports). With this syntax it is sufficient to simply declare a module port as 'input' or 'output' in the module body. - When defining a macro with `define, all text between triple double quotes is interpreted as macro body, even if it contains unescaped newlines. The triple double quotes are removed from the macro body. For example: `define MY_MACRO(a, b) """ assign a = 23; assign b = 42; """ - The attribute ``via_celltype`` can be used to implement a Verilog task or function by instantiating the specified cell type. The value is the name of the cell type to use. For functions the name of the output port can be specified by appending it to the cell type separated by a whitespace. The body of the task or function is unused in this case and can be used to specify a behavioral model of the cell type for simulation. For example: module my_add3(A, B, C, Y); parameter WIDTH = 8; input [WIDTH-1:0] A, B, C; output [WIDTH-1:0] Y; ... endmodule module top; ... (* via_celltype = "my_add3 Y" *) (* via_celltype_defparam_WIDTH = 32 *) function [31:0] add3; input [31:0] A, B, C; begin add3 = A + B + C; end endfunction ... endmodule - The ``wiretype`` attribute is added by the verilog parser for wires of a typedef'd type to indicate the type identifier. - Various ``enum_value_{value}`` attributes are added to wires of an enumerated type to give a map of possible enum items to their values. - The ``enum_base_type`` attribute is added to enum items to indicate which enum they belong to (enums -- anonymous and otherwise -- are automatically named with an auto-incrementing counter). Note that enums are currently not strongly typed. - A limited subset of DPI-C functions is supported. The plugin mechanism (see ``help plugin``) can be used to load .so files with implementations of DPI-C routines. As a non-standard extension it is possible to specify a plugin alias using the ``:`` syntax. For example: module dpitest; import "DPI-C" function foo:round = real my_round (real); parameter real r = my_round(12.345); endmodule $ yosys -p 'plugin -a foo -i /lib/libm.so; read_verilog dpitest.v' - Sized constants (the syntax ``'s?[bodh]``) support constant expressions as ````. If the expression is not a simple identifier, it must be put in parentheses. Examples: ``WIDTH'd42``, ``(4+2)'b101010`` - The system tasks ``$finish``, ``$stop`` and ``$display`` are supported in initial blocks in an unconditional context (only if/case statements on expressions over parameters and constant values are allowed). The intended use for this is synthesis-time DRC. - There is limited support for converting ``specify`` .. ``endspecify`` statements to special ``$specify2``, ``$specify3``, and ``$specrule`` cells, for use in blackboxes and whiteboxes. Use ``read_verilog -specify`` to enable this functionality. (By default these blocks are ignored.) - The ``reprocess_after`` internal attribute is used by the Verilog frontend to mark cells with bindings which might depend on the specified instantiated module. Modules with such cells will be reprocessed during the ``hierarchy`` pass once the referenced module definition(s) become available. - The ``smtlib2_module`` attribute can be set on a blackbox module to specify a formal model directly using SMT-LIB 2. For such a module, the ``smtlib2_comb_expr`` attribute can be used on output ports to define their value using an SMT-LIB 2 expression. For example: (* blackbox *) (* smtlib2_module *) module submod(a, b); input [7:0] a; (* smtlib2_comb_expr = "(bvnot a)" *) output [7:0] b; endmodule Non-standard or SystemVerilog features for formal verification ============================================================== - Support for ``assert``, ``assume``, ``restrict``, and ``cover`` is enabled when ``read_verilog`` is called with ``-formal``. - The system task ``$initstate`` evaluates to 1 in the initial state and to 0 otherwise. - The system function ``$anyconst`` evaluates to any constant value. This is equivalent to declaring a reg as ``rand const``, but also works outside of checkers. (Yosys also supports ``rand const`` outside checkers.) - The system function ``$anyseq`` evaluates to any value, possibly a different value in each cycle. This is equivalent to declaring a reg as ``rand``, but also works outside of checkers. (Yosys also supports ``rand`` variables outside checkers.) - The system functions ``$allconst`` and ``$allseq`` can be used to construct formal exist-forall problems. Assumptions only hold if the trace satisfies the assumption for all ``$allconst/$allseq`` values. For assertions and cover statements it is sufficient if just one ``$allconst/$allseq`` value triggers the property (similar to ``$anyconst/$anyseq``). - Wires/registers declared using the ``anyconst/anyseq/allconst/allseq`` attribute (for example ``(* anyconst *) reg [7:0] foobar;``) will behave as if driven by a ``$anyconst/$anyseq/$allconst/$allseq`` function. - The SystemVerilog tasks ``$past``, ``$stable``, ``$rose`` and ``$fell`` are supported in any clocked block. - The syntax ``@($global_clock)`` can be used to create FFs that have no explicit clock input (``$ff`` cells). The same can be achieved by using ``@(posedge )`` or ``@(negedge )`` when ```` is marked with the ``(* gclk *)`` Verilog attribute. Supported features from SystemVerilog ===================================== When ``read_verilog`` is called with ``-sv``, it accepts some language features from SystemVerilog: - The ``assert`` statement from SystemVerilog is supported in its most basic form. In module context: ``assert property ();`` and within an always block: ``assert();``. It is transformed to an ``$assert`` cell. - The ``assume``, ``restrict``, and ``cover`` statements from SystemVerilog are also supported. The same limitations as with the ``assert`` statement apply. - The keywords ``always_comb``, ``always_ff`` and ``always_latch``, ``logic`` and ``bit`` are supported. - Declaring free variables with ``rand`` and ``rand const`` is supported. - Checkers without a port list that do not need to be instantiated (but instead behave like a named block) are supported. - SystemVerilog packages are supported. Once a SystemVerilog file is read into a design with ``read_verilog``, all its packages are available to SystemVerilog files being read into the same design afterwards. - typedefs are supported (including inside packages) - type casts are currently not supported - enums are supported (including inside packages) - but are currently not strongly typed - packed structs and unions are supported. - SystemVerilog interfaces (SVIs) are supported. Modports for specifying whether ports are inputs or outputs are supported. Building the documentation ========================== Note that there is no need to build the manual if you just want to read it. Simply visit https://yosys.readthedocs.io/en/latest/ instead. In addition to those packages listed above for building Yosys from source, the following are used for building the website: $ sudo apt-get install pdf2svg faketime PDFLaTeX, included with most LaTeX distributions, is also needed during the build process for the website. The Python package, Sphinx, is needed along with those listed in `docs/source/requirements.txt`: $ pip install -U sphinx -r docs/source/requirements.txt From the root of the repository, run `make docs`. This will build/rebuild yosys as necessary before generating the website documentation from the yosys help commands. To build for pdf instead of html, call `make docs DOC_TARGET=latexpdf`. yosys-yosys-0.33/backends/000077500000000000000000000000001447554276300156045ustar00rootroot00000000000000yosys-yosys-0.33/backends/aiger/000077500000000000000000000000001447554276300166735ustar00rootroot00000000000000yosys-yosys-0.33/backends/aiger/Makefile.inc000066400000000000000000000001011447554276300210730ustar00rootroot00000000000000 OBJS += backends/aiger/aiger.o OBJS += backends/aiger/xaiger.o yosys-yosys-0.33/backends/aiger/aiger.cc000066400000000000000000000647071447554276300203070ustar00rootroot00000000000000/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ #include "kernel/yosys.h" #include "kernel/sigtools.h" #include "kernel/json.h" #include "kernel/yw.h" #include "libs/json11/json11.hpp" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN void aiger_encode(std::ostream &f, int x) { log_assert(x >= 0); while (x & ~0x7f) { f.put((x & 0x7f) | 0x80); x = x >> 7; } f.put(x); } struct AigerWriter { Module *module; bool zinit_mode; SigMap sigmap; dict init_map; pool input_bits, output_bits; dict not_map, ff_map, alias_map; dict> and_map; vector> asserts, assumes; vector> liveness, fairness; pool initstate_bits; vector> aig_gates; vector aig_latchin, aig_latchinit, aig_outputs; int aig_m = 0, aig_i = 0, aig_l = 0, aig_o = 0, aig_a = 0; int aig_b = 0, aig_c = 0, aig_j = 0, aig_f = 0; dict aig_map; dict ordered_outputs; dict ordered_latches; dict init_inputs; int initstate_ff = 0; dict ywmap_clocks; int mkgate(int a0, int a1) { aig_m++, aig_a++; aig_gates.push_back(a0 > a1 ? make_pair(a0, a1) : make_pair(a1, a0)); return 2*aig_m; } int bit2aig(SigBit bit) { auto it = aig_map.find(bit); if (it != aig_map.end()) { log_assert(it->second >= 0); return it->second; } // NB: Cannot use iterator returned from aig_map.insert() // since this function is called recursively int a = -1; if (not_map.count(bit)) { a = bit2aig(not_map.at(bit)) ^ 1; } else if (and_map.count(bit)) { auto args = and_map.at(bit); int a0 = bit2aig(args.first); int a1 = bit2aig(args.second); a = mkgate(a0, a1); } else if (alias_map.count(bit)) { a = bit2aig(alias_map.at(bit)); } else if (initstate_bits.count(bit)) { a = initstate_ff; } if (bit == State::Sx || bit == State::Sz) log_error("Design contains 'x' or 'z' bits. Use 'setundef' to replace those constants.\n"); log_assert(a >= 0); aig_map[bit] = a; return a; } AigerWriter(Module *module, bool zinit_mode, bool imode, bool omode, bool bmode, bool lmode) : module(module), zinit_mode(zinit_mode), sigmap(module) { pool undriven_bits; pool unused_bits; // promote public wires for (auto wire : module->wires()) if (wire->name.isPublic()) sigmap.add(wire); // promote output wires for (auto wire : module->wires()) if (wire->port_output) sigmap.add(wire); // promote input wires for (auto wire : module->wires()) if (wire->port_input) sigmap.add(wire); for (auto wire : module->wires()) { if (wire->attributes.count(ID::init)) { SigSpec initsig = sigmap(wire); Const initval = wire->attributes.at(ID::init); for (int i = 0; i < GetSize(wire) && i < GetSize(initval); i++) if (initval[i] == State::S0 || initval[i] == State::S1) init_map[initsig[i]] = initval[i] == State::S1; } for (int i = 0; i < GetSize(wire); i++) { SigBit wirebit(wire, i); SigBit bit = sigmap(wirebit); if (bit.wire == nullptr) { if (wire->port_output) { aig_map[wirebit] = (bit == State::S1) ? 1 : 0; output_bits.insert(wirebit); } continue; } undriven_bits.insert(bit); unused_bits.insert(bit); if (wire->port_input) input_bits.insert(bit); if (wire->port_output) { if (bit != wirebit) alias_map[wirebit] = bit; output_bits.insert(wirebit); } } if (wire->width == 1) { auto gclk_attr = wire->attributes.find(ID::replaced_by_gclk); if (gclk_attr != wire->attributes.end()) { SigBit bit = sigmap(wire); if (gclk_attr->second == State::S1) ywmap_clocks[bit] |= 1; else if (gclk_attr->second == State::S0) ywmap_clocks[bit] |= 2; } } } for (auto bit : input_bits) undriven_bits.erase(bit); for (auto bit : output_bits) unused_bits.erase(bit); for (auto cell : module->cells()) { if (cell->type == ID($_NOT_)) { SigBit A = sigmap(cell->getPort(ID::A).as_bit()); SigBit Y = sigmap(cell->getPort(ID::Y).as_bit()); unused_bits.erase(A); undriven_bits.erase(Y); not_map[Y] = A; continue; } if (cell->type.in(ID($_FF_), ID($_DFF_N_), ID($_DFF_P_))) { SigBit D = sigmap(cell->getPort(ID::D).as_bit()); SigBit Q = sigmap(cell->getPort(ID::Q).as_bit()); unused_bits.erase(D); undriven_bits.erase(Q); ff_map[Q] = D; if (cell->type != ID($_FF_)) { auto sig_clk = sigmap(cell->getPort(ID::C).as_bit()); ywmap_clocks[sig_clk] |= cell->type == ID($_DFF_N_) ? 2 : 1; } continue; } if (cell->type == ID($anyinit)) { auto sig_d = sigmap(cell->getPort(ID::D)); auto sig_q = sigmap(cell->getPort(ID::Q)); for (int i = 0; i < sig_d.size(); i++) { undriven_bits.erase(sig_q[i]); ff_map[sig_q[i]] = sig_d[i]; } continue; } if (cell->type == ID($_AND_)) { SigBit A = sigmap(cell->getPort(ID::A).as_bit()); SigBit B = sigmap(cell->getPort(ID::B).as_bit()); SigBit Y = sigmap(cell->getPort(ID::Y).as_bit()); unused_bits.erase(A); unused_bits.erase(B); undriven_bits.erase(Y); and_map[Y] = make_pair(A, B); continue; } if (cell->type == ID($initstate)) { SigBit Y = sigmap(cell->getPort(ID::Y).as_bit()); undriven_bits.erase(Y); initstate_bits.insert(Y); continue; } if (cell->type == ID($assert)) { SigBit A = sigmap(cell->getPort(ID::A).as_bit()); SigBit EN = sigmap(cell->getPort(ID::EN).as_bit()); unused_bits.erase(A); unused_bits.erase(EN); asserts.push_back(make_pair(A, EN)); continue; } if (cell->type == ID($assume)) { SigBit A = sigmap(cell->getPort(ID::A).as_bit()); SigBit EN = sigmap(cell->getPort(ID::EN).as_bit()); unused_bits.erase(A); unused_bits.erase(EN); assumes.push_back(make_pair(A, EN)); continue; } if (cell->type == ID($live)) { SigBit A = sigmap(cell->getPort(ID::A).as_bit()); SigBit EN = sigmap(cell->getPort(ID::EN).as_bit()); unused_bits.erase(A); unused_bits.erase(EN); liveness.push_back(make_pair(A, EN)); continue; } if (cell->type == ID($fair)) { SigBit A = sigmap(cell->getPort(ID::A).as_bit()); SigBit EN = sigmap(cell->getPort(ID::EN).as_bit()); unused_bits.erase(A); unused_bits.erase(EN); fairness.push_back(make_pair(A, EN)); continue; } if (cell->type == ID($anyconst)) { for (auto bit : sigmap(cell->getPort(ID::Y))) { undriven_bits.erase(bit); ff_map[bit] = bit; } continue; } if (cell->type == ID($anyseq)) { for (auto bit : sigmap(cell->getPort(ID::Y))) { undriven_bits.erase(bit); input_bits.insert(bit); } continue; } log_error("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell)); } for (auto bit : unused_bits) undriven_bits.erase(bit); if (!undriven_bits.empty()) { undriven_bits.sort(); for (auto bit : undriven_bits) { log_warning("Treating undriven bit %s.%s like $anyseq.\n", log_id(module), log_signal(bit)); input_bits.insert(bit); } log_warning("Treating a total of %d undriven bits in %s like $anyseq.\n", GetSize(undriven_bits), log_id(module)); } init_map.sort(); input_bits.sort(); output_bits.sort(); not_map.sort(); ff_map.sort(); and_map.sort(); aig_map[State::S0] = 0; aig_map[State::S1] = 1; for (auto bit : input_bits) { aig_m++, aig_i++; aig_map[bit] = 2*aig_m; } if (imode && input_bits.empty()) { aig_m++, aig_i++; } if (zinit_mode) { for (auto it : ff_map) { if (init_map.count(it.first)) continue; aig_m++, aig_i++; init_inputs[it.first] = 2*aig_m; } } int fair_live_inputs_cnt = GetSize(liveness); int fair_live_inputs_m = aig_m; aig_m += fair_live_inputs_cnt; aig_i += fair_live_inputs_cnt; for (auto it : ff_map) { aig_m++, aig_l++; aig_map[it.first] = 2*aig_m; ordered_latches[it.first] = aig_l-1; if (init_map.count(it.first) == 0) aig_latchinit.push_back(2); else aig_latchinit.push_back(init_map.at(it.first) ? 1 : 0); } if (!initstate_bits.empty() || !init_inputs.empty()) { aig_m++, aig_l++; initstate_ff = 2*aig_m+1; aig_latchinit.push_back(0); } int fair_live_latches_cnt = GetSize(fairness) + 2*GetSize(liveness); int fair_live_latches_m = aig_m; int fair_live_latches_l = aig_l; aig_m += fair_live_latches_cnt; aig_l += fair_live_latches_cnt; for (int i = 0; i < fair_live_latches_cnt; i++) aig_latchinit.push_back(0); if (zinit_mode) { for (auto it : ff_map) { int l = ordered_latches[it.first]; if (aig_latchinit.at(l) == 1) aig_map[it.first] ^= 1; if (aig_latchinit.at(l) == 2) { int gated_ffout = mkgate(aig_map[it.first], initstate_ff^1); int gated_initin = mkgate(init_inputs[it.first], initstate_ff); aig_map[it.first] = mkgate(gated_ffout^1, gated_initin^1)^1; } } } for (auto it : ff_map) { int a = bit2aig(it.second); int l = ordered_latches[it.first]; if (zinit_mode && aig_latchinit.at(l) == 1) aig_latchin.push_back(a ^ 1); else aig_latchin.push_back(a); } if (lmode && aig_l == 0) { aig_m++, aig_l++; aig_latchinit.push_back(0); aig_latchin.push_back(0); } if (!initstate_bits.empty() || !init_inputs.empty()) aig_latchin.push_back(1); for (auto bit : output_bits) { aig_o++; ordered_outputs[bit] = aig_o-1; aig_outputs.push_back(bit2aig(bit)); } if (omode && output_bits.empty()) { aig_o++; aig_outputs.push_back(0); } for (auto it : asserts) { aig_b++; int bit_a = bit2aig(it.first); int bit_en = bit2aig(it.second); aig_outputs.push_back(mkgate(bit_a^1, bit_en)); } if (bmode && asserts.empty()) { aig_b++; aig_outputs.push_back(0); } for (auto it : assumes) { aig_c++; int bit_a = bit2aig(it.first); int bit_en = bit2aig(it.second); aig_outputs.push_back(mkgate(bit_a^1, bit_en)^1); } for (auto it : liveness) { int input_m = ++fair_live_inputs_m; int latch_m1 = ++fair_live_latches_m; int latch_m2 = ++fair_live_latches_m; log_assert(GetSize(aig_latchin) == fair_live_latches_l); fair_live_latches_l += 2; int bit_a = bit2aig(it.first); int bit_en = bit2aig(it.second); int bit_s = 2*input_m; int bit_q1 = 2*latch_m1; int bit_q2 = 2*latch_m2; int bit_d1 = mkgate(mkgate(bit_s, bit_en)^1, bit_q1^1)^1; int bit_d2 = mkgate(mkgate(bit_d1, bit_a)^1, bit_q2^1)^1; aig_j++; aig_latchin.push_back(bit_d1); aig_latchin.push_back(bit_d2); aig_outputs.push_back(mkgate(bit_q1, bit_q2^1)); } for (auto it : fairness) { int latch_m = ++fair_live_latches_m; log_assert(GetSize(aig_latchin) == fair_live_latches_l); fair_live_latches_l += 1; int bit_a = bit2aig(it.first); int bit_en = bit2aig(it.second); int bit_q = 2*latch_m; aig_f++; aig_latchin.push_back(mkgate(mkgate(bit_q^1, bit_en^1)^1, bit_a^1)); aig_outputs.push_back(bit_q^1); } } void write_aiger(std::ostream &f, bool ascii_mode, bool miter_mode, bool symbols_mode) { int aig_obc = aig_o + aig_b + aig_c; int aig_obcj = aig_obc + aig_j; int aig_obcjf = aig_obcj + aig_f; log_assert(aig_m == aig_i + aig_l + aig_a); log_assert(aig_l == GetSize(aig_latchin)); log_assert(aig_l == GetSize(aig_latchinit)); log_assert(aig_obcjf == GetSize(aig_outputs)); if (miter_mode) { if (aig_b || aig_c || aig_j || aig_f) log_error("Running AIGER back-end in -miter mode, but design contains $assert, $assume, $live and/or $fair cells!\n"); f << stringf("%s %d %d %d 0 %d %d\n", ascii_mode ? "aag" : "aig", aig_m, aig_i, aig_l, aig_a, aig_o); } else { f << stringf("%s %d %d %d %d %d", ascii_mode ? "aag" : "aig", aig_m, aig_i, aig_l, aig_o, aig_a); if (aig_b || aig_c || aig_j || aig_f) f << stringf(" %d %d %d %d", aig_b, aig_c, aig_j, aig_f); f << stringf("\n"); } if (ascii_mode) { for (int i = 0; i < aig_i; i++) f << stringf("%d\n", 2*i+2); for (int i = 0; i < aig_l; i++) { if (zinit_mode || aig_latchinit.at(i) == 0) f << stringf("%d %d\n", 2*(aig_i+i)+2, aig_latchin.at(i)); else if (aig_latchinit.at(i) == 1) f << stringf("%d %d 1\n", 2*(aig_i+i)+2, aig_latchin.at(i)); else if (aig_latchinit.at(i) == 2) f << stringf("%d %d %d\n", 2*(aig_i+i)+2, aig_latchin.at(i), 2*(aig_i+i)+2); } for (int i = 0; i < aig_obc; i++) f << stringf("%d\n", aig_outputs.at(i)); for (int i = aig_obc; i < aig_obcj; i++) f << stringf("1\n"); for (int i = aig_obc; i < aig_obcj; i++) f << stringf("%d\n", aig_outputs.at(i)); for (int i = aig_obcj; i < aig_obcjf; i++) f << stringf("%d\n", aig_outputs.at(i)); for (int i = 0; i < aig_a; i++) f << stringf("%d %d %d\n", 2*(aig_i+aig_l+i)+2, aig_gates.at(i).first, aig_gates.at(i).second); } else { for (int i = 0; i < aig_l; i++) { if (zinit_mode || aig_latchinit.at(i) == 0) f << stringf("%d\n", aig_latchin.at(i)); else if (aig_latchinit.at(i) == 1) f << stringf("%d 1\n", aig_latchin.at(i)); else if (aig_latchinit.at(i) == 2) f << stringf("%d %d\n", aig_latchin.at(i), 2*(aig_i+i)+2); } for (int i = 0; i < aig_obc; i++) f << stringf("%d\n", aig_outputs.at(i)); for (int i = aig_obc; i < aig_obcj; i++) f << stringf("1\n"); for (int i = aig_obc; i < aig_obcj; i++) f << stringf("%d\n", aig_outputs.at(i)); for (int i = aig_obcj; i < aig_obcjf; i++) f << stringf("%d\n", aig_outputs.at(i)); for (int i = 0; i < aig_a; i++) { int lhs = 2*(aig_i+aig_l+i)+2; int rhs0 = aig_gates.at(i).first; int rhs1 = aig_gates.at(i).second; int delta0 = lhs - rhs0; int delta1 = rhs0 - rhs1; aiger_encode(f, delta0); aiger_encode(f, delta1); } } if (symbols_mode) { dict> symbols; for (auto wire : module->wires()) { if (wire->name[0] == '$') continue; SigSpec sig = sigmap(wire); for (int i = 0; i < GetSize(wire); i++) { if (sig[i].wire == nullptr) { if (wire->port_output) sig[i] = SigBit(wire, i); else continue; } if (wire->port_input) { int a = aig_map.at(sig[i]); log_assert((a & 1) == 0); if (GetSize(wire) != 1) symbols[stringf("i%d", (a >> 1)-1)].push_back(stringf("%s[%d]", log_id(wire), i)); else symbols[stringf("i%d", (a >> 1)-1)].push_back(stringf("%s", log_id(wire))); } if (wire->port_output) { int o = ordered_outputs.at(SigSpec(wire, i)); if (GetSize(wire) != 1) symbols[stringf("%c%d", miter_mode ? 'b' : 'o', o)].push_back(stringf("%s[%d]", log_id(wire), i)); else symbols[stringf("%c%d", miter_mode ? 'b' : 'o', o)].push_back(stringf("%s", log_id(wire))); } if (init_inputs.count(sig[i])) { int a = init_inputs.at(sig[i]); log_assert((a & 1) == 0); if (GetSize(wire) != 1) symbols[stringf("i%d", (a >> 1)-1)].push_back(stringf("init:%s[%d]", log_id(wire), i)); else symbols[stringf("i%d", (a >> 1)-1)].push_back(stringf("init:%s", log_id(wire))); } if (ordered_latches.count(sig[i])) { int l = ordered_latches.at(sig[i]); const char *p = (zinit_mode && (aig_latchinit.at(l) == 1)) ? "!" : ""; if (GetSize(wire) != 1) symbols[stringf("l%d", l)].push_back(stringf("%s%s[%d]", p, log_id(wire), i)); else symbols[stringf("l%d", l)].push_back(stringf("%s%s", p, log_id(wire))); } } } symbols.sort(); for (auto &sym : symbols) { f << sym.first; std::sort(sym.second.begin(), sym.second.end()); for (auto &s : sym.second) f << " " << s; f << std::endl; } } f << stringf("c\nGenerated by %s\n", yosys_version_str); } void write_map(std::ostream &f, bool verbose_map, bool no_startoffset) { dict input_lines; dict init_lines; dict output_lines; dict latch_lines; dict wire_lines; for (auto wire : module->wires()) { if (!verbose_map && wire->name[0] == '$') continue; SigSpec sig = sigmap(wire); for (int i = 0; i < GetSize(wire); i++) { if (aig_map.count(sig[i]) == 0 || sig[i].wire == nullptr) continue; int a = aig_map.at(sig[i]); int index = no_startoffset ? i : (wire->start_offset+i); if (verbose_map) wire_lines[a] += stringf("wire %d %d %s\n", a, index, log_id(wire)); if (wire->port_input) { log_assert((a & 1) == 0); input_lines[a] += stringf("input %d %d %s\n", (a >> 1)-1, index, log_id(wire)); } if (wire->port_output) { int o = ordered_outputs.at(sig[i]); output_lines[o] += stringf("output %d %d %s\n", o, index, log_id(wire)); } if (init_inputs.count(sig[i])) { int a = init_inputs.at(sig[i]); log_assert((a & 1) == 0); init_lines[a] += stringf("init %d %d %s\n", (a >> 1)-1, index, log_id(wire)); } if (ordered_latches.count(sig[i])) { int l = ordered_latches.at(sig[i]); if (zinit_mode && (aig_latchinit.at(l) == 1)) latch_lines[l] += stringf("invlatch %d %d %s\n", l, index, log_id(wire)); else latch_lines[l] += stringf("latch %d %d %s\n", l, index, log_id(wire)); } } } input_lines.sort(); for (auto &it : input_lines) f << it.second; init_lines.sort(); for (auto &it : init_lines) f << it.second; output_lines.sort(); for (auto &it : output_lines) f << it.second; latch_lines.sort(); for (auto &it : latch_lines) f << it.second; if (initstate_ff) f << stringf("ninitff %d\n", ((initstate_ff >> 1)-1-aig_i)); wire_lines.sort(); for (auto &it : wire_lines) f << it.second; } void write_ywmap(PrettyJson &json) { json.begin_object(); json.entry("version", "Yosys Witness Aiger map"); json.entry("gennerator", yosys_version_str); json.entry("latch_count", aig_l); json.entry("input_count", aig_i); dict clock_lines; dict input_lines; dict init_lines; dict seq_lines; for (auto cell : module->cells()) { if (cell->type.in(ID($_FF_), ID($_DFF_N_), ID($_DFF_P_), ID($anyinit), ID($anyconst), ID($anyseq))) { // Use sig_q to get the FF output name, but sig to lookup aiger bits auto sig_qy = cell->getPort(cell->type.in(ID($anyconst), ID($anyseq)) ? ID::Y : ID::Q); SigSpec sig = sigmap(sig_qy); if (cell->get_bool_attribute(ID(clk2fflogic))) sig_qy = cell->getPort(ID::D); // For a clk2fflogic $_FF_ the named signal is the D input not the Q output for (int i = 0; i < GetSize(sig_qy); i++) { if (sig_qy[i].wire == nullptr || sig[i].wire == nullptr) continue; auto wire = sig_qy[i].wire; if (init_inputs.count(sig[i])) { int a = init_inputs.at(sig[i]); log_assert((a & 1) == 0); init_lines[a] = json11::Json(json11::Json::object { { "path", witness_path(wire) }, { "input", (a >> 1) - 1 }, { "offset", sig_qy[i].offset }, }); } if (input_bits.count(sig[i])) { int a = aig_map.at(sig[i]); log_assert((a & 1) == 0); seq_lines[a] = json11::Json(json11::Json::object { { "path", witness_path(wire) }, { "input", (a >> 1) - 1 }, { "offset", sig_qy[i].offset }, }); } } } } for (auto wire : module->wires()) { SigSpec sig = sigmap(wire); if (wire->port_input) { auto path = witness_path(wire); for (int i = 0; i < GetSize(wire); i++) { if (aig_map.count(sig[i]) == 0 || sig[i].wire == nullptr) continue; int a = aig_map.at(sig[i]); log_assert((a & 1) == 0); input_lines[a] = json11::Json(json11::Json::object { { "path", path }, { "input", (a >> 1) - 1 }, { "offset", i }, }); if (ywmap_clocks.count(sig[i])) { int clock_mode = ywmap_clocks[sig[i]]; if (clock_mode != 3) { clock_lines[a] = json11::Json(json11::Json::object { { "path", path }, { "input", (a >> 1) - 1 }, { "offset", i }, { "edge", clock_mode == 1 ? "posedge" : "negedge" }, }); } } } } } json.name("clocks"); json.begin_array(); clock_lines.sort(); for (auto &it : clock_lines) json.value(it.second); json.end_array(); json.name("inputs"); json.begin_array(); input_lines.sort(); for (auto &it : input_lines) json.value(it.second); json.end_array(); json.name("seqs"); json.begin_array(); input_lines.sort(); for (auto &it : seq_lines) json.value(it.second); json.end_array(); json.name("inits"); json.begin_array(); input_lines.sort(); for (auto &it : init_lines) json.value(it.second); json.end_array(); json.end_object(); } }; struct AigerBackend : public Backend { AigerBackend() : Backend("aiger", "write design to AIGER file") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" write_aiger [options] [filename]\n"); log("\n"); log("Write the current design to an AIGER file. The design must be flattened and\n"); log("must not contain any cell types except $_AND_, $_NOT_, simple FF types,\n"); log("$assert and $assume cells, and $initstate cells.\n"); log("\n"); log("$assert and $assume cells are converted to AIGER bad state properties and\n"); log("invariant constraints.\n"); log("\n"); log(" -ascii\n"); log(" write ASCII version of AIGER format\n"); log("\n"); log(" -zinit\n"); log(" convert FFs to zero-initialized FFs, adding additional inputs for\n"); log(" uninitialized FFs.\n"); log("\n"); log(" -miter\n"); log(" design outputs are AIGER bad state properties\n"); log("\n"); log(" -symbols\n"); log(" include a symbol table in the generated AIGER file\n"); log("\n"); log(" -map \n"); log(" write an extra file with port and latch symbols\n"); log("\n"); log(" -vmap \n"); log(" like -map, but more verbose\n"); log("\n"); log(" -no-startoffset\n"); log(" make indexes zero based, enable using map files with smt solvers.\n"); log("\n"); log(" -ywmap \n"); log(" write a map file for conversion to and from yosys witness traces.\n"); log("\n"); log(" -I, -O, -B, -L\n"); log(" If the design contains no input/output/assert/flip-flop then create one\n"); log(" dummy input/output/bad_state-pin or latch to make the tools reading the\n"); log(" AIGER file happy.\n"); log("\n"); } void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { bool ascii_mode = false; bool zinit_mode = false; bool miter_mode = false; bool symbols_mode = false; bool verbose_map = false; bool imode = false; bool omode = false; bool bmode = false; bool lmode = false; bool no_startoffset = false; std::string map_filename; std::string yw_map_filename; log_header(design, "Executing AIGER backend.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-ascii") { ascii_mode = true; continue; } if (args[argidx] == "-zinit") { zinit_mode = true; continue; } if (args[argidx] == "-miter") { miter_mode = true; continue; } if (args[argidx] == "-symbols") { symbols_mode = true; continue; } if (map_filename.empty() && args[argidx] == "-map" && argidx+1 < args.size()) { map_filename = args[++argidx]; continue; } if (map_filename.empty() && args[argidx] == "-vmap" && argidx+1 < args.size()) { map_filename = args[++argidx]; verbose_map = true; continue; } if (yw_map_filename.empty() && args[argidx] == "-ywmap" && argidx+1 < args.size()) { yw_map_filename = args[++argidx]; continue; } if (args[argidx] == "-no-startoffset") { no_startoffset = true; continue; } if (args[argidx] == "-I") { imode = true; continue; } if (args[argidx] == "-O") { omode = true; continue; } if (args[argidx] == "-B") { bmode = true; continue; } if (args[argidx] == "-L") { lmode = true; continue; } break; } extra_args(f, filename, args, argidx, !ascii_mode); if (!yw_map_filename.empty() && !zinit_mode) log_error("Currently -ywmap requires -zinit.\n"); Module *top_module = design->top_module(); if (top_module == nullptr) log_error("Can't find top module in current design!\n"); if (!design->selected_whole_module(top_module)) log_cmd_error("Can't handle partially selected module %s!\n", log_id(top_module)); if (!top_module->processes.empty()) log_error("Found unmapped processes in module %s: unmapped processes are not supported in AIGER backend!\n", log_id(top_module)); if (!top_module->memories.empty()) log_error("Found unmapped memories in module %s: unmapped memories are not supported in AIGER backend!\n", log_id(top_module)); AigerWriter writer(top_module, zinit_mode, imode, omode, bmode, lmode); writer.write_aiger(*f, ascii_mode, miter_mode, symbols_mode); if (!map_filename.empty()) { rewrite_filename(filename); std::ofstream mapf; mapf.open(map_filename.c_str(), std::ofstream::trunc); if (mapf.fail()) log_error("Can't open file `%s' for writing: %s\n", map_filename.c_str(), strerror(errno)); writer.write_map(mapf, verbose_map, no_startoffset); } if (!yw_map_filename.empty()) { std::ofstream mapf; mapf.open(yw_map_filename.c_str(), std::ofstream::trunc); PrettyJson json; if (!json.write_to_file(yw_map_filename)) log_error("Can't open file `%s' for writing: %s\n", yw_map_filename.c_str(), strerror(errno)); writer.write_ywmap(json); } } } AigerBackend; PRIVATE_NAMESPACE_END yosys-yosys-0.33/backends/aiger/xaiger.cc000066400000000000000000000603771447554276300204760ustar00rootroot00000000000000/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Claire Xenia Wolf * 2019 Eddie Hung * * 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. * */ // 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" #include "kernel/timinginfo.h" 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); while (x & ~0x7f) { f.put((x & 0x7f) | 0x80); x = x >> 7; } f.put(x); } struct XAigerWriter { Design *design; Module *module; SigMap sigmap; dict init_map; pool input_bits, output_bits; dict not_map, alias_map; dict> and_map; vector ci_bits, co_bits; vector ff_list; dict arrival_times; vector> aig_gates; vector aig_outputs; int aig_m = 0, aig_i = 0, aig_l = 0, aig_o = 0, aig_a = 0; dict aig_map; dict ordered_outputs; vector box_list; int mkgate(int a0, int a1) { aig_m++, aig_a++; aig_gates.push_back(a0 > a1 ? make_pair(a0, a1) : make_pair(a1, a0)); return 2*aig_m; } int bit2aig(SigBit bit) { auto it = aig_map.find(bit); if (it != aig_map.end()) { log_assert(it->second >= 0); return it->second; } // NB: Cannot use iterator returned from aig_map.insert() // since this function is called recursively int a = -1; if (not_map.count(bit)) { a = bit2aig(not_map.at(bit)) ^ 1; } else if (and_map.count(bit)) { auto args = and_map.at(bit); int a0 = bit2aig(args.first); int a1 = bit2aig(args.second); a = mkgate(a0, a1); } else if (alias_map.count(bit)) { a = bit2aig(alias_map.at(bit)); } if (bit == State::Sx || bit == State::Sz) { log_debug("Design contains 'x' or 'z' bits. Treating as 1'b0.\n"); a = aig_map.at(State::S0); } log_assert(a >= 0); aig_map[bit] = a; return a; } XAigerWriter(Module *module, bool dff_mode) : design(module->design), module(module), sigmap(module) { pool undriven_bits; pool unused_bits; // promote public wires for (auto wire : module->wires()) if (wire->name.isPublic()) sigmap.add(wire); // promote input wires for (auto wire : module->wires()) if (wire->port_input) sigmap.add(wire); // promote keep wires for (auto wire : module->wires()) if (wire->get_bool_attribute(ID::keep)) sigmap.add(wire); for (auto wire : module->wires()) { auto it = wire->attributes.find(ID::init); for (int i = 0; i < GetSize(wire); i++) { SigBit wirebit(wire, i); SigBit bit = sigmap(wirebit); if (bit.wire == nullptr) { if (wire->port_output) { aig_map[wirebit] = (bit == State::S1) ? 1 : 0; output_bits.insert(wirebit); } continue; } undriven_bits.insert(bit); unused_bits.insert(bit); if (wire->port_input) input_bits.insert(bit); bool keep = wire->get_bool_attribute(ID::keep); if (wire->port_output || keep) { if (bit != wirebit) alias_map[wirebit] = bit; output_bits.insert(wirebit); } if (it != wire->attributes.end()) { auto s = it->second[i]; if (s != State::Sx) { auto r = init_map.insert(std::make_pair(bit, it->second[i])); if (!r.second && r.first->second != it->second[i]) log_error("Bit '%s' has a conflicting (* init *) value.\n", log_signal(bit)); } } } } TimingInfo timing; for (auto cell : module->cells()) { if (!cell->has_keep_attr()) { if (cell->type == ID($_NOT_)) { SigBit A = sigmap(cell->getPort(ID::A).as_bit()); SigBit Y = sigmap(cell->getPort(ID::Y).as_bit()); unused_bits.erase(A); undriven_bits.erase(Y); not_map[Y] = A; continue; } if (cell->type == ID($_AND_)) { SigBit A = sigmap(cell->getPort(ID::A).as_bit()); SigBit B = sigmap(cell->getPort(ID::B).as_bit()); SigBit Y = sigmap(cell->getPort(ID::Y).as_bit()); unused_bits.erase(A); unused_bits.erase(B); undriven_bits.erase(Y); and_map[Y] = make_pair(A, B); continue; } if (dff_mode && cell->type.in(ID($_DFF_N_), ID($_DFF_P_)) && !cell->get_bool_attribute(ID::abc9_keep)) { SigBit D = sigmap(cell->getPort(ID::D).as_bit()); SigBit Q = sigmap(cell->getPort(ID::Q).as_bit()); unused_bits.erase(D); undriven_bits.erase(Q); alias_map[Q] = D; ff_list.emplace_back(cell); continue; } if (cell->type.in(ID($specify2), ID($specify3), ID($specrule))) continue; } RTLIL::Module* inst_module = design->module(cell->type); if (inst_module && inst_module->get_blackbox_attribute()) { bool abc9_flop = false; auto it = cell->attributes.find(ID::abc9_box_seq); if (it != cell->attributes.end()) { log_assert(!cell->has_keep_attr()); log_assert(cell->parameters.empty()); int abc9_box_seq = it->second.as_int(); if (GetSize(box_list) <= abc9_box_seq) box_list.resize(abc9_box_seq+1); box_list[abc9_box_seq] = cell; // Only flop boxes may have arrival times // (all others are combinatorial) log_assert(cell->parameters.empty()); abc9_flop = inst_module->get_bool_attribute(ID::abc9_flop); if (!abc9_flop) continue; } if (!timing.count(inst_module->name)) timing.setup_module(inst_module); for (auto &i : timing.at(inst_module->name).arrival) { if (!cell->hasPort(i.first.name)) continue; auto port_wire = inst_module->wire(i.first.name); log_assert(port_wire->port_output); auto d = i.second.first; if (d == 0) continue; auto offset = i.first.offset; auto rhs = cell->getPort(i.first.name); if (offset >= rhs.size()) continue; #ifndef NDEBUG if (ys_debug(1)) { static pool> seen; if (seen.emplace(inst_module->name, i.first).second) log("%s.%s[%d] abc9_arrival = %d\n", log_id(cell->type), log_id(i.first.name), offset, d); } #endif arrival_times[rhs[offset]] = d; } if (abc9_flop) continue; } bool cell_known = inst_module || cell->known(); for (const auto &c : cell->connections()) { if (c.second.is_fully_const()) continue; auto port_wire = inst_module ? inst_module->wire(c.first) : nullptr; auto is_input = (port_wire && port_wire->port_input) || !cell_known || cell->input(c.first); auto is_output = (port_wire && port_wire->port_output) || !cell_known || cell->output(c.first); if (!is_input && !is_output) log_error("Connection '%s' on cell '%s' (type '%s') not recognised!\n", log_id(c.first), log_id(cell), log_id(cell->type)); if (is_input) for (auto b : c.second) { Wire *w = b.wire; if (!w) continue; // Do not add as PO if bit is already a PI if (input_bits.count(b)) continue; if (!w->port_output || !cell_known) { SigBit I = sigmap(b); if (I != b) alias_map[b] = I; output_bits.insert(b); } } } //log_warning("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell)); } dict> box_ports; for (auto cell : box_list) { log_assert(cell); RTLIL::Module* box_module = design->module(cell->type); log_assert(box_module); log_assert(box_module->has_attribute(ID::abc9_box_id)); auto r = box_ports.insert(cell->type); if (r.second) { // Make carry in the last PI, and carry out the last PO // since ABC requires it this way IdString carry_in, carry_out; for (const auto &port_name : box_module->ports) { auto w = box_module->wire(port_name); log_assert(w); if (w->get_bool_attribute(ID::abc9_carry)) { if (w->port_input) { if (carry_in != IdString()) log_error("Module '%s' contains more than one 'abc9_carry' input port.\n", log_id(box_module)); carry_in = port_name; } if (w->port_output) { if (carry_out != IdString()) log_error("Module '%s' contains more than one 'abc9_carry' output port.\n", log_id(box_module)); carry_out = port_name; } } else r.first->second.push_back(port_name); } if (carry_in != IdString() && carry_out == IdString()) log_error("Module '%s' contains an 'abc9_carry' input port but no output port.\n", log_id(box_module)); if (carry_in == IdString() && carry_out != IdString()) log_error("Module '%s' contains an 'abc9_carry' output port but no input port.\n", log_id(box_module)); if (carry_in != IdString()) { r.first->second.push_back(carry_in); r.first->second.push_back(carry_out); } } for (auto port_name : r.first->second) { auto w = box_module->wire(port_name); log_assert(w); auto rhs = cell->connections_.at(port_name, SigSpec()); rhs.append(Const(State::Sx, GetSize(w)-GetSize(rhs))); if (w->port_input) for (auto b : rhs) { SigBit I = sigmap(b); if (b == RTLIL::Sx) b = State::S0; else if (I != b) { if (I == RTLIL::Sx) alias_map[b] = State::S0; else alias_map[b] = I; } co_bits.emplace_back(b); unused_bits.erase(I); } if (w->port_output) for (const auto &b : rhs) { SigBit O = sigmap(b); if (O != b) alias_map[O] = b; ci_bits.emplace_back(b); undriven_bits.erase(O); } } } for (auto bit : input_bits) undriven_bits.erase(bit); for (auto bit : output_bits) unused_bits.erase(sigmap(bit)); for (auto bit : unused_bits) undriven_bits.erase(bit); // Make all undriven bits a primary input for (auto bit : undriven_bits) { input_bits.insert(bit); undriven_bits.erase(bit); } struct sort_by_port_id { bool operator()(const RTLIL::SigBit& a, const RTLIL::SigBit& b) const { return a.wire->port_id < b.wire->port_id || (a.wire->port_id == b.wire->port_id && a.offset < b.offset); } }; input_bits.sort(sort_by_port_id()); output_bits.sort(sort_by_port_id()); aig_map[State::S0] = 0; aig_map[State::S1] = 1; for (const auto &bit : input_bits) { aig_m++, aig_i++; log_assert(!aig_map.count(bit)); aig_map[bit] = 2*aig_m; } for (auto cell : ff_list) { const SigBit &q = sigmap(cell->getPort(ID::Q)); aig_m++, aig_i++; log_assert(!aig_map.count(q)); aig_map[q] = 2*aig_m; } for (auto &bit : ci_bits) { aig_m++, aig_i++; // 1'bx may exist here due to a box output // that has been padded to its full width if (bit == State::Sx) continue; if (aig_map.count(bit)) log_error("Visited AIG node more than once; this could be a combinatorial loop that has not been broken\n"); aig_map[bit] = 2*aig_m; } for (auto bit : co_bits) { ordered_outputs[bit] = aig_o++; aig_outputs.push_back(bit2aig(bit)); } for (const auto &bit : output_bits) { ordered_outputs[bit] = aig_o++; int aig; // Unlike bit2aig() which checks aig_map first for // inout/scc bits, since aig_map will point to // the PI, first attempt to find the NOT/AND driver // before resorting to an aig_map lookup (which // could be another PO) if (input_bits.count(bit)) { if (not_map.count(bit)) { aig = bit2aig(not_map.at(bit)) ^ 1; } else if (and_map.count(bit)) { auto args = and_map.at(bit); int a0 = bit2aig(args.first); int a1 = bit2aig(args.second); aig = mkgate(a0, a1); } else aig = aig_map.at(bit); } else aig = bit2aig(bit); aig_outputs.push_back(aig); } for (auto cell : ff_list) { const SigBit &d = sigmap(cell->getPort(ID::D)); aig_o++; aig_outputs.push_back(aig_map.at(d)); } } void write_aiger(std::ostream &f, bool ascii_mode) { int aig_obc = aig_o; int aig_obcj = aig_obc; int aig_obcjf = aig_obcj; log_assert(aig_m == aig_i + aig_l + aig_a); log_assert(aig_obcjf == GetSize(aig_outputs)); f << stringf("%s %d %d %d %d %d", ascii_mode ? "aag" : "aig", aig_m, aig_i, aig_l, aig_o, aig_a); f << stringf("\n"); if (ascii_mode) { for (int i = 0; i < aig_i; i++) f << stringf("%d\n", 2*i+2); for (int i = 0; i < aig_obc; i++) f << stringf("%d\n", aig_outputs.at(i)); for (int i = aig_obc; i < aig_obcj; i++) f << stringf("1\n"); for (int i = aig_obc; i < aig_obcj; i++) f << stringf("%d\n", aig_outputs.at(i)); for (int i = aig_obcj; i < aig_obcjf; i++) f << stringf("%d\n", aig_outputs.at(i)); for (int i = 0; i < aig_a; i++) f << stringf("%d %d %d\n", 2*(aig_i+aig_l+i)+2, aig_gates.at(i).first, aig_gates.at(i).second); } else { for (int i = 0; i < aig_obc; i++) f << stringf("%d\n", aig_outputs.at(i)); for (int i = aig_obc; i < aig_obcj; i++) f << stringf("1\n"); for (int i = aig_obc; i < aig_obcj; i++) f << stringf("%d\n", aig_outputs.at(i)); for (int i = aig_obcj; i < aig_obcjf; i++) f << stringf("%d\n", aig_outputs.at(i)); for (int i = 0; i < aig_a; i++) { int lhs = 2*(aig_i+aig_l+i)+2; int rhs0 = aig_gates.at(i).first; int rhs1 = aig_gates.at(i).second; int delta0 = lhs - rhs0; int delta1 = rhs0 - rhs1; aiger_encode(f, delta0); aiger_encode(f, delta1); } } 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)); }; std::stringstream h_buffer; auto write_h_buffer = std::bind(write_buffer, std::ref(h_buffer), std::placeholders::_1); write_h_buffer(1); log_debug("ciNum = %d\n", GetSize(input_bits) + GetSize(ff_list) + GetSize(ci_bits)); write_h_buffer(GetSize(input_bits) + GetSize(ff_list) + GetSize(ci_bits)); log_debug("coNum = %d\n", GetSize(output_bits) + GetSize(ff_list) + GetSize(co_bits)); write_h_buffer(GetSize(output_bits) + GetSize(ff_list) + GetSize(co_bits)); log_debug("piNum = %d\n", GetSize(input_bits) + GetSize(ff_list)); write_h_buffer(GetSize(input_bits) + GetSize(ff_list)); log_debug("poNum = %d\n", GetSize(output_bits) + GetSize(ff_list)); write_h_buffer(GetSize(output_bits) + GetSize(ff_list)); log_debug("boxNum = %d\n", GetSize(box_list)); write_h_buffer(GetSize(box_list)); auto write_buffer_float = [](std::stringstream &buffer, float f32) { buffer.write(reinterpret_cast(&f32), sizeof(f32)); }; std::stringstream i_buffer; auto write_i_buffer = std::bind(write_buffer_float, std::ref(i_buffer), std::placeholders::_1); for (auto bit : input_bits) write_i_buffer(arrival_times.at(bit, 0)); //std::stringstream o_buffer; //auto write_o_buffer = std::bind(write_buffer_float, std::ref(o_buffer), std::placeholders::_1); //for (auto bit : output_bits) // write_o_buffer(0); if (!box_list.empty() || !ff_list.empty()) { dict> cell_cache; int box_count = 0; for (auto cell : box_list) { log_assert(cell); log_assert(cell->parameters.empty()); auto r = cell_cache.insert(cell->type); auto &v = r.first->second; if (r.second) { RTLIL::Module* box_module = design->module(cell->type); log_assert(box_module); int box_inputs = 0, box_outputs = 0; for (auto port_name : box_module->ports) { RTLIL::Wire *w = box_module->wire(port_name); log_assert(w); if (w->port_input) box_inputs += GetSize(w); if (w->port_output) box_outputs += GetSize(w); } std::get<0>(v) = box_inputs; std::get<1>(v) = box_outputs; std::get<2>(v) = box_module->attributes.at(ID::abc9_box_id).as_int(); } write_h_buffer(std::get<0>(v)); write_h_buffer(std::get<1>(v)); write_h_buffer(std::get<2>(v)); write_h_buffer(box_count++); } std::stringstream r_buffer; auto write_r_buffer = std::bind(write_buffer, std::ref(r_buffer), std::placeholders::_1); log_debug("flopNum = %d\n", GetSize(ff_list)); write_r_buffer(ff_list.size()); std::stringstream s_buffer; auto write_s_buffer = std::bind(write_buffer, std::ref(s_buffer), std::placeholders::_1); write_s_buffer(ff_list.size()); dict clk_to_mergeability; for (const auto cell : ff_list) { const SigBit &d = sigmap(cell->getPort(ID::D)); const SigBit &q = sigmap(cell->getPort(ID::Q)); SigSpec clk_and_pol{sigmap(cell->getPort(ID::C)), cell->type[6] == 'P' ? State::S1 : State::S0}; auto r = clk_to_mergeability.insert(std::make_pair(clk_and_pol, clk_to_mergeability.size()+1)); int mergeability = r.first->second; log_assert(mergeability > 0); write_r_buffer(mergeability); State init = init_map.at(q, State::Sx); log_debug("Cell '%s' (type %s) has (* init *) value '%s'.\n", log_id(cell), log_id(cell->type), log_signal(init)); if (init == State::S1) write_s_buffer(1); else if (init == State::S0) write_s_buffer(0); else { log_assert(init == State::Sx); write_s_buffer(2); } // Use arrival time from output of flop box write_i_buffer(arrival_times.at(d, 0)); //write_o_buffer(0); } 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)); 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)); f.write(buffer_str.data(), buffer_str.size()); RTLIL::Design *holes_design; auto it = saved_designs.find("$abc9_holes"); if (it != saved_designs.end()) holes_design = it->second; else holes_design = nullptr; RTLIL::Module *holes_module = holes_design ? holes_design->module(module->name) : nullptr; if (holes_module) { std::stringstream a_buffer; XAigerWriter writer(holes_module, false /* dff_mode */); writer.write_aiger(a_buffer, false /*ascii_mode*/); 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)); 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)); 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)); f.write(buffer_str.data(), buffer_str.size()); //f << "o"; //buffer_str = o_buffer.str(); //buffer_size_be = to_big_endian(buffer_str.size()); //f.write(reinterpret_cast(&buffer_size_be), sizeof(buffer_size_be)); //f.write(buffer_str.data(), buffer_str.size()); f << stringf("Generated by %s\n", yosys_version_str); design->scratchpad_set_int("write_xaiger.num_ands", and_map.size()); design->scratchpad_set_int("write_xaiger.num_wires", aig_map.size()); design->scratchpad_set_int("write_xaiger.num_inputs", input_bits.size()); design->scratchpad_set_int("write_xaiger.num_outputs", output_bits.size()); } void write_map(std::ostream &f) { dict input_lines; dict output_lines; for (auto wire : module->wires()) { for (int i = 0; i < GetSize(wire); i++) { RTLIL::SigBit b(wire, i); if (input_bits.count(b)) { int a = aig_map.at(b); log_assert((a & 1) == 0); input_lines[a] += stringf("input %d %d %s\n", (a >> 1)-1, wire->start_offset+i, log_id(wire)); } if (output_bits.count(b)) { int o = ordered_outputs.at(b); output_lines[o] += stringf("output %d %d %s\n", o - GetSize(co_bits), wire->start_offset+i, log_id(wire)); } } } input_lines.sort(); for (auto &it : input_lines) f << it.second; log_assert(input_lines.size() == input_bits.size()); int box_count = 0; for (auto cell : box_list) f << stringf("box %d %d %s\n", box_count++, 0, log_id(cell->name)); output_lines.sort(); for (auto &it : output_lines) f << it.second; log_assert(output_lines.size() == output_bits.size()); } }; struct XAigerBackend : public Backend { XAigerBackend() : Backend("xaiger", "write design to XAIGER file") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" write_xaiger [options] [filename]\n"); log("\n"); log("Write the top module (according to the (* top *) attribute or if only one module\n"); log("is currently selected) to an XAIGER file. Any non $_NOT_, $_AND_, (optionally\n"); log("$_DFF_N_, $_DFF_P_), or non (* abc9_box *) cells will be converted into psuedo-\n"); log("inputs and pseudo-outputs. Whitebox contents will be taken from the equivalent\n"); log("module in the '$abc9_holes' design, if it exists.\n"); log("\n"); log(" -ascii\n"); log(" write ASCII version of AIGER format\n"); log("\n"); log(" -map \n"); log(" write an extra file with port and box symbols\n"); log("\n"); log(" -dff\n"); log(" write $_DFF_[NP]_ cells\n"); log("\n"); } void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { bool ascii_mode = false, dff_mode = false; std::string map_filename; log_header(design, "Executing XAIGER backend.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-ascii") { ascii_mode = true; continue; } if (map_filename.empty() && args[argidx] == "-map" && argidx+1 < args.size()) { map_filename = args[++argidx]; continue; } if (args[argidx] == "-dff") { dff_mode = true; continue; } break; } extra_args(f, filename, args, argidx, !ascii_mode); Module *top_module = design->top_module(); if (top_module == nullptr) log_error("Can't find top module in current design!\n"); if (!design->selected_whole_module(top_module)) log_cmd_error("Can't handle partially selected module %s!\n", log_id(top_module)); if (!top_module->processes.empty()) log_error("Found unmapped processes in module %s: unmapped processes are not supported in XAIGER backend!\n", log_id(top_module)); if (!top_module->memories.empty()) log_error("Found unmapped memories in module %s: unmapped memories are not supported in XAIGER backend!\n", log_id(top_module)); XAigerWriter writer(top_module, dff_mode); writer.write_aiger(*f, ascii_mode); if (!map_filename.empty()) { std::ofstream mapf; mapf.open(map_filename.c_str(), std::ofstream::trunc); if (mapf.fail()) log_error("Can't open file `%s' for writing: %s\n", map_filename.c_str(), strerror(errno)); writer.write_map(mapf); } } } XAigerBackend; PRIVATE_NAMESPACE_END yosys-yosys-0.33/backends/blif/000077500000000000000000000000001447554276300165205ustar00rootroot00000000000000yosys-yosys-0.33/backends/blif/Makefile.inc000066400000000000000000000000371447554276300207300ustar00rootroot00000000000000 OBJS += backends/blif/blif.o yosys-yosys-0.33/backends/blif/blif.cc000066400000000000000000000565151447554276300177570ustar00rootroot00000000000000/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ // [[CITE]] Berkeley Logic Interchange Format (BLIF) // University of California. Berkeley. July 28, 1992 // http://www.ece.cmu.edu/~ee760/760docs/blif.pdf #include "kernel/rtlil.h" #include "kernel/register.h" #include "kernel/sigtools.h" #include "kernel/celltypes.h" #include "kernel/log.h" #include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct BlifDumperConfig { bool icells_mode; bool conn_mode; bool impltf_mode; bool gates_mode; bool cname_mode; bool iname_mode; bool param_mode; bool attr_mode; bool iattr_mode; bool blackbox_mode; bool noalias_mode; std::string buf_type, buf_in, buf_out; std::map> unbuf_types; std::string true_type, true_out, false_type, false_out, undef_type, undef_out; BlifDumperConfig() : icells_mode(false), conn_mode(false), impltf_mode(false), gates_mode(false), cname_mode(false), iname_mode(false), param_mode(false), attr_mode(false), iattr_mode(false), blackbox_mode(false), noalias_mode(false) { } }; struct BlifDumper { std::ostream &f; RTLIL::Module *module; RTLIL::Design *design; BlifDumperConfig *config; CellTypes ct; SigMap sigmap; dict init_bits; BlifDumper(std::ostream &f, RTLIL::Module *module, RTLIL::Design *design, BlifDumperConfig *config) : f(f), module(module), design(design), config(config), ct(design), sigmap(module) { for (Wire *wire : module->wires()) if (wire->attributes.count(ID::init)) { SigSpec initsig = sigmap(wire); Const initval = wire->attributes.at(ID::init); for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++) switch (initval[i]) { case State::S0: init_bits[initsig[i]] = 0; break; case State::S1: init_bits[initsig[i]] = 1; break; default: break; } } } pool cstr_bits_seen; const std::string str(RTLIL::IdString id) { std::string str = RTLIL::unescape_id(id); for (size_t i = 0; i < str.size(); i++) if (str[i] == '#' || str[i] == '=' || str[i] == '<' || str[i] == '>') str[i] = '?'; return str; } const std::string str(RTLIL::SigBit sig) { cstr_bits_seen.insert(sig); if (sig.wire == NULL) { if (sig == RTLIL::State::S0) return config->false_type == "-" || config->false_type == "+" ? config->false_out.c_str() : "$false"; if (sig == RTLIL::State::S1) return config->true_type == "-" || config->true_type == "+" ? config->true_out.c_str() : "$true"; return config->undef_type == "-" || config->undef_type == "+" ? config->undef_out.c_str() : "$undef"; } std::string str = RTLIL::unescape_id(sig.wire->name); for (size_t i = 0; i < str.size(); i++) if (str[i] == '#' || str[i] == '=' || str[i] == '<' || str[i] == '>') str[i] = '?'; if (sig.wire->width != 1) str += stringf("[%d]", sig.wire->upto ? sig.wire->start_offset+sig.wire->width-sig.offset-1 : sig.wire->start_offset+sig.offset); return str; } const std::string str_init(RTLIL::SigBit sig) { sigmap.apply(sig); if (init_bits.count(sig) == 0) return " 2"; string str = stringf(" %d", init_bits.at(sig)); return str; } const char *subckt_or_gate(std::string cell_type) { if (!config->gates_mode) return "subckt"; if (design->module(RTLIL::escape_id(cell_type)) == nullptr) return "gate"; if (design->module(RTLIL::escape_id(cell_type))->get_blackbox_attribute()) return "gate"; return "subckt"; } void dump_params(const char *command, dict ¶ms) { for (auto ¶m : params) { f << stringf("%s %s ", command, log_id(param.first)); if (param.second.flags & RTLIL::CONST_FLAG_STRING) { std::string str = param.second.decode_string(); f << stringf("\""); for (char ch : str) if (ch == '"' || ch == '\\') f << stringf("\\%c", ch); else if (ch < 32 || ch >= 127) f << stringf("\\%03o", ch); else f << stringf("%c", ch); f << stringf("\"\n"); } else f << stringf("%s\n", param.second.as_string().c_str()); } } void dump() { f << stringf("\n"); f << stringf(".model %s\n", str(module->name).c_str()); std::map inputs, outputs; for (auto wire : module->wires()) { if (wire->port_input) inputs[wire->port_id] = wire; if (wire->port_output) outputs[wire->port_id] = wire; } f << stringf(".inputs"); for (auto &it : inputs) { RTLIL::Wire *wire = it.second; for (int i = 0; i < wire->width; i++) f << stringf(" %s", str(RTLIL::SigSpec(wire, i)).c_str()); } f << stringf("\n"); f << stringf(".outputs"); for (auto &it : outputs) { RTLIL::Wire *wire = it.second; for (int i = 0; i < wire->width; i++) f << stringf(" %s", str(RTLIL::SigSpec(wire, i)).c_str()); } f << stringf("\n"); if (module->get_blackbox_attribute()) { f << stringf(".blackbox\n"); f << stringf(".end\n"); return; } if (!config->impltf_mode) { if (!config->false_type.empty()) { if (config->false_type == "+") f << stringf(".names %s\n", config->false_out.c_str()); else if (config->false_type != "-") f << stringf(".%s %s %s=$false\n", subckt_or_gate(config->false_type), config->false_type.c_str(), config->false_out.c_str()); } else f << stringf(".names $false\n"); if (!config->true_type.empty()) { if (config->true_type == "+") f << stringf(".names %s\n1\n", config->true_out.c_str()); else if (config->true_type != "-") f << stringf(".%s %s %s=$true\n", subckt_or_gate(config->true_type), config->true_type.c_str(), config->true_out.c_str()); } else f << stringf(".names $true\n1\n"); if (!config->undef_type.empty()) { if (config->undef_type == "+") f << stringf(".names %s\n", config->undef_out.c_str()); else if (config->undef_type != "-") f << stringf(".%s %s %s=$undef\n", subckt_or_gate(config->undef_type), config->undef_type.c_str(), config->undef_out.c_str()); } else f << stringf(".names $undef\n"); } for (auto cell : module->cells()) { if (config->unbuf_types.count(cell->type)) { auto portnames = config->unbuf_types.at(cell->type); f << stringf(".names %s %s\n1 1\n", str(cell->getPort(portnames.first)).c_str(), str(cell->getPort(portnames.second)).c_str()); continue; } if (!config->icells_mode && cell->type == ID($_NOT_)) { f << stringf(".names %s %s\n0 1\n", str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_AND_)) { f << stringf(".names %s %s %s\n11 1\n", str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_OR_)) { f << stringf(".names %s %s %s\n1- 1\n-1 1\n", str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_XOR_)) { f << stringf(".names %s %s %s\n10 1\n01 1\n", str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_NAND_)) { f << stringf(".names %s %s %s\n0- 1\n-0 1\n", str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_NOR_)) { f << stringf(".names %s %s %s\n00 1\n", str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_XNOR_)) { f << stringf(".names %s %s %s\n11 1\n00 1\n", str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_ANDNOT_)) { f << stringf(".names %s %s %s\n10 1\n", str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_ORNOT_)) { f << stringf(".names %s %s %s\n1- 1\n-0 1\n", str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_AOI3_)) { f << stringf(".names %s %s %s %s\n-00 1\n0-0 1\n", str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::C)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_OAI3_)) { f << stringf(".names %s %s %s %s\n00- 1\n--0 1\n", str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::C)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_AOI4_)) { f << stringf(".names %s %s %s %s %s\n-0-0 1\n-00- 1\n0--0 1\n0-0- 1\n", str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::C)).c_str(), str(cell->getPort(ID::D)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_OAI4_)) { f << stringf(".names %s %s %s %s %s\n00-- 1\n--00 1\n", str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::C)).c_str(), str(cell->getPort(ID::D)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_MUX_)) { f << stringf(".names %s %s %s %s\n1-0 1\n-11 1\n", str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::S)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_NMUX_)) { f << stringf(".names %s %s %s %s\n0-0 1\n-01 1\n", str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::S)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_FF_)) { f << stringf(".latch %s %s%s\n", str(cell->getPort(ID::D)).c_str(), str(cell->getPort(ID::Q)).c_str(), str_init(cell->getPort(ID::Q)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_DFF_N_)) { f << stringf(".latch %s %s fe %s%s\n", str(cell->getPort(ID::D)).c_str(), str(cell->getPort(ID::Q)).c_str(), str(cell->getPort(ID::C)).c_str(), str_init(cell->getPort(ID::Q)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_DFF_P_)) { f << stringf(".latch %s %s re %s%s\n", str(cell->getPort(ID::D)).c_str(), str(cell->getPort(ID::Q)).c_str(), str(cell->getPort(ID::C)).c_str(), str_init(cell->getPort(ID::Q)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_DLATCH_N_)) { f << stringf(".latch %s %s al %s%s\n", str(cell->getPort(ID::D)).c_str(), str(cell->getPort(ID::Q)).c_str(), str(cell->getPort(ID::E)).c_str(), str_init(cell->getPort(ID::Q)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_DLATCH_P_)) { f << stringf(".latch %s %s ah %s%s\n", str(cell->getPort(ID::D)).c_str(), str(cell->getPort(ID::Q)).c_str(), str(cell->getPort(ID::E)).c_str(), str_init(cell->getPort(ID::Q)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($lut)) { f << stringf(".names"); auto &inputs = cell->getPort(ID::A); auto width = cell->parameters.at(ID::WIDTH).as_int(); log_assert(inputs.size() == width); for (int i = width-1; i >= 0; i--) f << stringf(" %s", str(inputs.extract(i, 1)).c_str()); auto &output = cell->getPort(ID::Y); log_assert(output.size() == 1); f << stringf(" %s", str(output).c_str()); f << stringf("\n"); RTLIL::SigSpec mask = cell->parameters.at(ID::LUT); for (int i = 0; i < (1 << width); i++) if (mask[i] == State::S1) { for (int j = width-1; j >= 0; j--) { f << ((i>>j)&1 ? '1' : '0'); } f << " 1\n"; } goto internal_cell; } if (!config->icells_mode && cell->type == ID($sop)) { f << stringf(".names"); 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; while (GetSize(table) < 2*width*depth) table.push_back(State::S0); log_assert(inputs.size() == width); for (int i = 0; i < width; i++) f << stringf(" %s", str(inputs.extract(i, 1)).c_str()); auto &output = cell->getPort(ID::Y); log_assert(output.size() == 1); f << stringf(" %s", str(output).c_str()); f << stringf("\n"); for (int i = 0; i < depth; i++) { for (int j = 0; j < width; j++) { bool pat0 = table.at(2*width*i + 2*j + 0) == State::S1; bool pat1 = table.at(2*width*i + 2*j + 1) == State::S1; if (pat0 && !pat1) f << "0"; else if (!pat0 && pat1) f << "1"; else f << "-"; } f << " 1\n"; } goto internal_cell; } f << stringf(".%s %s", subckt_or_gate(cell->type.str()), str(cell->type).c_str()); for (auto &conn : cell->connections()) { if (conn.second.size() == 1) { f << stringf(" %s=%s", str(conn.first).c_str(), str(conn.second[0]).c_str()); continue; } Module *m = design->module(cell->type); Wire *w = m ? m->wire(conn.first) : nullptr; if (w == nullptr) { for (int i = 0; i < GetSize(conn.second); i++) f << stringf(" %s[%d]=%s", str(conn.first).c_str(), i, str(conn.second[i]).c_str()); } else { for (int i = 0; i < std::min(GetSize(conn.second), GetSize(w)); i++) { SigBit sig(w, i); f << stringf(" %s[%d]=%s", str(conn.first).c_str(), sig.wire->upto ? sig.wire->start_offset+sig.wire->width-sig.offset-1 : sig.wire->start_offset+sig.offset, str(conn.second[i]).c_str()); } } } f << stringf("\n"); if (config->cname_mode) f << stringf(".cname %s\n", str(cell->name).c_str()); if (config->attr_mode) dump_params(".attr", cell->attributes); if (config->param_mode) dump_params(".param", cell->parameters); if (0) { internal_cell: if (config->iname_mode) f << stringf(".cname %s\n", str(cell->name).c_str()); if (config->iattr_mode) dump_params(".attr", cell->attributes); } } for (auto &conn : module->connections()) for (int i = 0; i < conn.first.size(); i++) { SigBit lhs_bit = conn.first[i]; SigBit rhs_bit = conn.second[i]; if (config->noalias_mode && cstr_bits_seen.count(lhs_bit) == 0) continue; if (config->conn_mode) f << stringf(".conn %s %s\n", str(rhs_bit).c_str(), str(lhs_bit).c_str()); else if (!config->buf_type.empty()) f << stringf(".%s %s %s=%s %s=%s\n", subckt_or_gate(config->buf_type), config->buf_type.c_str(), config->buf_in.c_str(), str(rhs_bit).c_str(), config->buf_out.c_str(), str(lhs_bit).c_str()); else f << stringf(".names %s %s\n1 1\n", str(rhs_bit).c_str(), str(lhs_bit).c_str()); } f << stringf(".end\n"); } static void dump(std::ostream &f, RTLIL::Module *module, RTLIL::Design *design, BlifDumperConfig &config) { BlifDumper dumper(f, module, design, &config); dumper.dump(); } }; struct BlifBackend : public Backend { BlifBackend() : Backend("blif", "write design to BLIF file") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" write_blif [options] [filename]\n"); log("\n"); log("Write the current design to an BLIF file.\n"); log("\n"); log(" -top top_module\n"); log(" set the specified module as design top module\n"); log("\n"); log(" -buf \n"); log(" use cells of type with the specified port names for buffers\n"); log("\n"); log(" -unbuf \n"); log(" replace buffer cells with the specified name and port names with\n"); log(" a .names statement that models a buffer\n"); log("\n"); log(" -true \n"); log(" -false \n"); log(" -undef \n"); log(" use the specified cell types to drive nets that are constant 1, 0, or\n"); log(" undefined. when '-' is used as , then specifies\n"); log(" the wire name to be used for the constant signal and no cell driving\n"); log(" that wire is generated. when '+' is used as , then \n"); log(" specifies the wire name to be used for the constant signal and a .names\n"); log(" statement is generated to drive the wire.\n"); log("\n"); log(" -noalias\n"); log(" if a net name is aliasing another net name, then by default a net\n"); log(" without fanout is created that is driven by the other net. This option\n"); log(" suppresses the generation of this nets without fanout.\n"); log("\n"); log("The following options can be useful when the generated file is not going to be\n"); log("read by a BLIF parser but a custom tool. It is recommended not to name the\n"); log("output file *.blif when any of these options are used.\n"); log("\n"); log(" -icells\n"); log(" do not translate Yosys's internal gates to generic BLIF logic\n"); log(" functions. Instead create .subckt or .gate lines for all cells.\n"); log("\n"); log(" -gates\n"); log(" print .gate instead of .subckt lines for all cells that are not\n"); log(" instantiations of other modules from this design.\n"); log("\n"); log(" -conn\n"); log(" do not generate buffers for connected wires. instead use the\n"); log(" non-standard .conn statement.\n"); log("\n"); log(" -attr\n"); log(" use the non-standard .attr statement to write cell attributes\n"); log("\n"); log(" -param\n"); log(" use the non-standard .param statement to write cell parameters\n"); log("\n"); log(" -cname\n"); log(" use the non-standard .cname statement to write cell names\n"); log("\n"); log(" -iname, -iattr\n"); log(" enable -cname and -attr functionality for .names statements\n"); log(" (the .cname and .attr statements will be included in the BLIF\n"); log(" output after the truth table for the .names statement)\n"); log("\n"); log(" -blackbox\n"); log(" write blackbox cells with .blackbox statement.\n"); log("\n"); log(" -impltf\n"); log(" do not write definitions for the $true, $false and $undef wires.\n"); log("\n"); } void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { std::string top_module_name; std::string buf_type, buf_in, buf_out; std::string true_type, true_out; std::string false_type, false_out; BlifDumperConfig config; log_header(design, "Executing BLIF backend.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-top" && argidx+1 < args.size()) { top_module_name = args[++argidx]; continue; } if (args[argidx] == "-buf" && argidx+3 < args.size()) { config.buf_type = args[++argidx]; config.buf_in = args[++argidx]; config.buf_out = args[++argidx]; continue; } if (args[argidx] == "-unbuf" && argidx+3 < args.size()) { RTLIL::IdString unbuf_type = RTLIL::escape_id(args[++argidx]); RTLIL::IdString unbuf_in = RTLIL::escape_id(args[++argidx]); RTLIL::IdString unbuf_out = RTLIL::escape_id(args[++argidx]); config.unbuf_types[unbuf_type] = std::pair(unbuf_in, unbuf_out); continue; } if (args[argidx] == "-true" && argidx+2 < args.size()) { config.true_type = args[++argidx]; config.true_out = args[++argidx]; continue; } if (args[argidx] == "-false" && argidx+2 < args.size()) { config.false_type = args[++argidx]; config.false_out = args[++argidx]; continue; } if (args[argidx] == "-undef" && argidx+2 < args.size()) { config.undef_type = args[++argidx]; config.undef_out = args[++argidx]; continue; } if (args[argidx] == "-icells") { config.icells_mode = true; continue; } if (args[argidx] == "-gates") { config.gates_mode = true; continue; } if (args[argidx] == "-conn") { config.conn_mode = true; continue; } if (args[argidx] == "-cname") { config.cname_mode = true; continue; } if (args[argidx] == "-param") { config.param_mode = true; continue; } if (args[argidx] == "-attr") { config.attr_mode = true; continue; } if (args[argidx] == "-iname") { config.iname_mode = true; continue; } if (args[argidx] == "-iattr") { config.iattr_mode = true; continue; } if (args[argidx] == "-blackbox") { config.blackbox_mode = true; continue; } if (args[argidx] == "-impltf") { config.impltf_mode = true; continue; } if (args[argidx] == "-noalias") { config.noalias_mode = true; continue; } break; } extra_args(f, filename, args, argidx); if (top_module_name.empty()) for (auto module : design->modules()) if (module->get_bool_attribute(ID::top)) top_module_name = module->name.str(); *f << stringf("# Generated by %s\n", yosys_version_str); std::vector mod_list; design->sort(); for (auto module : design->modules()) { if (module->get_blackbox_attribute() && !config.blackbox_mode) continue; if (module->processes.size() != 0) log_error("Found unmapped processes in module %s: unmapped processes are not supported in BLIF backend!\n", log_id(module->name)); if (module->memories.size() != 0) log_error("Found unmapped memories in module %s: unmapped memories are not supported in BLIF backend!\n", log_id(module->name)); if (module->name == RTLIL::escape_id(top_module_name)) { BlifDumper::dump(*f, module, design, config); top_module_name.clear(); continue; } mod_list.push_back(module); } if (!top_module_name.empty()) log_error("Can't find top module `%s'!\n", top_module_name.c_str()); for (auto module : mod_list) BlifDumper::dump(*f, module, design, config); } } BlifBackend; PRIVATE_NAMESPACE_END yosys-yosys-0.33/backends/btor/000077500000000000000000000000001447554276300165525ustar00rootroot00000000000000yosys-yosys-0.33/backends/btor/.gitignore000066400000000000000000000000211447554276300205330ustar00rootroot00000000000000/test_cells.tmp/ yosys-yosys-0.33/backends/btor/Makefile.inc000066400000000000000000000000371447554276300207620ustar00rootroot00000000000000 OBJS += backends/btor/btor.o yosys-yosys-0.33/backends/btor/btor.cc000066400000000000000000001303151447554276300200320ustar00rootroot00000000000000/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ // [[CITE]] Btor2 , BtorMC and Boolector 3.0 // Aina Niemetz, Mathias Preiner, C. Wolf, Armin Biere // Computer Aided Verification - 30th International Conference, CAV 2018 // https://cs.stanford.edu/people/niemetz/publication/2018/niemetzpreinerwolfbiere-cav18/ #include "kernel/rtlil.h" #include "kernel/register.h" #include "kernel/sigtools.h" #include "kernel/celltypes.h" #include "kernel/log.h" #include "kernel/mem.h" #include "kernel/json.h" #include "kernel/yw.h" #include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct BtorWorker { std::ostream &f; SigMap sigmap; RTLIL::Module *module; bool verbose; bool single_bad; bool cover_mode; bool print_internal_names; int next_nid = 1; int initstate_nid = -1; // => dict sorts_bv; // (, ) => dict, int> sorts_mem; // SigBit => (, ) dict> bit_nid; // => dict nid_width; // SigSpec => dict sig_nid; // bit to driving cell dict bit_cell; // nids for constants dict consts; // ff inputs that need to be evaluated (, ) vector> ff_todo; vector> mem_todo; pool cell_recursion_guard; vector bad_properties; dict initbits; pool statewires; pool srcsymbols; vector memories; dict mem_cells; string indent, info_filename; vector info_lines; dict info_clocks; struct ywmap_btor_sig { SigSpec sig; Cell *cell = nullptr; ywmap_btor_sig(const SigSpec &sig) : sig(sig) {} ywmap_btor_sig(Cell *cell) : cell(cell) {} }; vector ywmap_inputs; vector ywmap_states; dict ywmap_clock_bits; dict ywmap_clock_inputs; PrettyJson ywmap_json; void btorf(const char *fmt, ...) YS_ATTRIBUTE(format(printf, 2, 3)) { va_list ap; va_start(ap, fmt); f << indent << vstringf(fmt, ap); va_end(ap); } void infof(const char *fmt, ...) YS_ATTRIBUTE(format(printf, 2, 3)) { va_list ap; va_start(ap, fmt); info_lines.push_back(vstringf(fmt, ap)); va_end(ap); } template string getinfo(T *obj, bool srcsym = false) { string infostr = log_id(obj); if (!srcsym && !print_internal_names && infostr[0] == '$') return ""; if (obj->attributes.count(ID::src)) { string src = obj->attributes.at(ID::src).decode_string().c_str(); if (srcsym && infostr[0] == '$') { std::replace(src.begin(), src.end(), ' ', '_'); if (srcsymbols.count(src) || module->count_id("\\" + src)) { for (int i = 1;; i++) { string s = stringf("%s-%d", src.c_str(), i); if (!srcsymbols.count(s) && !module->count_id("\\" + s)) { src = s; break; } } } srcsymbols.insert(src); infostr = src; } else { infostr += " ; " + src; } } return " " + infostr; } void ywmap_state(const SigSpec &sig) { if (ywmap_json.active()) ywmap_states.emplace_back(sig); } void ywmap_state(Cell *cell) { if (ywmap_json.active()) ywmap_states.emplace_back(cell); } void ywmap_input(const SigSpec &sig) { if (ywmap_json.active()) ywmap_inputs.emplace_back(sig); } void emit_ywmap_btor_sig(const ywmap_btor_sig &btor_sig) { if (btor_sig.cell == nullptr) { if (btor_sig.sig.empty()) { ywmap_json.value(nullptr); return; } ywmap_json.begin_array(); ywmap_json.compact(); for (auto &chunk : btor_sig.sig.chunks()) { log_assert(chunk.is_wire()); ywmap_json.begin_object(); ywmap_json.entry("path", witness_path(chunk.wire)); ywmap_json.entry("width", chunk.width); ywmap_json.entry("offset", chunk.offset); ywmap_json.end_object(); } ywmap_json.end_array(); } else { ywmap_json.begin_object(); ywmap_json.compact(); ywmap_json.entry("path", witness_path(btor_sig.cell)); Mem *mem = mem_cells[btor_sig.cell]; ywmap_json.entry("width", mem->width); ywmap_json.entry("size", mem->size); ywmap_json.end_object(); } } void btorf_push(const string &id) { if (verbose) { f << indent << stringf(" ; begin %s\n", id.c_str()); indent += " "; } } void btorf_pop(const string &id) { if (verbose) { indent = indent.substr(4); f << indent << stringf(" ; end %s\n", id.c_str()); } } int get_bv_sid(int width) { if (sorts_bv.count(width) == 0) { int nid = next_nid++; btorf("%d sort bitvec %d\n", nid, width); sorts_bv[width] = nid; } return sorts_bv.at(width); } int get_mem_sid(int abits, int dbits) { pair key(abits, dbits); if (sorts_mem.count(key) == 0) { int addr_sid = get_bv_sid(abits); int data_sid = get_bv_sid(dbits); int nid = next_nid++; btorf("%d sort array %d %d\n", nid, addr_sid, data_sid); sorts_mem[key] = nid; } return sorts_mem.at(key); } void add_nid_sig(int nid, const SigSpec &sig) { if (verbose) f << indent << stringf("; %d %s\n", nid, log_signal(sig)); for (int i = 0; i < GetSize(sig); i++) bit_nid[sig[i]] = make_pair(nid, i); sig_nid[sig] = nid; nid_width[nid] = GetSize(sig); } void export_cell(Cell *cell) { if (cell_recursion_guard.count(cell)) { string cell_list; for (auto c : cell_recursion_guard) cell_list += stringf("\n %s", log_id(c)); log_error("Found topological loop while processing cell %s. Active cells:%s\n", log_id(cell), cell_list.c_str()); } cell_recursion_guard.insert(cell); btorf_push(log_id(cell)); if (cell->type.in(ID($add), ID($sub), ID($mul), ID($and), ID($or), ID($xor), ID($xnor), ID($shl), ID($sshl), ID($shr), ID($sshr), ID($shift), ID($shiftx), ID($concat), ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), ID($_XOR_), ID($_XNOR_))) { string btor_op; if (cell->type == ID($add)) btor_op = "add"; if (cell->type == ID($sub)) btor_op = "sub"; if (cell->type == ID($mul)) btor_op = "mul"; if (cell->type.in(ID($shl), ID($sshl))) btor_op = "sll"; if (cell->type == ID($shr)) btor_op = "srl"; if (cell->type == ID($sshr)) btor_op = "sra"; if (cell->type.in(ID($shift), ID($shiftx))) btor_op = "shift"; if (cell->type.in(ID($and), ID($_AND_))) btor_op = "and"; if (cell->type.in(ID($or), ID($_OR_))) btor_op = "or"; if (cell->type.in(ID($xor), ID($_XOR_))) btor_op = "xor"; if (cell->type == ID($concat)) btor_op = "concat"; if (cell->type == ID($_NAND_)) btor_op = "nand"; if (cell->type == ID($_NOR_)) btor_op = "nor"; if (cell->type.in(ID($xnor), ID($_XNOR_))) btor_op = "xnor"; log_assert(!btor_op.empty()); int width_ay = std::max(GetSize(cell->getPort(ID::A)), GetSize(cell->getPort(ID::Y))); int width = std::max(width_ay, GetSize(cell->getPort(ID::B))); bool a_signed = cell->hasParam(ID::A_SIGNED) ? cell->getParam(ID::A_SIGNED).as_bool() : false; bool b_signed = cell->hasParam(ID::B_SIGNED) ? cell->getParam(ID::B_SIGNED).as_bool() : false; if (btor_op == "shift" && !b_signed) btor_op = "srl"; if (cell->type.in(ID($shl), ID($sshl), ID($shr), ID($sshr))) b_signed = false; if (cell->type == ID($sshr) && !a_signed) btor_op = "srl"; int sid = get_bv_sid(width); int nid; int nid_a; if (cell->type.in(ID($shl), ID($shr), ID($shift), ID($shiftx)) && a_signed && width_ay < width) { // sign-extend A up to the width of Y int nid_a_padded = get_sig_nid(cell->getPort(ID::A), width_ay, a_signed); // zero-extend the rest int zeroes = get_sig_nid(Const(0, width-width_ay)); nid_a = next_nid++; btorf("%d concat %d %d %d\n", nid_a, sid, zeroes, nid_a_padded); } else { nid_a = get_sig_nid(cell->getPort(ID::A), width, a_signed); } int nid_b = get_sig_nid(cell->getPort(ID::B), width, b_signed); if (btor_op == "shift") { int nid_r = next_nid++; btorf("%d srl %d %d %d\n", nid_r, sid, nid_a, nid_b); int nid_b_neg = next_nid++; btorf("%d neg %d %d\n", nid_b_neg, sid, nid_b); int nid_l = next_nid++; btorf("%d sll %d %d %d\n", nid_l, sid, nid_a, nid_b_neg); int sid_bit = get_bv_sid(1); int nid_zero = get_sig_nid(Const(0, width)); int nid_b_ltz = next_nid++; btorf("%d slt %d %d %d\n", nid_b_ltz, sid_bit, nid_b, nid_zero); nid = next_nid++; btorf("%d ite %d %d %d %d%s\n", nid, sid, nid_b_ltz, nid_l, nid_r, getinfo(cell).c_str()); } else { nid = next_nid++; btorf("%d %s %d %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str()); } SigSpec sig = sigmap(cell->getPort(ID::Y)); if (GetSize(sig) < width) { int sid = get_bv_sid(GetSize(sig)); int nid2 = next_nid++; btorf("%d slice %d %d %d 0\n", nid2, sid, nid, GetSize(sig)-1); nid = nid2; } add_nid_sig(nid, sig); goto okay; } if (cell->type.in(ID($div), ID($mod), ID($modfloor))) { bool a_signed = cell->hasParam(ID::A_SIGNED) ? cell->getParam(ID::A_SIGNED).as_bool() : false; bool b_signed = cell->hasParam(ID::B_SIGNED) ? cell->getParam(ID::B_SIGNED).as_bool() : false; string btor_op; if (cell->type == ID($div)) btor_op = "div"; // "rem" = truncating modulo if (cell->type == ID($mod)) btor_op = "rem"; // "mod" = flooring modulo if (cell->type == ID($modfloor)) { // "umod" doesn't exist because it's the same as "urem" btor_op = a_signed || b_signed ? "mod" : "rem"; } log_assert(!btor_op.empty()); int width = GetSize(cell->getPort(ID::Y)); width = std::max(width, GetSize(cell->getPort(ID::A))); width = std::max(width, GetSize(cell->getPort(ID::B))); int nid_a = get_sig_nid(cell->getPort(ID::A), width, a_signed); int nid_b = get_sig_nid(cell->getPort(ID::B), width, b_signed); int sid = get_bv_sid(width); int nid = next_nid++; btorf("%d %c%s %d %d %d%s\n", nid, a_signed || b_signed ? 's' : 'u', btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str()); SigSpec sig = sigmap(cell->getPort(ID::Y)); if (GetSize(sig) < width) { int sid = get_bv_sid(GetSize(sig)); int nid2 = next_nid++; btorf("%d slice %d %d %d 0\n", nid2, sid, nid, GetSize(sig)-1); nid = nid2; } add_nid_sig(nid, sig); goto okay; } if (cell->type.in(ID($_ANDNOT_), ID($_ORNOT_))) { int sid = get_bv_sid(1); int nid_a = get_sig_nid(cell->getPort(ID::A)); int nid_b = get_sig_nid(cell->getPort(ID::B)); int nid1 = next_nid++; int nid2 = next_nid++; if (cell->type == ID($_ANDNOT_)) { btorf("%d not %d %d\n", nid1, sid, nid_b); btorf("%d and %d %d %d%s\n", nid2, sid, nid_a, nid1, getinfo(cell).c_str()); } if (cell->type == ID($_ORNOT_)) { btorf("%d not %d %d\n", nid1, sid, nid_b); btorf("%d or %d %d %d%s\n", nid2, sid, nid_a, nid1, getinfo(cell).c_str()); } SigSpec sig = sigmap(cell->getPort(ID::Y)); add_nid_sig(nid2, sig); goto okay; } if (cell->type.in(ID($_OAI3_), ID($_AOI3_))) { int sid = get_bv_sid(1); int nid_a = get_sig_nid(cell->getPort(ID::A)); int nid_b = get_sig_nid(cell->getPort(ID::B)); int nid_c = get_sig_nid(cell->getPort(ID::C)); int nid1 = next_nid++; int nid2 = next_nid++; int nid3 = next_nid++; if (cell->type == ID($_OAI3_)) { btorf("%d or %d %d %d\n", nid1, sid, nid_a, nid_b); btorf("%d and %d %d %d\n", nid2, sid, nid1, nid_c); btorf("%d not %d %d%s\n", nid3, sid, nid2, getinfo(cell).c_str()); } if (cell->type == ID($_AOI3_)) { btorf("%d and %d %d %d\n", nid1, sid, nid_a, nid_b); btorf("%d or %d %d %d\n", nid2, sid, nid1, nid_c); btorf("%d not %d %d%s\n", nid3, sid, nid2, getinfo(cell).c_str()); } SigSpec sig = sigmap(cell->getPort(ID::Y)); add_nid_sig(nid3, sig); goto okay; } if (cell->type.in(ID($_OAI4_), ID($_AOI4_))) { int sid = get_bv_sid(1); int nid_a = get_sig_nid(cell->getPort(ID::A)); int nid_b = get_sig_nid(cell->getPort(ID::B)); int nid_c = get_sig_nid(cell->getPort(ID::C)); int nid_d = get_sig_nid(cell->getPort(ID::D)); int nid1 = next_nid++; int nid2 = next_nid++; int nid3 = next_nid++; int nid4 = next_nid++; if (cell->type == ID($_OAI4_)) { btorf("%d or %d %d %d\n", nid1, sid, nid_a, nid_b); btorf("%d or %d %d %d\n", nid2, sid, nid_c, nid_d); btorf("%d and %d %d %d\n", nid3, sid, nid1, nid2); btorf("%d not %d %d%s\n", nid4, sid, nid3, getinfo(cell).c_str()); } if (cell->type == ID($_AOI4_)) { btorf("%d and %d %d %d\n", nid1, sid, nid_a, nid_b); btorf("%d and %d %d %d\n", nid2, sid, nid_c, nid_d); btorf("%d or %d %d %d\n", nid3, sid, nid1, nid2); btorf("%d not %d %d%s\n", nid4, sid, nid3, getinfo(cell).c_str()); } SigSpec sig = sigmap(cell->getPort(ID::Y)); add_nid_sig(nid4, sig); goto okay; } if (cell->type.in(ID($lt), ID($le), ID($eq), ID($eqx), ID($ne), ID($nex), ID($ge), ID($gt))) { string btor_op; if (cell->type == ID($lt)) btor_op = "lt"; if (cell->type == ID($le)) btor_op = "lte"; if (cell->type.in(ID($eq), ID($eqx))) btor_op = "eq"; if (cell->type.in(ID($ne), ID($nex))) btor_op = "neq"; if (cell->type == ID($ge)) btor_op = "gte"; if (cell->type == ID($gt)) btor_op = "gt"; log_assert(!btor_op.empty()); int width = 1; width = std::max(width, GetSize(cell->getPort(ID::A))); width = std::max(width, GetSize(cell->getPort(ID::B))); bool a_signed = cell->hasParam(ID::A_SIGNED) ? cell->getParam(ID::A_SIGNED).as_bool() : false; bool b_signed = cell->hasParam(ID::B_SIGNED) ? cell->getParam(ID::B_SIGNED).as_bool() : false; int sid = get_bv_sid(1); int nid_a = get_sig_nid(cell->getPort(ID::A), width, a_signed); int nid_b = get_sig_nid(cell->getPort(ID::B), width, b_signed); int nid = next_nid++; if (cell->type.in(ID($lt), ID($le), ID($ge), ID($gt))) { btorf("%d %c%s %d %d %d%s\n", nid, a_signed || b_signed ? 's' : 'u', btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str()); } else { btorf("%d %s %d %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str()); } SigSpec sig = sigmap(cell->getPort(ID::Y)); if (GetSize(sig) > 1) { int sid = get_bv_sid(GetSize(sig)); int nid2 = next_nid++; btorf("%d uext %d %d %d\n", nid2, sid, nid, GetSize(sig) - 1); nid = nid2; } add_nid_sig(nid, sig); goto okay; } if (cell->type.in(ID($not), ID($neg), ID($_NOT_), ID($pos))) { string btor_op; if (cell->type.in(ID($not), ID($_NOT_))) btor_op = "not"; if (cell->type == ID($neg)) btor_op = "neg"; int width = std::max(GetSize(cell->getPort(ID::A)), GetSize(cell->getPort(ID::Y))); bool a_signed = cell->hasParam(ID::A_SIGNED) ? cell->getParam(ID::A_SIGNED).as_bool() : false; int nid_a = get_sig_nid(cell->getPort(ID::A), width, a_signed); SigSpec sig = sigmap(cell->getPort(ID::Y)); // the $pos cell just passes through, all other cells need an actual operation applied int nid = nid_a; if (cell->type != ID($pos)) { log_assert(!btor_op.empty()); int sid = get_bv_sid(width); nid = next_nid++; btorf("%d %s %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, getinfo(cell).c_str()); } if (GetSize(sig) < width) { int sid = get_bv_sid(GetSize(sig)); int nid2 = next_nid++; btorf("%d slice %d %d %d 0\n", nid2, sid, nid, GetSize(sig)-1); nid = nid2; } add_nid_sig(nid, sig); goto okay; } if (cell->type.in(ID($logic_and), ID($logic_or), ID($logic_not))) { string btor_op; if (cell->type == ID($logic_and)) btor_op = "and"; if (cell->type == ID($logic_or)) btor_op = "or"; if (cell->type == ID($logic_not)) btor_op = "not"; log_assert(!btor_op.empty()); int sid = get_bv_sid(1); int nid_a = get_sig_nid(cell->getPort(ID::A)); int nid_b = btor_op != "not" ? get_sig_nid(cell->getPort(ID::B)) : 0; if (GetSize(cell->getPort(ID::A)) > 1) { int nid_red_a = next_nid++; btorf("%d redor %d %d\n", nid_red_a, sid, nid_a); nid_a = nid_red_a; } if (btor_op != "not" && GetSize(cell->getPort(ID::B)) > 1) { int nid_red_b = next_nid++; btorf("%d redor %d %d\n", nid_red_b, sid, nid_b); nid_b = nid_red_b; } int nid = next_nid++; if (btor_op != "not") btorf("%d %s %d %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str()); else btorf("%d %s %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, getinfo(cell).c_str()); SigSpec sig = sigmap(cell->getPort(ID::Y)); if (GetSize(sig) > 1) { int sid = get_bv_sid(GetSize(sig)); int zeros_nid = get_sig_nid(Const(0, GetSize(sig)-1)); int nid2 = next_nid++; btorf("%d concat %d %d %d\n", nid2, sid, zeros_nid, nid); nid = nid2; } add_nid_sig(nid, sig); goto okay; } if (cell->type.in(ID($reduce_and), ID($reduce_or), ID($reduce_bool), ID($reduce_xor), ID($reduce_xnor))) { string btor_op; if (cell->type == ID($reduce_and)) btor_op = "redand"; if (cell->type.in(ID($reduce_or), ID($reduce_bool))) btor_op = "redor"; if (cell->type.in(ID($reduce_xor), ID($reduce_xnor))) btor_op = "redxor"; log_assert(!btor_op.empty()); int sid = get_bv_sid(1); int nid_a = get_sig_nid(cell->getPort(ID::A)); int nid = next_nid++; if (cell->type == ID($reduce_xnor)) { int nid2 = next_nid++; btorf("%d %s %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, getinfo(cell).c_str()); btorf("%d not %d %d\n", nid2, sid, nid); nid = nid2; } else { btorf("%d %s %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, getinfo(cell).c_str()); } SigSpec sig = sigmap(cell->getPort(ID::Y)); if (GetSize(sig) > 1) { int sid = get_bv_sid(GetSize(sig)); int zeros_nid = get_sig_nid(Const(0, GetSize(sig)-1)); int nid2 = next_nid++; btorf("%d concat %d %d %d\n", nid2, sid, zeros_nid, nid); nid = nid2; } add_nid_sig(nid, sig); goto okay; } if (cell->type.in(ID($mux), ID($_MUX_), ID($_NMUX_))) { SigSpec sig_a = sigmap(cell->getPort(ID::A)); SigSpec sig_b = sigmap(cell->getPort(ID::B)); SigSpec sig_s = sigmap(cell->getPort(ID::S)); SigSpec sig_y = sigmap(cell->getPort(ID::Y)); int nid_a = get_sig_nid(sig_a); int nid_b = get_sig_nid(sig_b); int nid_s = get_sig_nid(sig_s); int sid = get_bv_sid(GetSize(sig_y)); int nid = next_nid++; if (cell->type == ID($_NMUX_)) { int tmp = nid; nid = next_nid++; btorf("%d ite %d %d %d %d\n", tmp, sid, nid_s, nid_b, nid_a); btorf("%d not %d %d%s\n", nid, sid, tmp, getinfo(cell).c_str()); } else { btorf("%d ite %d %d %d %d%s\n", nid, sid, nid_s, nid_b, nid_a, getinfo(cell).c_str()); } add_nid_sig(nid, sig_y); goto okay; } if (cell->type == ID($pmux)) { SigSpec sig_a = sigmap(cell->getPort(ID::A)); SigSpec sig_b = sigmap(cell->getPort(ID::B)); SigSpec sig_s = sigmap(cell->getPort(ID::S)); SigSpec sig_y = sigmap(cell->getPort(ID::Y)); int width = GetSize(sig_a); int sid = get_bv_sid(width); int nid = get_sig_nid(sig_a); for (int i = 0; i < GetSize(sig_s); i++) { int nid_b = get_sig_nid(sig_b.extract(i*width, width)); int nid_s = get_sig_nid(sig_s.extract(i)); int nid2 = next_nid++; if (i == GetSize(sig_s)-1) btorf("%d ite %d %d %d %d%s\n", nid2, sid, nid_s, nid_b, nid, getinfo(cell).c_str()); else btorf("%d ite %d %d %d %d\n", nid2, sid, nid_s, nid_b, nid); nid = nid2; } add_nid_sig(nid, sig_y); goto okay; } if (cell->type.in(ID($dff), ID($ff), ID($anyinit), ID($_DFF_P_), ID($_DFF_N), ID($_FF_))) { SigSpec sig_d = sigmap(cell->getPort(ID::D)); SigSpec sig_q = sigmap(cell->getPort(ID::Q)); if ((!info_filename.empty() || ywmap_json.active()) && cell->type.in(ID($dff), ID($_DFF_P_), ID($_DFF_N_))) { SigSpec sig_c = sigmap(cell->getPort(cell->type == ID($dff) ? ID::CLK : ID::C)); int nid = get_sig_nid(sig_c); bool negedge = false; if (cell->type == ID($_DFF_N_)) negedge = true; if (cell->type == ID($dff) && !cell->getParam(ID::CLK_POLARITY).as_bool()) negedge = true; if (!info_filename.empty()) info_clocks[nid] |= negedge ? 2 : 1; if (ywmap_json.active()) ywmap_clock_bits[sig_c] |= negedge ? 2 : 1; } IdString symbol; if (sig_q.is_wire()) { Wire *w = sig_q.as_wire(); if (w->port_id == 0) { statewires.insert(w); symbol = w->name; } } 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); else initval.bits.push_back(State::Sx); int nid_init_val = -1; if (!initval.is_fully_undef()) nid_init_val = get_sig_nid(initval, -1, false, true); int sid = get_bv_sid(GetSize(sig_q)); int nid = next_nid++; if (symbol.empty() || (!print_internal_names && symbol[0] == '$')) btorf("%d state %d\n", nid, sid); else btorf("%d state %d %s\n", nid, sid, log_id(symbol)); if (cell->get_bool_attribute(ID(clk2fflogic))) ywmap_state(cell->getPort(ID::D)); // For a clk2fflogic FF the named signal is the D input not the Q output else ywmap_state(sig_q); if (nid_init_val >= 0) { int nid_init = next_nid++; if (verbose) btorf("; initval = %s\n", log_signal(initval)); btorf("%d init %d %d %d\n", nid_init, sid, nid, nid_init_val); } ff_todo.push_back(make_pair(nid, cell)); add_nid_sig(nid, sig_q); goto okay; } if (cell->type.in(ID($anyconst), ID($anyseq))) { SigSpec sig_y = sigmap(cell->getPort(ID::Y)); int sid = get_bv_sid(GetSize(sig_y)); int nid = next_nid++; btorf("%d state %d%s\n", nid, sid, getinfo(cell).c_str()); ywmap_state(sig_y); if (cell->type == ID($anyconst)) { int nid2 = next_nid++; btorf("%d next %d %d %d\n", nid2, sid, nid, nid); } add_nid_sig(nid, sig_y); goto okay; } if (cell->type == ID($initstate)) { SigSpec sig_y = sigmap(cell->getPort(ID::Y)); if (initstate_nid < 0) { int sid = get_bv_sid(1); int one_nid = get_sig_nid(State::S1); int zero_nid = get_sig_nid(State::S0); initstate_nid = next_nid++; btorf("%d state %d%s\n", initstate_nid, sid, getinfo(cell).c_str()); btorf("%d init %d %d %d\n", next_nid++, sid, initstate_nid, one_nid); btorf("%d next %d %d %d\n", next_nid++, sid, initstate_nid, zero_nid); ywmap_state(sig_y); } add_nid_sig(initstate_nid, sig_y); goto okay; } if (cell->is_mem_cell()) { Mem *mem = mem_cells[cell]; int abits = ceil_log2(mem->size); bool asyncwr = false; bool syncwr = false; for (auto &port : mem->wr_ports) { if (port.clk_enable) syncwr = true; else asyncwr = true; } if (asyncwr && syncwr) log_error("Memory %s.%s has mixed async/sync write ports.\n", log_id(module), log_id(mem->memid)); for (auto &port : mem->rd_ports) { if (port.clk_enable) log_error("Memory %s.%s has sync read ports. Please use memory_nordff to convert them first.\n", log_id(module), log_id(mem->memid)); } int data_sid = get_bv_sid(mem->width); int bool_sid = get_bv_sid(1); int sid = get_mem_sid(abits, mem->width); int nid_init_val = -1; if (!mem->inits.empty()) { Const initdata = mem->get_init_data(); bool constword = true; Const firstword = initdata.extract(0, mem->width); for (int i = 1; i < mem->size; i++) { Const thisword = initdata.extract(i*mem->width, mem->width); if (thisword != firstword) { constword = false; break; } } if (constword) { if (verbose) btorf("; initval = %s\n", log_signal(firstword)); nid_init_val = get_sig_nid(firstword, -1, false, true); } else { nid_init_val = next_nid++; btorf("%d state %d\n", nid_init_val, sid); ywmap_state(nullptr); for (int i = 0; i < mem->size; i++) { Const thisword = initdata.extract(i*mem->width, mem->width); if (thisword.is_fully_undef()) continue; Const thisaddr(i, abits); int nid_thisword = get_sig_nid(thisword, -1, false, true); int nid_thisaddr = get_sig_nid(thisaddr, -1, false, true); int last_nid_init_val = nid_init_val; nid_init_val = next_nid++; if (verbose) btorf("; initval[%d] = %s\n", i, log_signal(thisword)); btorf("%d write %d %d %d %d\n", nid_init_val, sid, last_nid_init_val, nid_thisaddr, nid_thisword); } } } int nid = next_nid++; int nid_head = nid; if (mem->memid[0] == '$') btorf("%d state %d\n", nid, sid); else btorf("%d state %d %s\n", nid, sid, log_id(mem->memid)); ywmap_state(cell); if (nid_init_val >= 0) { int nid_init = next_nid++; btorf("%d init %d %d %d\n", nid_init, sid, nid, nid_init_val); } if (asyncwr) { for (auto &port : mem->wr_ports) { SigSpec wa = port.addr; wa.extend_u0(abits); int wa_nid = get_sig_nid(wa); int wd_nid = get_sig_nid(port.data); int we_nid = get_sig_nid(port.en); int nid2 = next_nid++; btorf("%d read %d %d %d\n", nid2, data_sid, nid_head, wa_nid); int nid3 = next_nid++; btorf("%d not %d %d\n", nid3, data_sid, we_nid); int nid4 = next_nid++; btorf("%d and %d %d %d\n", nid4, data_sid, nid2, nid3); int nid5 = next_nid++; btorf("%d and %d %d %d\n", nid5, data_sid, wd_nid, we_nid); int nid6 = next_nid++; btorf("%d or %d %d %d\n", nid6, data_sid, nid5, nid4); int nid7 = next_nid++; btorf("%d write %d %d %d %d\n", nid7, sid, nid_head, wa_nid, nid6); int nid8 = next_nid++; btorf("%d redor %d %d\n", nid8, bool_sid, we_nid); int nid9 = next_nid++; btorf("%d ite %d %d %d %d\n", nid9, sid, nid8, nid7, nid_head); nid_head = nid9; } } for (auto &port : mem->rd_ports) { SigSpec ra = port.addr; ra.extend_u0(abits); int ra_nid = get_sig_nid(ra); int rd_nid = next_nid++; btorf("%d read %d %d %d\n", rd_nid, data_sid, nid_head, ra_nid); add_nid_sig(rd_nid, port.data); } if (!asyncwr) { mem_todo.push_back(make_pair(nid, mem)); } else { int nid2 = next_nid++; btorf("%d next %d %d %d\n", nid2, sid, nid, nid_head); } goto okay; } if (cell->type.in(ID($dffe), ID($sdff), ID($sdffe), ID($sdffce)) || cell->type.str().substr(0, 6) == "$_SDFF" || (cell->type.str().substr(0, 6) == "$_DFFE" && cell->type.str().size() == 10)) { log_error("Unsupported cell type %s for cell %s.%s -- please run `dffunmap` before `write_btor`.\n", log_id(cell->type), log_id(module), log_id(cell)); } if (cell->type.in(ID($adff), ID($adffe), ID($aldff), ID($aldffe), ID($dffsr), ID($dffsre)) || cell->type.str().substr(0, 5) == "$_DFF" || cell->type.str().substr(0, 7) == "$_ALDFF") { log_error("Unsupported cell type %s for cell %s.%s -- please run `async2sync; dffunmap` or `clk2fflogic` before `write_btor`.\n", log_id(cell->type), log_id(module), log_id(cell)); } if (cell->type.in(ID($sr), ID($dlatch), ID($adlatch), ID($dlatchsr)) || cell->type.str().substr(0, 8) == "$_DLATCH" || cell->type.str().substr(0, 5) == "$_SR_") { log_error("Unsupported cell type %s for cell %s.%s -- please run `clk2fflogic` before `write_btor`.\n", log_id(cell->type), log_id(module), log_id(cell)); } log_error("Unsupported cell type %s for cell %s.%s.\n", log_id(cell->type), log_id(module), log_id(cell)); okay: btorf_pop(log_id(cell)); cell_recursion_guard.erase(cell); } int get_sig_nid(SigSpec sig, int to_width = -1, bool is_signed = false, bool is_init = false) { int nid = -1; sigmap.apply(sig); for (auto bit : sig) if (bit == State::Sx) goto has_undef_bits; if (0) { has_undef_bits: SigSpec sig_mask_undef, sig_noundef; int first_undef = -1; for (int i = 0; i < GetSize(sig); i++) if (sig[i] == State::Sx) { if (first_undef < 0) first_undef = i; sig_mask_undef.append(State::S1); sig_noundef.append(State::S0); } else { sig_mask_undef.append(State::S0); sig_noundef.append(sig[i]); } if (to_width < 0 || first_undef < to_width) { int sid = get_bv_sid(GetSize(sig)); int nid_input = next_nid++; if (is_init) { btorf("%d state %d\n", nid_input, sid); ywmap_state(sig); } else { btorf("%d input %d\n", nid_input, sid); ywmap_input(sig); } int nid_masked_input; if (sig_mask_undef.is_fully_ones()) { nid_masked_input = nid_input; } else { int nid_mask_undef = get_sig_nid(sig_mask_undef); nid_masked_input = next_nid++; btorf("%d and %d %d %d\n", nid_masked_input, sid, nid_input, nid_mask_undef); } if (sig_noundef.is_fully_zero()) { nid = nid_masked_input; } else { int nid_noundef = get_sig_nid(sig_noundef); nid = next_nid++; btorf("%d or %d %d %d\n", nid, sid, nid_masked_input, nid_noundef); } goto extend_or_trim; } sig = sig_noundef; } if (sig_nid.count(sig) == 0) { // , vector> nidbits; // collect all bits for (int i = 0; i < GetSize(sig); i++) { SigBit bit = sig[i]; if (bit_nid.count(bit) == 0) { if (bit.wire == nullptr) { 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); if (consts.count(c) == 0) { int sid = get_bv_sid(GetSize(c)); int nid = next_nid++; btorf("%d const %d %s\n", nid, sid, c.as_string().c_str()); consts[c] = nid; nid_width[nid] = GetSize(c); } int nid = consts.at(c); for (int j = 0; j < GetSize(c); j++) nidbits.push_back(make_pair(nid, j)); i += GetSize(c)-1; continue; } else { if (bit_cell.count(bit) == 0) { SigSpec s = bit; while (i+GetSize(s) < GetSize(sig) && sig[i+GetSize(s)].wire != nullptr && bit_cell.count(sig[i+GetSize(s)]) == 0) s.append(sig[i+GetSize(s)]); log_warning("No driver for signal %s.\n", log_signal(s)); int sid = get_bv_sid(GetSize(s)); int nid = next_nid++; btorf("%d input %d\n", nid, sid); ywmap_input(s); nid_width[nid] = GetSize(s); for (int j = 0; j < GetSize(s); j++) nidbits.push_back(make_pair(nid, j)); i += GetSize(s)-1; continue; } else { export_cell(bit_cell.at(bit)); log_assert(bit_nid.count(bit)); } } } nidbits.push_back(bit_nid.at(bit)); } int width = 0; int nid = -1; // group bits and emit slice-concat chain for (int i = 0; i < GetSize(nidbits); i++) { int nid2 = nidbits[i].first; int lower = nidbits[i].second; int upper = lower; while (i+1 < GetSize(nidbits) && nidbits[i+1].first == nidbits[i].first && nidbits[i+1].second == nidbits[i].second+1) upper++, i++; int nid3 = nid2; if (lower != 0 || upper+1 != nid_width.at(nid2)) { int sid = get_bv_sid(upper-lower+1); nid3 = next_nid++; btorf("%d slice %d %d %d %d\n", nid3, sid, nid2, upper, lower); } int nid4 = nid3; if (nid >= 0) { int sid = get_bv_sid(width+upper-lower+1); nid4 = next_nid++; btorf("%d concat %d %d %d\n", nid4, sid, nid3, nid); } width += upper-lower+1; nid = nid4; } sig_nid[sig] = nid; nid_width[nid] = width; } nid = sig_nid.at(sig); extend_or_trim: if (to_width >= 0 && to_width != GetSize(sig)) { if (to_width < GetSize(sig)) { int sid = get_bv_sid(to_width); int nid2 = next_nid++; btorf("%d slice %d %d %d 0\n", nid2, sid, nid, to_width-1); nid = nid2; } else { int sid = get_bv_sid(to_width); int nid2 = next_nid++; btorf("%d %s %d %d %d\n", nid2, is_signed ? "sext" : "uext", sid, nid, to_width - GetSize(sig)); nid = nid2; } } return nid; } BtorWorker(std::ostream &f, RTLIL::Module *module, bool verbose, bool single_bad, bool cover_mode, bool print_internal_names, string info_filename, string ywmap_filename) : f(f), sigmap(module), module(module), verbose(verbose), single_bad(single_bad), cover_mode(cover_mode), print_internal_names(print_internal_names), info_filename(info_filename) { if (!info_filename.empty()) infof("name %s\n", log_id(module)); if (!ywmap_filename.empty()) ywmap_json.write_to_file(ywmap_filename); memories = Mem::get_all_memories(module); dict mem_dict; for (auto &mem : memories) { mem.narrow(); mem_dict[mem.memid] = &mem; } for (auto cell : module->cells()) if (cell->is_mem_cell()) mem_cells[cell] = mem_dict[cell->parameters.at(ID::MEMID).decode_string()]; btorf_push("inputs"); if (ywmap_json.active()) { for (auto wire : module->wires()) { auto gclk_attr = wire->attributes.find(ID::replaced_by_gclk); if (gclk_attr == wire->attributes.end()) continue; SigSpec sig = sigmap(wire); if (gclk_attr->second == State::S1) ywmap_clock_bits[sig] |= 1; else if (gclk_attr->second == State::S0) ywmap_clock_bits[sig] |= 2; } } for (auto wire : module->wires()) { if (wire->attributes.count(ID::init)) { Const attrval = wire->attributes.at(ID::init); for (int i = 0; i < GetSize(wire) && i < GetSize(attrval); i++) if (attrval[i] == State::S0 || attrval[i] == State::S1) initbits[sigmap(SigBit(wire, i))] = (attrval[i] == State::S1); } if (!wire->port_id || !wire->port_input) continue; SigSpec sig = sigmap(wire); int sid = get_bv_sid(GetSize(sig)); int nid = next_nid++; btorf("%d input %d%s\n", nid, sid, getinfo(wire).c_str()); ywmap_input(wire); add_nid_sig(nid, sig); if (!info_filename.empty()) { auto gclk_attr = wire->attributes.find(ID::replaced_by_gclk); if (gclk_attr != wire->attributes.end()) { if (gclk_attr->second == State::S1) info_clocks[nid] |= 1; else if (gclk_attr->second == State::S0) info_clocks[nid] |= 2; } } if (ywmap_json.active()) { for (int i = 0; i < GetSize(sig); i++) { auto input_bit = SigBit(wire, i); auto bit = sigmap(input_bit); if (!ywmap_clock_bits.count(bit)) continue; ywmap_clock_inputs[input_bit] = ywmap_clock_bits[bit]; } } } btorf_pop("inputs"); for (auto cell : module->cells()) for (auto &conn : cell->connections()) { if (!cell->output(conn.first)) continue; for (auto bit : sigmap(conn.second)) bit_cell[bit] = cell; } for (auto wire : module->wires()) { if (!wire->port_id || !wire->port_output) continue; btorf_push(stringf("output %s", log_id(wire))); int nid = get_sig_nid(wire); btorf("%d output %d%s\n", next_nid++, nid, getinfo(wire).c_str()); btorf_pop(stringf("output %s", log_id(wire))); } for (auto cell : module->cells()) { if (cell->type == ID($assume)) { btorf_push(log_id(cell)); int sid = get_bv_sid(1); int nid_a = get_sig_nid(cell->getPort(ID::A)); int nid_en = get_sig_nid(cell->getPort(ID::EN)); int nid_not_en = next_nid++; int nid_a_or_not_en = next_nid++; int nid = next_nid++; btorf("%d not %d %d\n", nid_not_en, sid, nid_en); btorf("%d or %d %d %d\n", nid_a_or_not_en, sid, nid_a, nid_not_en); btorf("%d constraint %d\n", nid, nid_a_or_not_en); btorf_pop(log_id(cell)); } if (cell->type == ID($assert)) { btorf_push(log_id(cell)); int sid = get_bv_sid(1); int nid_a = get_sig_nid(cell->getPort(ID::A)); int nid_en = get_sig_nid(cell->getPort(ID::EN)); int nid_not_a = next_nid++; int nid_en_and_not_a = next_nid++; btorf("%d not %d %d\n", nid_not_a, sid, nid_a); btorf("%d and %d %d %d\n", nid_en_and_not_a, sid, nid_en, nid_not_a); if (single_bad && !cover_mode) { bad_properties.push_back(nid_en_and_not_a); } else { if (cover_mode) { infof("bad %d%s\n", nid_en_and_not_a, getinfo(cell, true).c_str()); } else { int nid = next_nid++; btorf("%d bad %d%s\n", nid, nid_en_and_not_a, getinfo(cell, true).c_str()); } } btorf_pop(log_id(cell)); } if (cell->type == ID($cover) && cover_mode) { btorf_push(log_id(cell)); int sid = get_bv_sid(1); int nid_a = get_sig_nid(cell->getPort(ID::A)); int nid_en = get_sig_nid(cell->getPort(ID::EN)); int nid_en_and_a = next_nid++; btorf("%d and %d %d %d\n", nid_en_and_a, sid, nid_en, nid_a); if (single_bad) { bad_properties.push_back(nid_en_and_a); } else { int nid = next_nid++; btorf("%d bad %d%s\n", nid, nid_en_and_a, getinfo(cell, true).c_str()); } btorf_pop(log_id(cell)); } } for (auto wire : module->wires()) { if (wire->port_id || wire->name[0] == '$') continue; btorf_push(stringf("wire %s", log_id(wire))); int sid = get_bv_sid(GetSize(wire)); int nid = get_sig_nid(sigmap(wire)); if (statewires.count(wire)) continue; int this_nid = next_nid++; btorf("%d uext %d %d %d%s\n", this_nid, sid, nid, 0, getinfo(wire).c_str()); if (info_clocks.count(nid)) info_clocks[this_nid] |= info_clocks[nid]; btorf_pop(stringf("wire %s", log_id(wire))); continue; } while (!ff_todo.empty() || !mem_todo.empty()) { vector> todo; todo.swap(ff_todo); for (auto &it : todo) { int nid = it.first; Cell *cell = it.second; btorf_push(stringf("next %s", log_id(cell))); SigSpec sig = sigmap(cell->getPort(ID::D)); int nid_q = get_sig_nid(sig); int sid = get_bv_sid(GetSize(sig)); btorf("%d next %d %d %d%s\n", next_nid++, sid, nid, nid_q, getinfo(cell).c_str()); btorf_pop(stringf("next %s", log_id(cell))); } vector> mtodo; mtodo.swap(mem_todo); for (auto &it : mtodo) { int nid = it.first; Mem *mem = it.second; btorf_push(stringf("next %s", log_id(mem->memid))); int abits = ceil_log2(mem->size); int data_sid = get_bv_sid(mem->width); int bool_sid = get_bv_sid(1); int sid = get_mem_sid(abits, mem->width); int nid_head = nid; for (auto &port : mem->wr_ports) { SigSpec wa = port.addr; wa.extend_u0(abits); int wa_nid = get_sig_nid(wa); int wd_nid = get_sig_nid(port.data); int we_nid = get_sig_nid(port.en); int nid2 = next_nid++; btorf("%d read %d %d %d\n", nid2, data_sid, nid_head, wa_nid); int nid3 = next_nid++; btorf("%d not %d %d\n", nid3, data_sid, we_nid); int nid4 = next_nid++; btorf("%d and %d %d %d\n", nid4, data_sid, nid2, nid3); int nid5 = next_nid++; btorf("%d and %d %d %d\n", nid5, data_sid, wd_nid, we_nid); int nid6 = next_nid++; btorf("%d or %d %d %d\n", nid6, data_sid, nid5, nid4); int nid7 = next_nid++; btorf("%d write %d %d %d %d\n", nid7, sid, nid_head, wa_nid, nid6); int nid8 = next_nid++; btorf("%d redor %d %d\n", nid8, bool_sid, we_nid); int nid9 = next_nid++; btorf("%d ite %d %d %d %d\n", nid9, sid, nid8, nid7, nid_head); nid_head = nid9; } int nid2 = next_nid++; btorf("%d next %d %d %d%s\n", nid2, sid, nid, nid_head, (mem->cell ? getinfo(mem->cell) : getinfo(mem->mem)).c_str()); btorf_pop(stringf("next %s", log_id(mem->memid))); } } while (!bad_properties.empty()) { vector todo; bad_properties.swap(todo); int sid = get_bv_sid(1); int cursor = 0; while (cursor+1 < GetSize(todo)) { int nid_a = todo[cursor++]; int nid_b = todo[cursor++]; int nid = next_nid++; bad_properties.push_back(nid); btorf("%d or %d %d %d\n", nid, sid, nid_a, nid_b); } if (!bad_properties.empty()) { if (cursor < GetSize(todo)) bad_properties.push_back(todo[cursor++]); log_assert(cursor == GetSize(todo)); } else { int nid = next_nid++; log_assert(cursor == 0); log_assert(GetSize(todo) == 1); btorf("%d bad %d\n", nid, todo[cursor]); } } if (!info_filename.empty()) { for (auto &it : info_clocks) { switch (it.second) { case 1: infof("posedge %d\n", it.first); break; case 2: infof("negedge %d\n", it.first); break; case 3: infof("event %d\n", it.first); break; default: log_abort(); } } std::ofstream f; f.open(info_filename.c_str(), std::ofstream::trunc); if (f.fail()) log_error("Can't open file `%s' for writing: %s\n", info_filename.c_str(), strerror(errno)); for (auto &it : info_lines) f << it; f.close(); } if (ywmap_json.active()) { ywmap_json.begin_object(); ywmap_json.entry("version", "Yosys Witness BTOR map"); ywmap_json.entry("generator", yosys_version_str); ywmap_json.name("clocks"); ywmap_json.begin_array(); for (auto &entry : ywmap_clock_inputs) { if (entry.second != 1 && entry.second != 2) continue; log_assert(entry.first.is_wire()); ywmap_json.begin_object(); ywmap_json.compact(); ywmap_json.entry("path", witness_path(entry.first.wire)); ywmap_json.entry("offset", entry.first.offset); ywmap_json.entry("edge", entry.second == 1 ? "posedge" : "negedge"); ywmap_json.end_object(); } ywmap_json.end_array(); ywmap_json.name("inputs"); ywmap_json.begin_array(); for (auto &entry : ywmap_inputs) emit_ywmap_btor_sig(entry); ywmap_json.end_array(); ywmap_json.name("states"); ywmap_json.begin_array(); for (auto &entry : ywmap_states) emit_ywmap_btor_sig(entry); ywmap_json.end_array(); ywmap_json.end_object(); } } }; struct BtorBackend : public Backend { BtorBackend() : Backend("btor", "write design to BTOR file") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" write_btor [options] [filename]\n"); log("\n"); log("Write a BTOR description of the current design.\n"); log("\n"); log(" -v\n"); log(" Add comments and indentation to BTOR output file\n"); log("\n"); log(" -s\n"); log(" Output only a single bad property for all asserts\n"); log("\n"); log(" -c\n"); log(" Output cover properties using 'bad' statements instead of asserts\n"); log("\n"); log(" -i \n"); log(" Create additional info file with auxiliary information\n"); log("\n"); log(" -x\n"); log(" Output symbols for internal netnames (starting with '$')\n"); log("\n"); log(" -ywmap \n"); log(" Create a map file for conversion to and from Yosys witness traces\n"); log("\n"); } void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { bool verbose = false, single_bad = false, cover_mode = false, print_internal_names = false; string info_filename; string ywmap_filename; log_header(design, "Executing BTOR backend.\n"); log_push(); Pass::call(design, "bmuxmap"); Pass::call(design, "demuxmap"); Pass::call(design, "bwmuxmap"); log_pop(); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-v") { verbose = true; continue; } if (args[argidx] == "-s") { single_bad = true; continue; } if (args[argidx] == "-c") { cover_mode = true; continue; } if (args[argidx] == "-i" && argidx+1 < args.size()) { info_filename = args[++argidx]; continue; } if (args[argidx] == "-x") { print_internal_names = true; continue; } if (args[argidx] == "-ywmap" && argidx+1 < args.size()) { ywmap_filename = args[++argidx]; continue; } break; } extra_args(f, filename, args, argidx); RTLIL::Module *topmod = design->top_module(); if (topmod == nullptr) log_cmd_error("No top module found.\n"); *f << stringf("; BTOR description generated by %s for module %s.\n", yosys_version_str, log_id(topmod)); BtorWorker(*f, topmod, verbose, single_bad, cover_mode, print_internal_names, info_filename, ywmap_filename); *f << stringf("; end of yosys output\n"); } } BtorBackend; PRIVATE_NAMESPACE_END yosys-yosys-0.33/backends/btor/test_cells.sh000077500000000000000000000011531447554276300212520ustar00rootroot00000000000000#!/bin/bash set -ex rm -rf test_cells.tmp mkdir -p test_cells.tmp cd test_cells.tmp ../../../yosys -p 'test_cell -n 5 -w test all /$alu /$fa /$lcu /$lut /$sop /$macc /$mul /$div /$mod /$divfloor /$modfloor /$shiftx' for fn in test_*.il; do ../../../yosys -p " read_ilang $fn rename gold gate synth read_ilang $fn miter -equiv -make_assert -flatten gold gate main hierarchy -top main write_btor ${fn%.il}.btor " btormc -kmax 1 --trace-gen --stop-first -v ${fn%.il}.btor > ${fn%.il}.out if grep " SATISFIABLE" ${fn%.il}.out; then echo "Check failed for ${fn%.il}." exit 1 fi done echo "OK." yosys-yosys-0.33/backends/cxxrtl/000077500000000000000000000000001447554276300171305ustar00rootroot00000000000000yosys-yosys-0.33/backends/cxxrtl/Makefile.inc000066400000000000000000000000521447554276300213350ustar00rootroot00000000000000 OBJS += backends/cxxrtl/cxxrtl_backend.o yosys-yosys-0.33/backends/cxxrtl/cxxrtl.h000066400000000000000000001641261447554276300206370ustar00rootroot00000000000000/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2019-2020 whitequark * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted. * * 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 file is included by the designs generated with `write_cxxrtl`. It is not used in Yosys itself. // // The CXXRTL support library implements compile time specialized arbitrary width arithmetics, as well as provides // composite lvalues made out of bit slices and concatenations of lvalues. This allows the `write_cxxrtl` pass // to perform a straightforward translation of RTLIL structures to readable C++, relying on the C++ compiler // to unwrap the abstraction and generate efficient code. #ifndef CXXRTL_H #define CXXRTL_H #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef __has_attribute # define __has_attribute(x) 0 #endif // CXXRTL essentially uses the C++ compiler as a hygienic macro engine that feeds an instruction selector. // It generates a lot of specialized template functions with relatively large bodies that, when inlined // into the caller and (for those with loops) unrolled, often expose many new optimization opportunities. // Because of this, most of the CXXRTL runtime must be always inlined for best performance. #if __has_attribute(always_inline) #define CXXRTL_ALWAYS_INLINE inline __attribute__((__always_inline__)) #else #define CXXRTL_ALWAYS_INLINE inline #endif // Conversely, some functions in the generated code are extremely large yet very cold, with both of these // properties being extreme enough to confuse C++ compilers into spending pathological amounts of time // on a futile (the code becomes worse) attempt to optimize the least important parts of code. #if __has_attribute(optnone) #define CXXRTL_EXTREMELY_COLD __attribute__((__optnone__)) #elif __has_attribute(optimize) #define CXXRTL_EXTREMELY_COLD __attribute__((__optimize__(0))) #else #define CXXRTL_EXTREMELY_COLD #endif // CXXRTL uses assert() to check for C++ contract violations (which may result in e.g. undefined behavior // of the simulation code itself), and CXXRTL_ASSERT to check for RTL contract violations (which may at // most result in undefined simulation results). // // Though by default, CXXRTL_ASSERT() expands to assert(), it may be overridden e.g. when integrating // the simulation into another process that should survive violating RTL contracts. #ifndef CXXRTL_ASSERT #ifndef CXXRTL_NDEBUG #define CXXRTL_ASSERT(x) assert(x) #else #define CXXRTL_ASSERT(x) #endif #endif namespace cxxrtl { // All arbitrary-width values in CXXRTL are backed by arrays of unsigned integers called chunks. The chunk size // is the same regardless of the value width to simplify manipulating values via FFI interfaces, e.g. driving // and introspecting the simulation in Python. // // It is practical to use chunk sizes between 32 bits and platform register size because when arithmetics on // narrower integer types is legalized by the C++ compiler, it inserts code to clear the high bits of the register. // However, (a) most of our operations do not change those bits in the first place because of invariants that are // invisible to the compiler, (b) we often operate on non-power-of-2 values and have to clear the high bits anyway. // Therefore, using relatively wide chunks and clearing the high bits explicitly and only when we know they may be // clobbered results in simpler generated code. typedef uint32_t chunk_t; typedef uint64_t wide_chunk_t; template struct chunk_traits { static_assert(std::is_integral::value && std::is_unsigned::value, "chunk type must be an unsigned integral type"); using type = T; static constexpr size_t bits = std::numeric_limits::digits; static constexpr T mask = std::numeric_limits::max(); }; template struct expr_base; template struct value : public expr_base> { static constexpr size_t bits = Bits; using chunk = chunk_traits; static constexpr chunk::type msb_mask = (Bits % chunk::bits == 0) ? chunk::mask : chunk::mask >> (chunk::bits - (Bits % chunk::bits)); static constexpr size_t chunks = (Bits + chunk::bits - 1) / chunk::bits; chunk::type data[chunks] = {}; value() = default; template explicit constexpr value(Init ...init) : data{init...} {} value(const value &) = default; value &operator=(const value &) = default; value(value &&) = default; value &operator=(value &&) = default; // A (no-op) helper that forces the cast to value<>. CXXRTL_ALWAYS_INLINE const value &val() const { return *this; } std::string str() const { std::stringstream ss; ss << *this; return ss.str(); } // Conversion operations. // // These functions ensure that a conversion is never out of range, and should be always used, if at all // possible, instead of direct manipulation of the `data` member. For very large types, .slice() and // .concat() can be used to split them into more manageable parts. template CXXRTL_ALWAYS_INLINE IntegerT get() const { static_assert(std::numeric_limits::is_integer && !std::numeric_limits::is_signed, "get() requires T to be an unsigned integral type"); static_assert(std::numeric_limits::digits >= Bits, "get() requires T to be at least as wide as the value is"); IntegerT result = 0; for (size_t n = 0; n < chunks; n++) result |= IntegerT(data[n]) << (n * chunk::bits); return result; } template CXXRTL_ALWAYS_INLINE void set(IntegerT other) { static_assert(std::numeric_limits::is_integer && !std::numeric_limits::is_signed, "set() requires T to be an unsigned integral type"); static_assert(std::numeric_limits::digits >= Bits, "set() requires the value to be at least as wide as T is"); for (size_t n = 0; n < chunks; n++) data[n] = (other >> (n * chunk::bits)) & chunk::mask; } // Operations with compile-time parameters. // // These operations are used to implement slicing, concatenation, and blitting. // The trunc, zext and sext operations add or remove most significant bits (i.e. on the left); // the rtrunc and rzext operations add or remove least significant bits (i.e. on the right). template CXXRTL_ALWAYS_INLINE value trunc() const { static_assert(NewBits <= Bits, "trunc() may not increase width"); value result; for (size_t n = 0; n < result.chunks; n++) result.data[n] = data[n]; result.data[result.chunks - 1] &= result.msb_mask; return result; } template CXXRTL_ALWAYS_INLINE value zext() const { static_assert(NewBits >= Bits, "zext() may not decrease width"); value result; for (size_t n = 0; n < chunks; n++) result.data[n] = data[n]; return result; } template CXXRTL_ALWAYS_INLINE value sext() const { static_assert(NewBits >= Bits, "sext() may not decrease width"); value result; for (size_t n = 0; n < chunks; n++) result.data[n] = data[n]; if (is_neg()) { result.data[chunks - 1] |= ~msb_mask; for (size_t n = chunks; n < result.chunks; n++) result.data[n] = chunk::mask; result.data[result.chunks - 1] &= result.msb_mask; } return result; } template CXXRTL_ALWAYS_INLINE value rtrunc() const { static_assert(NewBits <= Bits, "rtrunc() may not increase width"); value result; constexpr size_t shift_chunks = (Bits - NewBits) / chunk::bits; constexpr size_t shift_bits = (Bits - NewBits) % chunk::bits; chunk::type carry = 0; if (shift_chunks + result.chunks < chunks) { carry = (shift_bits == 0) ? 0 : data[shift_chunks + result.chunks] << (chunk::bits - shift_bits); } for (size_t n = result.chunks; n > 0; n--) { result.data[n - 1] = carry | (data[shift_chunks + n - 1] >> shift_bits); carry = (shift_bits == 0) ? 0 : data[shift_chunks + n - 1] << (chunk::bits - shift_bits); } return result; } template CXXRTL_ALWAYS_INLINE value rzext() const { static_assert(NewBits >= Bits, "rzext() may not decrease width"); value result; constexpr size_t shift_chunks = (NewBits - Bits) / chunk::bits; constexpr size_t shift_bits = (NewBits - Bits) % chunk::bits; chunk::type carry = 0; for (size_t n = 0; n < chunks; n++) { result.data[shift_chunks + n] = (data[n] << shift_bits) | carry; carry = (shift_bits == 0) ? 0 : data[n] >> (chunk::bits - shift_bits); } if (shift_chunks + chunks < result.chunks) result.data[shift_chunks + chunks] = carry; return result; } // Bit blit operation, i.e. a partial read-modify-write. template CXXRTL_ALWAYS_INLINE value blit(const value &source) const { static_assert(Stop >= Start, "blit() may not reverse bit order"); constexpr chunk::type start_mask = ~(chunk::mask << (Start % chunk::bits)); constexpr chunk::type stop_mask = (Stop % chunk::bits + 1 == chunk::bits) ? 0 : (chunk::mask << (Stop % chunk::bits + 1)); value masked = *this; if (Start / chunk::bits == Stop / chunk::bits) { masked.data[Start / chunk::bits] &= stop_mask | start_mask; } else { masked.data[Start / chunk::bits] &= start_mask; for (size_t n = Start / chunk::bits + 1; n < Stop / chunk::bits; n++) masked.data[n] = 0; masked.data[Stop / chunk::bits] &= stop_mask; } value shifted = source .template rzext() .template zext(); return masked.bit_or(shifted); } // Helpers for selecting extending or truncating operation depending on whether the result is wider or narrower // than the operand. In C++17 these can be replaced with `if constexpr`. template struct zext_cast { CXXRTL_ALWAYS_INLINE value operator()(const value &val) { return val.template zext(); } }; template struct zext_cast::type> { CXXRTL_ALWAYS_INLINE value operator()(const value &val) { return val.template trunc(); } }; template struct sext_cast { CXXRTL_ALWAYS_INLINE value operator()(const value &val) { return val.template sext(); } }; template struct sext_cast::type> { CXXRTL_ALWAYS_INLINE value operator()(const value &val) { return val.template trunc(); } }; template CXXRTL_ALWAYS_INLINE value zcast() const { return zext_cast()(*this); } template CXXRTL_ALWAYS_INLINE value scast() const { return sext_cast()(*this); } // Bit replication is far more efficient than the equivalent concatenation. template CXXRTL_ALWAYS_INLINE value repeat() const { static_assert(Bits == 1, "repeat() is implemented only for 1-bit values"); return *this ? value().bit_not() : value(); } // Operations with run-time parameters (offsets, amounts, etc). // // These operations are used for computations. bool bit(size_t offset) const { return data[offset / chunk::bits] & (1 << (offset % chunk::bits)); } void set_bit(size_t offset, bool value = true) { size_t offset_chunks = offset / chunk::bits; size_t offset_bits = offset % chunk::bits; data[offset_chunks] &= ~(1 << offset_bits); data[offset_chunks] |= value ? 1 << offset_bits : 0; } explicit operator bool() const { return !is_zero(); } bool is_zero() const { for (size_t n = 0; n < chunks; n++) if (data[n] != 0) return false; return true; } bool is_neg() const { return data[chunks - 1] & (1 << ((Bits - 1) % chunk::bits)); } bool operator ==(const value &other) const { for (size_t n = 0; n < chunks; n++) if (data[n] != other.data[n]) return false; return true; } bool operator !=(const value &other) const { return !(*this == other); } value bit_not() const { value result; for (size_t n = 0; n < chunks; n++) result.data[n] = ~data[n]; result.data[chunks - 1] &= msb_mask; return result; } value bit_and(const value &other) const { value result; for (size_t n = 0; n < chunks; n++) result.data[n] = data[n] & other.data[n]; return result; } value bit_or(const value &other) const { value result; for (size_t n = 0; n < chunks; n++) result.data[n] = data[n] | other.data[n]; return result; } value bit_xor(const value &other) const { value result; for (size_t n = 0; n < chunks; n++) result.data[n] = data[n] ^ other.data[n]; return result; } value update(const value &val, const value &mask) const { return bit_and(mask.bit_not()).bit_or(val.bit_and(mask)); } template value shl(const value &amount) const { // Ensure our early return is correct by prohibiting values larger than 4 Gbit. static_assert(Bits <= chunk::mask, "shl() of unreasonably large values is not supported"); // Detect shifts definitely large than Bits early. for (size_t n = 1; n < amount.chunks; n++) if (amount.data[n] != 0) return {}; // Past this point we can use the least significant chunk as the shift size. size_t shift_chunks = amount.data[0] / chunk::bits; size_t shift_bits = amount.data[0] % chunk::bits; if (shift_chunks >= chunks) return {}; value result; chunk::type carry = 0; for (size_t n = 0; n < chunks - shift_chunks; n++) { result.data[shift_chunks + n] = (data[n] << shift_bits) | carry; carry = (shift_bits == 0) ? 0 : data[n] >> (chunk::bits - shift_bits); } return result; } template value shr(const value &amount) const { // Ensure our early return is correct by prohibiting values larger than 4 Gbit. static_assert(Bits <= chunk::mask, "shr() of unreasonably large values is not supported"); // Detect shifts definitely large than Bits early. for (size_t n = 1; n < amount.chunks; n++) if (amount.data[n] != 0) return {}; // Past this point we can use the least significant chunk as the shift size. size_t shift_chunks = amount.data[0] / chunk::bits; size_t shift_bits = amount.data[0] % chunk::bits; if (shift_chunks >= chunks) return {}; value result; chunk::type carry = 0; for (size_t n = 0; n < chunks - shift_chunks; n++) { result.data[chunks - shift_chunks - 1 - n] = carry | (data[chunks - 1 - n] >> shift_bits); carry = (shift_bits == 0) ? 0 : data[chunks - 1 - n] << (chunk::bits - shift_bits); } if (Signed && is_neg()) { size_t top_chunk_idx = (Bits - shift_bits) / chunk::bits; size_t top_chunk_bits = (Bits - shift_bits) % chunk::bits; for (size_t n = top_chunk_idx + 1; n < chunks; n++) result.data[n] = chunk::mask; if (shift_bits != 0) result.data[top_chunk_idx] |= chunk::mask << top_chunk_bits; } return result; } template value sshr(const value &amount) const { return shr(amount); } template value bmux(const value &sel) const { static_assert(ResultBits << SelBits == Bits, "invalid sizes used in bmux()"); size_t amount = sel.data[0] * ResultBits; size_t shift_chunks = amount / chunk::bits; size_t shift_bits = amount % chunk::bits; value result; chunk::type carry = 0; if (ResultBits % chunk::bits + shift_bits > chunk::bits) carry = data[result.chunks + shift_chunks] << (chunk::bits - shift_bits); for (size_t n = 0; n < result.chunks; n++) { result.data[result.chunks - 1 - n] = carry | (data[result.chunks + shift_chunks - 1 - n] >> shift_bits); carry = (shift_bits == 0) ? 0 : data[result.chunks + shift_chunks - 1 - n] << (chunk::bits - shift_bits); } return result; } template value demux(const value &sel) const { static_assert(Bits << SelBits == ResultBits, "invalid sizes used in demux()"); size_t amount = sel.data[0] * Bits; size_t shift_chunks = amount / chunk::bits; size_t shift_bits = amount % chunk::bits; value result; chunk::type carry = 0; for (size_t n = 0; n < chunks; n++) { result.data[shift_chunks + n] = (data[n] << shift_bits) | carry; carry = (shift_bits == 0) ? 0 : data[n] >> (chunk::bits - shift_bits); } if (Bits % chunk::bits + shift_bits > chunk::bits) result.data[shift_chunks + chunks] = carry; return result; } size_t ctpop() const { size_t count = 0; for (size_t n = 0; n < chunks; n++) { // This loop implements the population count idiom as recognized by LLVM and GCC. for (chunk::type x = data[n]; x != 0; count++) x = x & (x - 1); } return count; } size_t ctlz() const { size_t count = 0; for (size_t n = 0; n < chunks; n++) { chunk::type x = data[chunks - 1 - n]; if (x == 0) { count += (n == 0 ? Bits % chunk::bits : chunk::bits); } else { // This loop implements the find first set idiom as recognized by LLVM. for (; x != 0; count++) x >>= 1; } } return count; } size_t chunks_used() const { for (size_t n = chunks; n > 0; n--) { if (data[n - 1] != 0) return n; } return 0; } template std::pair, bool /*CarryOut*/> alu(const value &other) const { value result; bool carry = CarryIn; for (size_t n = 0; n < result.chunks; n++) { result.data[n] = data[n] + (Invert ? ~other.data[n] : other.data[n]) + carry; if (result.chunks - 1 == n) result.data[result.chunks - 1] &= result.msb_mask; carry = (result.data[n] < data[n]) || (result.data[n] == data[n] && carry); } return {result, carry}; } value add(const value &other) const { return alu(other).first; } value sub(const value &other) const { return alu(other).first; } value neg() const { return value { 0u }.sub(*this); } bool ucmp(const value &other) const { bool carry; std::tie(std::ignore, carry) = alu(other); return !carry; // a.ucmp(b) ≡ a u< b } bool scmp(const value &other) const { value result; bool carry; std::tie(result, carry) = alu(other); bool overflow = (is_neg() == !other.is_neg()) && (is_neg() != result.is_neg()); return result.is_neg() ^ overflow; // a.scmp(b) ≡ a s< b } template value mul(const value &other) const { value result; wide_chunk_t wide_result[result.chunks + 1] = {}; for (size_t n = 0; n < chunks; n++) { for (size_t m = 0; m < chunks && n + m < result.chunks; m++) { wide_result[n + m] += wide_chunk_t(data[n]) * wide_chunk_t(other.data[m]); wide_result[n + m + 1] += wide_result[n + m] >> chunk::bits; wide_result[n + m] &= chunk::mask; } } for (size_t n = 0; n < result.chunks; n++) { result.data[n] = wide_result[n]; } result.data[result.chunks - 1] &= result.msb_mask; return result; } // parallel to BigUnsigned::divideWithRemainder; quotient is stored in q, // *this is left with the remainder. See that function for commentary describing // how/why this works. void divideWithRemainder(const value &b, value &q) { assert(this != &q); if (this == &b || &q == &b) { value tmpB(b); divideWithRemainder(tmpB, q); return; } q = value {0u}; size_t blen = b.chunks_used(); if (blen == 0) { return; } size_t len = chunks_used(); if (len < blen) { return; } size_t i, j, k; size_t i2; chunk_t temp; bool borrowIn, borrowOut; size_t origLen = len; len++; chunk::type blk[len]; std::copy(data, data + origLen, blk); blk[origLen] = 0; chunk::type subtractBuf[len]; std::fill(subtractBuf, subtractBuf + len, 0); size_t qlen = origLen - blen + 1; i = qlen; while (i > 0) { i--; i2 = chunk::bits; while (i2 > 0) { i2--; for (j = 0, k = i, borrowIn = false; j <= blen; j++, k++) { temp = blk[k] - getShiftedBlock(b, j, i2); borrowOut = (temp > blk[k]); if (borrowIn) { borrowOut |= (temp == 0); temp--; } subtractBuf[k] = temp; borrowIn = borrowOut; } for (; k < origLen && borrowIn; k++) { borrowIn = (blk[k] == 0); subtractBuf[k] = blk[k] - 1; } if (!borrowIn) { q.data[i] |= (chunk::type(1) << i2); while (k > i) { k--; blk[k] = subtractBuf[k]; } } } } std::copy(blk, blk + origLen, data); } static chunk::type getShiftedBlock(const value &num, size_t x, size_t y) { chunk::type part1 = (x == 0 || y == 0) ? 0 : (num.data[x - 1] >> (chunk::bits - y)); chunk::type part2 = (x == num.chunks) ? 0 : (num.data[x] << y); return part1 | part2; } }; // Expression template for a slice, usable as lvalue or rvalue, and composable with other expression templates here. template struct slice_expr : public expr_base> { static_assert(Stop >= Start, "slice_expr() may not reverse bit order"); static_assert(Start < T::bits && Stop < T::bits, "slice_expr() must be within bounds"); static constexpr size_t bits = Stop - Start + 1; T &expr; slice_expr(T &expr) : expr(expr) {} slice_expr(const slice_expr &) = delete; CXXRTL_ALWAYS_INLINE operator value() const { return static_cast &>(expr) .template rtrunc() .template trunc(); } CXXRTL_ALWAYS_INLINE slice_expr &operator=(const value &rhs) { // Generic partial assignment implemented using a read-modify-write operation on the sliced expression. expr = static_cast &>(expr) .template blit(rhs); return *this; } // A helper that forces the cast to value<>, which allows deduction to work. CXXRTL_ALWAYS_INLINE value val() const { return static_cast &>(*this); } }; // Expression template for a concatenation, usable as lvalue or rvalue, and composable with other expression templates here. template struct concat_expr : public expr_base> { static constexpr size_t bits = T::bits + U::bits; T &ms_expr; U &ls_expr; concat_expr(T &ms_expr, U &ls_expr) : ms_expr(ms_expr), ls_expr(ls_expr) {} concat_expr(const concat_expr &) = delete; CXXRTL_ALWAYS_INLINE operator value() const { value ms_shifted = static_cast &>(ms_expr) .template rzext(); value ls_extended = static_cast &>(ls_expr) .template zext(); return ms_shifted.bit_or(ls_extended); } CXXRTL_ALWAYS_INLINE concat_expr &operator=(const value &rhs) { ms_expr = rhs.template rtrunc(); ls_expr = rhs.template trunc(); return *this; } // A helper that forces the cast to value<>, which allows deduction to work. CXXRTL_ALWAYS_INLINE value val() const { return static_cast &>(*this); } }; // Base class for expression templates, providing helper methods for operations that are valid on both rvalues and lvalues. // // Note that expression objects (slices and concatenations) constructed in this way should NEVER be captured because // they refer to temporaries that will, in general, only live until the end of the statement. For example, both of // these snippets perform use-after-free: // // const auto &a = val.slice<7,0>().slice<1>(); // value<1> b = a; // // auto &&c = val.slice<7,0>().slice<1>(); // c = value<1>{1u}; // // An easy way to write code using slices and concatenations safely is to follow two simple rules: // * Never explicitly name any type except `value` or `const value &`. // * Never use a `const auto &` or `auto &&` in any such expression. // Then, any code that compiles will be well-defined. template struct expr_base { template CXXRTL_ALWAYS_INLINE slice_expr slice() const { return {*static_cast(this)}; } template CXXRTL_ALWAYS_INLINE slice_expr slice() { return {*static_cast(this)}; } template CXXRTL_ALWAYS_INLINE concat_expr::type> concat(const U &other) const { return {*static_cast(this), other}; } template CXXRTL_ALWAYS_INLINE concat_expr::type> concat(U &&other) { return {*static_cast(this), other}; } }; template std::ostream &operator<<(std::ostream &os, const value &val) { auto old_flags = os.flags(std::ios::right); auto old_width = os.width(0); auto old_fill = os.fill('0'); os << val.bits << '\'' << std::hex; for (size_t n = val.chunks - 1; n != (size_t)-1; n--) { if (n == val.chunks - 1 && Bits % value::chunk::bits != 0) os.width((Bits % value::chunk::bits + 3) / 4); else os.width((value::chunk::bits + 3) / 4); os << val.data[n]; } os.fill(old_fill); os.width(old_width); os.flags(old_flags); return os; } template struct value_formatted { const value &val; bool character; bool justify_left; char padding; int width; int base; bool signed_; bool plus; value_formatted(const value &val, bool character, bool justify_left, char padding, int width, int base, bool signed_, bool plus) : val(val), character(character), justify_left(justify_left), padding(padding), width(width), base(base), signed_(signed_), plus(plus) {} value_formatted(const value_formatted &) = delete; value_formatted &operator=(const value_formatted &rhs) = delete; }; template std::ostream &operator<<(std::ostream &os, const value_formatted &vf) { value val = vf.val; std::string buf; // We might want to replace some of these bit() calls with direct // chunk access if it turns out to be slow enough to matter. if (!vf.character) { size_t width = Bits; if (vf.base != 10) { width = 0; for (size_t index = 0; index < Bits; index++) if (val.bit(index)) width = index + 1; } if (vf.base == 2) { for (size_t i = width; i > 0; i--) buf += (val.bit(i - 1) ? '1' : '0'); } else if (vf.base == 8 || vf.base == 16) { size_t step = (vf.base == 16) ? 4 : 3; for (size_t index = 0; index < width; index += step) { uint8_t value = val.bit(index) | (val.bit(index + 1) << 1) | (val.bit(index + 2) << 2); if (step == 4) value |= val.bit(index + 3) << 3; buf += "0123456789abcdef"[value]; } std::reverse(buf.begin(), buf.end()); } else if (vf.base == 10) { bool negative = vf.signed_ && val.is_neg(); if (negative) val = val.neg(); if (val.is_zero()) buf += '0'; while (!val.is_zero()) { value quotient; val.divideWithRemainder(value{10u}, quotient); buf += '0' + val.template trunc<(Bits > 4 ? 4 : Bits)>().val().template get(); val = quotient; } if (negative || vf.plus) buf += negative ? '-' : '+'; std::reverse(buf.begin(), buf.end()); } else assert(false); } else { buf.reserve(Bits/8); for (int i = 0; i < Bits; i += 8) { char ch = 0; for (int j = 0; j < 8 && i + j < int(Bits); j++) if (val.bit(i + j)) ch |= 1 << j; if (ch != 0) buf.append({ch}); } std::reverse(buf.begin(), buf.end()); } assert(vf.width == 0 || vf.padding != '\0'); if (!vf.justify_left && buf.size() < vf.width) { size_t pad_width = vf.width - buf.size(); if (vf.padding == '0' && (buf.front() == '+' || buf.front() == '-')) { os << buf.front(); buf.erase(0, 1); } os << std::string(pad_width, vf.padding); } os << buf; if (vf.justify_left && buf.size() < vf.width) os << std::string(vf.width - buf.size(), vf.padding); return os; } template struct wire { static constexpr size_t bits = Bits; value curr; value next; wire() = default; explicit constexpr wire(const value &init) : curr(init), next(init) {} template explicit constexpr wire(Init ...init) : curr{init...}, next{init...} {} // Copying and copy-assigning values is natural. If, however, a value is replaced with a wire, // e.g. because a module is built with a different optimization level, then existing code could // unintentionally copy a wire instead, which would create a subtle but serious bug. To make sure // this doesn't happen, prohibit copying and copy-assigning wires. wire(const wire &) = delete; wire &operator=(const wire &) = delete; wire(wire &&) = default; wire &operator=(wire &&) = default; template CXXRTL_ALWAYS_INLINE IntegerT get() const { return curr.template get(); } template CXXRTL_ALWAYS_INLINE void set(IntegerT other) { next.template set(other); } bool commit() { if (curr != next) { curr = next; return true; } return false; } }; template std::ostream &operator<<(std::ostream &os, const wire &val) { os << val.curr; return os; } template struct memory { const size_t depth; std::unique_ptr[]> data; explicit memory(size_t depth) : depth(depth), data(new value[depth]) {} memory(const memory &) = delete; memory &operator=(const memory &) = delete; memory(memory &&) = default; memory &operator=(memory &&other) { assert(depth == other.depth); data = std::move(other.data); write_queue = std::move(other.write_queue); return *this; } // An operator for direct memory reads. May be used at any time during the simulation. const value &operator [](size_t index) const { assert(index < depth); return data[index]; } // An operator for direct memory writes. May only be used before the simulation is started. If used // after the simulation is started, the design may malfunction. value &operator [](size_t index) { assert(index < depth); return data[index]; } // A simple way to make a writable memory would be to use an array of wires instead of an array of values. // However, there are two significant downsides to this approach: first, it has large overhead (2× space // overhead, and O(depth) time overhead during commit); second, it does not simplify handling write port // priorities. Although in principle write ports could be ordered or conditionally enabled in generated // code based on their priorities and selected addresses, the feedback arc set problem is computationally // expensive, and the heuristic based algorithms are not easily modified to guarantee (rather than prefer) // a particular write port evaluation order. // // The approach used here instead is to queue writes into a buffer during the eval phase, then perform // the writes during the commit phase in the priority order. This approach has low overhead, with both space // and time proportional to the amount of write ports. Because virtually every memory in a practical design // has at most two write ports, linear search is used on every write, being the fastest and simplest approach. struct write { size_t index; value val; value mask; int priority; }; std::vector write_queue; void update(size_t index, const value &val, const value &mask, int priority = 0) { assert(index < depth); // Queue up the write while keeping the queue sorted by priority. write_queue.insert( std::upper_bound(write_queue.begin(), write_queue.end(), priority, [](const int a, const write& b) { return a < b.priority; }), write { index, val, mask, priority }); } bool commit() { bool changed = false; for (const write &entry : write_queue) { value elem = data[entry.index]; elem = elem.update(entry.val, entry.mask); changed |= (data[entry.index] != elem); data[entry.index] = elem; } write_queue.clear(); return changed; } }; struct metadata { const enum { MISSING = 0, UINT = 1, SINT = 2, STRING = 3, DOUBLE = 4, } value_type; // In debug mode, using the wrong .as_*() function will assert. // In release mode, using the wrong .as_*() function will safely return a default value. const unsigned uint_value = 0; const signed sint_value = 0; const std::string string_value = ""; const double double_value = 0.0; metadata() : value_type(MISSING) {} metadata(unsigned value) : value_type(UINT), uint_value(value) {} metadata(signed value) : value_type(SINT), sint_value(value) {} metadata(const std::string &value) : value_type(STRING), string_value(value) {} metadata(const char *value) : value_type(STRING), string_value(value) {} metadata(double value) : value_type(DOUBLE), double_value(value) {} metadata(const metadata &) = default; metadata &operator=(const metadata &) = delete; unsigned as_uint() const { assert(value_type == UINT); return uint_value; } signed as_sint() const { assert(value_type == SINT); return sint_value; } const std::string &as_string() const { assert(value_type == STRING); return string_value; } double as_double() const { assert(value_type == DOUBLE); return double_value; } }; typedef std::map metadata_map; // Tag class to disambiguate values/wires and their aliases. struct debug_alias {}; // Tag declaration to disambiguate values and debug outlines. using debug_outline = ::_cxxrtl_outline; // This structure is intended for consumption via foreign function interfaces, like Python's ctypes. // Because of this it uses a C-style layout that is easy to parse rather than more idiomatic C++. // // To avoid violating strict aliasing rules, this structure has to be a subclass of the one used // in the C API, or it would not be possible to cast between the pointers to these. struct debug_item : ::cxxrtl_object { // Object types. enum : uint32_t { VALUE = CXXRTL_VALUE, WIRE = CXXRTL_WIRE, MEMORY = CXXRTL_MEMORY, ALIAS = CXXRTL_ALIAS, OUTLINE = CXXRTL_OUTLINE, }; // Object flags. enum : uint32_t { INPUT = CXXRTL_INPUT, OUTPUT = CXXRTL_OUTPUT, INOUT = CXXRTL_INOUT, DRIVEN_SYNC = CXXRTL_DRIVEN_SYNC, DRIVEN_COMB = CXXRTL_DRIVEN_COMB, UNDRIVEN = CXXRTL_UNDRIVEN, }; debug_item(const ::cxxrtl_object &object) : cxxrtl_object(object) {} template debug_item(value &item, size_t lsb_offset = 0, uint32_t flags_ = 0) { static_assert(sizeof(item) == value::chunks * sizeof(chunk_t), "value is not compatible with C layout"); type = VALUE; flags = flags_; width = Bits; lsb_at = lsb_offset; depth = 1; zero_at = 0; curr = item.data; next = item.data; outline = nullptr; } template debug_item(const value &item, size_t lsb_offset = 0) { static_assert(sizeof(item) == value::chunks * sizeof(chunk_t), "value is not compatible with C layout"); type = VALUE; flags = DRIVEN_COMB; width = Bits; lsb_at = lsb_offset; depth = 1; zero_at = 0; curr = const_cast(item.data); next = nullptr; outline = nullptr; } template debug_item(wire &item, size_t lsb_offset = 0, uint32_t flags_ = 0) { static_assert(sizeof(item.curr) == value::chunks * sizeof(chunk_t) && sizeof(item.next) == value::chunks * sizeof(chunk_t), "wire is not compatible with C layout"); type = WIRE; flags = flags_; width = Bits; lsb_at = lsb_offset; depth = 1; zero_at = 0; curr = item.curr.data; next = item.next.data; outline = nullptr; } template debug_item(memory &item, size_t zero_offset = 0) { static_assert(sizeof(item.data[0]) == value::chunks * sizeof(chunk_t), "memory is not compatible with C layout"); type = MEMORY; flags = 0; width = Width; lsb_at = 0; depth = item.depth; zero_at = zero_offset; curr = item.data ? item.data[0].data : nullptr; next = nullptr; outline = nullptr; } template debug_item(debug_alias, const value &item, size_t lsb_offset = 0) { static_assert(sizeof(item) == value::chunks * sizeof(chunk_t), "value is not compatible with C layout"); type = ALIAS; flags = DRIVEN_COMB; width = Bits; lsb_at = lsb_offset; depth = 1; zero_at = 0; curr = const_cast(item.data); next = nullptr; outline = nullptr; } template debug_item(debug_alias, const wire &item, size_t lsb_offset = 0) { static_assert(sizeof(item.curr) == value::chunks * sizeof(chunk_t) && sizeof(item.next) == value::chunks * sizeof(chunk_t), "wire is not compatible with C layout"); type = ALIAS; flags = DRIVEN_COMB; width = Bits; lsb_at = lsb_offset; depth = 1; zero_at = 0; curr = const_cast(item.curr.data); next = nullptr; outline = nullptr; } template debug_item(debug_outline &group, const value &item, size_t lsb_offset = 0) { static_assert(sizeof(item) == value::chunks * sizeof(chunk_t), "value is not compatible with C layout"); type = OUTLINE; flags = DRIVEN_COMB; width = Bits; lsb_at = lsb_offset; depth = 1; zero_at = 0; curr = const_cast(item.data); next = nullptr; outline = &group; } template IntegerT get() const { assert(width == Bits && depth == 1); value item; std::copy(curr, curr + value::chunks, item.data); return item.template get(); } template void set(IntegerT other) const { assert(width == Bits && depth == 1); value item; item.template set(other); std::copy(item.data, item.data + value::chunks, next); } }; static_assert(std::is_standard_layout::value, "debug_item is not compatible with C layout"); struct debug_items { std::map> table; void add(const std::string &name, debug_item &&item) { std::vector &parts = table[name]; parts.emplace_back(item); std::sort(parts.begin(), parts.end(), [](const debug_item &a, const debug_item &b) { return a.lsb_at < b.lsb_at; }); } size_t count(const std::string &name) const { if (table.count(name) == 0) return 0; return table.at(name).size(); } const std::vector &parts_at(const std::string &name) const { return table.at(name); } const debug_item &at(const std::string &name) const { const std::vector &parts = table.at(name); assert(parts.size() == 1); return parts.at(0); } const debug_item &operator [](const std::string &name) const { return at(name); } }; // Tag class to disambiguate the default constructor used by the toplevel module that calls reset(), // and the constructor of interior modules that should not call it. struct interior {}; struct module { module() {} virtual ~module() {} // Modules with black boxes cannot be copied. Although not all designs include black boxes, // delete the copy constructor and copy assignment operator to make sure that any downstream // code that manipulates modules doesn't accidentally depend on their availability. module(const module &) = delete; module &operator=(const module &) = delete; module(module &&) = default; module &operator=(module &&) = default; virtual void reset() = 0; virtual bool eval() = 0; virtual bool commit() = 0; unsigned int steps = 0; size_t step() { ++steps; size_t deltas = 0; bool converged = false; do { converged = eval(); deltas++; } while (commit() && !converged); return deltas; } virtual void debug_info(debug_items &items, std::string path = "") { (void)items, (void)path; } }; } // namespace cxxrtl // Internal structures used to communicate with the implementation of the C interface. typedef struct _cxxrtl_toplevel { std::unique_ptr module; } *cxxrtl_toplevel; typedef struct _cxxrtl_outline { std::function eval; } *cxxrtl_outline; // Definitions of internal Yosys cells. Other than the functions in this namespace, CXXRTL is fully generic // and indepenent of Yosys implementation details. // // The `write_cxxrtl` pass translates internal cells (cells with names that start with `$`) to calls of these // functions. All of Yosys arithmetic and logical cells perform sign or zero extension on their operands, // whereas basic operations on arbitrary width values require operands to be of the same width. These functions // bridge the gap by performing the necessary casts. They are named similar to `cell_A[B]`, where A and B are `u` // if the corresponding operand is unsigned, and `s` if it is signed. namespace cxxrtl_yosys { using namespace cxxrtl; // std::max isn't constexpr until C++14 for no particular reason (it's an oversight), so we define our own. template CXXRTL_ALWAYS_INLINE constexpr T max(const T &a, const T &b) { return a > b ? a : b; } // Logic operations template CXXRTL_ALWAYS_INLINE value logic_not(const value &a) { return value { a ? 0u : 1u }; } template CXXRTL_ALWAYS_INLINE value logic_and(const value &a, const value &b) { return value { (bool(a) && bool(b)) ? 1u : 0u }; } template CXXRTL_ALWAYS_INLINE value logic_or(const value &a, const value &b) { return value { (bool(a) || bool(b)) ? 1u : 0u }; } // Reduction operations template CXXRTL_ALWAYS_INLINE value reduce_and(const value &a) { return value { a.bit_not().is_zero() ? 1u : 0u }; } template CXXRTL_ALWAYS_INLINE value reduce_or(const value &a) { return value { a ? 1u : 0u }; } template CXXRTL_ALWAYS_INLINE value reduce_xor(const value &a) { return value { (a.ctpop() % 2) ? 1u : 0u }; } template CXXRTL_ALWAYS_INLINE value reduce_xnor(const value &a) { return value { (a.ctpop() % 2) ? 0u : 1u }; } template CXXRTL_ALWAYS_INLINE value reduce_bool(const value &a) { return value { a ? 1u : 0u }; } // Bitwise operations template CXXRTL_ALWAYS_INLINE value not_u(const value &a) { return a.template zcast().bit_not(); } template CXXRTL_ALWAYS_INLINE value not_s(const value &a) { return a.template scast().bit_not(); } template CXXRTL_ALWAYS_INLINE value and_uu(const value &a, const value &b) { return a.template zcast().bit_and(b.template zcast()); } template CXXRTL_ALWAYS_INLINE value and_ss(const value &a, const value &b) { return a.template scast().bit_and(b.template scast()); } template CXXRTL_ALWAYS_INLINE value or_uu(const value &a, const value &b) { return a.template zcast().bit_or(b.template zcast()); } template CXXRTL_ALWAYS_INLINE value or_ss(const value &a, const value &b) { return a.template scast().bit_or(b.template scast()); } template CXXRTL_ALWAYS_INLINE value xor_uu(const value &a, const value &b) { return a.template zcast().bit_xor(b.template zcast()); } template CXXRTL_ALWAYS_INLINE value xor_ss(const value &a, const value &b) { return a.template scast().bit_xor(b.template scast()); } template CXXRTL_ALWAYS_INLINE value xnor_uu(const value &a, const value &b) { return a.template zcast().bit_xor(b.template zcast()).bit_not(); } template CXXRTL_ALWAYS_INLINE value xnor_ss(const value &a, const value &b) { return a.template scast().bit_xor(b.template scast()).bit_not(); } template CXXRTL_ALWAYS_INLINE value shl_uu(const value &a, const value &b) { return a.template zcast().shl(b); } template CXXRTL_ALWAYS_INLINE value shl_su(const value &a, const value &b) { return a.template scast().shl(b); } template CXXRTL_ALWAYS_INLINE value sshl_uu(const value &a, const value &b) { return a.template zcast().shl(b); } template CXXRTL_ALWAYS_INLINE value sshl_su(const value &a, const value &b) { return a.template scast().shl(b); } template CXXRTL_ALWAYS_INLINE value shr_uu(const value &a, const value &b) { return a.shr(b).template zcast(); } template CXXRTL_ALWAYS_INLINE value shr_su(const value &a, const value &b) { return a.shr(b).template scast(); } template CXXRTL_ALWAYS_INLINE value sshr_uu(const value &a, const value &b) { return a.shr(b).template zcast(); } template CXXRTL_ALWAYS_INLINE value sshr_su(const value &a, const value &b) { return a.sshr(b).template scast(); } template CXXRTL_ALWAYS_INLINE value shift_uu(const value &a, const value &b) { return shr_uu(a, b); } template CXXRTL_ALWAYS_INLINE value shift_su(const value &a, const value &b) { return shr_su(a, b); } template CXXRTL_ALWAYS_INLINE value shift_us(const value &a, const value &b) { return b.is_neg() ? shl_uu(a, b.template sext().neg()) : shr_uu(a, b); } template CXXRTL_ALWAYS_INLINE value shift_ss(const value &a, const value &b) { return b.is_neg() ? shl_su(a, b.template sext().neg()) : shr_su(a, b); } template CXXRTL_ALWAYS_INLINE value shiftx_uu(const value &a, const value &b) { return shift_uu(a, b); } template CXXRTL_ALWAYS_INLINE value shiftx_su(const value &a, const value &b) { return shift_su(a, b); } template CXXRTL_ALWAYS_INLINE value shiftx_us(const value &a, const value &b) { return shift_us(a, b); } template CXXRTL_ALWAYS_INLINE value shiftx_ss(const value &a, const value &b) { return shift_ss(a, b); } // Comparison operations template CXXRTL_ALWAYS_INLINE value eq_uu(const value &a, const value &b) { constexpr size_t BitsExt = max(BitsA, BitsB); return value{ a.template zext() == b.template zext() ? 1u : 0u }; } template CXXRTL_ALWAYS_INLINE value eq_ss(const value &a, const value &b) { constexpr size_t BitsExt = max(BitsA, BitsB); return value{ a.template sext() == b.template sext() ? 1u : 0u }; } template CXXRTL_ALWAYS_INLINE value ne_uu(const value &a, const value &b) { constexpr size_t BitsExt = max(BitsA, BitsB); return value{ a.template zext() != b.template zext() ? 1u : 0u }; } template CXXRTL_ALWAYS_INLINE value ne_ss(const value &a, const value &b) { constexpr size_t BitsExt = max(BitsA, BitsB); return value{ a.template sext() != b.template sext() ? 1u : 0u }; } template CXXRTL_ALWAYS_INLINE value eqx_uu(const value &a, const value &b) { return eq_uu(a, b); } template CXXRTL_ALWAYS_INLINE value eqx_ss(const value &a, const value &b) { return eq_ss(a, b); } template CXXRTL_ALWAYS_INLINE value nex_uu(const value &a, const value &b) { return ne_uu(a, b); } template CXXRTL_ALWAYS_INLINE value nex_ss(const value &a, const value &b) { return ne_ss(a, b); } template CXXRTL_ALWAYS_INLINE value gt_uu(const value &a, const value &b) { constexpr size_t BitsExt = max(BitsA, BitsB); return value { b.template zext().ucmp(a.template zext()) ? 1u : 0u }; } template CXXRTL_ALWAYS_INLINE value gt_ss(const value &a, const value &b) { constexpr size_t BitsExt = max(BitsA, BitsB); return value { b.template sext().scmp(a.template sext()) ? 1u : 0u }; } template CXXRTL_ALWAYS_INLINE value ge_uu(const value &a, const value &b) { constexpr size_t BitsExt = max(BitsA, BitsB); return value { !a.template zext().ucmp(b.template zext()) ? 1u : 0u }; } template CXXRTL_ALWAYS_INLINE value ge_ss(const value &a, const value &b) { constexpr size_t BitsExt = max(BitsA, BitsB); return value { !a.template sext().scmp(b.template sext()) ? 1u : 0u }; } template CXXRTL_ALWAYS_INLINE value lt_uu(const value &a, const value &b) { constexpr size_t BitsExt = max(BitsA, BitsB); return value { a.template zext().ucmp(b.template zext()) ? 1u : 0u }; } template CXXRTL_ALWAYS_INLINE value lt_ss(const value &a, const value &b) { constexpr size_t BitsExt = max(BitsA, BitsB); return value { a.template sext().scmp(b.template sext()) ? 1u : 0u }; } template CXXRTL_ALWAYS_INLINE value le_uu(const value &a, const value &b) { constexpr size_t BitsExt = max(BitsA, BitsB); return value { !b.template zext().ucmp(a.template zext()) ? 1u : 0u }; } template CXXRTL_ALWAYS_INLINE value le_ss(const value &a, const value &b) { constexpr size_t BitsExt = max(BitsA, BitsB); return value { !b.template sext().scmp(a.template sext()) ? 1u : 0u }; } // Arithmetic operations template CXXRTL_ALWAYS_INLINE value pos_u(const value &a) { return a.template zcast(); } template CXXRTL_ALWAYS_INLINE value pos_s(const value &a) { return a.template scast(); } template CXXRTL_ALWAYS_INLINE value neg_u(const value &a) { return a.template zcast().neg(); } template CXXRTL_ALWAYS_INLINE value neg_s(const value &a) { return a.template scast().neg(); } template CXXRTL_ALWAYS_INLINE value add_uu(const value &a, const value &b) { return a.template zcast().add(b.template zcast()); } template CXXRTL_ALWAYS_INLINE value add_ss(const value &a, const value &b) { return a.template scast().add(b.template scast()); } template CXXRTL_ALWAYS_INLINE value sub_uu(const value &a, const value &b) { return a.template zcast().sub(b.template zcast()); } template CXXRTL_ALWAYS_INLINE value sub_ss(const value &a, const value &b) { return a.template scast().sub(b.template scast()); } template CXXRTL_ALWAYS_INLINE value mul_uu(const value &a, const value &b) { constexpr size_t BitsM = BitsA >= BitsB ? BitsA : BitsB; return a.template zcast().template mul(b.template zcast()); } template CXXRTL_ALWAYS_INLINE value mul_ss(const value &a, const value &b) { return a.template scast().template mul(b.template scast()); } template CXXRTL_ALWAYS_INLINE std::pair, value> divmod_uu(const value &a, const value &b) { constexpr size_t Bits = max(BitsY, max(BitsA, BitsB)); value quotient; value dividend = a.template zext(); value divisor = b.template zext(); if (dividend.ucmp(divisor)) return {/*quotient=*/value { 0u }, /*remainder=*/dividend.template trunc()}; uint32_t divisor_shift = dividend.ctlz() - divisor.ctlz(); divisor = divisor.shl(value<32> { divisor_shift }); for (size_t step = 0; step <= divisor_shift; step++) { quotient = quotient.shl(value<1> { 1u }); if (!dividend.ucmp(divisor)) { dividend = dividend.sub(divisor); quotient.set_bit(0, true); } divisor = divisor.shr(value<1> { 1u }); } return {quotient.template trunc(), /*remainder=*/dividend.template trunc()}; } template CXXRTL_ALWAYS_INLINE std::pair, value> divmod_ss(const value &a, const value &b) { value ua = a.template sext(); value ub = b.template sext(); if (ua.is_neg()) ua = ua.neg(); if (ub.is_neg()) ub = ub.neg(); value y, r; std::tie(y, r) = divmod_uu(ua, ub); if (a.is_neg() != b.is_neg()) y = y.neg(); if (a.is_neg()) r = r.neg(); return {y, r}; } template CXXRTL_ALWAYS_INLINE value div_uu(const value &a, const value &b) { return divmod_uu(a, b).first; } template CXXRTL_ALWAYS_INLINE value div_ss(const value &a, const value &b) { return divmod_ss(a, b).first; } template CXXRTL_ALWAYS_INLINE value mod_uu(const value &a, const value &b) { return divmod_uu(a, b).second; } template CXXRTL_ALWAYS_INLINE value mod_ss(const value &a, const value &b) { return divmod_ss(a, b).second; } template CXXRTL_ALWAYS_INLINE value modfloor_uu(const value &a, const value &b) { return divmod_uu(a, b).second; } // GHDL Modfloor operator. Returns r=a mod b, such that r has the same sign as b and // a=b*N+r where N is some integer // In practical terms, when a and b have different signs and the remainder returned by divmod_ss is not 0 // then return the remainder + b template CXXRTL_ALWAYS_INLINE value modfloor_ss(const value &a, const value &b) { value r; r = divmod_ss(a, b).second; if((b.is_neg() != a.is_neg()) && !r.is_zero()) return add_ss(b, r); return r; } template CXXRTL_ALWAYS_INLINE value divfloor_uu(const value &a, const value &b) { return divmod_uu(a, b).first; } // Divfloor. Similar to above: returns q=a//b, where q has the sign of a*b and a=b*q+N. // In other words, returns (truncating) a/b, except if a and b have different signs // and there's non-zero remainder, subtract one more towards floor. template CXXRTL_ALWAYS_INLINE value divfloor_ss(const value &a, const value &b) { value q, r; std::tie(q, r) = divmod_ss(a, b); if ((b.is_neg() != a.is_neg()) && !r.is_zero()) return sub_uu(q, value<1> { 1u }); return q; } // Memory helper struct memory_index { bool valid; size_t index; template memory_index(const value &addr, size_t offset, size_t depth) { static_assert(value::chunks <= 1, "memory address is too wide"); size_t offset_index = addr.data[0]; valid = (offset_index >= offset && offset_index < offset + depth); index = offset_index - offset; } }; } // namespace cxxrtl_yosys #endif yosys-yosys-0.33/backends/cxxrtl/cxxrtl_backend.cc000066400000000000000000003650641447554276300224500ustar00rootroot00000000000000/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2019-2020 whitequark * * 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/rtlil.h" #include "kernel/register.h" #include "kernel/sigtools.h" #include "kernel/utils.h" #include "kernel/celltypes.h" #include "kernel/mem.h" #include "kernel/log.h" #include "kernel/fmt.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN // [[CITE]] // Peter Eades; Xuemin Lin; W. F. Smyth, "A Fast Effective Heuristic For The Feedback Arc Set Problem" // Information Processing Letters, Vol. 47, pp 319-323, 1993 // https://pdfs.semanticscholar.org/c7ed/d9acce96ca357876540e19664eb9d976637f.pdf // A topological sort (on a cell/wire graph) is always possible in a fully flattened RTLIL design without // processes or logic loops where every wire has a single driver. Logic loops are illegal in RTLIL and wires // with multiple drivers can be split by the `splitnets` pass; however, interdependencies between processes // or module instances can create strongly connected components without introducing evaluation nondeterminism. // We wish to support designs with such benign SCCs (as well as designs with multiple drivers per wire), so // we sort the graph in a way that minimizes feedback arcs. If there are no feedback arcs in the sorted graph, // then a more efficient evaluation method is possible, since eval() will always immediately converge. template struct Scheduler { struct Vertex { T *data; Vertex *prev, *next; pool preds, succs; Vertex() : data(NULL), prev(this), next(this) {} Vertex(T *data) : data(data), prev(NULL), next(NULL) {} bool empty() const { log_assert(data == NULL); if (next == this) { log_assert(prev == next); return true; } return false; } void link(Vertex *list) { log_assert(prev == NULL && next == NULL); next = list; prev = list->prev; list->prev->next = this; list->prev = this; } void unlink() { log_assert(prev->next == this && next->prev == this); prev->next = next; next->prev = prev; next = prev = NULL; } int delta() const { return succs.size() - preds.size(); } }; std::vector vertices; Vertex *sources = new Vertex; Vertex *sinks = new Vertex; dict bins; ~Scheduler() { delete sources; delete sinks; for (auto bin : bins) delete bin.second; for (auto vertex : vertices) delete vertex; } Vertex *add(T *data) { Vertex *vertex = new Vertex(data); vertices.push_back(vertex); return vertex; } void relink(Vertex *vertex) { if (vertex->succs.empty()) vertex->link(sinks); else if (vertex->preds.empty()) vertex->link(sources); else { int delta = vertex->delta(); if (!bins.count(delta)) bins[delta] = new Vertex; vertex->link(bins[delta]); } } Vertex *remove(Vertex *vertex) { vertex->unlink(); for (auto pred : vertex->preds) { if (pred == vertex) continue; log_assert(pred->succs[vertex]); pred->unlink(); pred->succs.erase(vertex); relink(pred); } for (auto succ : vertex->succs) { if (succ == vertex) continue; log_assert(succ->preds[vertex]); succ->unlink(); succ->preds.erase(vertex); relink(succ); } vertex->preds.clear(); vertex->succs.clear(); return vertex; } std::vector schedule() { std::vector s1, s2r; for (auto vertex : vertices) relink(vertex); bool bins_empty = false; while (!(sinks->empty() && sources->empty() && bins_empty)) { while (!sinks->empty()) s2r.push_back(remove(sinks->next)); while (!sources->empty()) s1.push_back(remove(sources->next)); // Choosing u in this implementation isn't O(1), but the paper handwaves which data structure they suggest // using to get O(1) relinking *and* find-max-key ("it is clear"... no it isn't), so this code uses a very // naive implementation of find-max-key. bins_empty = true; bins.template sort>(); for (auto bin : bins) { if (!bin.second->empty()) { bins_empty = false; s1.push_back(remove(bin.second->next)); break; } } } s1.insert(s1.end(), s2r.rbegin(), s2r.rend()); return s1; } }; bool is_unary_cell(RTLIL::IdString type) { return type.in( ID($not), ID($logic_not), ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool), ID($pos), ID($neg)); } bool is_binary_cell(RTLIL::IdString type) { return type.in( ID($and), ID($or), ID($xor), ID($xnor), ID($logic_and), ID($logic_or), ID($shl), ID($sshl), ID($shr), ID($sshr), ID($shift), ID($shiftx), ID($eq), ID($ne), ID($eqx), ID($nex), ID($gt), ID($ge), ID($lt), ID($le), ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($modfloor), ID($divfloor)); } bool is_extending_cell(RTLIL::IdString type) { return !type.in( ID($logic_not), ID($logic_and), ID($logic_or), ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool)); } bool is_inlinable_cell(RTLIL::IdString type) { return is_unary_cell(type) || is_binary_cell(type) || type.in( ID($mux), ID($concat), ID($slice), ID($pmux), ID($bmux), ID($demux)); } bool is_ff_cell(RTLIL::IdString type) { return type.in( ID($dff), ID($dffe), ID($sdff), ID($sdffe), ID($sdffce), ID($adff), ID($adffe), ID($dffsr), ID($dffsre), ID($aldff), ID($aldffe), ID($dlatch), ID($adlatch), ID($dlatchsr), ID($sr)); } bool is_internal_cell(RTLIL::IdString type) { return !type.isPublic() && !type.begins_with("$paramod"); } bool is_effectful_cell(RTLIL::IdString type) { return type.isPublic() || type == ID($print); } bool is_cxxrtl_blackbox_cell(const RTLIL::Cell *cell) { RTLIL::Module *cell_module = cell->module->design->module(cell->type); log_assert(cell_module != nullptr); return cell_module->get_bool_attribute(ID(cxxrtl_blackbox)); } bool is_memwr_process(const RTLIL::Process *process) { for (auto sync : process->syncs) if (!sync->mem_write_actions.empty()) return true; return false; } enum class CxxrtlPortType { UNKNOWN = 0, // or mixed comb/sync COMB = 1, SYNC = 2, }; CxxrtlPortType cxxrtl_port_type(RTLIL::Module *module, RTLIL::IdString port) { RTLIL::Wire *output_wire = module->wire(port); log_assert(output_wire != nullptr); bool is_comb = output_wire->get_bool_attribute(ID(cxxrtl_comb)); bool is_sync = output_wire->get_bool_attribute(ID(cxxrtl_sync)); if (is_comb && is_sync) log_cmd_error("Port `%s.%s' is marked as both `cxxrtl_comb` and `cxxrtl_sync`.\n", log_id(module), log_signal(output_wire)); else if (is_comb) return CxxrtlPortType::COMB; else if (is_sync) return CxxrtlPortType::SYNC; return CxxrtlPortType::UNKNOWN; } CxxrtlPortType cxxrtl_port_type(const RTLIL::Cell *cell, RTLIL::IdString port) { RTLIL::Module *cell_module = cell->module->design->module(cell->type); if (cell_module == nullptr || !cell_module->get_bool_attribute(ID(cxxrtl_blackbox))) return CxxrtlPortType::UNKNOWN; return cxxrtl_port_type(cell_module, port); } bool is_cxxrtl_comb_port(const RTLIL::Cell *cell, RTLIL::IdString port) { return cxxrtl_port_type(cell, port) == CxxrtlPortType::COMB; } bool is_cxxrtl_sync_port(const RTLIL::Cell *cell, RTLIL::IdString port) { return cxxrtl_port_type(cell, port) == CxxrtlPortType::SYNC; } struct FlowGraph { struct Node { enum class Type { CONNECT, CELL_SYNC, CELL_EVAL, PRINT_SYNC, PROCESS_SYNC, PROCESS_CASE, MEM_RDPORT, MEM_WRPORTS, }; Type type; RTLIL::SigSig connect = {}; const RTLIL::Cell *cell = nullptr; std::vector print_sync_cells; const RTLIL::Process *process = nullptr; const Mem *mem = nullptr; int portidx; }; std::vector nodes; dict> wire_comb_defs, wire_sync_defs, wire_uses; dict, hash_ptr_ops> node_comb_defs, node_sync_defs, node_uses; dict wire_def_inlinable; dict> wire_use_inlinable; dict bit_has_state; ~FlowGraph() { for (auto node : nodes) delete node; } void add_defs(Node *node, const RTLIL::SigSpec &sig, bool is_ff, bool inlinable) { for (auto chunk : sig.chunks()) if (chunk.wire) { if (is_ff) { // A sync def means that a wire holds design state because it is driven directly by // a flip-flop output. Such a wire can never be unbuffered. wire_sync_defs[chunk.wire].insert(node); node_sync_defs[node].insert(chunk.wire); } else { // A comb def means that a wire doesn't hold design state. It might still be connected, // indirectly, to a flip-flop output. wire_comb_defs[chunk.wire].insert(node); node_comb_defs[node].insert(chunk.wire); } } for (auto bit : sig.bits()) 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()) { // Only a single def of a wire can be inlined. (Multiple defs of a wire are unsound, but we // handle them anyway to avoid assertion failures later.) if (!wire_def_inlinable.count(sig.as_wire())) wire_def_inlinable[sig.as_wire()] = inlinable; else wire_def_inlinable[sig.as_wire()] = false; } } void add_uses(Node *node, const RTLIL::SigSpec &sig) { for (auto chunk : sig.chunks()) if (chunk.wire) { wire_uses[chunk.wire].insert(node); node_uses[node].insert(chunk.wire); // Only a single use of an entire wire in the right order can be inlined. (But the use can include // other chunks.) This is tracked per-node because a wire used by multiple nodes can still be inlined // if all but one of those nodes is dead. if (!wire_use_inlinable[chunk.wire].count(node)) wire_use_inlinable[chunk.wire][node] = true; else wire_use_inlinable[chunk.wire][node] = false; } } bool is_inlinable(const RTLIL::Wire *wire) const { // Can the wire be inlined at all? if (wire_def_inlinable.count(wire)) return wire_def_inlinable.at(wire); return false; } bool is_inlinable(const RTLIL::Wire *wire, const pool &nodes) const { // Can the wire be inlined, knowing that the given nodes are reachable? if (nodes.size() != 1) return false; Node *node = *nodes.begin(); log_assert(node_uses.at(node).count(wire)); if (is_inlinable(wire) && wire_use_inlinable.count(wire) && wire_use_inlinable.at(wire).count(node)) return wire_use_inlinable.at(wire).at(node); return false; } // Connections void add_connect_defs_uses(Node *node, const RTLIL::SigSig &conn) { add_defs(node, conn.first, /*is_ff=*/false, /*inlinable=*/true); add_uses(node, conn.second); } Node *add_node(const RTLIL::SigSig &conn) { Node *node = new Node; node->type = Node::Type::CONNECT; node->connect = conn; nodes.push_back(node); add_connect_defs_uses(node, conn); return node; } // Cells void add_cell_sync_defs(Node *node, const RTLIL::Cell *cell) { // To understand why this node type is necessary and why it produces comb defs, consider a cell // with input \i and sync output \o, used in a design such that \i is connected to \o. This does // not result in a feedback arc because the output is synchronous. However, a naive implementation // of code generation for cells that assigns to inputs, evaluates cells, assigns from outputs // would not be able to immediately converge... // // wire<1> i_tmp; // cell->p_i = i_tmp.curr; // cell->eval(); // i_tmp.next = cell->p_o.curr; // // ... since the wire connecting the input and output ports would not be localizable. To solve // this, the cell is split into two scheduling nodes; one exclusively for sync outputs, and // another for inputs and all non-sync outputs. This way the generated code can be rearranged... // // value<1> i_tmp; // i_tmp = cell->p_o.curr; // cell->p_i = i_tmp; // cell->eval(); // // eliminating the unnecessary delta cycle. Conceptually, the CELL_SYNC node type is a series of // connections of the form `connect \lhs \cell.\sync_output`; the right-hand side of these is not // expressible as a wire in RTLIL. If it was expressible, then `\cell.\sync_output` would have // a sync def, and this node would be an ordinary CONNECT node, with `\lhs` having a comb def. // Because it isn't, a special node type is used, the right-hand side does not appear anywhere, // and the left-hand side has a comb def. for (auto conn : cell->connections()) if (cell->output(conn.first)) if (is_cxxrtl_sync_port(cell, conn.first)) { // See note regarding inlinability below. add_defs(node, conn.second, /*is_ff=*/false, /*inlinable=*/false); } } void add_cell_eval_defs_uses(Node *node, const RTLIL::Cell *cell) { for (auto conn : cell->connections()) { if (cell->output(conn.first)) { if (is_inlinable_cell(cell->type)) add_defs(node, conn.second, /*is_ff=*/false, /*inlinable=*/true); else if (is_ff_cell(cell->type)) add_defs(node, conn.second, /*is_ff=*/true, /*inlinable=*/false); else if (is_internal_cell(cell->type)) add_defs(node, conn.second, /*is_ff=*/false, /*inlinable=*/false); else if (!is_cxxrtl_sync_port(cell, conn.first)) { // Although at first it looks like outputs of user-defined cells may always be inlined, the reality is // more complex. Fully sync outputs produce no defs and so don't participate in inlining. Fully comb // outputs are assigned in a different way depending on whether the cell's eval() immediately converged. // Unknown/mixed outputs could be inlined, but should be rare in practical designs and don't justify // the infrastructure required to inline outputs of cells with many of them. add_defs(node, conn.second, /*is_ff=*/false, /*inlinable=*/false); } } if (cell->input(conn.first)) add_uses(node, conn.second); } } Node *add_node(const RTLIL::Cell *cell) { log_assert(cell->known()); bool has_fully_sync_outputs = false; for (auto conn : cell->connections()) if (cell->output(conn.first) && is_cxxrtl_sync_port(cell, conn.first)) { has_fully_sync_outputs = true; break; } if (has_fully_sync_outputs) { Node *node = new Node; node->type = Node::Type::CELL_SYNC; node->cell = cell; nodes.push_back(node); add_cell_sync_defs(node, cell); } Node *node = new Node; node->type = Node::Type::CELL_EVAL; node->cell = cell; nodes.push_back(node); add_cell_eval_defs_uses(node, cell); return node; } Node *add_print_sync_node(std::vector cells) { Node *node = new Node; node->type = Node::Type::PRINT_SYNC; node->print_sync_cells = cells; nodes.push_back(node); return node; } // Processes void add_case_rule_defs_uses(Node *node, const RTLIL::CaseRule *case_) { for (auto &action : case_->actions) { add_defs(node, action.first, /*is_ff=*/false, /*inlinable=*/false); add_uses(node, action.second); } for (auto sub_switch : case_->switches) { add_uses(node, sub_switch->signal); for (auto sub_case : sub_switch->cases) { for (auto &compare : sub_case->compare) add_uses(node, compare); add_case_rule_defs_uses(node, sub_case); } } } void add_sync_rules_defs_uses(Node *node, const RTLIL::Process *process) { for (auto sync : process->syncs) { for (auto &action : sync->actions) { if (sync->type == RTLIL::STp || sync->type == RTLIL::STn || sync->type == RTLIL::STe) add_defs(node, action.first, /*is_ff=*/true, /*inlinable=*/false); else add_defs(node, action.first, /*is_ff=*/false, /*inlinable=*/false); add_uses(node, action.second); } for (auto &memwr : sync->mem_write_actions) { add_uses(node, memwr.address); add_uses(node, memwr.data); add_uses(node, memwr.enable); } } } Node *add_node(const RTLIL::Process *process) { Node *node = new Node; node->type = Node::Type::PROCESS_SYNC; node->process = process; nodes.push_back(node); add_sync_rules_defs_uses(node, process); node = new Node; node->type = Node::Type::PROCESS_CASE; node->process = process; nodes.push_back(node); add_case_rule_defs_uses(node, &process->root_case); return node; } // Memories void add_node(const Mem *mem) { for (int i = 0; i < GetSize(mem->rd_ports); i++) { auto &port = mem->rd_ports[i]; Node *node = new Node; node->type = Node::Type::MEM_RDPORT; node->mem = mem; node->portidx = i; nodes.push_back(node); add_defs(node, port.data, /*is_ff=*/port.clk_enable, /*inlinable=*/false); add_uses(node, port.clk); add_uses(node, port.en); add_uses(node, port.arst); add_uses(node, port.srst); add_uses(node, port.addr); bool transparent = false; for (int j = 0; j < GetSize(mem->wr_ports); j++) { auto &wrport = mem->wr_ports[j]; if (port.transparency_mask[j]) { // Our implementation of transparent read ports reads en, addr and data from every write port // the read port is transparent with. add_uses(node, wrport.en); add_uses(node, wrport.addr); add_uses(node, wrport.data); transparent = true; } } // Also we read the read address twice in this case (prevent inlining). if (transparent) add_uses(node, port.addr); } if (!mem->wr_ports.empty()) { Node *node = new Node; node->type = Node::Type::MEM_WRPORTS; node->mem = mem; nodes.push_back(node); for (auto &port : mem->wr_ports) { add_uses(node, port.clk); add_uses(node, port.en); add_uses(node, port.addr); add_uses(node, port.data); } } } }; std::vector split_by(const std::string &str, const std::string &sep) { std::vector result; size_t prev = 0; while (true) { size_t curr = str.find_first_of(sep, prev); if (curr == std::string::npos) { std::string part = str.substr(prev); if (!part.empty()) result.push_back(part); break; } else { std::string part = str.substr(prev, curr - prev); if (!part.empty()) result.push_back(part); prev = curr + 1; } } return result; } std::string escape_cxx_string(const std::string &input) { std::string output = "\""; for (auto c : input) { if (::isprint(c)) { if (c == '\\') output.push_back('\\'); output.push_back(c); } else { char l = c & 0xf, h = (c >> 4) & 0xf; output.append("\\x"); output.push_back((h < 10 ? '0' + h : 'a' + h - 10)); output.push_back((l < 10 ? '0' + l : 'a' + l - 10)); } } output.push_back('"'); if (output.find('\0') != std::string::npos) { output.insert(0, "std::string {"); output.append(stringf(", %zu}", input.size())); } return output; } template std::string get_hdl_name(T *object) { if (object->has_attribute(ID::hdlname)) return object->get_string_attribute(ID::hdlname); else return object->name.str().substr(1); } struct WireType { enum Type { // Non-referenced wire; is not a part of the design. UNUSED, // Double-buffered wire; is a class member, and holds design state. BUFFERED, // Single-buffered wire; is a class member, but holds no state. MEMBER, // Single-buffered wire; is a class member, and is computed on demand. OUTLINE, // Local wire; is a local variable in eval method. LOCAL, // Inline wire; is an unnamed temporary in eval method. INLINE, // Alias wire; is replaced with aliasee, except in debug info. ALIAS, // Const wire; is replaced with constant, except in debug info. CONST, }; Type type = UNUSED; const RTLIL::Cell *cell_subst = nullptr; // for INLINE RTLIL::SigSpec sig_subst = {}; // for INLINE, ALIAS, and CONST WireType() = default; WireType(Type type) : type(type) { log_assert(type == UNUSED || type == BUFFERED || type == MEMBER || type == OUTLINE || type == LOCAL); } WireType(Type type, const RTLIL::Cell *cell) : type(type), cell_subst(cell) { log_assert(type == INLINE && is_inlinable_cell(cell->type)); } WireType(Type type, RTLIL::SigSpec sig) : type(type), sig_subst(sig) { log_assert(type == INLINE || (type == ALIAS && sig.is_wire()) || (type == CONST && sig.is_fully_const())); } bool is_buffered() const { return type == BUFFERED; } bool is_member() const { return type == BUFFERED || type == MEMBER || type == OUTLINE; } bool is_outline() const { return type == OUTLINE; } bool is_named() const { return is_member() || type == LOCAL; } bool is_local() const { return type == LOCAL || type == INLINE; } bool is_exact() const { return type == ALIAS || type == CONST; } }; // Tests for a SigSpec that is a valid clock input, clocks have to have a backing wire and be a single bit // using this instead of sig.is_wire() solves issues when the clock is a slice instead of a full wire bool is_valid_clock(const RTLIL::SigSpec& sig) { return sig.is_chunk() && sig.is_bit() && sig[0].wire; } struct CxxrtlWorker { bool split_intf = false; std::string intf_filename; std::string design_ns = "cxxrtl_design"; std::string print_output = "std::cout"; std::ostream *impl_f = nullptr; std::ostream *intf_f = nullptr; bool print_wire_types = false; bool print_debug_wire_types = false; bool run_hierarchy = false; bool run_flatten = false; bool run_proc = false; bool unbuffer_internal = false; bool unbuffer_public = false; bool localize_internal = false; bool localize_public = false; bool inline_internal = false; bool inline_public = false; bool debug_info = false; bool debug_member = false; bool debug_alias = false; bool debug_eval = false; std::ostringstream f; std::string indent; int temporary = 0; dict sigmaps; dict> mod_memories; pool> writable_memories; pool edge_wires; dict wire_init; dict edge_types; dict> schedule, debug_schedule; dict wire_types, debug_wire_types; dict bit_has_state; dict> blackbox_specializations; dict eval_converges; void inc_indent() { indent += "\t"; } void dec_indent() { indent.resize(indent.size() - 1); } // RTLIL allows any characters in names other than whitespace. This presents an issue for generating C++ code // because C++ identifiers may be only alphanumeric, cannot clash with C++ keywords, and cannot clash with cxxrtl // identifiers. This issue can be solved with a name mangling scheme. We choose a name mangling scheme that results // in readable identifiers, does not depend on an up-to-date list of C++ keywords, and is easy to apply. Its rules: // 1. All generated identifiers start with `_`. // 1a. Generated identifiers for public names (beginning with `\`) start with `p_`. // 1b. Generated identifiers for internal names (beginning with `$`) start with `i_`. // 2. An underscore is escaped with another underscore, i.e. `__`. // 3. Any other non-alnum character is escaped with underscores around its lowercase hex code, e.g. `@` as `_40_`. std::string mangle_name(const RTLIL::IdString &name) { std::string mangled; bool first = true; for (char c : name.str()) { if (first) { first = false; if (c == '\\') mangled += "p_"; else if (c == '$') mangled += "i_"; else log_assert(false); } else { if (isalnum(c)) { mangled += c; } else if (c == '_') { mangled += "__"; } else { char l = c & 0xf, h = (c >> 4) & 0xf; mangled += '_'; mangled += (h < 10 ? '0' + h : 'a' + h - 10); mangled += (l < 10 ? '0' + l : 'a' + l - 10); mangled += '_'; } } } return mangled; } std::string mangle_module_name(const RTLIL::IdString &name, bool is_blackbox = false) { // Class namespace. if (is_blackbox) return "bb_" + mangle_name(name); return mangle_name(name); } std::string mangle_memory_name(const RTLIL::IdString &name) { // Class member namespace. return "memory_" + mangle_name(name); } std::string mangle_cell_name(const RTLIL::IdString &name) { // Class member namespace. return "cell_" + mangle_name(name); } std::string mangle_wire_name(const RTLIL::IdString &name) { // Class member namespace. return mangle_name(name); } std::string mangle(const RTLIL::Module *module) { return mangle_module_name(module->name, /*is_blackbox=*/module->get_bool_attribute(ID(cxxrtl_blackbox))); } std::string mangle(const Mem *mem) { return mangle_memory_name(mem->memid); } std::string mangle(const RTLIL::Memory *memory) { return mangle_memory_name(memory->name); } std::string mangle(const RTLIL::Cell *cell) { return mangle_cell_name(cell->name); } std::string mangle(const RTLIL::Wire *wire) { return mangle_wire_name(wire->name); } std::string mangle(RTLIL::SigBit sigbit) { log_assert(sigbit.wire != NULL); if (sigbit.wire->width == 1) return mangle(sigbit.wire); return mangle(sigbit.wire) + "_" + std::to_string(sigbit.offset); } std::vector template_param_names(const RTLIL::Module *module) { if (!module->has_attribute(ID(cxxrtl_template))) return {}; 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"); for (const auto ¶m_name : param_names) { // Various lowercase prefixes (p_, i_, cell_, ...) are used for member variables, so require // parameters to start with an uppercase letter to avoid name conflicts. (This is the convention // in both Verilog and C++, anyway.) if (!isupper(param_name[0])) log_cmd_error("Attribute `cxxrtl_template' of module `%s' includes a parameter `%s', " "which does not start with an uppercase letter.\n", log_id(module), param_name.c_str()); } return param_names; } std::string template_params(const RTLIL::Module *module, bool is_decl) { std::vector param_names = template_param_names(module); if (param_names.empty()) return ""; std::string params = "<"; bool first = true; for (const auto ¶m_name : param_names) { if (!first) params += ", "; first = false; if (is_decl) params += "size_t "; params += param_name; } params += ">"; return params; } std::string template_args(const RTLIL::Cell *cell) { RTLIL::Module *cell_module = cell->module->design->module(cell->type); log_assert(cell_module != nullptr); if (!cell_module->get_bool_attribute(ID(cxxrtl_blackbox))) return ""; std::vector param_names = template_param_names(cell_module); if (param_names.empty()) return ""; std::string params = "<"; bool first = true; for (const auto ¶m_name : param_names) { if (!first) params += ", "; first = false; params += "/*" + param_name + "=*/"; RTLIL::IdString id_param_name = '\\' + param_name; if (!cell->hasParam(id_param_name)) log_cmd_error("Cell `%s.%s' does not have a parameter `%s', which is required by the templated module `%s'.\n", log_id(cell->module), log_id(cell), param_name.c_str(), log_id(cell_module)); RTLIL::Const param_value = cell->getParam(id_param_name); if (((param_value.flags & ~RTLIL::CONST_FLAG_SIGNED) != 0) || param_value.as_int() < 0) log_cmd_error("Parameter `%s' of cell `%s.%s', which is required by the templated module `%s', " "is not a positive integer.\n", param_name.c_str(), log_id(cell->module), log_id(cell), log_id(cell_module)); params += std::to_string(cell->getParam(id_param_name).as_int()); } params += ">"; return params; } std::string fresh_temporary() { return stringf("tmp_%d", temporary++); } void dump_attrs(const RTLIL::AttrObject *object) { for (auto attr : object->attributes) { f << indent << "// " << attr.first.str() << ": "; if (attr.second.flags & RTLIL::CONST_FLAG_STRING) { f << attr.second.decode_string(); } else { f << attr.second.as_int(/*is_signed=*/attr.second.flags & RTLIL::CONST_FLAG_SIGNED); } f << "\n"; } } void dump_const_init(const RTLIL::Const &data, int width, int offset = 0, bool fixed_width = false) { const int CHUNK_SIZE = 32; f << "{"; while (width > 0) { int chunk_width = min(width, CHUNK_SIZE); uint32_t chunk = data.extract(offset, chunk_width).as_int(); if (fixed_width) f << stringf("0x%.*xu", (3 + chunk_width) / 4, chunk); else f << stringf("%#xu", chunk); if (width > CHUNK_SIZE) f << ','; offset += CHUNK_SIZE; width -= CHUNK_SIZE; } f << "}"; } void dump_const(const RTLIL::Const &data, int width, int offset = 0, bool fixed_width = false) { f << "value<" << width << ">"; dump_const_init(data, width, offset, fixed_width); } void dump_const(const RTLIL::Const &data) { dump_const(data, data.size()); } bool dump_sigchunk(const RTLIL::SigChunk &chunk, bool is_lhs, bool for_debug = false) { if (chunk.wire == NULL) { dump_const(chunk.data, chunk.width, chunk.offset); return false; } else { const auto &wire_type = (for_debug ? debug_wire_types : wire_types)[chunk.wire]; switch (wire_type.type) { case WireType::BUFFERED: f << mangle(chunk.wire) << (is_lhs ? ".next" : ".curr"); break; case WireType::MEMBER: case WireType::LOCAL: case WireType::OUTLINE: f << mangle(chunk.wire); break; case WireType::INLINE: log_assert(!is_lhs); if (wire_type.cell_subst != nullptr) { dump_cell_expr(wire_type.cell_subst, for_debug); break; } YS_FALLTHROUGH case WireType::ALIAS: case WireType::CONST: log_assert(!is_lhs); return dump_sigspec(wire_type.sig_subst.extract(chunk.offset, chunk.width), is_lhs, for_debug); case WireType::UNUSED: log_assert(is_lhs); f << "value<" << chunk.width << ">()"; return false; } if (chunk.width == chunk.wire->width && chunk.offset == 0) return false; else if (chunk.width == 1) f << ".slice<" << chunk.offset << ">()"; else f << ".slice<" << chunk.offset+chunk.width-1 << "," << chunk.offset << ">()"; return true; } } bool dump_sigspec(const RTLIL::SigSpec &sig, bool is_lhs, bool for_debug = false) { if (sig.empty()) { f << "value<0>()"; return false; } else if (sig.is_chunk()) { return dump_sigchunk(sig.as_chunk(), is_lhs, for_debug); } else { bool first = true; auto chunks = sig.chunks(); for (auto it = chunks.rbegin(); it != chunks.rend(); it++) { if (!first) f << ".concat("; bool is_complex = dump_sigchunk(*it, is_lhs, for_debug); if (!is_lhs && it->width == 1) { size_t repeat = 1; while ((it + repeat) != chunks.rend() && *(it + repeat) == *it) repeat++; if (repeat > 1) { if (is_complex) f << ".val()"; f << ".repeat<" << repeat << ">()"; } it += repeat - 1; } if (!first) f << ")"; first = false; } return true; } } void dump_sigspec_lhs(const RTLIL::SigSpec &sig, bool for_debug = false) { dump_sigspec(sig, /*is_lhs=*/true, for_debug); } void dump_sigspec_rhs(const RTLIL::SigSpec &sig, bool for_debug = false) { // In the contexts where we want template argument deduction to occur for `template ... value`, // it is necessary to have the argument to already be a `value`, since template argument deduction and implicit // type conversion are mutually exclusive. In these contexts, we use dump_sigspec_rhs() to emit an explicit // type conversion, but only if the expression needs it. bool is_complex = dump_sigspec(sig, /*is_lhs=*/false, for_debug); if (is_complex) f << ".val()"; } void dump_print(const RTLIL::Cell *cell) { Fmt fmt = {}; fmt.parse_rtlil(cell); f << indent << "if ("; dump_sigspec_rhs(cell->getPort(ID::EN)); f << " == value<1>{1u}) {\n"; inc_indent(); f << indent << print_output; fmt.emit_cxxrtl(f, [this](const RTLIL::SigSpec &sig) { dump_sigspec_rhs(sig); }); f << ";\n"; dec_indent(); f << indent << "}\n"; } void dump_sync_print(std::vector &cells) { log_assert(!cells.empty()); const auto &trg = cells[0]->getPort(ID::TRG); const auto &trg_polarity = cells[0]->getParam(ID::TRG_POLARITY); f << indent << "if ("; for (int i = 0; i < trg.size(); i++) { RTLIL::SigBit trg_bit = trg[i]; trg_bit = sigmaps[trg_bit.wire->module](trg_bit); log_assert(trg_bit.wire); if (i != 0) f << " || "; if (trg_polarity[i] == State::S1) f << "posedge_"; else f << "negedge_"; f << mangle(trg_bit); } f << ") {\n"; inc_indent(); std::sort(cells.begin(), cells.end(), [](const RTLIL::Cell *a, const RTLIL::Cell *b) { return a->getParam(ID::PRIORITY).as_int() > b->getParam(ID::PRIORITY).as_int(); }); for (auto cell : cells) { log_assert(cell->getParam(ID::TRG_ENABLE).as_bool()); log_assert(cell->getPort(ID::TRG) == trg); log_assert(cell->getParam(ID::TRG_POLARITY) == trg_polarity); std::vector inlined_cells; collect_cell_eval(cell, /*for_debug=*/false, inlined_cells); dump_inlined_cells(inlined_cells); dump_print(cell); } dec_indent(); f << indent << "}\n"; } void dump_inlined_cells(const std::vector &cells) { if (cells.empty()) { f << indent << "// connection\n"; } else if (cells.size() == 1) { dump_attrs(cells.front()); f << indent << "// cell " << cells.front()->name.str() << "\n"; } else { f << indent << "// cells"; for (auto cell : cells) f << " " << cell->name.str(); f << "\n"; } } void collect_sigspec_rhs(const RTLIL::SigSpec &sig, bool for_debug, std::vector &cells) { for (auto chunk : sig.chunks()) { if (!chunk.wire) continue; const auto &wire_type = wire_types[chunk.wire]; switch (wire_type.type) { case WireType::INLINE: if (wire_type.cell_subst != nullptr) { collect_cell_eval(wire_type.cell_subst, for_debug, cells); break; } YS_FALLTHROUGH case WireType::ALIAS: collect_sigspec_rhs(wire_type.sig_subst, for_debug, cells); break; default: break; } } } void dump_connect_expr(const RTLIL::SigSig &conn, bool for_debug = false) { dump_sigspec_rhs(conn.second, for_debug); } void dump_connect(const RTLIL::SigSig &conn, bool for_debug = false) { std::vector inlined_cells; collect_sigspec_rhs(conn.second, for_debug, inlined_cells); dump_inlined_cells(inlined_cells); f << indent; dump_sigspec_lhs(conn.first, for_debug); f << " = "; dump_connect_expr(conn, for_debug); f << ";\n"; } void collect_connect(const RTLIL::SigSig &conn, bool for_debug, std::vector &cells) { collect_sigspec_rhs(conn.second, for_debug, cells); } void dump_cell_sync(const RTLIL::Cell *cell, bool for_debug = false) { const char *access = is_cxxrtl_blackbox_cell(cell) ? "->" : "."; f << indent << "// cell " << cell->name.str() << " syncs\n"; for (auto conn : cell->connections()) if (cell->output(conn.first)) if (is_cxxrtl_sync_port(cell, conn.first)) { f << indent; dump_sigspec_lhs(conn.second, for_debug); f << " = " << mangle(cell) << access << mangle_wire_name(conn.first) << ".curr;\n"; } } void dump_cell_expr(const RTLIL::Cell *cell, bool for_debug = false) { // Unary cells if (is_unary_cell(cell->type)) { f << cell->type.substr(1); if (is_extending_cell(cell->type)) f << '_' << (cell->getParam(ID::A_SIGNED).as_bool() ? 's' : 'u'); f << "<" << cell->getParam(ID::Y_WIDTH).as_int() << ">("; dump_sigspec_rhs(cell->getPort(ID::A), for_debug); f << ")"; // Binary cells } else if (is_binary_cell(cell->type)) { f << cell->type.substr(1); if (is_extending_cell(cell->type)) f << '_' << (cell->getParam(ID::A_SIGNED).as_bool() ? 's' : 'u') << (cell->getParam(ID::B_SIGNED).as_bool() ? 's' : 'u'); f << "<" << cell->getParam(ID::Y_WIDTH).as_int() << ">("; dump_sigspec_rhs(cell->getPort(ID::A), for_debug); f << ", "; dump_sigspec_rhs(cell->getPort(ID::B), for_debug); f << ")"; // Muxes } else if (cell->type == ID($mux)) { f << "("; dump_sigspec_rhs(cell->getPort(ID::S), for_debug); f << " ? "; dump_sigspec_rhs(cell->getPort(ID::B), for_debug); f << " : "; dump_sigspec_rhs(cell->getPort(ID::A), for_debug); f << ")"; // Parallel (one-hot) muxes } else if (cell->type == ID($pmux)) { int width = cell->getParam(ID::WIDTH).as_int(); int s_width = cell->getParam(ID::S_WIDTH).as_int(); for (int part = 0; part < s_width; part++) { f << "("; dump_sigspec_rhs(cell->getPort(ID::S).extract(part), for_debug); f << " ? "; dump_sigspec_rhs(cell->getPort(ID::B).extract(part * width, width), for_debug); f << " : "; } dump_sigspec_rhs(cell->getPort(ID::A), for_debug); for (int part = 0; part < s_width; part++) { f << ")"; } // Big muxes } else if (cell->type == ID($bmux)) { dump_sigspec_rhs(cell->getPort(ID::A), for_debug); f << ".bmux<"; f << cell->getParam(ID::WIDTH).as_int(); f << ">("; dump_sigspec_rhs(cell->getPort(ID::S), for_debug); f << ").val()"; // Demuxes } else if (cell->type == ID($demux)) { dump_sigspec_rhs(cell->getPort(ID::A), for_debug); f << ".demux<"; f << GetSize(cell->getPort(ID::Y)); f << ">("; dump_sigspec_rhs(cell->getPort(ID::S), for_debug); f << ").val()"; // Concats } else if (cell->type == ID($concat)) { dump_sigspec_rhs(cell->getPort(ID::B), for_debug); f << ".concat("; dump_sigspec_rhs(cell->getPort(ID::A), for_debug); f << ").val()"; // Slices } else if (cell->type == ID($slice)) { dump_sigspec_rhs(cell->getPort(ID::A), for_debug); f << ".slice<"; f << cell->getParam(ID::OFFSET).as_int() + cell->getParam(ID::Y_WIDTH).as_int() - 1; f << ","; f << cell->getParam(ID::OFFSET).as_int(); f << ">().val()"; } else { log_assert(false); } } void dump_cell_eval(const RTLIL::Cell *cell, bool for_debug = false) { std::vector inlined_cells; collect_cell_eval(cell, for_debug, inlined_cells); dump_inlined_cells(inlined_cells); // Elidable cells if (is_inlinable_cell(cell->type)) { f << indent; dump_sigspec_lhs(cell->getPort(ID::Y), for_debug); f << " = "; dump_cell_expr(cell, for_debug); f << ";\n"; // $print cell } else if (cell->type == ID($print)) { log_assert(!for_debug); // Sync $print cells are grouped into PRINT_SYNC nodes in the FlowGraph. log_assert(!cell->getParam(ID::TRG_ENABLE).as_bool()); f << indent << "auto " << mangle(cell) << "_curr = "; dump_sigspec_rhs(cell->getPort(ID::EN)); f << ".concat("; dump_sigspec_rhs(cell->getPort(ID::ARGS)); f << ").val();\n"; f << indent << "if (" << mangle(cell) << " != " << mangle(cell) << "_curr) {\n"; inc_indent(); dump_print(cell); f << indent << mangle(cell) << " = " << mangle(cell) << "_curr;\n"; dec_indent(); f << indent << "}\n"; // Flip-flops } else if (is_ff_cell(cell->type)) { log_assert(!for_debug); // Clocks might be slices of larger signals but should only ever be single bit if (cell->hasPort(ID::CLK) && is_valid_clock(cell->getPort(ID::CLK))) { // Edge-sensitive logic RTLIL::SigBit clk_bit = cell->getPort(ID::CLK)[0]; clk_bit = sigmaps[clk_bit.wire->module](clk_bit); if (clk_bit.wire) { f << indent << "if (" << (cell->getParam(ID::CLK_POLARITY).as_bool() ? "posedge_" : "negedge_") << mangle(clk_bit) << ") {\n"; } else { f << indent << "if (false) {\n"; } inc_indent(); if (cell->hasPort(ID::EN)) { f << indent << "if ("; dump_sigspec_rhs(cell->getPort(ID::EN)); f << " == value<1> {" << cell->getParam(ID::EN_POLARITY).as_bool() << "u}) {\n"; inc_indent(); } f << indent; dump_sigspec_lhs(cell->getPort(ID::Q)); f << " = "; dump_sigspec_rhs(cell->getPort(ID::D)); f << ";\n"; if (cell->hasPort(ID::EN) && cell->type != ID($sdffce)) { dec_indent(); f << indent << "}\n"; } if (cell->hasPort(ID::SRST)) { f << indent << "if ("; dump_sigspec_rhs(cell->getPort(ID::SRST)); f << " == value<1> {" << cell->getParam(ID::SRST_POLARITY).as_bool() << "u}) {\n"; inc_indent(); f << indent; dump_sigspec_lhs(cell->getPort(ID::Q)); f << " = "; dump_const(cell->getParam(ID::SRST_VALUE)); f << ";\n"; dec_indent(); f << indent << "}\n"; } if (cell->hasPort(ID::EN) && cell->type == ID($sdffce)) { dec_indent(); f << indent << "}\n"; } dec_indent(); f << indent << "}\n"; } else if (cell->hasPort(ID::EN)) { // Level-sensitive logic f << indent << "if ("; dump_sigspec_rhs(cell->getPort(ID::EN)); f << " == value<1> {" << cell->getParam(ID::EN_POLARITY).as_bool() << "u}) {\n"; inc_indent(); f << indent; dump_sigspec_lhs(cell->getPort(ID::Q)); f << " = "; dump_sigspec_rhs(cell->getPort(ID::D)); f << ";\n"; dec_indent(); f << indent << "}\n"; } if (cell->hasPort(ID::ARST)) { // Asynchronous reset (entire coarse cell at once) f << indent << "if ("; dump_sigspec_rhs(cell->getPort(ID::ARST)); f << " == value<1> {" << cell->getParam(ID::ARST_POLARITY).as_bool() << "u}) {\n"; inc_indent(); f << indent; dump_sigspec_lhs(cell->getPort(ID::Q)); f << " = "; dump_const(cell->getParam(ID::ARST_VALUE)); f << ";\n"; dec_indent(); f << indent << "}\n"; } if (cell->hasPort(ID::ALOAD)) { // Asynchronous load f << indent << "if ("; dump_sigspec_rhs(cell->getPort(ID::ALOAD)); f << " == value<1> {" << cell->getParam(ID::ALOAD_POLARITY).as_bool() << "u}) {\n"; inc_indent(); f << indent; dump_sigspec_lhs(cell->getPort(ID::Q)); f << " = "; dump_sigspec_rhs(cell->getPort(ID::AD)); f << ";\n"; dec_indent(); f << indent << "}\n"; } if (cell->hasPort(ID::SET)) { // Asynchronous set (for individual bits) f << indent; dump_sigspec_lhs(cell->getPort(ID::Q)); f << " = "; dump_sigspec_lhs(cell->getPort(ID::Q)); f << ".update("; dump_const(RTLIL::Const(RTLIL::S1, cell->getParam(ID::WIDTH).as_int())); f << ", "; dump_sigspec_rhs(cell->getPort(ID::SET)); f << (cell->getParam(ID::SET_POLARITY).as_bool() ? "" : ".bit_not()") << ");\n"; } if (cell->hasPort(ID::CLR)) { // Asynchronous clear (for individual bits; priority over set) f << indent; dump_sigspec_lhs(cell->getPort(ID::Q)); f << " = "; dump_sigspec_lhs(cell->getPort(ID::Q)); f << ".update("; dump_const(RTLIL::Const(RTLIL::S0, cell->getParam(ID::WIDTH).as_int())); f << ", "; dump_sigspec_rhs(cell->getPort(ID::CLR)); f << (cell->getParam(ID::CLR_POLARITY).as_bool() ? "" : ".bit_not()") << ");\n"; } // Internal cells } else if (is_internal_cell(cell->type)) { log_cmd_error("Unsupported internal cell `%s'.\n", cell->type.c_str()); // User cells } else { log_assert(!for_debug); log_assert(cell->known()); bool buffered_inputs = false; const char *access = is_cxxrtl_blackbox_cell(cell) ? "->" : "."; for (auto conn : cell->connections()) if (cell->input(conn.first)) { RTLIL::Module *cell_module = cell->module->design->module(cell->type); log_assert(cell_module != nullptr && cell_module->wire(conn.first)); RTLIL::Wire *cell_module_wire = cell_module->wire(conn.first); f << indent << mangle(cell) << access << mangle_wire_name(conn.first); if (!is_cxxrtl_blackbox_cell(cell) && wire_types[cell_module_wire].is_buffered()) { buffered_inputs = true; f << ".next"; } f << " = "; dump_sigspec_rhs(conn.second); f << ";\n"; if (getenv("CXXRTL_VOID_MY_WARRANTY") && conn.second.is_wire()) { // Until we have proper clock tree detection, this really awful hack that opportunistically // propagates prev_* values for clocks can be used to estimate how much faster a design could // be if only one clock edge was simulated by replacing: // top.p_clk = value<1>{0u}; top.step(); // top.p_clk = value<1>{1u}; top.step(); // with: // top.prev_p_clk = value<1>{0u}; top.p_clk = value<1>{1u}; top.step(); // Don't rely on this; it will be removed without warning. if (edge_wires[conn.second.as_wire()] && edge_wires[cell_module_wire]) { f << indent << mangle(cell) << access << "prev_" << mangle(cell_module_wire) << " = "; f << "prev_" << mangle(conn.second.as_wire()) << ";\n"; } } } auto assign_from_outputs = [&](bool cell_converged) { for (auto conn : cell->connections()) { if (cell->output(conn.first)) { if (conn.second.empty()) continue; // ignore disconnected ports if (is_cxxrtl_sync_port(cell, conn.first)) continue; // fully sync ports are handled in CELL_SYNC nodes f << indent; dump_sigspec_lhs(conn.second); f << " = " << mangle(cell) << access << mangle_wire_name(conn.first); // Similarly to how there is no purpose to buffering cell inputs, there is also no purpose to buffering // combinatorial cell outputs in case the cell converges within one cycle. (To convince yourself that // this optimization is valid, consider that, since the cell converged within one cycle, it would not // have any buffered wires if they were not output ports. Imagine inlining the cell's eval() function, // and consider the fate of the localized wires that used to be output ports.) // // It is not possible to know apriori whether the cell (which may be late bound) will converge immediately. // Because of this, the choice between using .curr (appropriate for buffered outputs) and .next (appropriate // for unbuffered outputs) is made at runtime. if (cell_converged && is_cxxrtl_comb_port(cell, conn.first)) f << ".next;\n"; else f << ".curr;\n"; } } }; if (buffered_inputs) { // If we have any buffered inputs, there's no chance of converging immediately. f << indent << mangle(cell) << access << "eval();\n"; f << indent << "converged = false;\n"; assign_from_outputs(/*cell_converged=*/false); } else { f << indent << "if (" << mangle(cell) << access << "eval()) {\n"; inc_indent(); assign_from_outputs(/*cell_converged=*/true); dec_indent(); f << indent << "} else {\n"; inc_indent(); f << indent << "converged = false;\n"; assign_from_outputs(/*cell_converged=*/false); dec_indent(); f << indent << "}\n"; } } } void collect_cell_eval(const RTLIL::Cell *cell, bool for_debug, std::vector &cells) { cells.push_back(cell); for (auto port : cell->connections()) if (cell->input(port.first)) collect_sigspec_rhs(port.second, for_debug, cells); } void dump_assign(const RTLIL::SigSig &sigsig, bool for_debug = false) { f << indent; dump_sigspec_lhs(sigsig.first, for_debug); f << " = "; dump_sigspec_rhs(sigsig.second, for_debug); f << ";\n"; } void dump_case_rule(const RTLIL::CaseRule *rule, bool for_debug = false) { for (auto action : rule->actions) dump_assign(action, for_debug); for (auto switch_ : rule->switches) dump_switch_rule(switch_, for_debug); } void dump_switch_rule(const RTLIL::SwitchRule *rule, bool for_debug = false) { // The switch attributes are printed before the switch condition is captured. dump_attrs(rule); std::string signal_temp = fresh_temporary(); f << indent << "const value<" << rule->signal.size() << "> &" << signal_temp << " = "; dump_sigspec(rule->signal, /*is_lhs=*/false, for_debug); f << ";\n"; bool first = true; for (auto case_ : rule->cases) { // The case attributes (for nested cases) are printed before the if/else if/else statement. dump_attrs(rule); f << indent; if (!first) f << "} else "; first = false; if (!case_->compare.empty()) { f << "if ("; bool first = true; for (auto &compare : case_->compare) { if (!first) f << " || "; first = false; if (compare.is_fully_def()) { f << signal_temp << " == "; dump_sigspec(compare, /*is_lhs=*/false, for_debug); } else if (compare.is_fully_const()) { RTLIL::Const compare_mask, compare_value; for (auto bit : compare.as_const()) { switch (bit) { case RTLIL::S0: case RTLIL::S1: 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); break; default: log_assert(false); } } f << "and_uu<" << compare.size() << ">(" << signal_temp << ", "; dump_const(compare_mask); f << ") == "; dump_const(compare_value); } else { log_assert(false); } } f << ") "; } f << "{\n"; inc_indent(); dump_case_rule(case_, for_debug); dec_indent(); } f << indent << "}\n"; } void dump_process_case(const RTLIL::Process *proc, bool for_debug = false) { dump_attrs(proc); f << indent << "// process " << proc->name.str() << " case\n"; // The case attributes (for root case) are always empty. log_assert(proc->root_case.attributes.empty()); dump_case_rule(&proc->root_case, for_debug); } void dump_process_syncs(const RTLIL::Process *proc, bool for_debug = false) { dump_attrs(proc); f << indent << "// process " << proc->name.str() << " syncs\n"; for (auto sync : proc->syncs) { log_assert(!for_debug || sync->type == RTLIL::STa); RTLIL::SigBit sync_bit; if (!sync->signal.empty()) { sync_bit = sync->signal[0]; sync_bit = sigmaps[sync_bit.wire->module](sync_bit); if (!sync_bit.is_wire()) continue; // a clock, or more commonly a reset, can be tied to a constant driver } pool events; switch (sync->type) { case RTLIL::STp: log_assert(sync_bit.wire != nullptr); events.insert("posedge_" + mangle(sync_bit)); break; case RTLIL::STn: log_assert(sync_bit.wire != nullptr); events.insert("negedge_" + mangle(sync_bit)); break; case RTLIL::STe: log_assert(sync_bit.wire != nullptr); events.insert("posedge_" + mangle(sync_bit)); events.insert("negedge_" + mangle(sync_bit)); break; case RTLIL::STa: events.insert("true"); break; case RTLIL::ST0: case RTLIL::ST1: case RTLIL::STg: case RTLIL::STi: log_assert(false); } if (!events.empty()) { f << indent << "if ("; bool first = true; for (auto &event : events) { if (!first) f << " || "; first = false; f << event; } f << ") {\n"; inc_indent(); for (auto &action : sync->actions) dump_assign(action, for_debug); for (auto &memwr : sync->mem_write_actions) { RTLIL::Memory *memory = proc->module->memories.at(memwr.memid); std::string valid_index_temp = fresh_temporary(); f << indent << "auto " << valid_index_temp << " = memory_index("; dump_sigspec_rhs(memwr.address); f << ", " << memory->start_offset << ", " << memory->size << ");\n"; // See below for rationale of having both the assert and the condition. // // If assertions are disabled, out of bounds writes are defined to do nothing. f << indent << "CXXRTL_ASSERT(" << valid_index_temp << ".valid && \"out of bounds write\");\n"; f << indent << "if (" << valid_index_temp << ".valid) {\n"; inc_indent(); f << indent << mangle(memory) << ".update(" << valid_index_temp << ".index, "; dump_sigspec_rhs(memwr.data); f << ", "; dump_sigspec_rhs(memwr.enable); f << ");\n"; dec_indent(); f << indent << "}\n"; } dec_indent(); f << indent << "}\n"; } } } void dump_mem_rdport(const Mem *mem, int portidx, bool for_debug = false) { auto &port = mem->rd_ports[portidx]; dump_attrs(&port); f << indent << "// memory " << mem->memid.str() << " read port " << portidx << "\n"; if (port.clk_enable) { log_assert(!for_debug); RTLIL::SigBit clk_bit = port.clk[0]; clk_bit = sigmaps[clk_bit.wire->module](clk_bit); if (clk_bit.wire) { f << indent << "if (" << (port.clk_polarity ? "posedge_" : "negedge_") << mangle(clk_bit) << ") {\n"; } else { f << indent << "if (false) {\n"; } inc_indent(); } std::vector inlined_cells_addr; collect_sigspec_rhs(port.addr, for_debug, inlined_cells_addr); if (!inlined_cells_addr.empty()) dump_inlined_cells(inlined_cells_addr); std::string valid_index_temp = fresh_temporary(); f << indent << "auto " << valid_index_temp << " = memory_index("; // Almost all non-elidable cells cannot appear in debug_eval(), but $memrd is an exception; asynchronous // memory read ports can. dump_sigspec_rhs(port.addr, for_debug); f << ", " << mem->start_offset << ", " << mem->size << ");\n"; bool has_enable = port.clk_enable && !port.en.is_fully_ones(); if (has_enable) { std::vector inlined_cells_en; collect_sigspec_rhs(port.en, for_debug, inlined_cells_en); if (!inlined_cells_en.empty()) dump_inlined_cells(inlined_cells_en); f << indent << "if ("; dump_sigspec_rhs(port.en); f << ") {\n"; inc_indent(); } // The generated code has two bounds checks; one in an assertion, and another that guards the read. // This is done so that the code does not invoke undefined behavior under any conditions, but nevertheless // loudly crashes if an illegal condition is encountered. The assert may be turned off with -DCXXRTL_NDEBUG // not only for release builds, but also to make sure the simulator (which is presumably embedded in some // larger program) will never crash the code that calls into it. // // If assertions are disabled, out of bounds reads are defined to return zero. f << indent << "CXXRTL_ASSERT(" << valid_index_temp << ".valid && \"out of bounds read\");\n"; f << indent << "if(" << valid_index_temp << ".valid) {\n"; inc_indent(); if (!mem->wr_ports.empty()) { std::string lhs_temp = fresh_temporary(); f << indent << "value<" << mem->width << "> " << lhs_temp << " = " << mangle(mem) << "[" << valid_index_temp << ".index];\n"; bool transparent = false; for (auto bit : port.transparency_mask) if (bit) transparent = true; if (transparent) { std::string addr_temp = fresh_temporary(); f << indent << "const value<" << port.addr.size() << "> &" << addr_temp << " = "; dump_sigspec_rhs(port.addr); f << ";\n"; for (int i = 0; i < GetSize(mem->wr_ports); i++) { auto &wrport = mem->wr_ports[i]; if (!port.transparency_mask[i]) continue; f << indent << "if (" << addr_temp << " == "; dump_sigspec_rhs(wrport.addr); f << ") {\n"; inc_indent(); f << indent << lhs_temp << " = " << lhs_temp; f << ".update("; dump_sigspec_rhs(wrport.data); f << ", "; dump_sigspec_rhs(wrport.en); f << ");\n"; dec_indent(); f << indent << "}\n"; } } f << indent; dump_sigspec_lhs(port.data); f << " = " << lhs_temp << ";\n"; } else { f << indent; dump_sigspec_lhs(port.data); f << " = " << mangle(mem) << "[" << valid_index_temp << ".index];\n"; } dec_indent(); f << indent << "} else {\n"; inc_indent(); f << indent; dump_sigspec_lhs(port.data); f << " = value<" << mem->width << "> {};\n"; dec_indent(); f << indent << "}\n"; if (has_enable && !port.ce_over_srst) { dec_indent(); f << indent << "}\n"; } if (port.srst != State::S0) { // Synchronous reset std::vector inlined_cells_srst; collect_sigspec_rhs(port.srst, for_debug, inlined_cells_srst); if (!inlined_cells_srst.empty()) dump_inlined_cells(inlined_cells_srst); f << indent << "if ("; dump_sigspec_rhs(port.srst); f << " == value<1> {1u}) {\n"; inc_indent(); f << indent; dump_sigspec_lhs(port.data); f << " = "; dump_const(port.srst_value); f << ";\n"; dec_indent(); f << indent << "}\n"; } if (has_enable && port.ce_over_srst) { dec_indent(); f << indent << "}\n"; } if (port.clk_enable) { dec_indent(); f << indent << "}\n"; } if (port.arst != State::S0) { // Asynchronous reset std::vector inlined_cells_arst; collect_sigspec_rhs(port.arst, for_debug, inlined_cells_arst); if (!inlined_cells_arst.empty()) dump_inlined_cells(inlined_cells_arst); f << indent << "if ("; dump_sigspec_rhs(port.arst); f << " == value<1> {1u}) {\n"; inc_indent(); f << indent; dump_sigspec_lhs(port.data); f << " = "; dump_const(port.arst_value); f << ";\n"; dec_indent(); f << indent << "}\n"; } } void dump_mem_wrports(const Mem *mem, bool for_debug = false) { log_assert(!for_debug); for (int portidx = 0; portidx < GetSize(mem->wr_ports); portidx++) { auto &port = mem->wr_ports[portidx]; dump_attrs(&port); f << indent << "// memory " << mem->memid.str() << " write port " << portidx << "\n"; if (port.clk_enable) { RTLIL::SigBit clk_bit = port.clk[0]; clk_bit = sigmaps[clk_bit.wire->module](clk_bit); if (clk_bit.wire) { f << indent << "if (" << (port.clk_polarity ? "posedge_" : "negedge_") << mangle(clk_bit) << ") {\n"; } else { f << indent << "if (false) {\n"; } inc_indent(); } std::vector inlined_cells_addr; collect_sigspec_rhs(port.addr, for_debug, inlined_cells_addr); if (!inlined_cells_addr.empty()) dump_inlined_cells(inlined_cells_addr); std::string valid_index_temp = fresh_temporary(); f << indent << "auto " << valid_index_temp << " = memory_index("; dump_sigspec_rhs(port.addr); f << ", " << mem->start_offset << ", " << mem->size << ");\n"; // See above for rationale of having both the assert and the condition. // // If assertions are disabled, out of bounds writes are defined to do nothing. f << indent << "CXXRTL_ASSERT(" << valid_index_temp << ".valid && \"out of bounds write\");\n"; f << indent << "if (" << valid_index_temp << ".valid) {\n"; inc_indent(); std::vector inlined_cells; collect_sigspec_rhs(port.data, for_debug, inlined_cells); collect_sigspec_rhs(port.en, for_debug, inlined_cells); if (!inlined_cells.empty()) dump_inlined_cells(inlined_cells); f << indent << mangle(mem) << ".update(" << valid_index_temp << ".index, "; dump_sigspec_rhs(port.data); f << ", "; dump_sigspec_rhs(port.en); f << ", " << portidx << ");\n"; dec_indent(); f << indent << "}\n"; if (port.clk_enable) { dec_indent(); f << indent << "}\n"; } } } void dump_wire(const RTLIL::Wire *wire, bool is_local) { const auto &wire_type = wire_types[wire]; if (!wire_type.is_named() || wire_type.is_local() != is_local) return; dump_attrs(wire); f << indent; if (wire->port_input && wire->port_output) f << "/*inout*/ "; else if (wire->port_input) f << "/*input*/ "; else if (wire->port_output) f << "/*output*/ "; f << (wire_type.is_buffered() ? "wire" : "value"); if (wire->module->has_attribute(ID(cxxrtl_blackbox)) && wire->has_attribute(ID(cxxrtl_width))) { f << "<" << wire->get_string_attribute(ID(cxxrtl_width)) << ">"; } else { f << "<" << wire->width << ">"; } f << " " << mangle(wire) << ";\n"; if (edge_wires[wire]) { if (!wire_type.is_buffered()) { f << indent << "value<" << wire->width << "> prev_" << mangle(wire) << ";\n"; } for (auto edge_type : edge_types) { if (edge_type.first.wire == wire) { std::string prev, next; if (!wire_type.is_buffered()) { prev = "prev_" + mangle(edge_type.first.wire); next = mangle(edge_type.first.wire); } else { prev = mangle(edge_type.first.wire) + ".curr"; next = mangle(edge_type.first.wire) + ".next"; } prev += ".slice<" + std::to_string(edge_type.first.offset) + ">().val()"; next += ".slice<" + std::to_string(edge_type.first.offset) + ">().val()"; if (edge_type.second != RTLIL::STn) { f << indent << "bool posedge_" << mangle(edge_type.first) << "() const {\n"; inc_indent(); f << indent << "return !" << prev << " && " << next << ";\n"; dec_indent(); f << indent << "}\n"; } if (edge_type.second != RTLIL::STp) { f << indent << "bool negedge_" << mangle(edge_type.first) << "() const {\n"; inc_indent(); f << indent << "return " << prev << " && !" << next << ";\n"; dec_indent(); f << indent << "}\n"; } } } } } void dump_debug_wire(const RTLIL::Wire *wire, bool is_local) { const auto &wire_type = wire_types[wire]; if (wire_type.is_member()) return; const auto &debug_wire_type = debug_wire_types[wire]; if (!debug_wire_type.is_named() || debug_wire_type.is_local() != is_local) return; dump_attrs(wire); f << indent; if (debug_wire_type.is_outline()) f << "/*outline*/ "; f << "value<" << wire->width << "> " << mangle(wire) << ";\n"; } void dump_reset_method(RTLIL::Module *module) { int mem_init_idx = 0; inc_indent(); for (auto wire : module->wires()) { const auto &wire_type = wire_types[wire]; if (!wire_type.is_named() || wire_type.is_local()) continue; if (!wire_init.count(wire)) continue; f << indent << mangle(wire) << " = "; if (wire_types[wire].is_buffered()) { f << "wire<" << wire->width << ">"; } else { f << "value<" << wire->width << ">"; } dump_const_init(wire_init.at(wire), wire->width); f << ";\n"; if (edge_wires[wire] && !wire_types[wire].is_buffered()) { f << indent << "prev_" << mangle(wire) << " = "; dump_const(wire_init.at(wire), wire->width); f << ";\n"; } } for (auto &mem : mod_memories[module]) { for (auto &init : mem.inits) { if (init.removed) continue; dump_attrs(&init); int words = GetSize(init.data) / mem.width; f << indent << "static const value<" << mem.width << "> "; f << "mem_init_" << ++mem_init_idx << "[" << words << "] {"; inc_indent(); for (int n = 0; n < words; n++) { if (n % 4 == 0) f << "\n" << indent; else f << " "; dump_const(init.data, mem.width, n * mem.width, /*fixed_width=*/true); f << ","; } dec_indent(); f << "\n"; f << indent << "};\n"; f << indent << "std::copy(std::begin(mem_init_" << mem_init_idx << "), "; f << "std::end(mem_init_" << mem_init_idx << "), "; f << "&" << mangle(&mem) << ".data[" << stringf("%#x", init.addr.as_int()) << "]);\n"; } } for (auto cell : module->cells()) { if (is_internal_cell(cell->type)) continue; f << indent << mangle(cell); RTLIL::Module *cell_module = module->design->module(cell->type); if (cell_module->get_bool_attribute(ID(cxxrtl_blackbox))) { f << "->reset();\n"; } else { f << ".reset();\n"; } } dec_indent(); } void dump_eval_method(RTLIL::Module *module) { inc_indent(); f << indent << "bool converged = " << (eval_converges.at(module) ? "true" : "false") << ";\n"; if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) { for (auto wire : module->wires()) { if (edge_wires[wire]) { for (auto edge_type : edge_types) { if (edge_type.first.wire == wire) { if (edge_type.second != RTLIL::STn) { f << indent << "bool posedge_" << mangle(edge_type.first) << " = "; f << "this->posedge_" << mangle(edge_type.first) << "();\n"; } if (edge_type.second != RTLIL::STp) { f << indent << "bool negedge_" << mangle(edge_type.first) << " = "; f << "this->negedge_" << mangle(edge_type.first) << "();\n"; } } } } } for (auto wire : module->wires()) dump_wire(wire, /*is_local=*/true); for (auto node : schedule[module]) { switch (node.type) { case FlowGraph::Node::Type::CONNECT: dump_connect(node.connect); break; case FlowGraph::Node::Type::CELL_SYNC: dump_cell_sync(node.cell); break; case FlowGraph::Node::Type::CELL_EVAL: dump_cell_eval(node.cell); break; case FlowGraph::Node::Type::PRINT_SYNC: dump_sync_print(node.print_sync_cells); break; case FlowGraph::Node::Type::PROCESS_CASE: dump_process_case(node.process); break; case FlowGraph::Node::Type::PROCESS_SYNC: dump_process_syncs(node.process); break; case FlowGraph::Node::Type::MEM_RDPORT: dump_mem_rdport(node.mem, node.portidx); break; case FlowGraph::Node::Type::MEM_WRPORTS: dump_mem_wrports(node.mem); break; } } } f << indent << "return converged;\n"; dec_indent(); } void dump_debug_eval_method(RTLIL::Module *module) { inc_indent(); for (auto wire : module->wires()) dump_debug_wire(wire, /*is_local=*/true); for (auto node : debug_schedule[module]) { switch (node.type) { case FlowGraph::Node::Type::CONNECT: dump_connect(node.connect, /*for_debug=*/true); break; case FlowGraph::Node::Type::CELL_SYNC: dump_cell_sync(node.cell, /*for_debug=*/true); break; case FlowGraph::Node::Type::CELL_EVAL: dump_cell_eval(node.cell, /*for_debug=*/true); break; case FlowGraph::Node::Type::PROCESS_CASE: dump_process_case(node.process, /*for_debug=*/true); break; case FlowGraph::Node::Type::PROCESS_SYNC: dump_process_syncs(node.process, /*for_debug=*/true); break; case FlowGraph::Node::Type::MEM_RDPORT: dump_mem_rdport(node.mem, node.portidx, /*for_debug=*/true); break; case FlowGraph::Node::Type::MEM_WRPORTS: dump_mem_wrports(node.mem, /*for_debug=*/true); break; default: log_abort(); } } dec_indent(); } void dump_commit_method(RTLIL::Module *module) { inc_indent(); f << indent << "bool changed = false;\n"; for (auto wire : module->wires()) { const auto &wire_type = wire_types[wire]; if (wire_type.type == WireType::MEMBER && edge_wires[wire]) f << indent << "prev_" << mangle(wire) << " = " << mangle(wire) << ";\n"; if (wire_type.is_buffered()) f << indent << "if (" << mangle(wire) << ".commit()) changed = true;\n"; } if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) { for (auto &mem : mod_memories[module]) { if (!writable_memories.count({module, mem.memid})) continue; f << indent << "if (" << mangle(&mem) << ".commit()) changed = true;\n"; } for (auto cell : module->cells()) { if (is_internal_cell(cell->type)) continue; const char *access = is_cxxrtl_blackbox_cell(cell) ? "->" : "."; f << indent << "if (" << mangle(cell) << access << "commit()) changed = true;\n"; } } f << indent << "return changed;\n"; dec_indent(); } void dump_debug_info_method(RTLIL::Module *module) { size_t count_public_wires = 0; size_t count_member_wires = 0; size_t count_undriven = 0; size_t count_driven_sync = 0; size_t count_driven_comb = 0; size_t count_mixed_driver = 0; size_t count_alias_wires = 0; size_t count_const_wires = 0; size_t count_inline_wires = 0; size_t count_skipped_wires = 0; inc_indent(); f << indent << "assert(path.empty() || path[path.size() - 1] == ' ');\n"; for (auto wire : module->wires()) { const auto &debug_wire_type = debug_wire_types[wire]; if (!wire->name.isPublic()) continue; count_public_wires++; switch (debug_wire_type.type) { case WireType::BUFFERED: case WireType::MEMBER: { // Member wire std::vector flags; if (wire->port_input && wire->port_output) flags.push_back("INOUT"); else if (wire->port_output) flags.push_back("OUTPUT"); else if (wire->port_input) flags.push_back("INPUT"); bool has_driven_sync = false; bool has_driven_comb = false; bool has_undriven = false; if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) { for (auto bit : SigSpec(wire)) if (!bit_has_state.count(bit)) has_undriven = true; else if (bit_has_state[bit]) has_driven_sync = true; else has_driven_comb = true; } else if (wire->port_output) { switch (cxxrtl_port_type(module, wire->name)) { case CxxrtlPortType::SYNC: has_driven_sync = true; break; case CxxrtlPortType::COMB: has_driven_comb = true; break; case CxxrtlPortType::UNKNOWN: has_driven_sync = has_driven_comb = true; break; } } else { has_undriven = true; } if (has_undriven) flags.push_back("UNDRIVEN"); if (!has_driven_sync && !has_driven_comb && has_undriven) count_undriven++; if (has_driven_sync) flags.push_back("DRIVEN_SYNC"); if (has_driven_sync && !has_driven_comb && !has_undriven) count_driven_sync++; if (has_driven_comb) flags.push_back("DRIVEN_COMB"); if (!has_driven_sync && has_driven_comb && !has_undriven) count_driven_comb++; if (has_driven_sync + has_driven_comb + has_undriven > 1) count_mixed_driver++; f << indent << "items.add(path + " << escape_cxx_string(get_hdl_name(wire)); f << ", debug_item(" << mangle(wire) << ", " << wire->start_offset; bool first = true; for (auto flag : flags) { if (first) { first = false; f << ", "; } else { f << "|"; } f << "debug_item::" << flag; } f << "));\n"; count_member_wires++; break; } case WireType::ALIAS: { // Alias of a member wire const RTLIL::Wire *aliasee = debug_wire_type.sig_subst.as_wire(); f << indent << "items.add(path + " << escape_cxx_string(get_hdl_name(wire)); f << ", debug_item("; // If the aliasee is an outline, then the alias must be an outline, too; otherwise downstream // tooling has no way to find out about the outline. if (debug_wire_types[aliasee].is_outline()) f << "debug_eval_outline"; else f << "debug_alias()"; f << ", " << mangle(aliasee) << ", " << wire->start_offset << "));\n"; count_alias_wires++; break; } case WireType::CONST: { // Wire tied to a constant f << indent << "static const value<" << wire->width << "> const_" << mangle(wire) << " = "; dump_const(debug_wire_type.sig_subst.as_const()); f << ";\n"; f << indent << "items.add(path + " << escape_cxx_string(get_hdl_name(wire)); f << ", debug_item(const_" << mangle(wire) << ", " << wire->start_offset << "));\n"; count_const_wires++; break; } case WireType::OUTLINE: { // Localized or inlined, but rematerializable wire f << indent << "items.add(path + " << escape_cxx_string(get_hdl_name(wire)); f << ", debug_item(debug_eval_outline, " << mangle(wire) << ", " << wire->start_offset << "));\n"; count_inline_wires++; break; } default: { // Localized or inlined wire with no debug information count_skipped_wires++; break; } } } if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) { for (auto &mem : mod_memories[module]) { if (!mem.memid.isPublic()) continue; f << indent << "items.add(path + " << escape_cxx_string(mem.packed ? get_hdl_name(mem.cell) : get_hdl_name(mem.mem)); f << ", debug_item(" << mangle(&mem) << ", "; f << mem.start_offset << "));\n"; } for (auto cell : module->cells()) { if (is_internal_cell(cell->type)) continue; const char *access = is_cxxrtl_blackbox_cell(cell) ? "->" : "."; f << indent << mangle(cell) << access << "debug_info(items, "; f << "path + " << escape_cxx_string(get_hdl_name(cell) + ' ') << ");\n"; } } dec_indent(); log_debug("Debug information statistics for module `%s':\n", log_id(module)); log_debug(" Public wires: %zu, of which:\n", count_public_wires); log_debug(" Member wires: %zu, of which:\n", count_member_wires); log_debug(" Undriven: %zu (incl. inputs)\n", count_undriven); log_debug(" Driven sync: %zu\n", count_driven_sync); log_debug(" Driven comb: %zu\n", count_driven_comb); log_debug(" Mixed driver: %zu\n", count_mixed_driver); if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) { log_debug(" Inline wires: %zu\n", count_inline_wires); log_debug(" Alias wires: %zu\n", count_alias_wires); log_debug(" Const wires: %zu\n", count_const_wires); log_debug(" Other wires: %zu%s\n", count_skipped_wires, count_skipped_wires > 0 ? " (debug unavailable)" : ""); } } void dump_metadata_map(const dict &metadata_map) { if (metadata_map.empty()) { f << "metadata_map()"; return; } f << "metadata_map({\n"; inc_indent(); for (auto metadata_item : metadata_map) { if (!metadata_item.first.begins_with("\\")) continue; f << indent << "{ " << escape_cxx_string(metadata_item.first.str().substr(1)) << ", "; if (metadata_item.second.flags & RTLIL::CONST_FLAG_REAL) { f << std::showpoint << std::stod(metadata_item.second.decode_string()) << std::noshowpoint; } else if (metadata_item.second.flags & RTLIL::CONST_FLAG_STRING) { f << escape_cxx_string(metadata_item.second.decode_string()); } else { f << metadata_item.second.as_int(/*is_signed=*/metadata_item.second.flags & RTLIL::CONST_FLAG_SIGNED); if (!(metadata_item.second.flags & RTLIL::CONST_FLAG_SIGNED)) f << "u"; } f << " },\n"; } dec_indent(); f << indent << "})"; } void dump_module_intf(RTLIL::Module *module) { dump_attrs(module); if (module->get_bool_attribute(ID(cxxrtl_blackbox))) { if (module->has_attribute(ID(cxxrtl_template))) f << indent << "template" << template_params(module, /*is_decl=*/true) << "\n"; f << indent << "struct " << mangle(module) << " : public module {\n"; inc_indent(); for (auto wire : module->wires()) { if (wire->port_id != 0) dump_wire(wire, /*is_local=*/false); } f << "\n"; f << indent << "void reset() override {\n"; dump_reset_method(module); f << indent << "}\n"; f << "\n"; f << indent << "bool eval() override {\n"; dump_eval_method(module); f << indent << "}\n"; f << "\n"; f << indent << "bool commit() override {\n"; dump_commit_method(module); f << indent << "}\n"; f << "\n"; if (debug_info) { f << indent << "void debug_info(debug_items &items, std::string path = \"\") override {\n"; dump_debug_info_method(module); f << indent << "}\n"; f << "\n"; } f << indent << "static std::unique_ptr<" << mangle(module); f << template_params(module, /*is_decl=*/false) << "> "; f << "create(std::string name, metadata_map parameters, metadata_map attributes);\n"; dec_indent(); f << indent << "}; // struct " << mangle(module) << "\n"; f << "\n"; if (blackbox_specializations.count(module)) { // If templated black boxes are used, the constructor of any module which includes the black box cell // (which calls the declared but not defined in the generated code `create` function) may only be used // if (a) the create function is defined in the same translation unit, or (b) the create function has // a forward-declared explicit specialization. // // Option (b) makes it possible to have the generated code and the black box implementation in different // translation units, which is convenient. Of course, its downside is that black boxes must predefine // a specialization for every combination of parameters the generated code may use; but since the main // purpose of templated black boxes is abstracting over datapath width, it is expected that there would // be very few such combinations anyway. for (auto specialization : blackbox_specializations[module]) { f << indent << "template<>\n"; f << indent << "std::unique_ptr<" << mangle(module) << specialization << "> "; f << mangle(module) << specialization << "::"; f << "create(std::string name, metadata_map parameters, metadata_map attributes);\n"; f << "\n"; } } } else { f << indent << "struct " << mangle(module) << " : public module {\n"; inc_indent(); for (auto wire : module->wires()) dump_wire(wire, /*is_local=*/false); for (auto wire : module->wires()) dump_debug_wire(wire, /*is_local=*/false); bool has_memories = false; for (auto &mem : mod_memories[module]) { dump_attrs(&mem); f << indent << "memory<" << mem.width << "> " << mangle(&mem) << " { " << mem.size << "u };\n"; has_memories = true; } if (has_memories) f << "\n"; bool has_cells = false; for (auto cell : module->cells()) { if (cell->type == ID($print) && !cell->getParam(ID::TRG_ENABLE).as_bool()) { // comb $print cell -- store the last EN/ARGS values to know when they change. dump_attrs(cell); f << indent << "value<" << (1 + cell->getParam(ID::ARGS_WIDTH).as_int()) << "> " << mangle(cell) << ";\n"; } if (is_internal_cell(cell->type)) continue; dump_attrs(cell); RTLIL::Module *cell_module = module->design->module(cell->type); log_assert(cell_module != nullptr); if (cell_module->get_bool_attribute(ID(cxxrtl_blackbox))) { f << indent << "std::unique_ptr<" << mangle(cell_module) << template_args(cell) << "> "; f << mangle(cell) << " = " << mangle(cell_module) << template_args(cell); f << "::create(" << escape_cxx_string(get_hdl_name(cell)) << ", "; dump_metadata_map(cell->parameters); f << ", "; dump_metadata_map(cell->attributes); f << ");\n"; } else { f << indent << mangle(cell_module) << " " << mangle(cell) << " {interior()};\n"; } has_cells = true; } if (has_cells) f << "\n"; f << indent << mangle(module) << "(interior) {}\n"; f << indent << mangle(module) << "() {\n"; inc_indent(); f << indent << "reset();\n"; dec_indent(); f << indent << "};\n"; f << "\n"; f << indent << "void reset() override;\n"; f << indent << "bool eval() override;\n"; f << indent << "bool commit() override;\n"; if (debug_info) { if (debug_eval) { f << "\n"; f << indent << "void debug_eval();\n"; for (auto wire : module->wires()) if (debug_wire_types[wire].is_outline()) { f << indent << "debug_outline debug_eval_outline { std::bind(&" << mangle(module) << "::debug_eval, this) };\n"; break; } } f << "\n"; f << indent << "void debug_info(debug_items &items, std::string path = \"\") override;\n"; } dec_indent(); f << indent << "}; // struct " << mangle(module) << "\n"; f << "\n"; } } void dump_module_impl(RTLIL::Module *module) { if (module->get_bool_attribute(ID(cxxrtl_blackbox))) return; f << indent << "void " << mangle(module) << "::reset() {\n"; dump_reset_method(module); f << indent << "}\n"; f << "\n"; f << indent << "bool " << mangle(module) << "::eval() {\n"; dump_eval_method(module); f << indent << "}\n"; f << "\n"; f << indent << "bool " << mangle(module) << "::commit() {\n"; dump_commit_method(module); f << indent << "}\n"; f << "\n"; if (debug_info) { if (debug_eval) { f << indent << "void " << mangle(module) << "::debug_eval() {\n"; dump_debug_eval_method(module); f << indent << "}\n"; f << "\n"; } f << indent << "CXXRTL_EXTREMELY_COLD\n"; f << indent << "void " << mangle(module) << "::debug_info(debug_items &items, std::string path) {\n"; dump_debug_info_method(module); f << indent << "}\n"; f << "\n"; } } void dump_design(RTLIL::Design *design) { RTLIL::Module *top_module = nullptr; std::vector modules; TopoSort topo_design; bool has_prints = false; for (auto module : design->modules()) { if (!design->selected_module(module)) continue; if (module->get_bool_attribute(ID(cxxrtl_blackbox))) modules.push_back(module); // cxxrtl blackboxes first if (module->get_blackbox_attribute() || module->get_bool_attribute(ID(cxxrtl_blackbox))) continue; if (module->get_bool_attribute(ID::top)) top_module = module; topo_design.node(module); for (auto cell : module->cells()) { if (cell->type == ID($print)) has_prints = true; if (is_internal_cell(cell->type) || is_cxxrtl_blackbox_cell(cell)) continue; RTLIL::Module *cell_module = design->module(cell->type); log_assert(cell_module != nullptr); topo_design.edge(cell_module, module); } } bool no_loops = topo_design.sort(); log_assert(no_loops); modules.insert(modules.end(), topo_design.sorted.begin(), topo_design.sorted.end()); if (split_intf) { // The only thing more depraved than include guards, is mangling filenames to turn them into include guards. std::string include_guard = design_ns + "_header"; std::transform(include_guard.begin(), include_guard.end(), include_guard.begin(), ::toupper); f << "#ifndef " << include_guard << "\n"; f << "#define " << include_guard << "\n"; f << "\n"; if (top_module != nullptr && debug_info) { f << "#include \n"; f << "\n"; f << "#ifdef __cplusplus\n"; f << "extern \"C\" {\n"; f << "#endif\n"; f << "\n"; f << "cxxrtl_toplevel " << design_ns << "_create();\n"; f << "\n"; f << "#ifdef __cplusplus\n"; f << "}\n"; f << "#endif\n"; f << "\n"; } else { f << "// The CXXRTL C API is not available because the design is built without debug information.\n"; f << "\n"; } f << "#ifdef __cplusplus\n"; f << "\n"; f << "#include \n"; f << "\n"; f << "using namespace cxxrtl;\n"; f << "\n"; f << "namespace " << design_ns << " {\n"; f << "\n"; for (auto module : modules) dump_module_intf(module); f << "} // namespace " << design_ns << "\n"; f << "\n"; f << "#endif // __cplusplus\n"; f << "\n"; f << "#endif\n"; *intf_f << f.str(); f.str(""); } if (split_intf) f << "#include \"" << intf_filename << "\"\n"; else f << "#include \n"; if (has_prints) f << "#include \n"; f << "\n"; f << "#if defined(CXXRTL_INCLUDE_CAPI_IMPL) || \\\n"; f << " defined(CXXRTL_INCLUDE_VCD_CAPI_IMPL)\n"; f << "#include \n"; f << "#endif\n"; f << "\n"; f << "#if defined(CXXRTL_INCLUDE_VCD_CAPI_IMPL)\n"; f << "#include \n"; f << "#endif\n"; f << "\n"; f << "using namespace cxxrtl_yosys;\n"; f << "\n"; f << "namespace " << design_ns << " {\n"; f << "\n"; for (auto module : modules) { if (!split_intf) dump_module_intf(module); dump_module_impl(module); } f << "} // namespace " << design_ns << "\n"; f << "\n"; if (top_module != nullptr && debug_info) { f << "extern \"C\"\n"; f << "cxxrtl_toplevel " << design_ns << "_create() {\n"; inc_indent(); std::string top_type = design_ns + "::" + mangle(top_module); f << indent << "return new _cxxrtl_toplevel { "; f << "std::unique_ptr<" << top_type << ">(new " + top_type + ")"; f << " };\n"; dec_indent(); f << "}\n"; } *impl_f << f.str(); f.str(""); } // Edge-type sync rules require us to emit edge detectors, which require coordination between // eval and commit phases. To do this we need to collect them upfront. // // Note that the simulator commit phase operates at wire granularity but edge-type sync rules // operate at wire bit granularity; it is possible to have code similar to: // wire [3:0] clocks; // always @(posedge clocks[0]) ... // To handle this we track edge sensitivity both for wires and wire bits. void register_edge_signal(SigMap &sigmap, RTLIL::SigSpec signal, RTLIL::SyncType type) { signal = sigmap(signal); if (signal.is_fully_const()) return; // a clock, or more commonly a reset, can be tied to a constant driver log_assert(is_valid_clock(signal)); log_assert(type == RTLIL::STp || type == RTLIL::STn || type == RTLIL::STe); RTLIL::SigBit sigbit = signal[0]; if (!edge_types.count(sigbit)) edge_types[sigbit] = type; else if (edge_types[sigbit] != type) edge_types[sigbit] = RTLIL::STe; // Cannot use as_wire because signal might not be a full wire, instead extract the wire from the sigbit edge_wires.insert(sigbit.wire); } void analyze_design(RTLIL::Design *design) { bool has_feedback_arcs = false; bool has_buffered_comb_wires = false; for (auto module : design->modules()) { if (!design->selected_module(module)) continue; SigMap &sigmap = sigmaps[module]; sigmap.set(module); std::vector &memories = mod_memories[module]; memories = Mem::get_all_memories(module); for (auto &mem : memories) { mem.narrow(); mem.coalesce_inits(); } if (module->get_bool_attribute(ID(cxxrtl_blackbox))) { for (auto port : module->ports) { RTLIL::Wire *wire = module->wire(port); if (wire->port_input && !wire->port_output) { wire_types[wire] = debug_wire_types[wire] = {WireType::MEMBER}; } else if (wire->port_input || wire->port_output) { wire_types[wire] = debug_wire_types[wire] = {WireType::BUFFERED}; } if (wire->has_attribute(ID(cxxrtl_edge))) { RTLIL::Const edge_attr = wire->attributes[ID(cxxrtl_edge)]; if (!(edge_attr.flags & RTLIL::CONST_FLAG_STRING) || (int)edge_attr.decode_string().size() != GetSize(wire)) log_cmd_error("Attribute `cxxrtl_edge' of port `%s.%s' is not a string with one character per bit.\n", log_id(module), log_signal(wire)); std::string edges = wire->get_string_attribute(ID(cxxrtl_edge)); for (int i = 0; i < GetSize(wire); i++) { RTLIL::SigSpec wire_sig = wire; switch (edges[i]) { case '-': break; case 'p': register_edge_signal(sigmap, wire_sig[i], RTLIL::STp); break; case 'n': register_edge_signal(sigmap, wire_sig[i], RTLIL::STn); break; case 'a': register_edge_signal(sigmap, wire_sig[i], RTLIL::STe); break; default: log_cmd_error("Attribute `cxxrtl_edge' of port `%s.%s' contains specifiers " "other than '-', 'p', 'n', or 'a'.\n", log_id(module), log_signal(wire)); } } } } // Black boxes converge by default, since their implementations are quite unlikely to require // internal propagation of comb signals. eval_converges[module] = true; continue; } for (auto wire : module->wires()) if (wire->has_attribute(ID::init)) wire_init[wire] = wire->attributes.at(ID::init); // Construct a flow graph where each node is a basic computational operation generally corresponding // to a fragment of the RTLIL netlist. FlowGraph flow; for (auto conn : module->connections()) flow.add_node(conn); for (auto cell : module->cells()) { if (!cell->known()) log_cmd_error("Unknown cell `%s'.\n", log_id(cell->type)); if (cell->is_mem_cell()) continue; RTLIL::Module *cell_module = design->module(cell->type); if (cell_module && cell_module->get_blackbox_attribute() && !cell_module->get_bool_attribute(ID(cxxrtl_blackbox))) log_cmd_error("External blackbox cell `%s' is not marked as a CXXRTL blackbox.\n", log_id(cell->type)); if (cell_module && cell_module->get_bool_attribute(ID(cxxrtl_blackbox)) && cell_module->get_bool_attribute(ID(cxxrtl_template))) blackbox_specializations[cell_module].insert(template_args(cell)); flow.add_node(cell); // Various DFF cells are treated like posedge/negedge processes, see above for details. if (cell->type.in(ID($dff), ID($dffe), ID($adff), ID($adffe), ID($aldff), ID($aldffe), ID($dffsr), ID($dffsre), ID($sdff), ID($sdffe), ID($sdffce))) { if (is_valid_clock(cell->getPort(ID::CLK))) register_edge_signal(sigmap, cell->getPort(ID::CLK), cell->parameters[ID::CLK_POLARITY].as_bool() ? RTLIL::STp : RTLIL::STn); } // $print cells may be triggered on posedge/negedge events. if (cell->type == ID($print) && cell->getParam(ID::TRG_ENABLE).as_bool()) { for (size_t i = 0; i < (size_t)cell->getParam(ID::TRG_WIDTH).as_int(); i++) { RTLIL::SigBit trg = cell->getPort(ID::TRG).extract(i, 1); if (is_valid_clock(trg)) register_edge_signal(sigmap, trg, cell->parameters[ID::TRG_POLARITY][i] == RTLIL::S1 ? RTLIL::STp : RTLIL::STn); } } } for (auto &mem : memories) { flow.add_node(&mem); // Clocked memory cells are treated like posedge/negedge processes as well. for (auto &port : mem.rd_ports) { if (port.clk_enable) if (is_valid_clock(port.clk)) register_edge_signal(sigmap, port.clk, port.clk_polarity ? RTLIL::STp : RTLIL::STn); // For read ports, also move initial value to wire_init (if any). for (int i = 0; i < GetSize(port.data); i++) { if (port.init_value[i] != State::Sx) { SigBit bit = port.data[i]; if (bit.wire) { auto &init = wire_init[bit.wire]; if (init == RTLIL::Const()) { init = RTLIL::Const(State::Sx, GetSize(bit.wire)); } init[bit.offset] = port.init_value[i]; } } } } for (auto &port : mem.wr_ports) { if (port.clk_enable) if (is_valid_clock(port.clk)) register_edge_signal(sigmap, port.clk, port.clk_polarity ? RTLIL::STp : RTLIL::STn); } if (!mem.wr_ports.empty()) writable_memories.insert({module, mem.memid}); } for (auto proc : module->processes) { flow.add_node(proc.second); for (auto sync : proc.second->syncs) { switch (sync->type) { // Edge-type sync rules require pre-registration. case RTLIL::STp: case RTLIL::STn: case RTLIL::STe: register_edge_signal(sigmap, sync->signal, sync->type); break; // Level-type sync rules require no special handling. case RTLIL::ST0: case RTLIL::ST1: case RTLIL::STa: break; case RTLIL::STg: log_cmd_error("Global clock is not supported.\n"); // Handling of init-type sync rules is delegated to the `proc_init` pass, so we can use the wire // attribute regardless of input. case RTLIL::STi: log_assert(false); } for (auto &memwr : sync->mem_write_actions) { writable_memories.insert({module, memwr.memid}); } } } // Construct a linear order of the flow graph that minimizes the amount of feedback arcs. A flow graph // without feedback arcs can generally be evaluated in a single pass, i.e. it always requires only // a single delta cycle. Scheduler scheduler; dict::Vertex*, hash_ptr_ops> node_vertex_map; for (auto node : flow.nodes) node_vertex_map[node] = scheduler.add(node); for (auto node_comb_def : flow.node_comb_defs) { auto vertex = node_vertex_map[node_comb_def.first]; for (auto wire : node_comb_def.second) for (auto succ_node : flow.wire_uses[wire]) { auto succ_vertex = node_vertex_map[succ_node]; vertex->succs.insert(succ_vertex); succ_vertex->preds.insert(vertex); } } // Find out whether the order includes any feedback arcs. std::vector node_order; pool evaluated_nodes; pool feedback_wires; for (auto vertex : scheduler.schedule()) { auto node = vertex->data; node_order.push_back(node); // Any wire that is an output of node vo and input of node vi where vo is scheduled later than vi // is a feedback wire. Feedback wires indicate apparent logic loops in the design, which may be // caused by a true logic loop, but usually are a benign result of dependency tracking that works // on wire, not bit, level. Nevertheless, feedback wires cannot be unbuffered. evaluated_nodes.insert(node); for (auto wire : flow.node_comb_defs[node]) for (auto succ_node : flow.wire_uses[wire]) if (evaluated_nodes[succ_node]) feedback_wires.insert(wire); } if (!feedback_wires.empty()) { has_feedback_arcs = true; log("Module `%s' contains feedback arcs through wires:\n", log_id(module)); for (auto wire : feedback_wires) log(" %s\n", log_id(wire)); } // Conservatively assign wire types. Assignment of types BUFFERED and MEMBER is final, but assignment // of type LOCAL may be further refined to UNUSED or INLINE. for (auto wire : module->wires()) { auto &wire_type = wire_types[wire]; wire_type = {WireType::BUFFERED}; if (feedback_wires[wire]) continue; if (wire->port_output && !module->get_bool_attribute(ID::top)) continue; if (!wire->name.isPublic() && !unbuffer_internal) continue; if (wire->name.isPublic() && !unbuffer_public) continue; if (flow.wire_sync_defs.count(wire) > 0) continue; wire_type = {WireType::MEMBER}; if (edge_wires[wire]) continue; if (wire->get_bool_attribute(ID::keep)) continue; if (wire->port_input || wire->port_output) continue; if (!wire->name.isPublic() && !localize_internal) continue; if (wire->name.isPublic() && !localize_public) continue; wire_type = {WireType::LOCAL}; } // Discover nodes reachable from primary outputs (i.e. members) and collect reachable wire users. pool worklist; for (auto node : flow.nodes) { if (node->type == FlowGraph::Node::Type::CELL_EVAL && is_effectful_cell(node->cell->type)) worklist.insert(node); // node has effects else if (node->type == FlowGraph::Node::Type::PRINT_SYNC) worklist.insert(node); // node is sync $print else if (node->type == FlowGraph::Node::Type::MEM_WRPORTS) worklist.insert(node); // node is memory write else if (node->type == FlowGraph::Node::Type::PROCESS_SYNC && is_memwr_process(node->process)) worklist.insert(node); // node is memory write else if (flow.node_sync_defs.count(node)) worklist.insert(node); // node is a flip-flop else if (flow.node_comb_defs.count(node)) { for (auto wire : flow.node_comb_defs[node]) if (wire_types[wire].is_member()) worklist.insert(node); // node drives public wires } } dict> live_wires; pool live_nodes; while (!worklist.empty()) { auto node = worklist.pop(); live_nodes.insert(node); for (auto wire : flow.node_uses[node]) { live_wires[wire].insert(node); for (auto pred_node : flow.wire_comb_defs[wire]) if (!live_nodes[pred_node]) worklist.insert(pred_node); } } // Refine wire types taking into account the amount of uses from reachable nodes only. for (auto wire : module->wires()) { auto &wire_type = wire_types[wire]; if (!wire_type.is_local()) continue; if (live_wires[wire].empty()) { wire_type = {WireType::UNUSED}; // wire never used continue; } if (!wire->name.isPublic() && !inline_internal) continue; if (wire->name.isPublic() && !inline_public) continue; if (flow.is_inlinable(wire, live_wires[wire])) { if (flow.wire_comb_defs[wire].size() > 1) log_cmd_error("Wire %s.%s has multiple drivers!\n", log_id(module), log_id(wire)); log_assert(flow.wire_comb_defs[wire].size() == 1); FlowGraph::Node *node = *flow.wire_comb_defs[wire].begin(); switch (node->type) { case FlowGraph::Node::Type::CELL_EVAL: if (!is_inlinable_cell(node->cell->type)) continue; wire_type = {WireType::INLINE, node->cell}; // wire replaced with cell break; case FlowGraph::Node::Type::CONNECT: wire_type = {WireType::INLINE, node->connect.second}; // wire replaced with sig break; default: continue; } live_nodes.erase(node); } } // Emit reachable nodes in eval(). // Accumulate sync $print cells per trigger condition. dict, std::vector> sync_print_cells; for (auto node : node_order) if (live_nodes[node]) { if (node->type == FlowGraph::Node::Type::CELL_EVAL && node->cell->type == ID($print) && node->cell->getParam(ID::TRG_ENABLE).as_bool()) sync_print_cells[make_pair(node->cell->getPort(ID::TRG), node->cell->getParam(ID::TRG_POLARITY))].push_back(node->cell); else schedule[module].push_back(*node); } for (auto &it : sync_print_cells) { auto node = flow.add_print_sync_node(it.second); schedule[module].push_back(*node); } // For maximum performance, the state of the simulation (which is the same as the set of its double buffered // wires, since using a singly buffered wire for any kind of state introduces a race condition) should contain // no wires attached to combinatorial outputs. Feedback wires, by definition, make that impossible. However, // it is possible that a design with no feedback arcs would end up with doubly buffered wires in such cases // as a wire with multiple drivers where one of them is combinatorial and the other is synchronous. Such designs // also require more than one delta cycle to converge. pool buffered_comb_wires; for (auto wire : module->wires()) if (wire_types[wire].is_buffered() && !feedback_wires[wire] && flow.wire_comb_defs[wire].size() > 0) buffered_comb_wires.insert(wire); if (!buffered_comb_wires.empty()) { has_buffered_comb_wires = true; log("Module `%s' contains buffered combinatorial wires:\n", log_id(module)); for (auto wire : buffered_comb_wires) log(" %s\n", log_id(wire)); } // Record whether eval() requires only one delta cycle in this module. eval_converges[module] = feedback_wires.empty() && buffered_comb_wires.empty(); if (debug_info) { // Annotate wire bits with the type of their driver; this is exposed in the debug metadata. for (auto item : flow.bit_has_state) bit_has_state.insert(item); // Assign debug information wire types to public wires according to the chosen debug level. // Unlike with optimized wire types, all assignments here are final. for (auto wire : module->wires()) { const auto &wire_type = wire_types[wire]; auto &debug_wire_type = debug_wire_types[wire]; if (!debug_info) continue; if (wire->port_input || wire_type.is_buffered()) debug_wire_type = wire_type; // wire contains state else if (!wire->name.isPublic()) continue; // internal and stateless if (!debug_member) continue; if (wire_type.is_member()) debug_wire_type = wire_type; // wire is a member if (!debug_alias) continue; const RTLIL::Wire *it = wire; while (flow.is_inlinable(it)) { log_assert(flow.wire_comb_defs[it].size() == 1); FlowGraph::Node *node = *flow.wire_comb_defs[it].begin(); if (node->type != FlowGraph::Node::Type::CONNECT) break; // not an alias RTLIL::SigSpec rhs = node->connect.second; if (rhs.is_fully_const()) { debug_wire_type = {WireType::CONST, rhs}; // wire replaced with const } else if (rhs.is_wire()) { if (wire_types[rhs.as_wire()].is_member()) debug_wire_type = {WireType::ALIAS, rhs}; // wire replaced with wire else if (debug_eval && rhs.as_wire()->name.isPublic()) debug_wire_type = {WireType::ALIAS, rhs}; // wire replaced with outline it = rhs.as_wire(); // and keep looking continue; } break; } if (!debug_eval) continue; if (!debug_wire_type.is_exact() && !wire_type.is_member()) debug_wire_type = {WireType::OUTLINE}; // wire is local or inlined } // Discover nodes reachable from primary outputs (i.e. outlines) up until primary inputs (i.e. members) // and collect reachable wire users. pool worklist; for (auto node : flow.nodes) { if (flow.node_comb_defs.count(node)) for (auto wire : flow.node_comb_defs[node]) if (debug_wire_types[wire].is_outline()) worklist.insert(node); // node drives outline } dict> debug_live_wires; pool debug_live_nodes; while (!worklist.empty()) { auto node = worklist.pop(); debug_live_nodes.insert(node); for (auto wire : flow.node_uses[node]) { if (debug_wire_types[wire].is_member()) continue; // node uses member if (debug_wire_types[wire].is_exact()) continue; // node uses alias or const debug_live_wires[wire].insert(node); for (auto pred_node : flow.wire_comb_defs[wire]) if (!debug_live_nodes[pred_node]) worklist.insert(pred_node); } } // Assign debug information wire types to internal wires used by reachable nodes. This is similar // to refining optimized wire types with the exception that the assignments here are first and final. for (auto wire : module->wires()) { const auto &wire_type = wire_types[wire]; auto &debug_wire_type = debug_wire_types[wire]; if (wire->name.isPublic()) continue; if (debug_live_wires[wire].empty()) { continue; // wire never used } else if (flow.is_inlinable(wire, debug_live_wires[wire])) { log_assert(flow.wire_comb_defs[wire].size() == 1); FlowGraph::Node *node = *flow.wire_comb_defs[wire].begin(); switch (node->type) { case FlowGraph::Node::Type::CELL_EVAL: if (!is_inlinable_cell(node->cell->type)) continue; debug_wire_type = {WireType::INLINE, node->cell}; // wire replaced with cell break; case FlowGraph::Node::Type::CONNECT: debug_wire_type = {WireType::INLINE, node->connect.second}; // wire replaced with sig break; default: continue; } debug_live_nodes.erase(node); } else if (wire_type.is_member() || wire_type.type == WireType::LOCAL) { debug_wire_type = wire_type; // wire not inlinable } else { log_assert(wire_type.type == WireType::INLINE || wire_type.type == WireType::UNUSED); if (flow.wire_comb_defs[wire].size() == 0) { if (wire_init.count(wire)) { // wire never modified debug_wire_type = {WireType::CONST, wire_init.at(wire)}; } else { debug_wire_type = {WireType::CONST, RTLIL::SigSpec(RTLIL::S0, wire->width)}; } } else { debug_wire_type = {WireType::LOCAL}; // wire used only for debug } } } // Emit reachable nodes in debug_eval(). for (auto node : node_order) if (debug_live_nodes[node]) debug_schedule[module].push_back(*node); } auto show_wire_type = [&](const RTLIL::Wire* wire, const WireType &wire_type) { const char *type_str; switch (wire_type.type) { case WireType::UNUSED: type_str = "UNUSED"; break; case WireType::BUFFERED: type_str = "BUFFERED"; break; case WireType::MEMBER: type_str = "MEMBER"; break; case WireType::OUTLINE: type_str = "OUTLINE"; break; case WireType::LOCAL: type_str = "LOCAL"; break; case WireType::INLINE: type_str = "INLINE"; break; case WireType::ALIAS: type_str = "ALIAS"; break; case WireType::CONST: type_str = "CONST"; break; default: type_str = "(invalid)"; } if (wire_type.sig_subst.empty()) log_debug(" %s: %s\n", log_signal((RTLIL::Wire*)wire), type_str); else log_debug(" %s: %s = %s\n", log_signal((RTLIL::Wire*)wire), type_str, log_signal(wire_type.sig_subst)); }; if (print_wire_types && !wire_types.empty()) { log_debug("Wire types:\n"); for (auto wire_type : wire_types) show_wire_type(wire_type.first, wire_type.second); } if (print_debug_wire_types && !debug_wire_types.empty()) { log_debug("Debug wire types:\n"); for (auto debug_wire_type : debug_wire_types) show_wire_type(debug_wire_type.first, debug_wire_type.second); } } if (has_feedback_arcs || has_buffered_comb_wires) { // Although both non-feedback buffered combinatorial wires and apparent feedback wires may be eliminated // by optimizing the design, if after `proc; flatten` there are any feedback wires remaining, it is very // likely that these feedback wires are indicative of a true logic loop, so they get emphasized in the message. const char *why_pessimistic = nullptr; if (has_feedback_arcs) why_pessimistic = "feedback wires"; else if (has_buffered_comb_wires) why_pessimistic = "buffered combinatorial wires"; log_warning("Design contains %s, which require delta cycles during evaluation.\n", why_pessimistic); if (!run_flatten) log("Flattening may eliminate %s from the design.\n", why_pessimistic); if (!run_proc) log("Converting processes to netlists may eliminate %s from the design.\n", why_pessimistic); } } void check_design(RTLIL::Design *design, bool &has_sync_init) { has_sync_init = false; for (auto module : design->modules()) { if (module->get_blackbox_attribute() && !module->has_attribute(ID(cxxrtl_blackbox))) continue; if (!design->selected_whole_module(module)) if (design->selected_module(module)) log_cmd_error("Can't handle partially selected module `%s'!\n", id2cstr(module->name)); if (!design->selected_module(module)) continue; for (auto proc : module->processes) for (auto sync : proc.second->syncs) if (sync->type == RTLIL::STi) has_sync_init = true; } } void prepare_design(RTLIL::Design *design) { bool did_anything = false; bool has_sync_init; log_push(); check_design(design, has_sync_init); if (run_hierarchy) { Pass::call(design, "hierarchy -auto-top"); did_anything = true; } if (run_flatten) { Pass::call(design, "flatten"); did_anything = true; } if (run_proc) { Pass::call(design, "proc"); did_anything = true; } else if (has_sync_init) { // We're only interested in proc_init, but it depends on proc_prune and proc_clean, so call those // in case they weren't already. (This allows `yosys foo.v -o foo.cc` to work.) Pass::call(design, "proc_prune"); Pass::call(design, "proc_clean"); Pass::call(design, "proc_init"); did_anything = true; } // Recheck the design if it was modified. if (did_anything) check_design(design, has_sync_init); log_assert(!has_sync_init); log_pop(); if (did_anything) log_spacer(); analyze_design(design); } }; struct CxxrtlBackend : public Backend { static const int DEFAULT_OPT_LEVEL = 6; static const int DEFAULT_DEBUG_LEVEL = 4; CxxrtlBackend() : Backend("cxxrtl", "convert design to C++ RTL simulation") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" write_cxxrtl [options] [filename]\n"); log("\n"); log("Write C++ code that simulates the design. The generated code requires a driver\n"); log("that instantiates the design, toggles its clock, and interacts with its ports.\n"); log("\n"); log("The following driver may be used as an example for a design with a single clock\n"); log("driving rising edge triggered flip-flops:\n"); log("\n"); log(" #include \"top.cc\"\n"); log("\n"); log(" int main() {\n"); log(" cxxrtl_design::p_top top;\n"); log(" top.step();\n"); log(" while (1) {\n"); log(" /* user logic */\n"); log(" top.p_clk.set(false);\n"); log(" top.step();\n"); log(" top.p_clk.set(true);\n"); log(" top.step();\n"); log(" }\n"); log(" }\n"); log("\n"); log("Note that CXXRTL simulations, just like the hardware they are simulating, are\n"); log("subject to race conditions. If, in the example above, the user logic would run\n"); log("simultaneously with the rising edge of the clock, the design would malfunction.\n"); log("\n"); log("This backend supports replacing parts of the design with black boxes implemented\n"); log("in C++. If a module marked as a CXXRTL black box, its implementation is ignored,\n"); log("and the generated code consists only of an interface and a factory function.\n"); log("The driver must implement the factory function that creates an implementation of\n"); log("the black box, taking into account the parameters it is instantiated with.\n"); log("\n"); log("For example, the following Verilog code defines a CXXRTL black box interface for\n"); log("a synchronous debug sink:\n"); log("\n"); log(" (* cxxrtl_blackbox *)\n"); log(" module debug(...);\n"); log(" (* cxxrtl_edge = \"p\" *) input clk;\n"); log(" input en;\n"); log(" input [7:0] i_data;\n"); log(" (* cxxrtl_sync *) output [7:0] o_data;\n"); log(" endmodule\n"); log("\n"); log("For this HDL interface, this backend will generate the following C++ interface:\n"); log("\n"); log(" struct bb_p_debug : public module {\n"); log(" value<1> p_clk;\n"); log(" bool posedge_p_clk() const { /* ... */ }\n"); log(" value<1> p_en;\n"); log(" value<8> p_i_data;\n"); log(" wire<8> p_o_data;\n"); log("\n"); log(" bool eval() override;\n"); log(" bool commit() override;\n"); log("\n"); log(" static std::unique_ptr\n"); log(" create(std::string name, metadata_map parameters, metadata_map attributes);\n"); log(" };\n"); log("\n"); log("The `create' function must be implemented by the driver. For example, it could\n"); log("always provide an implementation logging the values to standard error stream:\n"); log("\n"); log(" namespace cxxrtl_design {\n"); log("\n"); log(" struct stderr_debug : public bb_p_debug {\n"); log(" bool eval() override {\n"); log(" if (posedge_p_clk() && p_en)\n"); log(" fprintf(stderr, \"debug: %%02x\\n\", p_i_data.data[0]);\n"); log(" p_o_data.next = p_i_data;\n"); log(" return bb_p_debug::eval();\n"); log(" }\n"); log(" };\n"); log("\n"); log(" std::unique_ptr\n"); log(" bb_p_debug::create(std::string name, cxxrtl::metadata_map parameters,\n"); log(" cxxrtl::metadata_map attributes) {\n"); log(" return std::make_unique();\n"); log(" }\n"); log("\n"); log(" }\n"); log("\n"); log("For complex applications of black boxes, it is possible to parameterize their\n"); log("port widths. For example, the following Verilog code defines a CXXRTL black box\n"); log("interface for a configurable width debug sink:\n"); log("\n"); log(" (* cxxrtl_blackbox, cxxrtl_template = \"WIDTH\" *)\n"); log(" module debug(...);\n"); log(" parameter WIDTH = 8;\n"); log(" (* cxxrtl_edge = \"p\" *) input clk;\n"); log(" input en;\n"); log(" (* cxxrtl_width = \"WIDTH\" *) input [WIDTH - 1:0] i_data;\n"); log(" (* cxxrtl_width = \"WIDTH\" *) output [WIDTH - 1:0] o_data;\n"); log(" endmodule\n"); log("\n"); log("For this parametric HDL interface, this backend will generate the following C++\n"); log("interface (only the differences are shown):\n"); log("\n"); log(" template\n"); log(" struct bb_p_debug : public module {\n"); log(" // ...\n"); log(" value p_i_data;\n"); log(" wire p_o_data;\n"); log(" // ...\n"); log(" static std::unique_ptr>\n"); log(" create(std::string name, metadata_map parameters, metadata_map attributes);\n"); log(" };\n"); log("\n"); log("The `create' function must be implemented by the driver, specialized for every\n"); log("possible combination of template parameters. (Specialization is necessary to\n"); log("enable separate compilation of generated code and black box implementations.)\n"); log("\n"); log(" template\n"); log(" struct stderr_debug : public bb_p_debug {\n"); log(" // ...\n"); log(" };\n"); log("\n"); log(" template<>\n"); log(" std::unique_ptr>\n"); log(" bb_p_debug<8>::create(std::string name, cxxrtl::metadata_map parameters,\n"); log(" cxxrtl::metadata_map attributes) {\n"); log(" return std::make_unique>();\n"); log(" }\n"); log("\n"); log("The following attributes are recognized by this backend:\n"); log("\n"); log(" cxxrtl_blackbox\n"); log(" only valid on modules. if specified, the module contents are ignored,\n"); log(" and the generated code includes only the module interface and a factory\n"); log(" function, which will be called to instantiate the module.\n"); log("\n"); log(" cxxrtl_edge\n"); log(" only valid on inputs of black boxes. must be one of \"p\", \"n\", \"a\".\n"); log(" if specified on signal `clk`, the generated code includes edge detectors\n"); log(" `posedge_p_clk()` (if \"p\"), `negedge_p_clk()` (if \"n\"), or both (if\n"); log(" \"a\"), simplifying implementation of clocked black boxes.\n"); log("\n"); log(" cxxrtl_template\n"); log(" only valid on black boxes. must contain a space separated sequence of\n"); log(" identifiers that have a corresponding black box parameters. for each\n"); log(" of them, the generated code includes a `size_t` template parameter.\n"); log("\n"); log(" cxxrtl_width\n"); log(" only valid on ports of black boxes. must be a constant expression, which\n"); log(" is directly inserted into generated code.\n"); log("\n"); log(" cxxrtl_comb, cxxrtl_sync\n"); log(" only valid on outputs of black boxes. if specified, indicates that every\n"); log(" bit of the output port is driven, correspondingly, by combinatorial or\n"); log(" synchronous logic. this knowledge is used for scheduling optimizations.\n"); log(" if neither is specified, the output will be pessimistically treated as\n"); log(" driven by both combinatorial and synchronous logic.\n"); log("\n"); log("The following options are supported by this backend:\n"); log("\n"); log(" -print-wire-types, -print-debug-wire-types\n"); log(" enable additional debug logging, for pass developers.\n"); log("\n"); log(" -header\n"); log(" generate separate interface (.h) and implementation (.cc) files.\n"); log(" if specified, the backend must be called with a filename, and filename\n"); log(" of the interface is derived from filename of the implementation.\n"); log(" otherwise, interface and implementation are generated together.\n"); log("\n"); log(" -namespace \n"); log(" place the generated code into namespace . if not specified,\n"); log(" \"cxxrtl_design\" is used.\n"); log("\n"); log(" -print-output \n"); log(" $print cells in the generated code direct their output to .\n"); log(" must be one of \"std::cout\", \"std::cerr\". if not specified,\n"); log(" \"std::cout\" is used.\n"); log("\n"); log(" -nohierarchy\n"); log(" use design hierarchy as-is. in most designs, a top module should be\n"); log(" present as it is exposed through the C API and has unbuffered outputs\n"); log(" for improved performance; it will be determined automatically if absent.\n"); log("\n"); log(" -noflatten\n"); log(" don't flatten the design. fully flattened designs can evaluate within\n"); log(" one delta cycle if they have no combinatorial feedback.\n"); log(" note that the debug interface and waveform dumps use full hierarchical\n"); log(" names for all wires even in flattened designs.\n"); log("\n"); log(" -noproc\n"); log(" don't convert processes to netlists. in most designs, converting\n"); log(" processes significantly improves evaluation performance at the cost of\n"); log(" slight increase in compilation time.\n"); log("\n"); log(" -O \n"); log(" set the optimization level. the default is -O%d. higher optimization\n", DEFAULT_OPT_LEVEL); log(" levels dramatically decrease compile and run time, and highest level\n"); log(" possible for a design should be used.\n"); log("\n"); log(" -O0\n"); log(" no optimization.\n"); log("\n"); log(" -O1\n"); log(" unbuffer internal wires if possible.\n"); log("\n"); log(" -O2\n"); log(" like -O1, and localize internal wires if possible.\n"); log("\n"); log(" -O3\n"); log(" like -O2, and inline internal wires if possible.\n"); log("\n"); log(" -O4\n"); log(" like -O3, and unbuffer public wires not marked (*keep*) if possible.\n"); log("\n"); log(" -O5\n"); log(" like -O4, and localize public wires not marked (*keep*) if possible.\n"); log("\n"); log(" -O6\n"); log(" like -O5, and inline public wires not marked (*keep*) if possible.\n"); log("\n"); log(" -g \n"); log(" set the debug level. the default is -g%d. higher debug levels provide\n", DEFAULT_DEBUG_LEVEL); log(" more visibility and generate more code, but do not pessimize evaluation.\n"); log("\n"); log(" -g0\n"); log(" no debug information. the C API is disabled.\n"); log("\n"); log(" -g1\n"); log(" include bare minimum of debug information necessary to access all design\n"); log(" state. the C API is enabled.\n"); log("\n"); log(" -g2\n"); log(" like -g1, but include debug information for all public wires that are\n"); log(" directly accessible through the C++ interface.\n"); log("\n"); log(" -g3\n"); log(" like -g2, and include debug information for public wires that are tied\n"); log(" to a constant or another public wire.\n"); log("\n"); log(" -g4\n"); log(" like -g3, and compute debug information on demand for all public wires\n"); log(" that were optimized out.\n"); log("\n"); } void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { bool print_wire_types = false; bool print_debug_wire_types = false; bool nohierarchy = false; bool noflatten = false; bool noproc = false; int opt_level = DEFAULT_OPT_LEVEL; int debug_level = DEFAULT_DEBUG_LEVEL; CxxrtlWorker worker; log_header(design, "Executing CXXRTL backend.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-print-wire-types") { print_wire_types = true; continue; } if (args[argidx] == "-print-debug-wire-types") { print_debug_wire_types = true; continue; } if (args[argidx] == "-nohierarchy") { nohierarchy = true; continue; } if (args[argidx] == "-noflatten") { noflatten = true; continue; } if (args[argidx] == "-noproc") { noproc = true; continue; } if (args[argidx] == "-Og") { log_warning("The `-Og` option has been removed. Use `-g3` instead for complete " "design coverage regardless of optimization level.\n"); continue; } if (args[argidx] == "-O" && argidx+1 < args.size() && args[argidx+1] == "g") { argidx++; log_warning("The `-Og` option has been removed. Use `-g3` instead for complete " "design coverage regardless of optimization level.\n"); continue; } if (args[argidx] == "-O" && argidx+1 < args.size()) { opt_level = std::stoi(args[++argidx]); continue; } if (args[argidx].substr(0, 2) == "-O" && args[argidx].size() == 3 && isdigit(args[argidx][2])) { opt_level = std::stoi(args[argidx].substr(2)); continue; } if (args[argidx] == "-g" && argidx+1 < args.size()) { debug_level = std::stoi(args[++argidx]); continue; } if (args[argidx].substr(0, 2) == "-g" && args[argidx].size() == 3 && isdigit(args[argidx][2])) { debug_level = std::stoi(args[argidx].substr(2)); continue; } if (args[argidx] == "-header") { worker.split_intf = true; continue; } if (args[argidx] == "-namespace" && argidx+1 < args.size()) { worker.design_ns = args[++argidx]; continue; } if (args[argidx] == "-print-output" && argidx+1 < args.size()) { worker.print_output = args[++argidx]; if (!(worker.print_output == "std::cout" || worker.print_output == "std::cerr")) { log_cmd_error("Invalid output stream \"%s\".\n", worker.print_output.c_str()); worker.print_output = "std::cout"; } continue; } break; } extra_args(f, filename, args, argidx); worker.print_wire_types = print_wire_types; worker.print_debug_wire_types = print_debug_wire_types; worker.run_hierarchy = !nohierarchy; worker.run_flatten = !noflatten; worker.run_proc = !noproc; switch (opt_level) { // the highest level here must match DEFAULT_OPT_LEVEL case 6: worker.inline_public = true; YS_FALLTHROUGH case 5: worker.localize_public = true; YS_FALLTHROUGH case 4: worker.unbuffer_public = true; YS_FALLTHROUGH case 3: worker.inline_internal = true; YS_FALLTHROUGH case 2: worker.localize_internal = true; YS_FALLTHROUGH case 1: worker.unbuffer_internal = true; YS_FALLTHROUGH case 0: break; default: log_cmd_error("Invalid optimization level %d.\n", opt_level); } switch (debug_level) { // the highest level here must match DEFAULT_DEBUG_LEVEL case 4: worker.debug_eval = true; YS_FALLTHROUGH case 3: worker.debug_alias = true; YS_FALLTHROUGH case 2: worker.debug_member = true; YS_FALLTHROUGH case 1: worker.debug_info = true; YS_FALLTHROUGH case 0: break; default: log_cmd_error("Invalid debug information level %d.\n", debug_level); } std::ofstream intf_f; if (worker.split_intf) { if (filename == "") log_cmd_error("Option -header must be used with a filename.\n"); worker.intf_filename = filename.substr(0, filename.rfind('.')) + ".h"; intf_f.open(worker.intf_filename, std::ofstream::trunc); if (intf_f.fail()) log_cmd_error("Can't open file `%s' for writing: %s\n", worker.intf_filename.c_str(), strerror(errno)); worker.intf_f = &intf_f; } worker.impl_f = f; worker.prepare_design(design); worker.dump_design(design); } } CxxrtlBackend; PRIVATE_NAMESPACE_END yosys-yosys-0.33/backends/cxxrtl/cxxrtl_capi.cc000066400000000000000000000056071447554276300217670ustar00rootroot00000000000000/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2020 whitequark * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted. * * 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 file is a part of the CXXRTL C API. It should be used together with `cxxrtl_capi.h`. #include #include struct _cxxrtl_handle { std::unique_ptr module; cxxrtl::debug_items objects; }; // Private function for use by other units of the C API. const cxxrtl::debug_items &cxxrtl_debug_items_from_handle(cxxrtl_handle handle) { return handle->objects; } cxxrtl_handle cxxrtl_create(cxxrtl_toplevel design) { return cxxrtl_create_at(design, ""); } cxxrtl_handle cxxrtl_create_at(cxxrtl_toplevel design, const char *root) { std::string path = root; if (!path.empty()) { // module::debug_info() accepts either an empty path, or a path ending in space to simplify // the logic in generated code. While this is sketchy at best to expose in the C++ API, this // would be a lot worse in the C API, so don't expose it here. assert(path.back() != ' '); path += ' '; } cxxrtl_handle handle = new _cxxrtl_handle; handle->module = std::move(design->module); handle->module->debug_info(handle->objects, path); delete design; return handle; } void cxxrtl_destroy(cxxrtl_handle handle) { delete handle; } void cxxrtl_reset(cxxrtl_handle handle) { handle->module->reset(); } int cxxrtl_eval(cxxrtl_handle handle) { return handle->module->eval(); } int cxxrtl_commit(cxxrtl_handle handle) { return handle->module->commit(); } size_t cxxrtl_step(cxxrtl_handle handle) { return handle->module->step(); } struct cxxrtl_object *cxxrtl_get_parts(cxxrtl_handle handle, const char *name, size_t *parts) { auto it = handle->objects.table.find(name); if (it == handle->objects.table.end()) return nullptr; *parts = it->second.size(); return static_cast(&it->second[0]); } void cxxrtl_enum(cxxrtl_handle handle, void *data, void (*callback)(void *data, const char *name, cxxrtl_object *object, size_t parts)) { for (auto &it : handle->objects.table) callback(data, it.first.c_str(), static_cast(&it.second[0]), it.second.size()); } void cxxrtl_outline_eval(cxxrtl_outline outline) { outline->eval(); } yosys-yosys-0.33/backends/cxxrtl/cxxrtl_capi.h000066400000000000000000000321451447554276300216260ustar00rootroot00000000000000/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2020 whitequark * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted. * * 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 CXXRTL_CAPI_H #define CXXRTL_CAPI_H // This file is a part of the CXXRTL C API. It should be used together with `cxxrtl_capi.cc`. // // The CXXRTL C API makes it possible to drive CXXRTL designs using C or any other language that // supports the C ABI, for example, Python. It does not provide a way to implement black boxes. #include #include #include #ifdef __cplusplus extern "C" { #endif // Opaque reference to a design toplevel. // // A design toplevel can only be used to create a design handle. typedef struct _cxxrtl_toplevel *cxxrtl_toplevel; // The constructor for a design toplevel is provided as a part of generated code for that design. // Its prototype matches: // // cxxrtl_toplevel _create(); // Opaque reference to a design handle. // // A design handle is required by all operations in the C API. typedef struct _cxxrtl_handle *cxxrtl_handle; // Create a design handle from a design toplevel. // // The `design` is consumed by this operation and cannot be used afterwards. cxxrtl_handle cxxrtl_create(cxxrtl_toplevel design); // Create a design handle at a given hierarchy position from a design toplevel. // // This operation is similar to `cxxrtl_create`, except the full hierarchical name of every object // is prepended with `root`. cxxrtl_handle cxxrtl_create_at(cxxrtl_toplevel design, const char *root); // Release all resources used by a design and its handle. void cxxrtl_destroy(cxxrtl_handle handle); // Reinitialize the design, replacing the internal state with the reset values while preserving // black boxes. // // This operation is essentially equivalent to a power-on reset. Values, wires, and memories are // returned to their reset state while preserving the state of black boxes and keeping all of // the interior pointers obtained with e.g. `cxxrtl_get` valid. void cxxrtl_reset(cxxrtl_handle handle); // Evaluate the design, propagating changes on inputs to the `next` value of internal state and // output wires. // // Returns 1 if the design is known to immediately converge, 0 otherwise. int cxxrtl_eval(cxxrtl_handle handle); // Commit the design, replacing the `curr` value of internal state and output wires with the `next` // value. // // Return 1 if any of the `curr` values were updated, 0 otherwise. int cxxrtl_commit(cxxrtl_handle handle); // Simulate the design to a fixed point. // // Returns the number of delta cycles. size_t cxxrtl_step(cxxrtl_handle handle); // Type of a simulated object. // // The type of a simulated object indicates the way it is stored and the operations that are legal // to perform on it (i.e. won't crash the simulation). It says very little about object semantics, // which is specified through flags. enum cxxrtl_type { // Values correspond to singly buffered netlist nodes, i.e. nodes driven exclusively by // combinatorial cells, or toplevel input nodes. // // Values can be inspected via the `curr` pointer. If the `next` pointer is NULL, the value is // driven by a constant and can never be modified. Otherwise, the value can be modified through // the `next` pointer (which is equal to `curr` if not NULL). Note that changes to the bits // driven by combinatorial cells will be ignored. // // Values always have depth 1. CXXRTL_VALUE = 0, // Wires correspond to doubly buffered netlist nodes, i.e. nodes driven, at least in part, by // storage cells, or by combinatorial cells that are a part of a feedback path. They are also // present in non-optimized builds. // // Wires can be inspected via the `curr` pointer and modified via the `next` pointer (which are // distinct for wires). Note that changes to the bits driven by combinatorial cells will be // ignored. // // Wires always have depth 1. CXXRTL_WIRE = 1, // Memories correspond to memory cells. // // Memories can be inspected and modified via the `curr` pointer. Due to a limitation of this // API, memories cannot yet be modified in a guaranteed race-free way, and the `next` pointer is // always NULL. CXXRTL_MEMORY = 2, // Aliases correspond to netlist nodes driven by another node such that their value is always // exactly equal. // // Aliases can be inspected via the `curr` pointer. They cannot be modified, and the `next` // pointer is always NULL. CXXRTL_ALIAS = 3, // Outlines correspond to netlist nodes that were optimized in a way that makes them inaccessible // outside of a module's `eval()` function. At the highest debug information level, every inlined // node has a corresponding outline object. // // Outlines can be inspected via the `curr` pointer and can never be modified; the `next` pointer // is always NULL. Unlike all other objects, the bits of an outline object are meaningful only // after a call to `cxxrtl_outline_eval` and until any subsequent modification to the netlist. // Observing this requirement is the responsibility of the caller; it is not enforced. // // Outlines always correspond to combinatorial netlist nodes that are not ports. CXXRTL_OUTLINE = 4, // More object types may be added in the future, but the existing ones will never change. }; // Flags of a simulated object. // // The flags of a simulated object indicate its role in the netlist: // * The flags `CXXRTL_INPUT` and `CXXRTL_OUTPUT` designate module ports. // * The flags `CXXRTL_DRIVEN_SYNC`, `CXXRTL_DRIVEN_COMB`, and `CXXRTL_UNDRIVEN` specify // the semantics of node state. An object with several of these flags set has different bits // follow different semantics. enum cxxrtl_flag { // Node is a module input port. // // This flag can be set on objects of type `CXXRTL_VALUE` and `CXXRTL_WIRE`. It may be combined // with `CXXRTL_OUTPUT`, as well as other flags. CXXRTL_INPUT = 1 << 0, // Node is a module output port. // // This flag can be set on objects of type `CXXRTL_WIRE`. It may be combined with `CXXRTL_INPUT`, // as well as other flags. CXXRTL_OUTPUT = 1 << 1, // Node is a module inout port. // // This flag can be set on objects of type `CXXRTL_WIRE`. It may be combined with other flags. CXXRTL_INOUT = (CXXRTL_INPUT|CXXRTL_OUTPUT), // Node has bits that are driven by a storage cell. // // This flag can be set on objects of type `CXXRTL_WIRE`. It may be combined with // `CXXRTL_DRIVEN_COMB` and `CXXRTL_UNDRIVEN`, as well as other flags. // // This flag is set on wires that have bits connected directly to the output of a flip-flop or // a latch, and hold its state. Many `CXXRTL_WIRE` objects may not have the `CXXRTL_DRIVEN_SYNC` // flag set; for example, output ports and feedback wires generally won't. Writing to the `next` // pointer of these wires updates stored state, and for designs without combinatorial loops, // capturing the value from every of these wires through the `curr` pointer creates a complete // snapshot of the design state. CXXRTL_DRIVEN_SYNC = 1 << 2, // Node has bits that are driven by a combinatorial cell or another node. // // This flag can be set on objects of type `CXXRTL_VALUE`, `CXXRTL_WIRE`, and `CXXRTL_OUTLINE`. // It may be combined with `CXXRTL_DRIVEN_SYNC` and `CXXRTL_UNDRIVEN`, as well as other flags. // // This flag is set on objects that have bits connected to the output of a combinatorial cell, // or directly to another node. For designs without combinatorial loops, writing to such bits // through the `next` pointer (if it is not NULL) has no effect. CXXRTL_DRIVEN_COMB = 1 << 3, // Node has bits that are not driven. // // This flag can be set on objects of type `CXXRTL_VALUE` and `CXXRTL_WIRE`. It may be combined // with `CXXRTL_DRIVEN_SYNC` and `CXXRTL_DRIVEN_COMB`, as well as other flags. // // This flag is set on objects that have bits not driven by an output of any cell or by another // node, such as inputs and dangling wires. CXXRTL_UNDRIVEN = 1 << 4, // More object flags may be added in the future, but the existing ones will never change. }; // Description of a simulated object. // // The `curr` and `next` arrays can be accessed directly to inspect and, if applicable, modify // the bits stored in the object. struct cxxrtl_object { // Type of the object. // // All objects have the same memory layout determined by `width` and `depth`, but the type // determines all other properties of the object. uint32_t type; // actually `enum cxxrtl_type` // Flags of the object. uint32_t flags; // actually bit mask of `enum cxxrtl_flags` // Width of the object in bits. size_t width; // Index of the least significant bit. size_t lsb_at; // Depth of the object. Only meaningful for memories; for other objects, always 1. size_t depth; // Index of the first word. Only meaningful for memories; for other objects, always 0; size_t zero_at; // Bits stored in the object, as 32-bit chunks, least significant bits first. // // The width is rounded up to a multiple of 32; the padding bits are always set to 0 by // the simulation code, and must be always written as 0 when modified by user code. // In memories, every element is stored contiguously. Therefore, the total number of chunks // in any object is `((width + 31) / 32) * depth`. // // To allow the simulation to be partitioned into multiple independent units communicating // through wires, the bits are double buffered. To avoid race conditions, user code should // always read from `curr` and write to `next`. The `curr` pointer is always valid; for objects // that cannot be modified, or cannot be modified in a race-free way, `next` is NULL. uint32_t *curr; uint32_t *next; // Opaque reference to an outline. Only meaningful for outline objects. // // See the documentation of `cxxrtl_outline` for details. When creating a `cxxrtl_object`, set // this field to NULL. struct _cxxrtl_outline *outline; // More description fields may be added in the future, but the existing ones will never change. }; // Retrieve description of a simulated object. // // The `name` is the full hierarchical name of the object in the Yosys notation, where public names // have a `\` prefix and hierarchy levels are separated by single spaces. For example, if // the top-level module instantiates a module `foo`, which in turn contains a wire `bar`, the full // hierarchical name is `\foo \bar`. // // The storage of a single abstract object may be split (usually with the `splitnets` pass) into // many physical parts, all of which correspond to the same hierarchical name. To handle such cases, // this function returns an array and writes its length to `parts`. The array is sorted by `lsb_at`. // // Returns the object parts if it was found, NULL otherwise. The returned parts are valid until // the design is destroyed. struct cxxrtl_object *cxxrtl_get_parts(cxxrtl_handle handle, const char *name, size_t *parts); // Retrieve description of a single part simulated object. // // This function is a shortcut for the most common use of `cxxrtl_get_parts`. It asserts that, // if the object exists, it consists of a single part. If assertions are disabled, it returns NULL // for multi-part objects. static inline struct cxxrtl_object *cxxrtl_get(cxxrtl_handle handle, const char *name) { size_t parts = 0; struct cxxrtl_object *object = cxxrtl_get_parts(handle, name, &parts); assert(object == NULL || parts == 1); if (object == NULL || parts == 1) return object; return NULL; } // Enumerate simulated objects. // // For every object in the simulation, `callback` is called with the provided `data`, the full // hierarchical name of the object (see `cxxrtl_get` for details), and the object parts. // The provided `name` and `object` values are valid until the design is destroyed. void cxxrtl_enum(cxxrtl_handle handle, void *data, void (*callback)(void *data, const char *name, struct cxxrtl_object *object, size_t parts)); // Opaque reference to an outline. // // An outline is a group of outline objects that are evaluated simultaneously. The identity of // an outline can be compared to determine whether any two objects belong to the same outline. typedef struct _cxxrtl_outline *cxxrtl_outline; // Evaluate an outline. // // After evaluating an outline, the bits of every outline object contained in it are consistent // with the current state of the netlist. In general, any further modification to the netlist // causes every outline object to become stale, after which the corresponding outline must be // re-evaluated, otherwise the bits read from that object are meaningless. void cxxrtl_outline_eval(cxxrtl_outline outline); #ifdef __cplusplus } #endif #endif yosys-yosys-0.33/backends/cxxrtl/cxxrtl_vcd.h000066400000000000000000000204611447554276300214640ustar00rootroot00000000000000/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2020 whitequark * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted. * * 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 CXXRTL_VCD_H #define CXXRTL_VCD_H #include namespace cxxrtl { class vcd_writer { struct variable { size_t ident; size_t width; chunk_t *curr; size_t cache_offset; debug_outline *outline; bool *outline_warm; }; std::vector current_scope; std::map outlines; std::vector variables; std::vector cache; std::map aliases; bool streaming = false; void emit_timescale(unsigned number, const std::string &unit) { assert(!streaming); assert(number == 1 || number == 10 || number == 100); assert(unit == "s" || unit == "ms" || unit == "us" || unit == "ns" || unit == "ps" || unit == "fs"); buffer += "$timescale " + std::to_string(number) + " " + unit + " $end\n"; } 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])) { buffer += "$upscope $end\n"; current_scope.pop_back(); } while (current_scope.size() < scope.size()) { buffer += "$scope module " + scope[current_scope.size()] + " $end\n"; current_scope.push_back(scope[current_scope.size()]); } } void emit_ident(size_t ident) { do { buffer += '!' + ident % 94; // "base94" ident /= 94; } while (ident != 0); } void emit_name(const std::string &name) { for (char c : name) { if (c == ':') { // Due to a bug, GTKWave cannot parse a colon in the variable name, causing the VCD file // to be unreadable. It cannot be escaped either, so replace it with the sideways colon. buffer += ".."; } else { buffer += c; } } } void emit_var(const variable &var, const std::string &type, const std::string &name, size_t lsb_at, bool multipart) { assert(!streaming); buffer += "$var " + type + " " + std::to_string(var.width) + " "; emit_ident(var.ident); buffer += " "; emit_name(name); if (multipart || name.back() == ']' || lsb_at != 0) { if (var.width == 1) buffer += " [" + std::to_string(lsb_at) + "]"; else buffer += " [" + std::to_string(lsb_at + var.width - 1) + ":" + std::to_string(lsb_at) + "]"; } buffer += " $end\n"; } void emit_enddefinitions() { assert(!streaming); buffer += "$enddefinitions $end\n"; streaming = true; } void emit_time(uint64_t timestamp) { assert(streaming); buffer += "#" + std::to_string(timestamp) + "\n"; } void emit_scalar(const variable &var) { assert(streaming); assert(var.width == 1); buffer += (*var.curr ? '1' : '0'); emit_ident(var.ident); buffer += '\n'; } void emit_vector(const variable &var) { assert(streaming); buffer += 'b'; for (size_t bit = var.width - 1; bit != (size_t)-1; bit--) { bool bit_curr = var.curr[bit / (8 * sizeof(chunk_t))] & (1 << (bit % (8 * sizeof(chunk_t)))); buffer += (bit_curr ? '1' : '0'); } buffer += ' '; emit_ident(var.ident); buffer += '\n'; } void reset_outlines() { for (auto &outline_it : outlines) outline_it.second = /*warm=*/(outline_it.first == nullptr); } variable ®ister_variable(size_t width, chunk_t *curr, bool constant = false, debug_outline *outline = nullptr) { if (aliases.count(curr)) { return variables[aliases[curr]]; } else { auto outline_it = outlines.emplace(outline, /*warm=*/(outline == nullptr)).first; const size_t chunks = (width + (sizeof(chunk_t) * 8 - 1)) / (sizeof(chunk_t) * 8); aliases[curr] = variables.size(); if (constant) { variables.emplace_back(variable { variables.size(), width, curr, (size_t)-1, outline_it->first, &outline_it->second }); } else { variables.emplace_back(variable { variables.size(), width, curr, cache.size(), outline_it->first, &outline_it->second }); cache.insert(cache.end(), &curr[0], &curr[chunks]); } return variables.back(); } } bool test_variable(const variable &var) { if (var.cache_offset == (size_t)-1) return false; // constant if (!*var.outline_warm) { var.outline->eval(); *var.outline_warm = true; } const size_t chunks = (var.width + (sizeof(chunk_t) * 8 - 1)) / (sizeof(chunk_t) * 8); if (std::equal(&var.curr[0], &var.curr[chunks], &cache[var.cache_offset])) { return false; } else { std::copy(&var.curr[0], &var.curr[chunks], &cache[var.cache_offset]); return true; } } static std::vector split_hierarchy(const std::string &hier_name) { std::vector hierarchy; size_t prev = 0; while (true) { size_t curr = hier_name.find_first_of(' ', prev); if (curr == std::string::npos) { hierarchy.push_back(hier_name.substr(prev)); break; } else { hierarchy.push_back(hier_name.substr(prev, curr - prev)); prev = curr + 1; } } return hierarchy; } public: std::string buffer; void timescale(unsigned number, const std::string &unit) { emit_timescale(number, unit); } void add(const std::string &hier_name, const debug_item &item, bool multipart = false) { std::vector scope = split_hierarchy(hier_name); std::string name = scope.back(); scope.pop_back(); emit_scope(scope); switch (item.type) { // Not the best naming but oh well... case debug_item::VALUE: emit_var(register_variable(item.width, item.curr, /*constant=*/item.next == nullptr), "wire", name, item.lsb_at, multipart); break; case debug_item::WIRE: emit_var(register_variable(item.width, item.curr), "reg", name, item.lsb_at, multipart); break; case debug_item::MEMORY: { const size_t stride = (item.width + (sizeof(chunk_t) * 8 - 1)) / (sizeof(chunk_t) * 8); for (size_t index = 0; index < item.depth; index++) { chunk_t *nth_curr = &item.curr[stride * index]; std::string nth_name = name + '[' + std::to_string(index) + ']'; emit_var(register_variable(item.width, nth_curr), "reg", nth_name, item.lsb_at, multipart); } break; } case debug_item::ALIAS: // Like VALUE, but, even though `item.next == nullptr` always holds, the underlying value // can actually change, and must be tracked. In most cases the VCD identifier will be // unified with the aliased reg, but we should handle the case where only the alias is // added to the VCD writer, too. emit_var(register_variable(item.width, item.curr), "wire", name, item.lsb_at, multipart); break; case debug_item::OUTLINE: emit_var(register_variable(item.width, item.curr, /*constant=*/false, item.outline), "wire", name, item.lsb_at, multipart); break; } } template void add(const debug_items &items, const Filter &filter) { // `debug_items` is a map, so the items are already sorted in an order optimal for emitting // VCD scope sections. for (auto &it : items.table) for (auto &part : it.second) if (filter(it.first, part)) add(it.first, part, it.second.size() > 1); } void add(const debug_items &items) { this->add(items, [](const std::string &, const debug_item &) { return true; }); } void add_without_memories(const debug_items &items) { this->add(items, [](const std::string &, const debug_item &item) { return item.type != debug_item::MEMORY; }); } void sample(uint64_t timestamp) { bool first_sample = !streaming; if (first_sample) { emit_scope({}); emit_enddefinitions(); } reset_outlines(); emit_time(timestamp); for (auto var : variables) if (test_variable(var) || first_sample) { if (var.width == 1) emit_scalar(var); else emit_vector(var); } } }; } #endif yosys-yosys-0.33/backends/cxxrtl/cxxrtl_vcd_capi.cc000066400000000000000000000054001447554276300226120ustar00rootroot00000000000000/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2020 whitequark * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted. * * 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 file is a part of the CXXRTL C API. It should be used together with `cxxrtl_vcd_capi.h`. #include #include extern const cxxrtl::debug_items &cxxrtl_debug_items_from_handle(cxxrtl_handle handle); struct _cxxrtl_vcd { cxxrtl::vcd_writer writer; bool flush = false; }; cxxrtl_vcd cxxrtl_vcd_create() { return new _cxxrtl_vcd; } void cxxrtl_vcd_destroy(cxxrtl_vcd vcd) { delete vcd; } void cxxrtl_vcd_timescale(cxxrtl_vcd vcd, int number, const char *unit) { vcd->writer.timescale(number, unit); } void cxxrtl_vcd_add(cxxrtl_vcd vcd, const char *name, cxxrtl_object *object) { // Note the copy. We don't know whether `object` came from a design (in which case it is // an instance of `debug_item`), or from user code (in which case it is an instance of // `cxxrtl_object`), so casting the pointer wouldn't be safe. vcd->writer.add(name, cxxrtl::debug_item(*object)); } void cxxrtl_vcd_add_from(cxxrtl_vcd vcd, cxxrtl_handle handle) { vcd->writer.add(cxxrtl_debug_items_from_handle(handle)); } void cxxrtl_vcd_add_from_if(cxxrtl_vcd vcd, cxxrtl_handle handle, void *data, int (*filter)(void *data, const char *name, const cxxrtl_object *object)) { vcd->writer.add(cxxrtl_debug_items_from_handle(handle), [=](const std::string &name, const cxxrtl::debug_item &item) { return filter(data, name.c_str(), static_cast(&item)); }); } void cxxrtl_vcd_add_from_without_memories(cxxrtl_vcd vcd, cxxrtl_handle handle) { vcd->writer.add_without_memories(cxxrtl_debug_items_from_handle(handle)); } void cxxrtl_vcd_sample(cxxrtl_vcd vcd, uint64_t time) { if (vcd->flush) { vcd->writer.buffer.clear(); vcd->flush = false; } vcd->writer.sample(time); } void cxxrtl_vcd_read(cxxrtl_vcd vcd, const char **data, size_t *size) { if (vcd->flush) { vcd->writer.buffer.clear(); vcd->flush = false; } *data = vcd->writer.buffer.c_str(); *size = vcd->writer.buffer.size(); vcd->flush = true; } yosys-yosys-0.33/backends/cxxrtl/cxxrtl_vcd_capi.h000066400000000000000000000103021447554276300224510ustar00rootroot00000000000000/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2020 whitequark * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted. * * 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 CXXRTL_VCD_CAPI_H #define CXXRTL_VCD_CAPI_H // This file is a part of the CXXRTL C API. It should be used together with `cxxrtl_vcd_capi.cc`. // // The CXXRTL C API for VCD writing makes it possible to insert virtual probes into designs and // dump waveforms to Value Change Dump files. #include #include #include #ifdef __cplusplus extern "C" { #endif // Opaque reference to a VCD writer. typedef struct _cxxrtl_vcd *cxxrtl_vcd; // Create a VCD writer. cxxrtl_vcd cxxrtl_vcd_create(); // Release all resources used by a VCD writer. void cxxrtl_vcd_destroy(cxxrtl_vcd vcd); // Set VCD timescale. // // The `number` must be 1, 10, or 100, and the `unit` must be one of `"s"`, `"ms"`, `"us"`, `"ns"`, // `"ps"`, or `"fs"`. // // Timescale can only be set before the first call to `cxxrtl_vcd_sample`. void cxxrtl_vcd_timescale(cxxrtl_vcd vcd, int number, const char *unit); // Schedule a specific CXXRTL object to be sampled. // // The `name` is a full hierarchical name as described for `cxxrtl_get`; it does not need to match // the original name of `object`, if any. The `object` must outlive the VCD writer, but there are // no other requirements; if desired, it can be provided by user code, rather than come from // a design. // // Objects can only be scheduled before the first call to `cxxrtl_vcd_sample`. void cxxrtl_vcd_add(cxxrtl_vcd vcd, const char *name, struct cxxrtl_object *object); // Schedule all CXXRTL objects in a simulation. // // The design `handle` must outlive the VCD writer. // // Objects can only be scheduled before the first call to `cxxrtl_vcd_sample`. void cxxrtl_vcd_add_from(cxxrtl_vcd vcd, cxxrtl_handle handle); // Schedule CXXRTL objects in a simulation that match a given predicate. // // For every object in the simulation, `filter` is called with the provided `data`, the full // hierarchical name of the object (see `cxxrtl_get` for details), and the object description. // The object will be sampled if the predicate returns a non-zero value. // // Objects can only be scheduled before the first call to `cxxrtl_vcd_sample`. void cxxrtl_vcd_add_from_if(cxxrtl_vcd vcd, cxxrtl_handle handle, void *data, int (*filter)(void *data, const char *name, const struct cxxrtl_object *object)); // Schedule all CXXRTL objects in a simulation except for memories. // // The design `handle` must outlive the VCD writer. // // Objects can only be scheduled before the first call to `cxxrtl_vcd_sample`. void cxxrtl_vcd_add_from_without_memories(cxxrtl_vcd vcd, cxxrtl_handle handle); // Sample all scheduled objects. // // First, `time` is written to the internal buffer. Second, the values of every signal changed since // the previous call to `cxxrtl_vcd_sample` (all values if this is the first call) are written to // the internal buffer. The contents of the buffer can be retrieved with `cxxrtl_vcd_read`. void cxxrtl_vcd_sample(cxxrtl_vcd vcd, uint64_t time); // Retrieve buffered VCD data. // // The pointer to the start of the next chunk of VCD data is assigned to `*data`, and the length // of that chunk is assigned to `*size`. The pointer to the data is valid until the next call to // `cxxrtl_vcd_sample` or `cxxrtl_vcd_read`. Once all of the buffered data has been retrieved, // this function will always return zero sized chunks. void cxxrtl_vcd_read(cxxrtl_vcd vcd, const char **data, size_t *size); #ifdef __cplusplus } #endif #endif yosys-yosys-0.33/backends/edif/000077500000000000000000000000001447554276300165135ustar00rootroot00000000000000yosys-yosys-0.33/backends/edif/Makefile.inc000066400000000000000000000000371447554276300207230ustar00rootroot00000000000000 OBJS += backends/edif/edif.o yosys-yosys-0.33/backends/edif/edif.cc000066400000000000000000000501321447554276300177320ustar00rootroot00000000000000/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ // [[CITE]] EDIF Version 2 0 0 Grammar // http://web.archive.org/web/20050730021644/http://www.edif.org/documentation/BNF_GRAMMAR/index.html #include "kernel/rtlil.h" #include "kernel/register.h" #include "kernel/sigtools.h" #include "kernel/celltypes.h" #include "kernel/log.h" #include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN #define EDIF_DEF(_id) edif_names(RTLIL::unescape_id(_id), true).c_str() #define EDIF_DEFR(_id, _ren, _bl, _br) edif_names(RTLIL::unescape_id(_id), true, _ren, _bl, _br).c_str() #define EDIF_REF(_id) edif_names(RTLIL::unescape_id(_id), false).c_str() struct EdifNames { int counter; char delim_left, delim_right; std::set generated_names, used_names; std::map name_map; EdifNames() : counter(1), delim_left('['), delim_right(']') { } std::string operator()(std::string id, bool define, bool port_rename = false, int range_left = 0, int range_right = 0) { if (define) { std::string new_id = operator()(id, false); if (port_rename) return stringf("(rename %s \"%s%c%d:%d%c\")", new_id.c_str(), id.c_str(), delim_left, range_left, range_right, delim_right); return new_id != id ? stringf("(rename %s \"%s\")", new_id.c_str(), id.c_str()) : id; } if (name_map.count(id) > 0) return name_map.at(id); if (generated_names.count(id) > 0) goto do_rename; if (id == "GND" || id == "VCC") goto do_rename; for (size_t i = 0; i < id.size(); i++) { if ('A' <= id[i] && id[i] <= 'Z') continue; if ('a' <= id[i] && id[i] <= 'z') continue; if ('0' <= id[i] && id[i] <= '9' && i > 0) continue; if (id[i] == '_' && i > 0 && i != id.size()-1) continue; goto do_rename; } used_names.insert(id); return id; do_rename:; std::string gen_name; while (1) { gen_name = stringf("id%05d", counter++); if (generated_names.count(gen_name) == 0 && used_names.count(gen_name) == 0) break; } generated_names.insert(gen_name); name_map[id] = gen_name; return gen_name; } }; struct EdifBackend : public Backend { EdifBackend() : Backend("edif", "write design to EDIF netlist file") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" write_edif [options] [filename]\n"); log("\n"); log("Write the current design to an EDIF netlist file.\n"); log("\n"); log(" -top top_module\n"); log(" set the specified module as design top module\n"); log("\n"); log(" -nogndvcc\n"); log(" do not create \"GND\" and \"VCC\" cells. (this will produce an error\n"); log(" if the design contains constant nets. use \"hilomap\" to map to custom\n"); log(" constant drivers first)\n"); log("\n"); log(" -gndvccy\n"); log(" create \"GND\" and \"VCC\" cells with \"Y\" outputs. (the default is\n"); log(" \"G\" for \"GND\" and \"P\" for \"VCC\".)\n"); log("\n"); log(" -attrprop\n"); log(" create EDIF properties for cell attributes\n"); log("\n"); log(" -keep\n"); log(" create extra KEEP nets by allowing a cell to drive multiple nets.\n"); log("\n"); log(" -pvector {par|bra|ang}\n"); log(" sets the delimiting character for module port rename clauses to\n"); log(" parentheses, square brackets, or angle brackets.\n"); log("\n"); log(" -lsbidx\n"); log(" use index 0 for the LSB bit of a net or port instead of MSB.\n"); log("\n"); log("Unfortunately there are different \"flavors\" of the EDIF file format. This\n"); log("command generates EDIF files for the Xilinx place&route tools. It might be\n"); log("necessary to make small modifications to this command when a different tool\n"); log("is targeted.\n"); log("\n"); } void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { log_header(design, "Executing EDIF backend.\n"); std::string top_module_name; bool port_rename = false; bool attr_properties = false; bool lsbidx = false; std::map> lib_cell_ports; bool nogndvcc = false, gndvccy = false, keepmode = false; CellTypes ct(design); EdifNames edif_names; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-top" && argidx+1 < args.size()) { top_module_name = args[++argidx]; continue; } if (args[argidx] == "-nogndvcc") { nogndvcc = true; continue; } if (args[argidx] == "-gndvccy") { gndvccy = true; continue; } if (args[argidx] == "-attrprop") { attr_properties = true; continue; } if (args[argidx] == "-keep") { keepmode = true; continue; } if (args[argidx] == "-pvector" && argidx+1 < args.size()) { std::string parray; port_rename = true; parray = args[++argidx]; if (parray == "par") { edif_names.delim_left = '(';edif_names.delim_right = ')'; } else if (parray == "ang") { edif_names.delim_left = '<';edif_names.delim_right = '>'; } else { edif_names.delim_left = '[';edif_names.delim_right = ']'; } continue; } if (args[argidx] == "-lsbidx") { lsbidx = true; continue; } break; } extra_args(f, filename, args, argidx); if (top_module_name.empty()) for (auto module : design->modules()) if (module->get_bool_attribute(ID::top)) top_module_name = module->name.str(); for (auto module : design->modules()) { lib_cell_ports[module->name]; for (auto port : module->ports) { Wire *wire = module->wire(port); lib_cell_ports[module->name][port] = std::max(lib_cell_ports[module->name][port], GetSize(wire)); } if (module->get_blackbox_attribute()) continue; if (top_module_name.empty()) top_module_name = module->name.str(); if (module->processes.size() != 0) log_error("Found unmapped processes in module %s: unmapped processes are not supported in EDIF backend!\n", log_id(module->name)); if (module->memories.size() != 0) log_error("Found unmapped memories in module %s: unmapped memories are not supported in EDIF backend!\n", log_id(module->name)); for (auto cell : module->cells()) { if (design->module(cell->type) == nullptr || design->module(cell->type)->get_blackbox_attribute()) { lib_cell_ports[cell->type]; for (auto p : cell->connections()) lib_cell_ports[cell->type][p.first] = std::max(lib_cell_ports[cell->type][p.first], GetSize(p.second)); } } } if (top_module_name.empty()) log_error("No module found in design!\n"); *f << stringf("(edif %s\n", EDIF_DEF(top_module_name)); *f << stringf(" (edifVersion 2 0 0)\n"); *f << stringf(" (edifLevel 0)\n"); *f << stringf(" (keywordMap (keywordLevel 0))\n"); *f << stringf(" (comment \"Generated by %s\")\n", yosys_version_str); *f << stringf(" (external LIB\n"); *f << stringf(" (edifLevel 0)\n"); *f << stringf(" (technology (numberDefinition))\n"); if (!nogndvcc) { *f << stringf(" (cell GND\n"); *f << stringf(" (cellType GENERIC)\n"); *f << stringf(" (view VIEW_NETLIST\n"); *f << stringf(" (viewType NETLIST)\n"); *f << stringf(" (interface (port %c (direction OUTPUT)))\n", gndvccy ? 'Y' : 'G'); *f << stringf(" )\n"); *f << stringf(" )\n"); *f << stringf(" (cell VCC\n"); *f << stringf(" (cellType GENERIC)\n"); *f << stringf(" (view VIEW_NETLIST\n"); *f << stringf(" (viewType NETLIST)\n"); *f << stringf(" (interface (port %c (direction OUTPUT)))\n", gndvccy ? 'Y' : 'P'); *f << stringf(" )\n"); *f << stringf(" )\n"); } for (auto &cell_it : lib_cell_ports) { *f << stringf(" (cell %s\n", EDIF_DEF(cell_it.first)); *f << stringf(" (cellType GENERIC)\n"); *f << stringf(" (view VIEW_NETLIST\n"); *f << stringf(" (viewType NETLIST)\n"); *f << stringf(" (interface\n"); for (auto &port_it : cell_it.second) { const char *dir = "INOUT"; if (ct.cell_known(cell_it.first)) { if (!ct.cell_output(cell_it.first, port_it.first)) dir = "INPUT"; else if (!ct.cell_input(cell_it.first, port_it.first)) dir = "OUTPUT"; } int width = port_it.second; int start = 0; bool upto = false; auto m = design->module(cell_it.first); if (m) { auto w = m->wire(port_it.first); if (w) { width = GetSize(w); start = w->start_offset; upto = w->upto; } } if (width == 1) *f << stringf(" (port %s (direction %s))\n", EDIF_DEF(port_it.first), dir); else { int b[2]; b[upto ? 0 : 1] = start; b[upto ? 1 : 0] = start+width-1; *f << stringf(" (port (array %s %d) (direction %s))\n", EDIF_DEFR(port_it.first, port_rename, b[0], b[1]), width, dir); } } *f << stringf(" )\n"); *f << stringf(" )\n"); *f << stringf(" )\n"); } *f << stringf(" )\n"); std::vector sorted_modules; // extract module dependencies std::map> module_deps; for (auto module : design->modules()) { module_deps[module] = std::set(); for (auto cell : module->cells()) if (design->module(cell->type) != nullptr) module_deps[module].insert(design->module(cell->type)); } // simple good-enough topological sort // (O(n*m) on n elements and depth m) while (module_deps.size() > 0) { size_t sorted_modules_idx = sorted_modules.size(); for (auto &it : module_deps) { for (auto &dep : it.second) if (module_deps.count(dep) > 0) goto not_ready_yet; // log("Next in topological sort: %s\n", log_id(it.first->name)); sorted_modules.push_back(it.first); not_ready_yet:; } if (sorted_modules_idx == sorted_modules.size()) log_error("Cyclic dependency between modules found! Cycle includes module %s.\n", log_id(module_deps.begin()->first->name)); while (sorted_modules_idx < sorted_modules.size()) module_deps.erase(sorted_modules.at(sorted_modules_idx++)); } *f << stringf(" (library DESIGN\n"); *f << stringf(" (edifLevel 0)\n"); *f << stringf(" (technology (numberDefinition))\n"); 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()) *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) { 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; 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()); } }; for (auto module : sorted_modules) { if (module->get_blackbox_attribute()) continue; SigMap sigmap(module); std::map>> net_join_db; *f << stringf(" (cell %s\n", EDIF_DEF(module->name)); *f << stringf(" (cellType GENERIC)\n"); *f << stringf(" (view VIEW_NETLIST\n"); *f << stringf(" (viewType NETLIST)\n"); *f << stringf(" (interface\n"); for (auto cell : module->cells()) { for (auto &conn : cell->connections()) if (cell->output(conn.first)) sigmap.add(conn.second); } for (auto wire : module->wires()) for (auto b1 : SigSpec(wire)) { auto b2 = sigmap(b1); if (b1 == b2 || !b2.wire) continue; log_assert(b1.wire != nullptr); Wire *w1 = b1.wire; Wire *w2 = b2.wire; { int c1 = w1->get_bool_attribute(ID::keep); int c2 = w2->get_bool_attribute(ID::keep); if (c1 > c2) goto promote; if (c1 < c2) goto nopromote; } { int c1 = w1->name.isPublic(); int c2 = w2->name.isPublic(); if (c1 > c2) goto promote; if (c1 < c2) goto nopromote; } { auto count_nontrivial_attr = [](Wire *w) { int count = w->attributes.size(); count -= w->attributes.count(ID::src); count -= w->attributes.count(ID::unused_bits); return count; }; int c1 = count_nontrivial_attr(w1); int c2 = count_nontrivial_attr(w2); if (c1 > c2) goto promote; if (c1 < c2) goto nopromote; } { int c1 = w1->port_id ? INT_MAX - w1->port_id : 0; int c2 = w2->port_id ? INT_MAX - w2->port_id : 0; if (c1 > c2) goto promote; if (c1 < c2) goto nopromote; } nopromote: if (0) promote: sigmap.add(b1); } for (auto wire : module->wires()) { if (wire->port_id == 0) continue; const char *dir = "INOUT"; if (!wire->port_output) dir = "INPUT"; else if (!wire->port_input) dir = "OUTPUT"; if (wire->width == 1) { *f << stringf(" (port %s (direction %s)", EDIF_DEF(wire->name), dir); if (attr_properties) for (auto &p : wire->attributes) add_prop(p.first, p.second); *f << ")\n"; RTLIL::SigSpec sig = sigmap(RTLIL::SigSpec(wire)); net_join_db[sig].insert(make_pair(stringf("(portRef %s)", EDIF_REF(wire->name)), wire->port_input)); } else { int b[2]; b[wire->upto ? 0 : 1] = wire->start_offset; b[wire->upto ? 1 : 0] = wire->start_offset + GetSize(wire) - 1; *f << stringf(" (port (array %s %d) (direction %s)", EDIF_DEFR(wire->name, port_rename, b[0], b[1]), wire->width, dir); if (attr_properties) for (auto &p : wire->attributes) add_prop(p.first, p.second); *f << ")\n"; for (int i = 0; i < wire->width; i++) { RTLIL::SigSpec sig = sigmap(RTLIL::SigSpec(wire, i)); net_join_db[sig].insert(make_pair(stringf("(portRef (member %s %d))", EDIF_REF(wire->name), lsbidx ? i : GetSize(wire)-i-1), wire->port_input)); } } } *f << stringf(" )\n"); *f << stringf(" (contents\n"); if (!nogndvcc) { *f << stringf(" (instance GND (viewRef VIEW_NETLIST (cellRef GND (libraryRef LIB))))\n"); *f << stringf(" (instance VCC (viewRef VIEW_NETLIST (cellRef VCC (libraryRef LIB))))\n"); } for (auto cell : module->cells()) { *f << stringf(" (instance %s\n", EDIF_DEF(cell->name)); *f << stringf(" (viewRef VIEW_NETLIST (cellRef %s%s))", EDIF_REF(cell->type), lib_cell_ports.count(cell->type) > 0 ? " (libraryRef LIB)" : ""); for (auto &p : cell->parameters) add_prop(p.first, p.second); if (attr_properties) for (auto &p : cell->attributes) add_prop(p.first, p.second); *f << stringf(")\n"); for (auto &p : cell->connections()) { RTLIL::SigSpec sig = sigmap(p.second); for (int i = 0; i < GetSize(sig); i++) if (sig[i].wire == NULL && sig[i] != RTLIL::State::S0 && sig[i] != RTLIL::State::S1) log_warning("Bit %d of cell port %s.%s.%s driven by %s will be left unconnected in EDIF output.\n", i, log_id(module), log_id(cell), log_id(p.first), log_signal(sig[i])); else { int member_idx = lsbidx ? i : GetSize(sig)-i-1; auto m = design->module(cell->type); int width = sig.size(); if (m) { auto w = m->wire(p.first); if (w) { member_idx = lsbidx ? i : GetSize(w)-i-1; width = GetSize(w); } } if (width == 1) net_join_db[sig[i]].insert(make_pair(stringf("(portRef %s (instanceRef %s))", EDIF_REF(p.first), EDIF_REF(cell->name)), cell->output(p.first))); else { net_join_db[sig[i]].insert(make_pair(stringf("(portRef (member %s %d) (instanceRef %s))", EDIF_REF(p.first), member_idx, EDIF_REF(cell->name)), cell->output(p.first))); } } } } for (auto &it : net_join_db) { RTLIL::SigBit sig = it.first; if (sig.wire == NULL && sig != RTLIL::State::S0 && sig != RTLIL::State::S1) { if (sig == RTLIL::State::Sx) { for (auto &ref : it.second) log_warning("Exporting x-bit on %s as zero bit.\n", ref.first.c_str()); sig = RTLIL::State::S0; } else if (sig == RTLIL::State::Sz) { continue; } else { for (auto &ref : it.second) log_error("Don't know how to handle %s on %s.\n", log_signal(sig), ref.first.c_str()); log_abort(); } } std::string netname; if (sig == RTLIL::State::S0) netname = "GND_NET"; else if (sig == RTLIL::State::S1) netname = "VCC_NET"; else { netname = log_signal(sig); for (size_t i = 0; i < netname.size(); i++) if (netname[i] == ' ' || netname[i] == '\\') netname.erase(netname.begin() + i--); } *f << stringf(" (net %s (joined\n", EDIF_DEF(netname)); for (auto &ref : it.second) *f << stringf(" %s\n", ref.first.c_str()); if (sig.wire == NULL) { if (nogndvcc) log_error("Design contains constant nodes (map with \"hilomap\" first).\n"); if (sig == RTLIL::State::S0) *f << stringf(" (portRef %c (instanceRef GND))\n", gndvccy ? 'Y' : 'G'); if (sig == RTLIL::State::S1) *f << stringf(" (portRef %c (instanceRef VCC))\n", gndvccy ? 'Y' : 'P'); } *f << stringf(" )"); if (attr_properties && sig.wire != NULL) for (auto &p : sig.wire->attributes) add_prop(p.first, p.second); *f << stringf("\n )\n"); } for (auto wire : module->wires()) { if (!wire->get_bool_attribute(ID::keep)) continue; for(int i = 0; i < wire->width; i++) { SigBit raw_sig = RTLIL::SigSpec(wire, i); SigBit mapped_sig = sigmap(raw_sig); if (raw_sig == mapped_sig || net_join_db.count(mapped_sig) == 0) continue; std::string netname = log_signal(raw_sig); for (size_t i = 0; i < netname.size(); i++) if (netname[i] == ' ' || netname[i] == '\\') netname.erase(netname.begin() + i--); if (keepmode) { *f << stringf(" (net %s (joined\n", EDIF_DEF(netname)); auto &refs = net_join_db.at(mapped_sig); for (auto &ref : refs) if (ref.second) *f << stringf(" %s\n", ref.first.c_str()); *f << stringf(" )"); if (attr_properties && raw_sig.wire != NULL) for (auto &p : raw_sig.wire->attributes) add_prop(p.first, p.second); *f << stringf("\n )\n"); } else { log_warning("Ignoring conflicting 'keep' property on net %s. Use -keep to generate the extra net nevertheless.\n", EDIF_DEF(netname)); } } } *f << stringf(" )\n"); *f << stringf(" )\n"); *f << stringf(" )\n"); } *f << stringf(" )\n"); *f << stringf(" (design %s\n", EDIF_DEF(top_module_name)); *f << stringf(" (cellRef %s (libraryRef DESIGN))\n", EDIF_REF(top_module_name)); *f << stringf(" )\n"); *f << stringf(")\n"); } } EdifBackend; PRIVATE_NAMESPACE_END yosys-yosys-0.33/backends/edif/runtest.py000066400000000000000000000103551447554276300205750ustar00rootroot00000000000000#!/usr/bin/env python3 import os import numpy as np enable_upto = True enable_offset = True enable_hierarchy = True enable_logic = True def make_module(f, modname, width, subs): print("module %s (A, B, C, X, Y, Z);" % modname, file=f) inbits = list() outbits = list() for p in "ABC": offset = np.random.randint(10) if enable_offset else 0 if enable_upto and np.random.randint(2): print(" input [%d:%d] %s;" % (offset, offset+width-1, p), file=f) else: print(" input [%d:%d] %s;" % (offset+width-1, offset, p), file=f) for i in range(offset, offset+width): inbits.append("%s[%d]" % (p, i)) for p in "XYZ": offset = np.random.randint(10) if enable_offset else 0 if enable_upto and np.random.randint(2): print(" output [%d:%d] %s;" % (offset, offset+width-1, p), file=f) else: print(" output [%d:%d] %s;" % (offset+width-1, offset, p), file=f) for i in range(offset, offset+width): outbits.append("%s[%d]" % (p, i)) instidx = 0 subcandidates = list(subs.keys()) while len(outbits) > 0: submod = None if len(subcandidates): submod = np.random.choice(subcandidates) subcandidates.remove(submod) if submod is None or 3*subs[submod] >= len(outbits): for bit in outbits: if enable_logic: print(" assign %s = %s & ~%s;" % (bit, np.random.choice(inbits), np.random.choice(inbits)), file=f) else: print(" assign %s = %s;" % (bit, np.random.choice(inbits)), file=f) break instidx += 1 print(" %s inst%d (" % (submod, instidx), file=f) for p in "ABC": print(" .%s({%s})," % (p, ",".join(np.random.choice(inbits, subs[submod]))), file=f) for p in "XYZ": bits = list(np.random.choice(outbits, subs[submod], False)) for bit in bits: outbits.remove(bit) print(" .%s({%s})%s" % (p, ",".join(bits), "," if p != "Z" else ""), file=f) print(" );", file=f); print("endmodule", file=f) with open("test_top.v", "w") as f: if enable_hierarchy: make_module(f, "sub1", 2, {}) make_module(f, "sub2", 3, {}) make_module(f, "sub3", 4, {}) make_module(f, "sub4", 8, {"sub1": 2, "sub2": 3, "sub3": 4}) make_module(f, "sub5", 8, {"sub1": 2, "sub2": 3, "sub3": 4}) make_module(f, "sub6", 8, {"sub1": 2, "sub2": 3, "sub3": 4}) make_module(f, "top", 32, {"sub4": 8, "sub5": 8, "sub6": 8}) else: make_module(f, "top", 32, {}) os.system("set -x; ../../yosys -p 'synth_xilinx -top top; write_edif -pvector par test_syn.edif' test_top.v") with open("test_syn.tcl", "w") as f: print("read_edif test_syn.edif", file=f) print("link_design", file=f) print("write_verilog -force test_syn.v", file=f) os.system("set -x; vivado -nojournal -nolog -mode batch -source test_syn.tcl") with open("test_tb.v", "w") as f: print("module tb;", file=f) print(" reg [31:0] A, B, C;", file=f) print(" wire [31:0] X, Y, Z;", file=f) print("", file=f) print(" top uut (", file=f) print(" .A(A),", file=f) print(" .B(B),", file=f) print(" .C(C),", file=f) print(" .X(X),", file=f) print(" .Y(Y),", file=f) print(" .Z(Z)", file=f) print(" );", file=f) print("", file=f) print(" initial begin", file=f) for i in range(100): print(" A = 32'h%08x;" % np.random.randint(2**32), file=f) print(" B = 32'h%08x;" % np.random.randint(2**32), file=f) print(" C = 32'h%08x;" % np.random.randint(2**32), file=f) print(" #10;", file=f) print(" $display(\"%x %x %x\", X, Y, Z);", file=f) print(" #10;", file=f) print(" $finish;", file=f) print(" end", file=f) print("endmodule", file=f) os.system("set -x; iverilog -o test_gold test_tb.v test_top.v") os.system("set -x; iverilog -o test_gate test_tb.v test_syn.v ../../techlibs/xilinx/cells_sim.v") os.system("set -x; ./test_gold > test_gold.out") os.system("set -x; ./test_gate > test_gate.out") os.system("set -x; md5sum test_gold.out test_gate.out") yosys-yosys-0.33/backends/firrtl/000077500000000000000000000000001447554276300171065ustar00rootroot00000000000000yosys-yosys-0.33/backends/firrtl/.gitignore000066400000000000000000000000241447554276300210720ustar00rootroot00000000000000test.fir test_out.v yosys-yosys-0.33/backends/firrtl/Makefile.inc000066400000000000000000000000431447554276300213130ustar00rootroot00000000000000 OBJS += backends/firrtl/firrtl.o yosys-yosys-0.33/backends/firrtl/firrtl.cc000066400000000000000000001232451447554276300207260ustar00rootroot00000000000000/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ #include "kernel/rtlil.h" #include "kernel/register.h" #include "kernel/sigtools.h" #include "kernel/celltypes.h" #include "kernel/log.h" #include "kernel/mem.h" #include #include #include #include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN pool used_names; dict namecache; int autoid_counter; typedef unsigned FDirection; static const FDirection FD_NODIRECTION = 0x0; static const FDirection FD_IN = 0x1; static const FDirection FD_OUT = 0x2; static const FDirection FD_INOUT = 0x3; static const int FIRRTL_MAX_DSH_WIDTH_ERROR = 20; // For historic reasons, this is actually one greater than the maximum allowed shift width std::string getFileinfo(const RTLIL::AttrObject *design_entity) { std::string src(design_entity->get_src_attribute()); std::string fileinfo_str = src.empty() ? "" : "@[" + src + "]"; return fileinfo_str; } // Get a port direction with respect to a specific module. FDirection getPortFDirection(IdString id, Module *module) { Wire *wire = module->wires_.at(id); FDirection direction = FD_NODIRECTION; if (wire && wire->port_id) { if (wire->port_input) direction |= FD_IN; if (wire->port_output) direction |= FD_OUT; } return direction; } string next_id() { string new_id; while (1) { new_id = stringf("_%d", autoid_counter++); if (used_names.count(new_id) == 0) break; } used_names.insert(new_id); return new_id; } const char *make_id(IdString id) { if (namecache.count(id) != 0) return namecache.at(id).c_str(); string new_id = log_id(id); for (int i = 0; i < GetSize(new_id); i++) { char &ch = new_id[i]; if ('a' <= ch && ch <= 'z') continue; if ('A' <= ch && ch <= 'Z') continue; if ('0' <= ch && ch <= '9' && i != 0) continue; if ('_' == ch) continue; ch = '_'; } while (used_names.count(new_id) != 0) new_id += '_'; namecache[id] = new_id; used_names.insert(new_id); return namecache.at(id).c_str(); } std::string dump_const_string(const RTLIL::Const &data) { std::string res_str; std::string str = data.decode_string(); for (size_t i = 0; i < str.size(); i++) { if (str[i] == '\n') res_str += "\\n"; else if (str[i] == '\t') res_str += "\\t"; else if (str[i] < 32) res_str += stringf("\\%03o", str[i]); else if (str[i] == '"') res_str += "\\\""; else if (str[i] == '\\') res_str += "\\\\"; else res_str += str[i]; } return res_str; } std::string dump_const(const RTLIL::Const &data) { std::string res_str; // // For debugging purposes to find out how Yosys encodes flags. // res_str += stringf("flags_%x --> ", data.flags); // Real-valued parameter. if (data.flags & RTLIL::CONST_FLAG_REAL) { // Yosys stores real values as strings, so we call the string dumping code. res_str += dump_const_string(data); } // String parameter. else if (data.flags & RTLIL::CONST_FLAG_STRING) { res_str += "\""; res_str += dump_const_string(data); res_str += "\""; } // Numeric (non-real) parameter. else { int width = data.bits.size(); // If a standard 32-bit int, then emit standard int value like "56" or // "-56". Firrtl supports negative-valued int literals. // // SignedInt // : ( '+' | '-' ) PosInt // ; if (width <= 32) { int32_t int_val = 0; for (int i = 0; i < width; i++) { switch (data.bits[i]) { case State::S0: break; case State::S1: int_val |= (1 << i); break; default: log_error("Unexpected int value\n"); break; } } res_str += stringf("%d", int_val); } else { // If value is larger than 32 bits, then emit a binary representation of // the number as integers are not large enough to contain the result. // There is a caveat to this approach though: // // Note that parameter may be defined as having a fixed width as follows: // // parameter signed [26:0] test_signed; // parameter [26:0] test_unsigned; // parameter signed [40:0] test_signed_large; // // However, if you assign a value on the RHS without specifying the // precision, then yosys considers the value you used as an int and // assigns it a width of 32 bits regardless of the type of the parameter. // // defparam .test_signed = 49; (width = 32, though should be 27 based on definition) // defparam .test_unsigned = 40'd35; (width = 40, though should be 27 based on definition) // defparam .test_signed_large = 40'd12; (width = 40) // // We therefore may lose the precision of the original verilog literal if // it was written without its bitwidth specifier. // Emit binary prefix for string. res_str += "\"b"; // Emit bits. for (int i = width - 1; i >= 0; i--) { log_assert(i < width); switch (data.bits[i]) { case State::S0: res_str += "0"; break; case State::S1: res_str += "1"; break; case State::Sx: res_str += "x"; break; case State::Sz: res_str += "z"; break; case State::Sa: res_str += "-"; break; case State::Sm: res_str += "m"; break; } } res_str += "\""; } } return res_str; } std::string extmodule_name(RTLIL::Cell *cell, RTLIL::Module *mod_instance) { // Since we are creating a custom extmodule for every cell that instantiates // this blackbox, we need to create a custom name for it. We just use the // name of the blackbox itself followed by the name of the cell. const std::string cell_name = std::string(make_id(cell->name)); const std::string blackbox_name = std::string(make_id(mod_instance->name)); const std::string extmodule_name = blackbox_name + "_" + cell_name; return extmodule_name; } /** * Emits a parameterized extmodule. Instance parameters are obtained from * ''cell'' as it represents the instantiation of the blackbox defined by * ''mod_instance'' and therefore contains all its instance parameters. */ void emit_extmodule(RTLIL::Cell *cell, RTLIL::Module *mod_instance, std::ostream &f) { const std::string indent = " "; const std::string blackbox_name = std::string(make_id(mod_instance->name)); const std::string exported_name = extmodule_name(cell, mod_instance); // We use the cell's fileinfo for this extmodule as its parameters come from // the cell and not from the module itself (the module contains default // parameters, not the instance-specific ones we're using to emit the // extmodule). const std::string extmoduleFileinfo = getFileinfo(cell); // Emit extmodule header. f << stringf(" extmodule %s: %s\n", exported_name.c_str(), extmoduleFileinfo.c_str()); // Emit extmodule ports. for (auto wire : mod_instance->wires()) { const auto wireName = make_id(wire->name); const std::string wireFileinfo = getFileinfo(wire); if (wire->port_input && wire->port_output) { log_error("Module port %s.%s is inout!\n", log_id(mod_instance), log_id(wire)); } const std::string portDecl = stringf("%s%s %s: UInt<%d> %s\n", indent.c_str(), wire->port_input ? "input" : "output", wireName, wire->width, wireFileinfo.c_str() ); f << portDecl; } // Emit extmodule "defname" field. This is the name of the verilog blackbox // that is used when verilog is emitted, so we use the name of mod_instance // here. f << stringf("%sdefname = %s\n", indent.c_str(), blackbox_name.c_str()); // Emit extmodule generic parameters. for (const auto &p : cell->parameters) { const RTLIL::IdString p_id = p.first; const RTLIL::Const p_value = p.second; std::string param_name(p_id.c_str()); const std::string param_value = dump_const(p_value); // Remove backslashes from parameters as these come from the internal RTLIL // naming scheme, but should not exist in the emitted firrtl blackboxes. // When firrtl is converted to verilog and given to downstream synthesis // tools, these tools expect to find blackbox names and parameters as they // were originally defined, i.e. without the extra RTLIL naming conventions. param_name.erase( std::remove(param_name.begin(), param_name.end(), '\\'), param_name.end() ); f << stringf("%sparameter %s = %s\n", indent.c_str(), param_name.c_str(), param_value.c_str()); } f << "\n"; } /** * Emits extmodules for every instantiated blackbox in the design. * * RTLIL stores instance parameters at the cell's instantiation location. * However, firrtl does not support module parameterization (everything is * already elaborated). Firrtl instead supports external modules (extmodule), * i.e. blackboxes that are defined by verilog and which have no body in * firrtl itself other than the declaration of the blackboxes ports and * parameters. * * Furthermore, firrtl does not support parameterization (even of extmodules) * at a module's instantiation location and users must instead declare * different extmodules with different instance parameters in the extmodule * definition itself. * * This function goes through the design to identify all RTLIL blackboxes * and emit parameterized extmodules with a unique name for each of them. The * name that's given to the extmodule is * * _ * * Beware that it is therefore necessary for users to replace "parameterized" * instances in the RTLIL sense with these custom extmodules for the firrtl to * be valid. */ void emit_elaborated_extmodules(RTLIL::Design *design, std::ostream &f) { for (auto module : design->modules()) { for (auto cell : module->cells()) { // Is this cell a module instance? bool cellIsModuleInstance = cell->type[0] != '$'; if (cellIsModuleInstance) { // Find the module corresponding to this instance. auto modInstance = design->module(cell->type); // Ensure that we actually have a module instance if (modInstance == nullptr) { log_error("Unknown cell type %s\n", cell->type.c_str()); return; } bool modIsBlackbox = modInstance->get_blackbox_attribute(); if (modIsBlackbox) { emit_extmodule(cell, modInstance, f); } } } } } struct FirrtlWorker { Module *module; std::ostream &f; dict> reverse_wire_map; string unconn_id; RTLIL::Design *design; std::string indent; void register_reverse_wire_map(string id, SigSpec sig) { for (int i = 0; i < GetSize(sig); i++) reverse_wire_map[sig[i]] = make_pair(id, i); } FirrtlWorker(Module *module, std::ostream &f, RTLIL::Design *theDesign) : module(module), f(f), design(theDesign), indent(" ") { } static string make_expr(const SigSpec &sig) { string expr; for (auto chunk : sig.chunks()) { string new_expr; if (chunk.wire == nullptr) { std::vector bits = chunk.data; new_expr = stringf("UInt<%d>(\"h", GetSize(bits)); while (GetSize(bits) % 4 != 0) bits.push_back(State::S0); for (int i = GetSize(bits)-4; i >= 0; i -= 4) { int val = 0; if (bits[i+0] == State::S1) val += 1; if (bits[i+1] == State::S1) val += 2; if (bits[i+2] == State::S1) val += 4; if (bits[i+3] == State::S1) val += 8; new_expr.push_back(val < 10 ? '0' + val : 'a' + val - 10); } new_expr += "\")"; } else if (chunk.offset == 0 && chunk.width == chunk.wire->width) { new_expr = make_id(chunk.wire->name); } else { string wire_id = make_id(chunk.wire->name); new_expr = stringf("bits(%s, %d, %d)", wire_id.c_str(), chunk.offset + chunk.width - 1, chunk.offset); } if (expr.empty()) expr = new_expr; else expr = "cat(" + new_expr + ", " + expr + ")"; } return expr; } std::string fid(RTLIL::IdString internal_id) { return make_id(internal_id); } std::string cellname(RTLIL::Cell *cell) { return fid(cell->name).c_str(); } void process_instance(RTLIL::Cell *cell, vector &wire_exprs) { std::string cell_type = fid(cell->type); std::string instanceOf; // If this is a parameterized module, its parent module is encoded in the cell type if (cell->type.begins_with("$paramod")) { log_assert(cell->has_attribute(ID::hdlname)); instanceOf = cell->get_string_attribute(ID::hdlname); } else { instanceOf = cell_type; } std::string cell_name = cellname(cell); std::string cell_name_comment; if (cell_name != fid(cell->name)) cell_name_comment = " /* " + fid(cell->name) + " */ "; else cell_name_comment = ""; // Find the module corresponding to this instance. auto instModule = design->module(cell->type); // If there is no instance for this, just return. if (instModule == NULL) { log_warning("No instance for %s.%s\n", cell_type.c_str(), cell_name.c_str()); return; } // If the instance is that of a blackbox, use the modified extmodule name // that contains per-instance parameterizations. These instances were // emitted earlier in the firrtl backend. const std::string instanceName = instModule->get_blackbox_attribute() ? extmodule_name(cell, instModule) : instanceOf; std::string cellFileinfo = getFileinfo(cell); wire_exprs.push_back(stringf("%s" "inst %s%s of %s %s", indent.c_str(), cell_name.c_str(), cell_name_comment.c_str(), instanceName.c_str(), cellFileinfo.c_str())); for (auto it = cell->connections().begin(); it != cell->connections().end(); ++it) { if (it->second.size() > 0) { const SigSpec &secondSig = it->second; const std::string firstName = cell_name + "." + make_id(it->first); const std::string secondExpr = make_expr(secondSig); // Find the direction for this port. FDirection dir = getPortFDirection(it->first, instModule); std::string sourceExpr, sinkExpr; const SigSpec *sinkSig = nullptr; switch (dir) { case FD_INOUT: log_warning("Instance port connection %s.%s is INOUT; treating as OUT\n", cell_type.c_str(), log_signal(it->second)); YS_FALLTHROUGH case FD_OUT: sourceExpr = firstName; sinkExpr = secondExpr; sinkSig = &secondSig; break; case FD_NODIRECTION: log_warning("Instance port connection %s.%s is NODIRECTION; treating as IN\n", cell_type.c_str(), log_signal(it->second)); YS_FALLTHROUGH case FD_IN: sourceExpr = secondExpr; sinkExpr = firstName; break; default: log_error("Instance port %s.%s unrecognized connection direction 0x%x !\n", cell_type.c_str(), log_signal(it->second), dir); break; } // Check for subfield assignment. std::string bitsString = "bits("; if (sinkExpr.compare(0, bitsString.length(), bitsString) == 0) { if (sinkSig == nullptr) log_error("Unknown subfield %s.%s\n", cell_type.c_str(), sinkExpr.c_str()); // Don't generate the assignment here. // Add the source and sink to the "reverse_wire_map" and we'll output the assignment // as part of the coalesced subfield assignments for this wire. register_reverse_wire_map(sourceExpr, *sinkSig); } else { wire_exprs.push_back(stringf("\n%s%s <= %s %s", indent.c_str(), sinkExpr.c_str(), sourceExpr.c_str(), cellFileinfo.c_str())); } } } wire_exprs.push_back(stringf("\n")); } // Given an expression for a shift amount, and a maximum width, // generate the FIRRTL expression for equivalent dynamic shift taking into account FIRRTL shift semantics. std::string gen_dshl(const string b_expr, const int b_width) { string result = b_expr; if (b_width >= FIRRTL_MAX_DSH_WIDTH_ERROR) { int max_shift_width_bits = FIRRTL_MAX_DSH_WIDTH_ERROR - 1; string max_shift_string = stringf("UInt<%d>(%d)", max_shift_width_bits, (1<name), moduleFileinfo.c_str()); vector port_decls, wire_decls, mem_exprs, cell_exprs, wire_exprs; std::vector memories = Mem::get_all_memories(module); for (auto &mem : memories) mem.narrow(); for (auto wire : module->wires()) { const auto wireName = make_id(wire->name); std::string wireFileinfo = getFileinfo(wire); // If a wire has initial data, issue a warning since FIRRTL doesn't currently support it. if (wire->attributes.count(ID::init)) { log_warning("Initial value (%s) for (%s.%s) not supported\n", wire->attributes.at(ID::init).as_string().c_str(), log_id(module), log_id(wire)); } if (wire->port_id) { if (wire->port_input && wire->port_output) log_error("Module port %s.%s is inout!\n", log_id(module), log_id(wire)); port_decls.push_back(stringf("%s%s %s: UInt<%d> %s\n", indent.c_str(), wire->port_input ? "input" : "output", wireName, wire->width, wireFileinfo.c_str())); } else { wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent.c_str(), wireName, wire->width, wireFileinfo.c_str())); } } for (auto cell : module->cells()) { Const ndef(0, 0); // Is this cell is a module instance? if (module->design->module(cell->type)) { process_instance(cell, wire_exprs); continue; } // Not a module instance. Set up cell properties bool extract_y_bits = false; // Assume no extraction of final bits will be required. int a_width = cell->parameters.at(ID::A_WIDTH, ndef).as_int(); // The width of "A" int b_width = cell->parameters.at(ID::B_WIDTH, ndef).as_int(); // The width of "A" const int y_width = cell->parameters.at(ID::Y_WIDTH, ndef).as_int(); // The width of the result const bool a_signed = cell->parameters.at(ID::A_SIGNED, ndef).as_bool(); const bool b_signed = cell->parameters.at(ID::B_SIGNED, ndef).as_bool(); bool firrtl_is_signed = a_signed; // The result is signed (subsequent code may change this). int firrtl_width = 0; string primop; bool always_uint = false; string y_id = make_id(cell->name); std::string cellFileinfo = getFileinfo(cell); if (cell->type.in(ID($not), ID($logic_not), ID($_NOT_), ID($neg), ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_bool), ID($reduce_xnor))) { string a_expr = make_expr(cell->getPort(ID::A)); wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent.c_str(), y_id.c_str(), y_width, cellFileinfo.c_str())); if (a_signed) { a_expr = "asSInt(" + a_expr + ")"; } // Don't use the results of logical operations (a single bit) to control padding if (!(cell->type.in(ID($eq), ID($eqx), ID($gt), ID($ge), ID($lt), ID($le), ID($ne), ID($nex), ID($reduce_bool), ID($logic_not)) && y_width == 1) ) { a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width); } // Assume the FIRRTL width is a single bit. firrtl_width = 1; if (cell->type.in(ID($not), ID($_NOT_))) primop = "not"; else if (cell->type == ID($neg)) { primop = "neg"; firrtl_is_signed = true; // Result of "neg" is signed (an SInt). firrtl_width = a_width; } else if (cell->type == ID($logic_not)) { primop = "eq"; a_expr = stringf("%s, UInt(0)", a_expr.c_str()); } else if (cell->type == ID($reduce_and)) primop = "andr"; else if (cell->type == ID($reduce_or)) primop = "orr"; else if (cell->type == ID($reduce_xor)) primop = "xorr"; else if (cell->type == ID($reduce_xnor)) { primop = "not"; a_expr = stringf("xorr(%s)", a_expr.c_str()); } else if (cell->type == ID($reduce_bool)) { primop = "neq"; // Use the sign of the a_expr and its width as the type (UInt/SInt) and width of the comparand. a_expr = stringf("%s, %cInt<%d>(0)", a_expr.c_str(), a_signed ? 'S' : 'U', a_width); } string expr = stringf("%s(%s)", primop.c_str(), a_expr.c_str()); if ((firrtl_is_signed && !always_uint)) expr = stringf("asUInt(%s)", expr.c_str()); cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); register_reverse_wire_map(y_id, cell->getPort(ID::Y)); continue; } if (cell->type.in(ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($xor), ID($_XOR_), ID($xnor), ID($and), ID($_AND_), ID($or), ID($_OR_), ID($eq), ID($eqx), ID($gt), ID($ge), ID($lt), ID($le), ID($ne), ID($nex), ID($shr), ID($sshr), ID($sshl), ID($shl), ID($logic_and), ID($logic_or), ID($pow))) { string a_expr = make_expr(cell->getPort(ID::A)); string b_expr = make_expr(cell->getPort(ID::B)); std::string cellFileinfo = getFileinfo(cell); wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent.c_str(), y_id.c_str(), y_width, cellFileinfo.c_str())); if (a_signed) { a_expr = "asSInt(" + a_expr + ")"; // Expand the "A" operand to the result width if (a_width < y_width) { a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width); a_width = y_width; } } // Shift amount is always unsigned, and needn't be padded to result width, // otherwise, we need to cast the b_expr appropriately if (b_signed && !cell->type.in(ID($shr), ID($sshr), ID($shl), ID($sshl), ID($pow))) { b_expr = "asSInt(" + b_expr + ")"; // Expand the "B" operand to the result width if (b_width < y_width) { b_expr = stringf("pad(%s, %d)", b_expr.c_str(), y_width); b_width = y_width; } } // For the arithmetic ops, expand operand widths to result widths befor performing the operation. // This corresponds (according to iverilog) to what verilog compilers implement. if (cell->type.in(ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($xor), ID($_XOR_), ID($xnor), ID($and), ID($_AND_), ID($or), ID($_OR_))) { if (a_width < y_width) { a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width); a_width = y_width; } if (b_width < y_width) { b_expr = stringf("pad(%s, %d)", b_expr.c_str(), y_width); b_width = y_width; } } // Assume the FIRRTL width is the width of "A" firrtl_width = a_width; auto a_sig = cell->getPort(ID::A); if (cell->type == ID($add)) { primop = "add"; firrtl_is_signed = a_signed | b_signed; firrtl_width = max(a_width, b_width); } else if (cell->type == ID($sub)) { primop = "sub"; firrtl_is_signed = true; int a_widthInc = (!a_signed && b_signed) ? 2 : (a_signed && !b_signed) ? 1 : 0; int b_widthInc = (a_signed && !b_signed) ? 2 : (!a_signed && b_signed) ? 1 : 0; firrtl_width = max(a_width + a_widthInc, b_width + b_widthInc); } else if (cell->type == ID($mul)) { primop = "mul"; firrtl_is_signed = a_signed | b_signed; firrtl_width = a_width + b_width; } else if (cell->type == ID($div)) { primop = "div"; firrtl_is_signed = a_signed | b_signed; firrtl_width = a_width; } else if (cell->type == ID($mod)) { // "rem" = truncating modulo primop = "rem"; firrtl_width = min(a_width, b_width); } else if (cell->type.in(ID($and), ID($_AND_))) { primop = "and"; always_uint = true; firrtl_width = max(a_width, b_width); } else if (cell->type.in(ID($or), ID($_OR_))) { primop = "or"; always_uint = true; firrtl_width = max(a_width, b_width); } else if (cell->type.in(ID($xor), ID($_XOR_))) { primop = "xor"; always_uint = true; firrtl_width = max(a_width, b_width); } else if (cell->type == ID($xnor)) { primop = "xnor"; always_uint = true; firrtl_width = max(a_width, b_width); } else if ((cell->type == ID($eq)) || (cell->type == ID($eqx))) { primop = "eq"; always_uint = true; firrtl_width = 1; } else if ((cell->type == ID($ne)) || (cell->type == ID($nex))) { primop = "neq"; always_uint = true; firrtl_width = 1; } else if (cell->type == ID($gt)) { primop = "gt"; always_uint = true; firrtl_width = 1; } else if (cell->type == ID($ge)) { primop = "geq"; always_uint = true; firrtl_width = 1; } else if (cell->type == ID($lt)) { primop = "lt"; always_uint = true; firrtl_width = 1; } else if (cell->type == ID($le)) { primop = "leq"; always_uint = true; firrtl_width = 1; } else if ((cell->type == ID($shl)) || (cell->type == ID($sshl))) { // FIRRTL will widen the result (y) by the amount of the shift. // We'll need to offset this by extracting the un-widened portion as Verilog would do. extract_y_bits = true; // Is the shift amount constant? auto b_sig = cell->getPort(ID::B); if (b_sig.is_fully_const()) { primop = "shl"; int shift_amount = b_sig.as_int(); b_expr = std::to_string(shift_amount); firrtl_width = a_width + shift_amount; } else { primop = "dshl"; // Convert from FIRRTL left shift semantics. b_expr = gen_dshl(b_expr, b_width); firrtl_width = a_width + (1 << b_width) - 1; } } else if ((cell->type == ID($shr)) || (cell->type == ID($sshr))) { // We don't need to extract a specific range of bits. extract_y_bits = false; // Is the shift amount constant? auto b_sig = cell->getPort(ID::B); if (b_sig.is_fully_const()) { primop = "shr"; int shift_amount = b_sig.as_int(); b_expr = std::to_string(shift_amount); firrtl_width = max(1, a_width - shift_amount); } else { primop = "dshr"; firrtl_width = a_width; } // We'll need to do some special fixups if the source (and thus result) is signed. if (firrtl_is_signed) { // If this is a "logical" shift right, pretend the source is unsigned. if (cell->type == ID($shr)) { a_expr = "asUInt(" + a_expr + ")"; } } } else if ((cell->type == ID($logic_and))) { primop = "and"; a_expr = "neq(" + a_expr + ", UInt(0))"; b_expr = "neq(" + b_expr + ", UInt(0))"; always_uint = true; firrtl_width = 1; } else if ((cell->type == ID($logic_or))) { primop = "or"; a_expr = "neq(" + a_expr + ", UInt(0))"; b_expr = "neq(" + b_expr + ", UInt(0))"; always_uint = true; firrtl_width = 1; } else if ((cell->type == ID($pow))) { if (a_sig.is_fully_const() && a_sig.as_int() == 2) { // We'll convert this to a shift. To simplify things, change the a_expr to "1" // so we can use b_expr directly as a shift amount. // Only support 2 ** N (i.e., shift left) // FIRRTL will widen the result (y) by the amount of the shift. // We'll need to offset this by extracting the un-widened portion as Verilog would do. a_expr = firrtl_is_signed ? "SInt(1)" : "UInt(1)"; extract_y_bits = true; // Is the shift amount constant? auto b_sig = cell->getPort(ID::B); if (b_sig.is_fully_const()) { primop = "shl"; int shiftAmount = b_sig.as_int(); if (shiftAmount < 0) { log_error("Negative power exponent - %d: %s.%s\n", shiftAmount, log_id(module), log_id(cell)); } b_expr = std::to_string(shiftAmount); firrtl_width = a_width + shiftAmount; } else { primop = "dshl"; // Convert from FIRRTL left shift semantics. b_expr = gen_dshl(b_expr, b_width); firrtl_width = a_width + (1 << b_width) - 1; } } else { log_error("Non power 2: %s.%s\n", log_id(module), log_id(cell)); } } auto it = cell->parameters.find(ID::B_SIGNED); if (it == cell->parameters.end() || !it->second.as_bool()) { b_expr = "asUInt(" + b_expr + ")"; } string expr; // Deal with $xnor == ~^ (not xor) if (primop == "xnor") { expr = stringf("not(xor(%s, %s))", a_expr.c_str(), b_expr.c_str()); } else { expr = stringf("%s(%s, %s)", primop.c_str(), a_expr.c_str(), b_expr.c_str()); } // Deal with FIRRTL's "shift widens" semantics, or the need to widen the FIRRTL result. // If the operation is signed, the FIRRTL width will be 1 one bit larger. if (extract_y_bits) { expr = stringf("bits(%s, %d, 0)", expr.c_str(), y_width - 1); } else if (firrtl_is_signed && (firrtl_width + 1) < y_width) { expr = stringf("pad(%s, %d)", expr.c_str(), y_width); } if ((firrtl_is_signed && !always_uint)) expr = stringf("asUInt(%s)", expr.c_str()); cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); register_reverse_wire_map(y_id, cell->getPort(ID::Y)); continue; } if (cell->type.in(ID($mux), ID($_MUX_))) { auto it = cell->parameters.find(ID::WIDTH); int width = it == cell->parameters.end()? 1 : it->second.as_int(); string a_expr = make_expr(cell->getPort(ID::A)); string b_expr = make_expr(cell->getPort(ID::B)); string s_expr = make_expr(cell->getPort(ID::S)); wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent.c_str(), y_id.c_str(), width, cellFileinfo.c_str())); string expr = stringf("mux(%s, %s, %s)", s_expr.c_str(), b_expr.c_str(), a_expr.c_str()); cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); register_reverse_wire_map(y_id, cell->getPort(ID::Y)); continue; } if (cell->is_mem_cell()) { // Will be handled below, as part of a Mem. continue; } if (cell->type.in(ID($dff))) { bool clkpol = cell->parameters.at(ID::CLK_POLARITY).as_bool(); if (clkpol == false) log_error("Negative edge clock on FF %s.%s.\n", log_id(module), log_id(cell)); int width = cell->parameters.at(ID::WIDTH).as_int(); string expr = make_expr(cell->getPort(ID::D)); string clk_expr = "asClock(" + make_expr(cell->getPort(ID::CLK)) + ")"; wire_decls.push_back(stringf("%sreg %s: UInt<%d>, %s %s\n", indent.c_str(), y_id.c_str(), width, clk_expr.c_str(), cellFileinfo.c_str())); cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); register_reverse_wire_map(y_id, cell->getPort(ID::Q)); continue; } if (cell->type == ID($shiftx)) { // assign y = a[b +: y_width]; // We'll extract the correct bits as part of the primop. string a_expr = make_expr(cell->getPort(ID::A)); // Get the initial bit selector string b_expr = make_expr(cell->getPort(ID::B)); wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent.c_str(), y_id.c_str(), y_width)); if (cell->getParam(ID::B_SIGNED).as_bool()) { // Use validif to constrain the selection (test the sign bit) auto b_string = b_expr.c_str(); int b_sign = cell->parameters.at(ID::B_WIDTH).as_int() - 1; b_expr = stringf("validif(not(bits(%s, %d, %d)), %s)", b_string, b_sign, b_sign, b_string); } string expr = stringf("dshr(%s, %s)", a_expr.c_str(), b_expr.c_str()); cell_exprs.push_back(stringf("%s%s <= %s\n", indent.c_str(), y_id.c_str(), expr.c_str())); register_reverse_wire_map(y_id, cell->getPort(ID::Y)); continue; } if (cell->type == ID($shift)) { // assign y = a >> b; // where b may be negative string a_expr = make_expr(cell->getPort(ID::A)); string b_expr = make_expr(cell->getPort(ID::B)); auto b_string = b_expr.c_str(); string expr; wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent.c_str(), y_id.c_str(), y_width)); if (cell->getParam(ID::B_SIGNED).as_bool()) { // We generate a left or right shift based on the sign of b. std::string dshl = stringf("bits(dshl(%s, %s), 0, %d)", a_expr.c_str(), gen_dshl(b_expr, b_width).c_str(), y_width); std::string dshr = stringf("dshr(%s, %s)", a_expr.c_str(), b_string); expr = stringf("mux(%s < 0, %s, %s)", b_string, dshl.c_str(), dshr.c_str() ); } else { expr = stringf("dshr(%s, %s)", a_expr.c_str(), b_string); } cell_exprs.push_back(stringf("%s%s <= %s\n", indent.c_str(), y_id.c_str(), expr.c_str())); register_reverse_wire_map(y_id, cell->getPort(ID::Y)); continue; } if (cell->type == ID($pos)) { // assign y = a; // printCell(cell); string a_expr = make_expr(cell->getPort(ID::A)); // Verilog appears to treat the result as signed, so if the result is wider than "A", // we need to pad. if (a_width < y_width) { a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width); } wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent.c_str(), y_id.c_str(), y_width)); cell_exprs.push_back(stringf("%s%s <= %s\n", indent.c_str(), y_id.c_str(), a_expr.c_str())); register_reverse_wire_map(y_id, cell->getPort(ID::Y)); continue; } log_error("Cell type not supported: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell)); } for (auto &mem : memories) { string mem_id = make_id(mem.memid); Const init_data = mem.get_init_data(); if (!init_data.is_fully_undef()) log_error("Memory with initialization data: %s.%s\n", log_id(module), log_id(mem.memid)); if (mem.start_offset != 0) log_error("Memory with nonzero offset: %s.%s\n", log_id(module), log_id(mem.memid)); for (int i = 0; i < GetSize(mem.rd_ports); i++) { auto &port = mem.rd_ports[i]; string port_name(stringf("%s.r%d", mem_id.c_str(), i)); if (port.clk_enable) log_error("Clocked read port %d on memory %s.%s.\n", i, log_id(module), log_id(mem.memid)); std::ostringstream rpe; string addr_expr = make_expr(port.addr); string ena_expr = make_expr(State::S1); string clk_expr = make_expr(State::S0); rpe << stringf("%s%s.addr <= %s\n", indent.c_str(), port_name.c_str(), addr_expr.c_str()); rpe << stringf("%s%s.en <= %s\n", indent.c_str(), port_name.c_str(), ena_expr.c_str()); rpe << stringf("%s%s.clk <= asClock(%s)\n", indent.c_str(), port_name.c_str(), clk_expr.c_str()); cell_exprs.push_back(rpe.str()); register_reverse_wire_map(stringf("%s.data", port_name.c_str()), port.data); } for (int i = 0; i < GetSize(mem.wr_ports); i++) { auto &port = mem.wr_ports[i]; string port_name(stringf("%s.w%d", mem_id.c_str(), i)); if (!port.clk_enable) log_error("Unclocked write port %d on memory %s.%s.\n", i, log_id(module), log_id(mem.memid)); if (!port.clk_polarity) log_error("Negedge write port %d on memory %s.%s.\n", i, log_id(module), log_id(mem.memid)); for (int i = 1; i < GetSize(port.en); i++) if (port.en[0] != port.en[i]) log_error("Complex write enable on port %d on memory %s.%s.\n", i, log_id(module), log_id(mem.memid)); std::ostringstream wpe; string data_expr = make_expr(port.data); string addr_expr = make_expr(port.addr); string ena_expr = make_expr(port.en[0]); string clk_expr = make_expr(port.clk); string mask_expr = make_expr(State::S1); wpe << stringf("%s%s.data <= %s\n", indent.c_str(), port_name.c_str(), data_expr.c_str()); wpe << stringf("%s%s.addr <= %s\n", indent.c_str(), port_name.c_str(), addr_expr.c_str()); wpe << stringf("%s%s.en <= %s\n", indent.c_str(), port_name.c_str(), ena_expr.c_str()); wpe << stringf("%s%s.clk <= asClock(%s)\n", indent.c_str(), port_name.c_str(), clk_expr.c_str()); wpe << stringf("%s%s.mask <= %s\n", indent.c_str(), port_name.c_str(), mask_expr.c_str()); cell_exprs.push_back(wpe.str()); } std::ostringstream me; me << stringf(" mem %s:\n", mem_id.c_str()); me << stringf(" data-type => UInt<%d>\n", mem.width); me << stringf(" depth => %d\n", mem.size); for (int i = 0; i < GetSize(mem.rd_ports); i++) me << stringf(" reader => r%d\n", i); for (int i = 0; i < GetSize(mem.wr_ports); i++) me << stringf(" writer => w%d\n", i); me << stringf(" read-latency => %d\n", 0); me << stringf(" write-latency => %d\n", 1); me << stringf(" read-under-write => undefined\n"); mem_exprs.push_back(me.str()); } for (auto conn : module->connections()) { string y_id = next_id(); int y_width = GetSize(conn.first); string expr = make_expr(conn.second); wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent.c_str(), y_id.c_str(), y_width)); cell_exprs.push_back(stringf("%s%s <= %s\n", indent.c_str(), y_id.c_str(), expr.c_str())); register_reverse_wire_map(y_id, conn.first); } for (auto wire : module->wires()) { string expr; std::string wireFileinfo = getFileinfo(wire); if (wire->port_input) continue; int cursor = 0; bool is_valid = false; bool make_unconn_id = false; while (cursor < wire->width) { int chunk_width = 1; string new_expr; SigBit start_bit(wire, cursor); if (reverse_wire_map.count(start_bit)) { pair start_map = reverse_wire_map.at(start_bit); while (cursor+chunk_width < wire->width) { SigBit stop_bit(wire, cursor+chunk_width); if (reverse_wire_map.count(stop_bit) == 0) break; pair stop_map = reverse_wire_map.at(stop_bit); stop_map.second -= chunk_width; if (start_map != stop_map) break; chunk_width++; } new_expr = stringf("bits(%s, %d, %d)", start_map.first.c_str(), start_map.second + chunk_width - 1, start_map.second); is_valid = true; } else { if (unconn_id.empty()) { unconn_id = next_id(); make_unconn_id = true; } new_expr = unconn_id; } if (expr.empty()) expr = new_expr; else expr = "cat(" + new_expr + ", " + expr + ")"; cursor += chunk_width; } if (is_valid) { if (make_unconn_id) { wire_decls.push_back(stringf("%swire %s: UInt<1> %s\n", indent.c_str(), unconn_id.c_str(), wireFileinfo.c_str())); // `invalid` is a firrtl construction for simulation so we will not // tag it with a @[fileinfo] tag as it doesn't directly correspond to // a specific line of verilog code. wire_decls.push_back(stringf("%s%s is invalid\n", indent.c_str(), unconn_id.c_str())); } wire_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), make_id(wire->name), expr.c_str(), wireFileinfo.c_str())); } else { if (make_unconn_id) { unconn_id.clear(); } // `invalid` is a firrtl construction for simulation so we will not // tag it with a @[fileinfo] tag as it doesn't directly correspond to // a specific line of verilog code. wire_decls.push_back(stringf("%s%s is invalid\n", indent.c_str(), make_id(wire->name))); } } for (auto str : port_decls) f << str; f << stringf("\n"); for (auto str : wire_decls) f << str; f << stringf("\n"); for (auto str : mem_exprs) f << str; f << stringf("\n"); for (auto str : cell_exprs) f << str; f << stringf("\n"); for (auto str : wire_exprs) f << str; f << stringf("\n"); } void run() { emit_module(); } }; struct FirrtlBackend : public Backend { FirrtlBackend() : Backend("firrtl", "write design to a FIRRTL file") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" write_firrtl [options] [filename]\n"); log("\n"); log("Write a FIRRTL netlist of the current design.\n"); log("The following commands are executed by this command:\n"); log(" pmuxtree\n"); log(" bmuxmap\n"); log(" demuxmap\n"); log(" bwmuxmap\n"); log("\n"); } void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { size_t argidx = args.size(); // We aren't expecting any arguments. // If we weren't explicitly passed a filename, use the last argument (if it isn't a flag). if (filename == "") { if (argidx > 0 && args[argidx - 1][0] != '-') { // extra_args and friends need to see this argument. argidx -= 1; filename = args[argidx]; } } extra_args(f, filename, args, argidx); if (!design->full_selection()) log_cmd_error("This command only operates on fully selected designs!\n"); log_header(design, "Executing FIRRTL backend.\n"); log_push(); Pass::call(design, "pmuxtree"); Pass::call(design, "bmuxmap"); Pass::call(design, "demuxmap"); Pass::call(design, "bwmuxmap"); namecache.clear(); autoid_counter = 0; // Get the top module, or a reasonable facsimile - we need something for the circuit name. Module *top = design->top_module(); Module *last = nullptr; // Generate module and wire names. for (auto module : design->modules()) { make_id(module->name); last = module; if (top == nullptr && module->get_bool_attribute(ID::top)) { top = module; } for (auto wire : module->wires()) if (wire->port_id) make_id(wire->name); } if (top == nullptr) top = last; if (!top) log_cmd_error("There is no top module in this design!\n"); std::string circuitFileinfo = getFileinfo(top); *f << stringf("circuit %s: %s\n", make_id(top->name), circuitFileinfo.c_str()); emit_elaborated_extmodules(design, *f); // Emit non-blackbox modules. for (auto module : design->modules()) { if (!module->get_blackbox_attribute()) { FirrtlWorker worker(module, *f, design); worker.run(); } } namecache.clear(); autoid_counter = 0; } } FirrtlBackend; PRIVATE_NAMESPACE_END yosys-yosys-0.33/backends/firrtl/test.sh000066400000000000000000000006131447554276300204210ustar00rootroot00000000000000#!/bin/bash set -ex cd ../../ make cd backends/firrtl ../../yosys -q -p 'prep -nordff; write_firrtl test.fir' $1 firrtl -i test.fir -o test_out.v -ll Info ../../yosys -p " read_verilog $1 rename Top gold read_verilog test_out.v rename Top gate prep memory_map miter -equiv -flatten gold gate miter hierarchy -top miter sat -verify -prove trigger 0 -set-init-zero -seq 10 miter " yosys-yosys-0.33/backends/firrtl/test.v000066400000000000000000000034021447554276300202530ustar00rootroot00000000000000module test( input clk, wen, input [7:0] uns, input signed [7:0] a, b, input signed [23:0] c, input signed [2:0] sel, output [15:0] s, d, y, z, u, q, p, mul, div, mod, mux, And, Or, Xor, eq, neq, gt, lt, geq, leq, eqx, shr, sshr, shl, sshl, Land, Lor, Lnot, Not, Neg, pos, Andr, Orr, Xorr, Xnorr, Reduce_bool, output [7:0] PMux ); //initial begin //$display("shr = %b", shr); //end assign s = a+{b[6:2], 2'b1}; assign d = a-b; assign y = x; assign z[7:0] = s+d; assign z[15:8] = s-d; assign p = a & b | x; assign mul = a * b; assign div = a / b; assign mod = a % b; assign mux = x[0] ? a : b; assign And = a & b; assign Or = a | b; assign Xor = a ^ b; assign Not = ~a; assign Neg = -a; assign eq = a == b; assign neq = a != b; assign gt = a > b; assign lt = a < b; assign geq = a >= b; assign leq = a <= b; assign eqx = a === b; assign shr = a >> b; //0111111111000000 assign sshr = a >>> b; assign shl = a << b; assign sshl = a <<< b; assign Land = a && b; assign Lor = a || b; assign Lnot = !a; assign pos = $signed(uns); assign Andr = &a; assign Orr = |a; assign Xorr = ^a; assign Xnorr = ~^a; always @* if(!a) begin Reduce_bool = a; end else begin Reduce_bool = b; end //always @(sel or c or a) // begin // case (sel) // 3'b000: PMux = a; // 3'b001: PMux = c[7:0]; // 3'b010: PMux = c[15:8]; // 3'b100: PMux = c[23:16]; // endcase // end endmodule yosys-yosys-0.33/backends/intersynth/000077500000000000000000000000001447554276300200135ustar00rootroot00000000000000yosys-yosys-0.33/backends/intersynth/Makefile.inc000066400000000000000000000000531447554276300222210ustar00rootroot00000000000000 OBJS += backends/intersynth/intersynth.o yosys-yosys-0.33/backends/intersynth/intersynth.cc000066400000000000000000000177101447554276300225370ustar00rootroot00000000000000/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ #include "kernel/rtlil.h" #include "kernel/register.h" #include "kernel/sigtools.h" #include "kernel/celltypes.h" #include "kernel/log.h" #include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN static std::string netname(std::set &conntypes_code, std::set &celltypes_code, std::set &constcells_code, RTLIL::SigSpec sig) { if (!sig.is_fully_const() && !sig.is_wire()) log_error("Can't export composite or non-word-wide signal %s.\n", log_signal(sig)); conntypes_code.insert(stringf("conntype b%d %d 2 %d\n", sig.size(), sig.size(), sig.size())); if (sig.is_fully_const()) { celltypes_code.insert(stringf("celltype CONST_%d b%d *CONST cfg:%d VALUE\n", sig.size(), sig.size(), sig.size())); constcells_code.insert(stringf("node CONST_%d_0x%x CONST_%d CONST CONST_%d_0x%x VALUE 0x%x\n", sig.size(), sig.as_int(), sig.size(), sig.size(), sig.as_int(), sig.as_int())); return stringf("CONST_%d_0x%x", sig.size(), sig.as_int()); } return RTLIL::unescape_id(sig.as_wire()->name); } struct IntersynthBackend : public Backend { IntersynthBackend() : Backend("intersynth", "write design to InterSynth netlist file") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" write_intersynth [options] [filename]\n"); log("\n"); log("Write the current design to an 'intersynth' netlist file. InterSynth is\n"); log("a tool for Coarse-Grain Example-Driven Interconnect Synthesis.\n"); log("\n"); log(" -notypes\n"); log(" do not generate celltypes and conntypes commands. i.e. just output\n"); log(" the netlists. this is used for postsilicon synthesis.\n"); log("\n"); log(" -lib \n"); log(" Use the specified library file for determining whether cell ports are\n"); log(" inputs or outputs. This option can be used multiple times to specify\n"); log(" more than one library.\n"); log("\n"); log(" -selected\n"); log(" only write selected modules. modules must be selected entirely or\n"); log(" not at all.\n"); log("\n"); log("http://bygone.clairexen.net/intersynth/\n"); log("\n"); } void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { log_header(design, "Executing INTERSYNTH backend.\n"); log_push(); std::vector libfiles; std::vector libs; bool flag_notypes = false; bool selected = false; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-notypes") { flag_notypes = true; continue; } if (args[argidx] == "-lib" && argidx+1 < args.size()) { libfiles.push_back(args[++argidx]); continue; } if (args[argidx] == "-selected") { selected = true; continue; } break; } extra_args(f, filename, args, argidx); log("Output filename: %s\n", filename.c_str()); for (auto filename : libfiles) { std::ifstream f; f.open(filename.c_str()); if (f.fail()) log_error("Can't open lib file `%s'.\n", filename.c_str()); RTLIL::Design *lib = new RTLIL::Design; Frontend::frontend_call(lib, &f, filename, (filename.size() > 3 && filename.compare(filename.size()-3, std::string::npos, ".il") == 0 ? "rtlil" : "verilog")); libs.push_back(lib); } if (libs.size() > 0) log_header(design, "Continuing INTERSYNTH backend.\n"); std::set conntypes_code, celltypes_code; std::string netlists_code; CellTypes ct(design); for (auto lib : libs) ct.setup_design(lib); for (auto module : design->modules()) { SigMap sigmap(module); if (module->get_blackbox_attribute()) continue; if (module->memories.size() == 0 && module->processes.size() == 0 && module->cells().size() == 0) continue; if (selected && !design->selected_whole_module(module->name)) { if (design->selected_module(module->name)) log_cmd_error("Can't handle partially selected module %s!\n", log_id(module->name)); continue; } log("Generating netlist %s.\n", log_id(module->name)); if (module->memories.size() != 0 || module->processes.size() != 0) log_error("Can't generate a netlist for a module with unprocessed memories or processes!\n"); std::set constcells_code; netlists_code += stringf("# Netlist of module %s\n", log_id(module->name)); netlists_code += stringf("netlist %s\n", log_id(module->name)); // Module Ports: "std::set celltypes_code" prevents duplicate top level ports for (auto wire : module->wires()) { if (wire->port_input || wire->port_output) { celltypes_code.insert(stringf("celltype !%s b%d %sPORT\n" "%s %s %d %s PORT\n", log_id(wire->name), wire->width, wire->port_input ? "*" : "", wire->port_input ? "input" : "output", log_id(wire->name), wire->width, log_id(wire->name))); netlists_code += stringf("node %s %s PORT %s\n", log_id(wire->name), log_id(wire->name), netname(conntypes_code, celltypes_code, constcells_code, sigmap(wire)).c_str()); } } // Submodules: "std::set celltypes_code" prevents duplicate cell types for (auto cell : module->cells()) { std::string celltype_code, node_code; if (!ct.cell_known(cell->type)) log_error("Found unknown cell type %s in module!\n", log_id(cell->type)); celltype_code = stringf("celltype %s", log_id(cell->type)); node_code = stringf("node %s %s", log_id(cell->name), log_id(cell->type)); for (auto &port : cell->connections()) { RTLIL::SigSpec sig = sigmap(port.second); if (sig.size() != 0) { conntypes_code.insert(stringf("conntype b%d %d 2 %d\n", sig.size(), sig.size(), sig.size())); celltype_code += stringf(" b%d %s%s", sig.size(), ct.cell_output(cell->type, port.first) ? "*" : "", log_id(port.first)); node_code += stringf(" %s %s", log_id(port.first), netname(conntypes_code, celltypes_code, constcells_code, sig).c_str()); } } 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) { 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"; } else node_code += stringf(" %s 0x%x", log_id(param.first), param.second.as_int()); } celltypes_code.insert(celltype_code + "\n"); netlists_code += node_code + "\n"; } if (constcells_code.size() > 0) netlists_code += "# constant cells\n"; for (auto code : constcells_code) netlists_code += code; netlists_code += "\n"; } if (!flag_notypes) { *f << stringf("### Connection Types\n"); for (auto code : conntypes_code) *f << stringf("%s", code.c_str()); *f << stringf("\n### Cell Types\n"); for (auto code : celltypes_code) *f << stringf("%s", code.c_str()); } *f << stringf("\n### Netlists\n"); *f << stringf("%s", netlists_code.c_str()); for (auto lib : libs) delete lib; log_pop(); } } IntersynthBackend; PRIVATE_NAMESPACE_END yosys-yosys-0.33/backends/jny/000077500000000000000000000000001447554276300164045ustar00rootroot00000000000000yosys-yosys-0.33/backends/jny/Makefile.inc000066400000000000000000000000341447554276300206110ustar00rootroot00000000000000 OBJS += backends/jny/jny.o yosys-yosys-0.33/backends/jny/jny.cc000066400000000000000000000432301447554276300175150ustar00rootroot00000000000000/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Aki "lethalbit" Van Ness * * 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/rtlil.h" #include "kernel/register.h" #include "kernel/sigtools.h" #include "kernel/celltypes.h" #include "kernel/log.h" #include #include #include #include #include #include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct JnyWriter { private: std::ostream &f; bool _use_selection; // XXX(aki): TODO: this needs to be updated to us // dict and then coalesce_cells needs to be updated // but for now for the PoC this looks to be sufficient std::unordered_map> _cells{}; bool _include_connections; bool _include_attributes; bool _include_properties; string escape_string(string str) { std::string newstr; auto itr = str.begin(); for(; itr != str.end(); ++itr) { switch (*itr) { case '\\': { newstr += "\\\\"; break; } case '\n': { newstr += "\\n"; break; } case '\f': { newstr += "\\f"; break; } case '\t': { newstr += "\\t"; break; } case '\r': { newstr += "\\r"; break; } case '\"': { newstr += "\\\""; break; } case '\b': { newstr += "\\b"; break; } default: { newstr += *itr; } } } return newstr; } // XXX(aki): I know this is far from ideal but i'm out of spoons and cant focus so // it'll have to do for now, void coalesce_cells(Module* mod) { _cells.clear(); for (auto cell : mod->cells()) { const auto cell_type = escape_string(RTLIL::unescape_id(cell->type)); if (_cells.find(cell_type) == _cells.end()) _cells.emplace(cell_type, std::vector()); _cells.at(cell_type).push_back(cell); } } // XXX(aki): this is a lazy way to do this i know,,, std::string gen_indent(const uint16_t level) { std::stringstream s; for (uint16_t i = 0; i <= level; ++i) { s << " "; } return s.str(); } public: JnyWriter(std::ostream &f, bool use_selection, bool connections, bool attributes, bool properties) noexcept: f(f), _use_selection(use_selection), _include_connections(connections), _include_attributes(attributes), _include_properties(properties) { } void write_metadata(Design *design, uint16_t indent_level = 0, std::string invk = "") { log_assert(design != nullptr); design->sort(); f << "{\n"; f << " \"$schema\": \"https://raw.githubusercontent.com/YosysHQ/yosys/master/misc/jny.schema.json\",\n"; f << stringf(" \"generator\": \"%s\",\n", escape_string(yosys_version_str).c_str()); f << " \"version\": \"0.0.1\",\n"; f << " \"invocation\": \"" << escape_string(invk) << "\",\n"; f << " \"features\": ["; size_t fnum{0}; if (_include_connections) { ++fnum; f << "\"connections\""; } if (_include_attributes) { if (fnum > 0) f << ", "; ++fnum; f << "\"attributes\""; } if (_include_properties) { if (fnum > 0) f << ", "; ++fnum; f << "\"properties\""; } f << "],\n"; f << " \"modules\": [\n"; bool first{true}; for (auto mod : _use_selection ? design->selected_modules() : design->modules()) { if (!first) f << ",\n"; write_module(mod, indent_level + 2); first = false; } f << "\n"; f << " ]\n"; f << "}\n"; } void write_sigspec(const RTLIL::SigSpec& sig, uint16_t indent_level = 0) { const auto _indent = gen_indent(indent_level); f << _indent << " {\n"; f << _indent << " \"width\": \"" << sig.size() << "\",\n"; f << _indent << " \"type\": \""; if (sig.is_wire()) { f << "wire"; } else if (sig.is_chunk()) { f << "chunk"; } else if (sig.is_bit()) { f << "bit"; } else { f << "unknown"; } f << "\",\n"; f << _indent << " \"const\": "; if (sig.has_const()) { f << "true"; } else { f << "false"; } f << "\n"; f << _indent << " }"; } void write_mod_conn(const std::pair& conn, uint16_t indent_level = 0) { const auto _indent = gen_indent(indent_level); f << _indent << " {\n"; f << _indent << " \"signals\": [\n"; write_sigspec(conn.first, indent_level + 2); f << ",\n"; write_sigspec(conn.second, indent_level + 2); f << "\n"; f << _indent << " ]\n"; f << _indent << " }"; } void write_cell_conn(const std::pair& sig, uint16_t indent_level = 0) { const auto _indent = gen_indent(indent_level); f << _indent << " {\n"; f << _indent << " \"name\": \"" << escape_string(RTLIL::unescape_id(sig.first)) << "\",\n"; f << _indent << " \"signals\": [\n"; write_sigspec(sig.second, indent_level + 2); f << "\n"; f << _indent << " ]\n"; f << _indent << " }"; } void write_module(Module* mod, uint16_t indent_level = 0) { log_assert(mod != nullptr); coalesce_cells(mod); const auto _indent = gen_indent(indent_level); f << _indent << "{\n"; f << stringf(" %s\"name\": \"%s\",\n", _indent.c_str(), escape_string(RTLIL::unescape_id(mod->name)).c_str()); f << _indent << " \"cell_sorts\": [\n"; bool first_sort{true}; for (auto& sort : _cells) { if (!first_sort) f << ",\n"; write_cell_sort(sort, indent_level + 2); first_sort = false; } f << "\n"; f << _indent << " ]"; if (_include_connections) { f << ",\n" << _indent << " \"connections\": [\n"; bool first_conn{true}; for (const auto& conn : mod->connections()) { if (!first_conn) f << ",\n"; write_mod_conn(conn, indent_level + 2); first_conn = false; } f << _indent << " ]"; } if (_include_attributes) { f << ",\n" << _indent << " \"attributes\": {\n"; write_prams(mod->attributes, indent_level + 2); f << "\n"; f << _indent << " }"; } f << "\n" << _indent << "}"; } void write_cell_ports(RTLIL::Cell* port_cell, uint64_t indent_level = 0) { const auto _indent = gen_indent(indent_level); bool first_port{true}; for (auto con : port_cell->connections()) { if (!first_port) f << ",\n"; f << _indent << " {\n"; f << stringf(" %s\"name\": \"%s\",\n", _indent.c_str(), escape_string(RTLIL::unescape_id(con.first)).c_str()); f << _indent << " \"direction\": \""; if (port_cell->input(con.first)) f << "i"; if (port_cell->input(con.first)) f << "o"; f << "\",\n"; if (con.second.size() == 1) f << _indent << " \"range\": [0, 0]\n"; else f << stringf(" %s\"range\": [%d, %d]\n", _indent.c_str(), con.second.size(), 0); f << _indent << " }"; first_port = false; } f << "\n"; } void write_cell_sort(std::pair>& sort, uint16_t indent_level = 0) { const auto port_cell = sort.second.front(); const auto _indent = gen_indent(indent_level); f << _indent << "{\n"; f << stringf(" %s\"type\": \"%s\",\n", _indent.c_str(), sort.first.c_str()); f << _indent << " \"ports\": [\n"; write_cell_ports(port_cell, indent_level + 2); f << _indent << " ],\n" << _indent << " \"cells\": [\n"; bool first_cell{true}; for (auto& cell : sort.second) { if (!first_cell) f << ",\n"; write_cell(cell, indent_level + 2); first_cell = false; } f << "\n"; f << _indent << " ]\n"; f << _indent << "}"; } void write_param_val(const Const& v) { if ((v.flags & RTLIL::ConstFlags::CONST_FLAG_STRING) == RTLIL::ConstFlags::CONST_FLAG_STRING) { const auto str = v.decode_string(); // XXX(aki): TODO, uh, yeah f << "\"" << escape_string(str) << "\""; } else if ((v.flags & RTLIL::ConstFlags::CONST_FLAG_SIGNED) == RTLIL::ConstFlags::CONST_FLAG_SIGNED) { f << stringf("\"%dsd %d\"", v.size(), v.as_int(true)); } else if ((v.flags & RTLIL::ConstFlags::CONST_FLAG_REAL) == RTLIL::ConstFlags::CONST_FLAG_REAL) { } else { f << "\"" << escape_string(v.as_string()) << "\""; } } void write_prams(dict& params, uint16_t indent_level = 0) { const auto _indent = gen_indent(indent_level); bool first_param{true}; for (auto& param : params) { if (!first_param) f << stringf(",\n"); const auto param_val = param.second; if (!param_val.empty()) { f << stringf(" %s\"%s\": ", _indent.c_str(), escape_string(RTLIL::unescape_id(param.first)).c_str()); write_param_val(param_val); } else { f << stringf(" %s\"%s\": true", _indent.c_str(), escape_string(RTLIL::unescape_id(param.first)).c_str()); } first_param = false; } } void write_cell(Cell* cell, uint16_t indent_level = 0) { const auto _indent = gen_indent(indent_level); log_assert(cell != nullptr); f << _indent << " {\n"; f << stringf(" %s\"name\": \"%s\"", _indent.c_str(), escape_string(RTLIL::unescape_id(cell->name)).c_str()); if (_include_connections) { f << ",\n" << _indent << " \"connections\": [\n"; bool first_conn{true}; for (const auto& conn : cell->connections()) { if (!first_conn) f << ",\n"; write_cell_conn(conn, indent_level + 2); first_conn = false; } f << "\n"; f << _indent << " ]"; } if (_include_attributes) { f << ",\n" << _indent << " \"attributes\": {\n"; write_prams(cell->attributes, indent_level + 2); f << "\n"; f << _indent << " }"; } if (_include_properties) { f << ",\n" << _indent << " \"parameters\": {\n"; write_prams(cell->parameters, indent_level + 2); f << "\n"; f << _indent << " }"; } f << "\n" << _indent << " }"; } }; struct JnyBackend : public Backend { JnyBackend() : Backend("jny", "generate design metadata") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" jny [options] [selection]\n"); log("\n"); log("Write JSON netlist metadata for the current design\n"); log("\n"); log(" -no-connections\n"); log(" Don't include connection information in the netlist output.\n"); log("\n"); log(" -no-attributes\n"); log(" Don't include attributed information in the netlist output.\n"); log("\n"); log(" -no-properties\n"); log(" Don't include property information in the netlist output.\n"); log("\n"); log("The JSON schema for JNY output files is located in the \"jny.schema.json\" file\n"); log("which is located at \"https://raw.githubusercontent.com/YosysHQ/yosys/master/misc/jny.schema.json\"\n"); log("\n"); } void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { bool connections{true}; bool attributes{true}; bool properties{true}; size_t argidx{1}; for (; argidx < args.size(); argidx++) { if (args[argidx] == "-no-connections") { connections = false; continue; } if (args[argidx] == "-no-attributes") { attributes = false; continue; } if (args[argidx] == "-no-properties") { properties = false; continue; } break; } // Compose invocation line std::ostringstream invk; if (!args.empty()) { std::copy(args.begin(), args.end(), std::ostream_iterator(invk, " ") ); } invk << filename; extra_args(f, filename, args, argidx); log_header(design, "Executing jny backend.\n"); JnyWriter jny_writer(*f, false, connections, attributes, properties); jny_writer.write_metadata(design, 0, invk.str()); } } JnyBackend; struct JnyPass : public Pass { JnyPass() : Pass("jny", "write design and metadata") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" jny [options] [selection]\n"); log("\n"); log("Write JSON netlist metadata for the current design\n"); log("\n"); log(" -o \n"); log(" write to the specified file.\n"); log("\n"); log(" -no-connections\n"); log(" Don't include connection information in the netlist output.\n"); log("\n"); log(" -no-attributes\n"); log(" Don't include attributed information in the netlist output.\n"); log("\n"); log(" -no-properties\n"); log(" Don't include property information in the netlist output.\n"); log("\n"); log("See 'help write_jny' for a description of the JSON format used.\n"); log("\n"); } void execute(std::vector args, RTLIL::Design *design) override { std::string filename{}; bool connections{true}; bool attributes{true}; bool properties{true}; size_t argidx{1}; for (; argidx < args.size(); argidx++) { if (args[argidx] == "-o" && argidx+1 < args.size()) { filename = args[++argidx]; continue; } if (args[argidx] == "-no-connections") { connections = false; continue; } if (args[argidx] == "-no-attributes") { attributes = false; continue; } if (args[argidx] == "-no-properties") { properties = false; continue; } break; } // Compose invocation line std::ostringstream invk; if (!args.empty()) { std::copy(args.begin(), args.end(), std::ostream_iterator(invk, " ") ); } extra_args(args, argidx, design); std::ostream *f; std::stringstream buf; bool empty = filename.empty(); if (!empty) { rewrite_filename(filename); std::ofstream *ff = new std::ofstream; ff->open(filename.c_str(), std::ofstream::trunc); if (ff->fail()) { delete ff; log_error("Can't open file `%s' for writing: %s\n", filename.c_str(), strerror(errno)); } f = ff; invk << filename; } else { f = &buf; } JnyWriter jny_writer(*f, false, connections, attributes, properties); jny_writer.write_metadata(design, 0, invk.str()); if (!empty) { delete f; } else { log("%s", buf.str().c_str()); } } } JnyPass; PRIVATE_NAMESPACE_END yosys-yosys-0.33/backends/json/000077500000000000000000000000001447554276300165555ustar00rootroot00000000000000yosys-yosys-0.33/backends/json/Makefile.inc000066400000000000000000000000371447554276300207650ustar00rootroot00000000000000 OBJS += backends/json/json.o yosys-yosys-0.33/backends/json/json.cc000066400000000000000000000553711447554276300200500ustar00rootroot00000000000000/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ #include "kernel/rtlil.h" #include "kernel/register.h" #include "kernel/sigtools.h" #include "kernel/celltypes.h" #include "kernel/cellaigs.h" #include "kernel/log.h" #include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct JsonWriter { std::ostream &f; bool use_selection; bool aig_mode; bool compat_int_mode; Design *design; Module *module; SigMap sigmap; int sigidcounter; dict sigids; pool aig_models; JsonWriter(std::ostream &f, bool use_selection, bool aig_mode, bool compat_int_mode) : f(f), use_selection(use_selection), aig_mode(aig_mode), compat_int_mode(compat_int_mode) { } string get_string(string str) { string newstr = "\""; for (char c : str) { if (c == '\\') newstr += "\\\\"; else if (c == '"') newstr += "\\\""; else if (c == '\b') newstr += "\\b"; else if (c == '\f') newstr += "\\f"; else if (c == '\n') newstr += "\\n"; else if (c == '\r') newstr += "\\r"; else if (c == '\t') newstr += "\\t"; else if (c < 0x20) newstr += stringf("\\u%04X", c); else newstr += c; } return newstr + "\""; } string get_name(IdString name) { return get_string(RTLIL::unescape_id(name)); } string get_bits(SigSpec sig) { bool first = true; string str = "["; for (auto bit : sigmap(sig)) { str += first ? " " : ", "; first = false; if (sigids.count(bit) == 0) { string &s = sigids[bit]; if (bit.wire == nullptr) { if (bit == State::S0) s = "\"0\""; else if (bit == State::S1) s = "\"1\""; else if (bit == State::Sz) s = "\"z\""; else s = "\"x\""; } else s = stringf("%d", sigidcounter++); } str += sigids[bit]; } return str + " ]"; } void write_parameter_value(const Const &value) { if ((value.flags & RTLIL::ConstFlags::CONST_FLAG_STRING) != 0) { string str = value.decode_string(); int state = 0; for (char c : str) { if (state == 0) { if (c == '0' || c == '1' || c == 'x' || c == 'z') state = 0; else if (c == ' ') state = 1; else state = 2; } else if (state == 1 && c != ' ') state = 2; } if (state < 2) str += " "; f << get_string(str); } else if (compat_int_mode && GetSize(value) <= 32 && value.is_fully_def()) { if ((value.flags & RTLIL::ConstFlags::CONST_FLAG_SIGNED) != 0) f << stringf("%d", value.as_int()); else f << stringf("%u", value.as_int()); } else { f << get_string(value.as_string()); } } void write_parameters(const dict ¶meters, bool for_module=false) { bool first = true; for (auto ¶m : parameters) { f << stringf("%s\n", first ? "" : ","); f << stringf(" %s%s: ", for_module ? "" : " ", get_name(param.first).c_str()); write_parameter_value(param.second); first = false; } } void write_module(Module *module_) { module = module_; log_assert(module->design == design); sigmap.set(module); sigids.clear(); // reserve 0 and 1 to avoid confusion with "0" and "1" sigidcounter = 2; if (module->has_processes()) { log_error("Module %s contains processes, which are not supported by JSON backend (run `proc` first).\n", log_id(module)); } f << stringf(" %s: {\n", get_name(module->name).c_str()); f << stringf(" \"attributes\": {"); write_parameters(module->attributes, /*for_module=*/true); f << stringf("\n },\n"); if (module->parameter_default_values.size()) { f << stringf(" \"parameter_default_values\": {"); write_parameters(module->parameter_default_values, /*for_module=*/true); f << stringf("\n },\n"); } f << stringf(" \"ports\": {"); bool first = true; for (auto n : module->ports) { Wire *w = module->wire(n); if (use_selection && !module->selected(w)) continue; f << stringf("%s\n", first ? "" : ","); f << stringf(" %s: {\n", get_name(n).c_str()); f << stringf(" \"direction\": \"%s\",\n", w->port_input ? w->port_output ? "inout" : "input" : "output"); if (w->start_offset) f << stringf(" \"offset\": %d,\n", w->start_offset); if (w->upto) f << stringf(" \"upto\": 1,\n"); if (w->is_signed) f << stringf(" \"signed\": %d,\n", w->is_signed); f << stringf(" \"bits\": %s\n", get_bits(w).c_str()); f << stringf(" }"); first = false; } f << stringf("\n },\n"); f << stringf(" \"cells\": {"); first = true; for (auto c : module->cells()) { if (use_selection && !module->selected(c)) continue; f << stringf("%s\n", first ? "" : ","); f << stringf(" %s: {\n", get_name(c->name).c_str()); f << stringf(" \"hide_name\": %s,\n", c->name[0] == '$' ? "1" : "0"); f << stringf(" \"type\": %s,\n", get_name(c->type).c_str()); if (aig_mode) { Aig aig(c); if (!aig.name.empty()) { f << stringf(" \"model\": \"%s\",\n", aig.name.c_str()); aig_models.insert(aig); } } f << stringf(" \"parameters\": {"); write_parameters(c->parameters); f << stringf("\n },\n"); f << stringf(" \"attributes\": {"); write_parameters(c->attributes); f << stringf("\n },\n"); if (c->known()) { f << stringf(" \"port_directions\": {"); bool first2 = true; for (auto &conn : c->connections()) { string direction = "output"; if (c->input(conn.first)) direction = c->output(conn.first) ? "inout" : "input"; f << stringf("%s\n", first2 ? "" : ","); f << stringf(" %s: \"%s\"", get_name(conn.first).c_str(), direction.c_str()); first2 = false; } f << stringf("\n },\n"); } f << stringf(" \"connections\": {"); bool first2 = true; for (auto &conn : c->connections()) { f << stringf("%s\n", first2 ? "" : ","); f << stringf(" %s: %s", get_name(conn.first).c_str(), get_bits(conn.second).c_str()); first2 = false; } f << stringf("\n }\n"); f << stringf(" }"); first = false; } f << stringf("\n },\n"); if (!module->memories.empty()) { f << stringf(" \"memories\": {"); first = true; for (auto &it : module->memories) { if (use_selection && !module->selected(it.second)) continue; f << stringf("%s\n", first ? "" : ","); f << stringf(" %s: {\n", get_name(it.second->name).c_str()); f << stringf(" \"hide_name\": %s,\n", it.second->name[0] == '$' ? "1" : "0"); f << stringf(" \"attributes\": {"); write_parameters(it.second->attributes); f << stringf("\n },\n"); f << stringf(" \"width\": %d,\n", it.second->width); f << stringf(" \"start_offset\": %d,\n", it.second->start_offset); f << stringf(" \"size\": %d\n", it.second->size); f << stringf(" }"); first = false; } f << stringf("\n },\n"); } f << stringf(" \"netnames\": {"); first = true; for (auto w : module->wires()) { if (use_selection && !module->selected(w)) continue; f << stringf("%s\n", first ? "" : ","); f << stringf(" %s: {\n", get_name(w->name).c_str()); f << stringf(" \"hide_name\": %s,\n", w->name[0] == '$' ? "1" : "0"); f << stringf(" \"bits\": %s,\n", get_bits(w).c_str()); if (w->start_offset) f << stringf(" \"offset\": %d,\n", w->start_offset); if (w->upto) f << stringf(" \"upto\": 1,\n"); if (w->is_signed) f << stringf(" \"signed\": %d,\n", w->is_signed); f << stringf(" \"attributes\": {"); write_parameters(w->attributes); f << stringf("\n }\n"); f << stringf(" }"); first = false; } f << stringf("\n }\n"); f << stringf(" }"); } void write_design(Design *design_) { design = design_; design->sort(); f << stringf("{\n"); f << stringf(" \"creator\": %s,\n", get_string(yosys_version_str).c_str()); f << stringf(" \"modules\": {\n"); vector modules = use_selection ? design->selected_modules() : design->modules(); bool first_module = true; for (auto mod : modules) { if (!first_module) f << stringf(",\n"); write_module(mod); first_module = false; } f << stringf("\n }"); if (!aig_models.empty()) { f << stringf(",\n \"models\": {\n"); bool first_model = true; for (auto &aig : aig_models) { if (!first_model) f << stringf(",\n"); f << stringf(" \"%s\": [\n", aig.name.c_str()); int node_idx = 0; for (auto &node : aig.nodes) { if (node_idx != 0) f << stringf(",\n"); f << stringf(" /* %3d */ [ ", node_idx); if (node.portbit >= 0) f << stringf("\"%sport\", \"%s\", %d", node.inverter ? "n" : "", log_id(node.portname), node.portbit); else if (node.left_parent < 0 && node.right_parent < 0) f << stringf("\"%s\"", node.inverter ? "true" : "false"); else f << stringf("\"%s\", %d, %d", node.inverter ? "nand" : "and", node.left_parent, node.right_parent); for (auto &op : node.outports) f << stringf(", \"%s\", %d", log_id(op.first), op.second); f << stringf(" ]"); node_idx++; } f << stringf("\n ]"); first_model = false; } f << stringf("\n }"); } f << stringf("\n}\n"); } }; struct JsonBackend : public Backend { JsonBackend() : Backend("json", "write design to a JSON file") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" write_json [options] [filename]\n"); log("\n"); log("Write a JSON netlist of the current design.\n"); log("\n"); log(" -aig\n"); log(" include AIG models for the different gate types\n"); log("\n"); log(" -compat-int\n"); log(" emit 32-bit or smaller fully-defined parameter values directly\n"); log(" as JSON numbers (for compatibility with old parsers)\n"); log("\n"); log("\n"); log("The general syntax of the JSON output created by this command is as follows:\n"); log("\n"); log(" {\n"); log(" \"creator\": \"Yosys \",\n"); log(" \"modules\": {\n"); log(" : {\n"); log(" \"attributes\": {\n"); log(" : ,\n"); log(" ...\n"); log(" },\n"); log(" \"parameter_default_values\": {\n"); log(" : ,\n"); log(" ...\n"); log(" },\n"); log(" \"ports\": {\n"); log(" : ,\n"); log(" ...\n"); log(" },\n"); log(" \"cells\": {\n"); log(" : ,\n"); log(" ...\n"); log(" },\n"); log(" \"memories\": {\n"); log(" : ,\n"); log(" ...\n"); log(" },\n"); log(" \"netnames\": {\n"); log(" : ,\n"); log(" ...\n"); log(" }\n"); log(" }\n"); log(" },\n"); log(" \"models\": {\n"); log(" ...\n"); log(" },\n"); log(" }\n"); log("\n"); log("Where is:\n"); log("\n"); log(" {\n"); log(" \"direction\": <\"input\" | \"output\" | \"inout\">,\n"); log(" \"bits\": \n"); log(" \"offset\": \n"); log(" \"upto\": <1 if the port bit indexing is MSB-first>\n"); log(" \"signed\": <1 if the port is signed>\n"); log(" }\n"); log("\n"); log("The \"offset\" and \"upto\" fields are skipped if their value would be 0.\n"); log("They don't affect connection semantics, and are only used to preserve original\n"); log("HDL bit indexing."); log("And is:\n"); log("\n"); log(" {\n"); log(" \"hide_name\": <1 | 0>,\n"); log(" \"type\": ,\n"); log(" \"model\": ,\n"); log(" \"parameters\": {\n"); log(" : ,\n"); log(" ...\n"); log(" },\n"); log(" \"attributes\": {\n"); log(" : ,\n"); log(" ...\n"); log(" },\n"); log(" \"port_directions\": {\n"); log(" : <\"input\" | \"output\" | \"inout\">,\n"); log(" ...\n"); log(" },\n"); log(" \"connections\": {\n"); log(" : ,\n"); log(" ...\n"); log(" },\n"); log(" }\n"); log("\n"); log("And is:\n"); log("\n"); log(" {\n"); log(" \"hide_name\": <1 | 0>,\n"); log(" \"attributes\": {\n"); log(" : ,\n"); log(" ...\n"); log(" },\n"); log(" \"width\": \n"); log(" \"start_offset\": \n"); log(" \"size\": \n"); log(" }\n"); log("\n"); log("And is:\n"); log("\n"); log(" {\n"); log(" \"hide_name\": <1 | 0>,\n"); log(" \"bits\": \n"); log(" \"offset\": \n"); log(" \"upto\": <1 if the port bit indexing is MSB-first>\n"); log(" \"signed\": <1 if the port is signed>\n"); log(" }\n"); log("\n"); log("The \"hide_name\" fields are set to 1 when the name of this cell or net is\n"); log("automatically created and is likely not of interest for a regular user.\n"); log("\n"); log("The \"port_directions\" section is only included for cells for which the\n"); log("interface is known.\n"); log("\n"); log("Module and cell ports and nets can be single bit wide or vectors of multiple\n"); log("bits. Each individual signal bit is assigned a unique integer. The \n"); log("values referenced above are vectors of this integers. Signal bits that are\n"); log("connected to a constant driver are denoted as string \"0\", \"1\", \"x\", or\n"); log("\"z\" instead of a number.\n"); log("\n"); log("Bit vectors (including integers) are written as string holding the binary\n"); log("representation of the value. Strings are written as strings, with an appended\n"); log("blank in cases of strings of the form /[01xz]* */.\n"); log("\n"); log("For example the following Verilog code:\n"); log("\n"); log(" module test(input x, y);\n"); log(" (* keep *) foo #(.P(42), .Q(1337))\n"); log(" foo_inst (.A({x, y}), .B({y, x}), .C({4'd10, {4{x}}}));\n"); log(" endmodule\n"); log("\n"); log("Translates to the following JSON output:\n"); log("\n"); log(" {\n"); log(" \"creator\": \"Yosys 0.9+2406 (git sha1 fb1168d8, clang 9.0.1 -fPIC -Os)\",\n"); log(" \"modules\": {\n"); log(" \"test\": {\n"); log(" \"attributes\": {\n"); log(" \"cells_not_processed\": \"00000000000000000000000000000001\",\n"); log(" \"src\": \"test.v:1.1-4.10\"\n"); log(" },\n"); log(" \"ports\": {\n"); log(" \"x\": {\n"); log(" \"direction\": \"input\",\n"); log(" \"bits\": [ 2 ]\n"); log(" },\n"); log(" \"y\": {\n"); log(" \"direction\": \"input\",\n"); log(" \"bits\": [ 3 ]\n"); log(" }\n"); log(" },\n"); log(" \"cells\": {\n"); log(" \"foo_inst\": {\n"); log(" \"hide_name\": 0,\n"); log(" \"type\": \"foo\",\n"); log(" \"parameters\": {\n"); log(" \"P\": \"00000000000000000000000000101010\",\n"); log(" \"Q\": \"00000000000000000000010100111001\"\n"); log(" },\n"); log(" \"attributes\": {\n"); log(" \"keep\": \"00000000000000000000000000000001\",\n"); log(" \"module_not_derived\": \"00000000000000000000000000000001\",\n"); log(" \"src\": \"test.v:3.1-3.55\"\n"); log(" },\n"); log(" \"connections\": {\n"); log(" \"A\": [ 3, 2 ],\n"); log(" \"B\": [ 2, 3 ],\n"); log(" \"C\": [ 2, 2, 2, 2, \"0\", \"1\", \"0\", \"1\" ]\n"); log(" }\n"); log(" }\n"); log(" },\n"); log(" \"netnames\": {\n"); log(" \"x\": {\n"); log(" \"hide_name\": 0,\n"); log(" \"bits\": [ 2 ],\n"); log(" \"attributes\": {\n"); log(" \"src\": \"test.v:1.19-1.20\"\n"); log(" }\n"); log(" },\n"); log(" \"y\": {\n"); log(" \"hide_name\": 0,\n"); log(" \"bits\": [ 3 ],\n"); log(" \"attributes\": {\n"); log(" \"src\": \"test.v:1.22-1.23\"\n"); log(" }\n"); log(" }\n"); log(" }\n"); log(" }\n"); log(" }\n"); log(" }\n"); log("\n"); log("The models are given as And-Inverter-Graphs (AIGs) in the following form:\n"); log("\n"); log(" \"models\": {\n"); log(" : [\n"); log(" /* 0 */ [ ],\n"); log(" /* 1 */ [ ],\n"); log(" /* 2 */ [ ],\n"); log(" ...\n"); log(" ],\n"); log(" ...\n"); log(" },\n"); log("\n"); log("The following node-types may be used:\n"); log("\n"); log(" [ \"port\", , , ]\n"); log(" - the value of the specified input port bit\n"); log("\n"); log(" [ \"nport\", , , ]\n"); log(" - the inverted value of the specified input port bit\n"); log("\n"); log(" [ \"and\", , , ]\n"); log(" - the ANDed value of the specified nodes\n"); log("\n"); log(" [ \"nand\", , , ]\n"); log(" - the inverted ANDed value of the specified nodes\n"); log("\n"); log(" [ \"true\", ]\n"); log(" - the constant value 1\n"); log("\n"); log(" [ \"false\", ]\n"); log(" - the constant value 0\n"); log("\n"); log("All nodes appear in topological order. I.e. only nodes with smaller indices\n"); log("are referenced by \"and\" and \"nand\" nodes.\n"); log("\n"); log("The optional at the end of a node specification is a list of\n"); log("output portname and bitindex pairs, specifying the outputs driven by this node.\n"); log("\n"); log("For example, the following is the model for a 3-input 3-output $reduce_and cell\n"); log("inferred by the following code:\n"); log("\n"); log(" module test(input [2:0] in, output [2:0] out);\n"); log(" assign in = &out;\n"); log(" endmodule\n"); log("\n"); log(" \"$reduce_and:3U:3\": [\n"); log(" /* 0 */ [ \"port\", \"A\", 0 ],\n"); log(" /* 1 */ [ \"port\", \"A\", 1 ],\n"); log(" /* 2 */ [ \"and\", 0, 1 ],\n"); log(" /* 3 */ [ \"port\", \"A\", 2 ],\n"); log(" /* 4 */ [ \"and\", 2, 3, \"Y\", 0 ],\n"); log(" /* 5 */ [ \"false\", \"Y\", 1, \"Y\", 2 ]\n"); log(" ]\n"); log("\n"); log("Future version of Yosys might add support for additional fields in the JSON\n"); log("format. A program processing this format must ignore all unknown fields.\n"); log("\n"); } void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { bool aig_mode = false; bool compat_int_mode = false; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-aig") { aig_mode = true; continue; } if (args[argidx] == "-compat-int") { compat_int_mode = true; continue; } break; } extra_args(f, filename, args, argidx); log_header(design, "Executing JSON backend.\n"); JsonWriter json_writer(*f, false, aig_mode, compat_int_mode); json_writer.write_design(design); } } JsonBackend; struct JsonPass : public Pass { JsonPass() : Pass("json", "write design in JSON format") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" json [options] [selection]\n"); log("\n"); log("Write a JSON netlist of all selected objects.\n"); log("\n"); log(" -o \n"); log(" write to the specified file.\n"); log("\n"); log(" -aig\n"); log(" also include AIG models for the different gate types\n"); log("\n"); log(" -compat-int\n"); log(" emit 32-bit or smaller fully-defined parameter values directly\n"); log(" as JSON numbers (for compatibility with old parsers)\n"); log("\n"); log("See 'help write_json' for a description of the JSON format used.\n"); log("\n"); } void execute(std::vector args, RTLIL::Design *design) override { std::string filename; bool aig_mode = false; bool compat_int_mode = false; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-o" && argidx+1 < args.size()) { filename = args[++argidx]; continue; } if (args[argidx] == "-aig") { aig_mode = true; continue; } if (args[argidx] == "-compat-int") { compat_int_mode = true; continue; } break; } extra_args(args, argidx, design); std::ostream *f; std::stringstream buf; bool empty = filename.empty(); if (!empty) { rewrite_filename(filename); std::ofstream *ff = new std::ofstream; ff->open(filename.c_str(), std::ofstream::trunc); if (ff->fail()) { delete ff; log_error("Can't open file `%s' for writing: %s\n", filename.c_str(), strerror(errno)); } f = ff; } else { f = &buf; } JsonWriter json_writer(*f, true, aig_mode, compat_int_mode); json_writer.write_design(design); if (!empty) { delete f; } else { log("%s", buf.str().c_str()); } } } JsonPass; PRIVATE_NAMESPACE_END yosys-yosys-0.33/backends/rtlil/000077500000000000000000000000001447554276300167325ustar00rootroot00000000000000yosys-yosys-0.33/backends/rtlil/Makefile.inc000066400000000000000000000000511447554276300211360ustar00rootroot00000000000000 OBJS += backends/rtlil/rtlil_backend.o yosys-yosys-0.33/backends/rtlil/rtlil_backend.cc000066400000000000000000000406501447554276300220430ustar00rootroot00000000000000/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * --- * * A very simple and straightforward backend for the RTLIL text * representation. * */ #include "rtlil_backend.h" #include "kernel/yosys.h" #include USING_YOSYS_NAMESPACE using namespace RTLIL_BACKEND; 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()) { 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]) { case State::S0: break; case State::S1: val |= 1 << i; break; default: val = -1; break; } } if (val >= 0) { f << stringf("%d", val); return; } } f << stringf("%d'", width); if (data.is_fully_undef_x_only()) { f << "x"; } else { for (int i = offset+width-1; i >= offset; i--) { log_assert(i < (int)data.bits.size()); switch (data.bits[i]) { case State::S0: f << stringf("0"); break; case State::S1: f << stringf("1"); break; case RTLIL::Sx: f << stringf("x"); break; case RTLIL::Sz: f << stringf("z"); break; case RTLIL::Sa: f << stringf("-"); break; case RTLIL::Sm: f << stringf("m"); break; } } } } else { f << stringf("\""); std::string str = data.decode_string(); for (size_t i = 0; i < str.size(); i++) { if (str[i] == '\n') f << stringf("\\n"); else if (str[i] == '\t') f << stringf("\\t"); else if (str[i] < 32) f << stringf("\\%03o", (unsigned char)str[i]); else if (str[i] == '"') f << stringf("\\\""); else if (str[i] == '\\') f << stringf("\\\\"); else f << str[i]; } f << stringf("\""); } } void RTLIL_BACKEND::dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool autoint) { if (chunk.wire == NULL) { dump_const(f, chunk.data, chunk.width, chunk.offset, autoint); } else { if (chunk.width == chunk.wire->width && chunk.offset == 0) f << stringf("%s", chunk.wire->name.c_str()); else if (chunk.width == 1) f << stringf("%s [%d]", chunk.wire->name.c_str(), chunk.offset); else f << stringf("%s [%d:%d]", chunk.wire->name.c_str(), chunk.offset+chunk.width-1, chunk.offset); } } void RTLIL_BACKEND::dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig, bool autoint) { if (sig.is_chunk()) { dump_sigchunk(f, sig.as_chunk(), autoint); } else { f << stringf("{ "); for (auto it = sig.chunks().rbegin(); it != sig.chunks().rend(); ++it) { dump_sigchunk(f, *it, false); f << stringf(" "); } f << stringf("}"); } } void RTLIL_BACKEND::dump_wire(std::ostream &f, std::string indent, const RTLIL::Wire *wire) { for (auto &it : wire->attributes) { f << stringf("%s" "attribute %s ", indent.c_str(), it.first.c_str()); dump_const(f, it.second); f << stringf("\n"); } f << stringf("%s" "wire ", indent.c_str()); if (wire->width != 1) f << stringf("width %d ", wire->width); if (wire->upto) f << stringf("upto "); if (wire->start_offset != 0) f << stringf("offset %d ", wire->start_offset); if (wire->port_input && !wire->port_output) f << stringf("input %d ", wire->port_id); if (!wire->port_input && wire->port_output) f << stringf("output %d ", wire->port_id); if (wire->port_input && wire->port_output) f << stringf("inout %d ", wire->port_id); if (wire->is_signed) f << stringf("signed "); f << stringf("%s\n", wire->name.c_str()); } void RTLIL_BACKEND::dump_memory(std::ostream &f, std::string indent, const RTLIL::Memory *memory) { for (auto &it : memory->attributes) { f << stringf("%s" "attribute %s ", indent.c_str(), it.first.c_str()); dump_const(f, it.second); f << stringf("\n"); } f << stringf("%s" "memory ", indent.c_str()); if (memory->width != 1) f << stringf("width %d ", memory->width); if (memory->size != 0) f << stringf("size %d ", memory->size); if (memory->start_offset != 0) f << stringf("offset %d ", memory->start_offset); f << stringf("%s\n", memory->name.c_str()); } void RTLIL_BACKEND::dump_cell(std::ostream &f, std::string indent, const RTLIL::Cell *cell) { for (auto &it : cell->attributes) { f << stringf("%s" "attribute %s ", indent.c_str(), it.first.c_str()); dump_const(f, it.second); f << stringf("\n"); } f << stringf("%s" "cell %s %s\n", indent.c_str(), cell->type.c_str(), cell->name.c_str()); for (auto &it : cell->parameters) { f << stringf("%s parameter%s%s %s ", indent.c_str(), (it.second.flags & RTLIL::CONST_FLAG_SIGNED) != 0 ? " signed" : "", (it.second.flags & RTLIL::CONST_FLAG_REAL) != 0 ? " real" : "", it.first.c_str()); dump_const(f, it.second); f << stringf("\n"); } for (auto &it : cell->connections()) { f << stringf("%s connect %s ", indent.c_str(), it.first.c_str()); dump_sigspec(f, it.second); f << stringf("\n"); } f << stringf("%s" "end\n", indent.c_str()); } void RTLIL_BACKEND::dump_proc_case_body(std::ostream &f, std::string indent, const RTLIL::CaseRule *cs) { for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it) { f << stringf("%s" "assign ", indent.c_str()); dump_sigspec(f, it->first); f << stringf(" "); dump_sigspec(f, it->second); f << stringf("\n"); } for (auto it = cs->switches.begin(); it != cs->switches.end(); ++it) dump_proc_switch(f, indent, *it); } void RTLIL_BACKEND::dump_proc_switch(std::ostream &f, std::string indent, const RTLIL::SwitchRule *sw) { for (auto it = sw->attributes.begin(); it != sw->attributes.end(); ++it) { f << stringf("%s" "attribute %s ", indent.c_str(), it->first.c_str()); dump_const(f, it->second); f << stringf("\n"); } f << stringf("%s" "switch ", indent.c_str()); dump_sigspec(f, sw->signal); f << stringf("\n"); for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) { for (auto ait = (*it)->attributes.begin(); ait != (*it)->attributes.end(); ++ait) { f << stringf("%s attribute %s ", indent.c_str(), ait->first.c_str()); dump_const(f, ait->second); f << stringf("\n"); } f << stringf("%s case ", indent.c_str()); for (size_t i = 0; i < (*it)->compare.size(); i++) { if (i > 0) f << stringf(" , "); dump_sigspec(f, (*it)->compare[i]); } f << stringf("\n"); dump_proc_case_body(f, indent + " ", *it); } f << stringf("%s" "end\n", indent.c_str()); } void RTLIL_BACKEND::dump_proc_sync(std::ostream &f, std::string indent, const RTLIL::SyncRule *sy) { f << stringf("%s" "sync ", indent.c_str()); switch (sy->type) { case RTLIL::ST0: f << stringf("low "); if (0) case RTLIL::ST1: f << stringf("high "); if (0) case RTLIL::STp: f << stringf("posedge "); if (0) case RTLIL::STn: f << stringf("negedge "); if (0) case RTLIL::STe: f << stringf("edge "); dump_sigspec(f, sy->signal); f << stringf("\n"); break; case RTLIL::STa: f << stringf("always\n"); break; case RTLIL::STg: f << stringf("global\n"); break; case RTLIL::STi: f << stringf("init\n"); break; } for (auto &it: sy->actions) { f << stringf("%s update ", indent.c_str()); dump_sigspec(f, it.first); f << stringf(" "); dump_sigspec(f, it.second); f << stringf("\n"); } for (auto &it: sy->mem_write_actions) { for (auto it2 = it.attributes.begin(); it2 != it.attributes.end(); ++it2) { f << stringf("%s attribute %s ", indent.c_str(), it2->first.c_str()); dump_const(f, it2->second); f << stringf("\n"); } f << stringf("%s memwr %s ", indent.c_str(), it.memid.c_str()); dump_sigspec(f, it.address); f << stringf(" "); dump_sigspec(f, it.data); f << stringf(" "); dump_sigspec(f, it.enable); f << stringf(" "); dump_const(f, it.priority_mask); f << stringf("\n"); } } void RTLIL_BACKEND::dump_proc(std::ostream &f, std::string indent, const RTLIL::Process *proc) { for (auto it = proc->attributes.begin(); it != proc->attributes.end(); ++it) { f << stringf("%s" "attribute %s ", indent.c_str(), it->first.c_str()); dump_const(f, it->second); f << stringf("\n"); } f << stringf("%s" "process %s\n", indent.c_str(), proc->name.c_str()); dump_proc_case_body(f, indent + " ", &proc->root_case); for (auto it = proc->syncs.begin(); it != proc->syncs.end(); ++it) dump_proc_sync(f, indent + " ", *it); f << stringf("%s" "end\n", indent.c_str()); } void RTLIL_BACKEND::dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right) { f << stringf("%s" "connect ", indent.c_str()); dump_sigspec(f, left); f << stringf(" "); dump_sigspec(f, right); f << stringf("\n"); } void RTLIL_BACKEND::dump_module(std::ostream &f, std::string indent, RTLIL::Module *module, RTLIL::Design *design, bool only_selected, bool flag_m, bool flag_n) { bool print_header = flag_m || design->selected_whole_module(module->name); bool print_body = !flag_n || !design->selected_whole_module(module->name); if (print_header) { for (auto it = module->attributes.begin(); it != module->attributes.end(); ++it) { f << stringf("%s" "attribute %s ", indent.c_str(), it->first.c_str()); dump_const(f, it->second); f << stringf("\n"); } f << stringf("%s" "module %s\n", indent.c_str(), module->name.c_str()); if (!module->avail_parameters.empty()) { if (only_selected) f << stringf("\n"); for (const auto &p : module->avail_parameters) { const auto &it = module->parameter_default_values.find(p); if (it == module->parameter_default_values.end()) { f << stringf("%s" " parameter %s\n", indent.c_str(), p.c_str()); } else { f << stringf("%s" " parameter %s ", indent.c_str(), p.c_str()); dump_const(f, it->second); f << stringf("\n"); } } } } if (print_body) { for (auto it : module->wires()) if (!only_selected || design->selected(module, it)) { if (only_selected) f << stringf("\n"); dump_wire(f, indent + " ", it); } for (auto it : module->memories) if (!only_selected || design->selected(module, it.second)) { if (only_selected) f << stringf("\n"); dump_memory(f, indent + " ", it.second); } for (auto it : module->cells()) if (!only_selected || design->selected(module, it)) { if (only_selected) f << stringf("\n"); dump_cell(f, indent + " ", it); } for (auto it : module->processes) if (!only_selected || design->selected(module, it.second)) { if (only_selected) f << stringf("\n"); dump_proc(f, indent + " ", it.second); } bool first_conn_line = true; for (auto it = module->connections().begin(); it != module->connections().end(); ++it) { bool show_conn = !only_selected || design->selected_whole_module(module->name); if (!show_conn) { RTLIL::SigSpec sigs = it->first; sigs.append(it->second); for (auto &c : sigs.chunks()) { if (c.wire == NULL || !design->selected(module, c.wire)) continue; show_conn = true; } } if (show_conn) { if (only_selected && first_conn_line) f << stringf("\n"); dump_conn(f, indent + " ", it->first, it->second); first_conn_line = false; } } } if (print_header) f << stringf("%s" "end\n", indent.c_str()); } void RTLIL_BACKEND::dump_design(std::ostream &f, RTLIL::Design *design, bool only_selected, bool flag_m, bool flag_n) { int init_autoidx = autoidx; if (!flag_m) { int count_selected_mods = 0; for (auto module : design->modules()) { if (design->selected_whole_module(module->name)) flag_m = true; if (design->selected(module)) count_selected_mods++; } if (count_selected_mods > 1) flag_m = true; } if (!only_selected || flag_m) { if (only_selected) f << stringf("\n"); f << stringf("autoidx %d\n", autoidx); } for (auto module : design->modules()) { if (!only_selected || design->selected(module)) { if (only_selected) f << stringf("\n"); dump_module(f, "", module, design, only_selected, flag_m, flag_n); } } log_assert(init_autoidx == autoidx); } YOSYS_NAMESPACE_END PRIVATE_NAMESPACE_BEGIN struct RTLILBackend : public Backend { RTLILBackend() : Backend("rtlil", "write design to RTLIL file") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" write_rtlil [filename]\n"); log("\n"); log("Write the current design to an RTLIL file. (RTLIL is a text representation\n"); log("of a design in yosys's internal format.)\n"); log("\n"); log(" -selected\n"); log(" only write selected parts of the design.\n"); log("\n"); } void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { bool selected = false; log_header(design, "Executing RTLIL backend.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; if (arg == "-selected") { selected = true; continue; } break; } extra_args(f, filename, args, argidx); design->sort(); log("Output filename: %s\n", filename.c_str()); *f << stringf("# Generated by %s\n", yosys_version_str); RTLIL_BACKEND::dump_design(*f, design, selected, true, false); } } RTLILBackend; struct IlangBackend : public Backend { IlangBackend() : Backend("ilang", "(deprecated) alias of write_rtlil") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log("See `help write_rtlil`.\n"); log("\n"); } void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { RTLILBackend.execute(f, filename, args, design); } } IlangBackend; struct DumpPass : public Pass { DumpPass() : Pass("dump", "print parts of the design in RTLIL format") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" dump [options] [selection]\n"); log("\n"); log("Write the selected parts of the design to the console or specified file in\n"); log("RTLIL format.\n"); log("\n"); log(" -m\n"); log(" also dump the module headers, even if only parts of a single\n"); log(" module is selected\n"); log("\n"); log(" -n\n"); log(" only dump the module headers if the entire module is selected\n"); log("\n"); log(" -o \n"); log(" write to the specified file.\n"); log("\n"); log(" -a \n"); log(" like -outfile but append instead of overwrite\n"); log("\n"); } void execute(std::vector args, RTLIL::Design *design) override { std::string filename; bool flag_m = false, flag_n = false, append = false; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; if ((arg == "-o" || arg == "-outfile") && argidx+1 < args.size()) { filename = args[++argidx]; append = false; continue; } if ((arg == "-a" || arg == "-append") && argidx+1 < args.size()) { filename = args[++argidx]; append = true; continue; } if (arg == "-m") { flag_m = true; continue; } if (arg == "-n") { flag_n = true; continue; } break; } extra_args(args, argidx, design); std::ostream *f; std::stringstream buf; bool empty = filename.empty(); if (!empty) { rewrite_filename(filename); std::ofstream *ff = new std::ofstream; ff->open(filename.c_str(), append ? std::ofstream::app : std::ofstream::trunc); if (ff->fail()) { delete ff; log_error("Can't open file `%s' for writing: %s\n", filename.c_str(), strerror(errno)); } f = ff; } else { f = &buf; } RTLIL_BACKEND::dump_design(*f, design, true, flag_m, flag_n); if (!empty) { delete f; } else { log("%s", buf.str().c_str()); } } } DumpPass; PRIVATE_NAMESPACE_END yosys-yosys-0.33/backends/rtlil/rtlil_backend.h000066400000000000000000000045471447554276300217120ustar00rootroot00000000000000/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * --- * * A very simple and straightforward backend for the RTLIL text * representation. * */ #ifndef RTLIL_BACKEND_H #define RTLIL_BACKEND_H #include "kernel/yosys.h" #include YOSYS_NAMESPACE_BEGIN namespace RTLIL_BACKEND { void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int offset = 0, bool autoint = true); void dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool autoint = true); void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig, bool autoint = true); void dump_wire(std::ostream &f, std::string indent, const RTLIL::Wire *wire); void dump_memory(std::ostream &f, std::string indent, const RTLIL::Memory *memory); void dump_cell(std::ostream &f, std::string indent, const RTLIL::Cell *cell); void dump_proc_case_body(std::ostream &f, std::string indent, const RTLIL::CaseRule *cs); void dump_proc_switch(std::ostream &f, std::string indent, const RTLIL::SwitchRule *sw); void dump_proc_sync(std::ostream &f, std::string indent, const RTLIL::SyncRule *sy); void dump_proc(std::ostream &f, std::string indent, const RTLIL::Process *proc); void dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right); void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module, RTLIL::Design *design, bool only_selected, bool flag_m = true, bool flag_n = false); void dump_design(std::ostream &f, RTLIL::Design *design, bool only_selected, bool flag_m = true, bool flag_n = false); } YOSYS_NAMESPACE_END #endif yosys-yosys-0.33/backends/simplec/000077500000000000000000000000001447554276300172405ustar00rootroot00000000000000yosys-yosys-0.33/backends/simplec/.gitignore000066400000000000000000000000271447554276300212270ustar00rootroot00000000000000test00_tb test00_uut.c yosys-yosys-0.33/backends/simplec/Makefile.inc000066400000000000000000000000451447554276300214470ustar00rootroot00000000000000 OBJS += backends/simplec/simplec.o yosys-yosys-0.33/backends/simplec/simplec.cc000066400000000000000000000632721447554276300212150ustar00rootroot00000000000000/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ #include "kernel/yosys.h" #include "kernel/sigtools.h" #include "kernel/utils.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct HierDirtyFlags; static pool reserved_cids; static dict id2cid; static string cid(IdString id) { if (id2cid.count(id) == 0) { string s = id.str(); if (GetSize(s) < 2) log_abort(); if (s[0] == '\\') s = s.substr(1); if ('0' <= s[0] && s[0] <= '9') { s = "_" + s; } for (int i = 0; i < GetSize(s); i++) { if ('0' <= s[i] && s[i] <= '9') continue; if ('A' <= s[i] && s[i] <= 'Z') continue; if ('a' <= s[i] && s[i] <= 'z') continue; s[i] = '_'; } while (reserved_cids.count(s)) s += "_"; reserved_cids.insert(s); id2cid[id] = s; } return id2cid.at(id); } struct HierDirtyFlags { int dirty; Module *module; IdString hiername; HierDirtyFlags *parent; pool dirty_bits; pool dirty_cells; pool sticky_dirty_bits; dict children; string prefix, log_prefix; HierDirtyFlags(Module *module, IdString hiername, HierDirtyFlags *parent, const string &prefix, const string &log_prefix) : dirty(0), module(module), hiername(hiername), parent(parent), prefix(prefix), log_prefix(log_prefix) { for (Cell *cell : module->cells()) { Module *mod = module->design->module(cell->type); if (mod) children[cell->name] = new HierDirtyFlags(mod, cell->name, this, prefix + cid(cell->name) + ".", log_prefix + "." + prefix + log_id(cell->name)); } } ~HierDirtyFlags() { for (auto &child : children) delete child.second; } void set_dirty(SigBit bit) { if (dirty_bits.count(bit)) return; dirty_bits.insert(bit); sticky_dirty_bits.insert(bit); HierDirtyFlags *p = this; while (p != nullptr) { p->dirty++; p = p->parent; } } void unset_dirty(SigBit bit) { if (dirty_bits.count(bit) == 0) return; dirty_bits.erase(bit); HierDirtyFlags *p = this; while (p != nullptr) { p->dirty--; log_assert(p->dirty >= 0); p = p->parent; } } void set_dirty(Cell *cell) { if (dirty_cells.count(cell)) return; dirty_cells.insert(cell); HierDirtyFlags *p = this; while (p != nullptr) { p->dirty++; p = p->parent; } } void unset_dirty(Cell *cell) { if (dirty_cells.count(cell) == 0) return; dirty_cells.erase(cell); HierDirtyFlags *p = this; while (p != nullptr) { p->dirty--; log_assert(p->dirty >= 0); p = p->parent; } } }; struct SimplecWorker { bool verbose = false; int max_uintsize = 32; Design *design; dict sigmaps; vector signal_declarations; pool generated_sigtypes; vector util_declarations; pool generated_utils; vector struct_declarations; pool generated_structs; vector funct_declarations; dict>>> bit2cell; dict>> bit2output; dict> driven_bits; dict topoidx; pool activated_cells; pool reactivated_cells; SimplecWorker(Design *design) : design(design) { } string sigtype(int n) { string struct_name = stringf("signal%d_t", n); if (generated_sigtypes.count(n) == 0) { signal_declarations.push_back(""); signal_declarations.push_back(stringf("#ifndef YOSYS_SIMPLEC_SIGNAL%d_T", n)); signal_declarations.push_back(stringf("#define YOSYS_SIMPLEC_SIGNAL%d_T", n)); signal_declarations.push_back(stringf("typedef struct {")); for (int k = 8; k <= max_uintsize; k = 2*k) if (n <= k && k <= max_uintsize) { signal_declarations.push_back(stringf(" uint%d_t value_%d_0 : %d;", k, n-1, n)); goto end_struct; } for (int k = 0; k < n; k += max_uintsize) { int bits = std::min(max_uintsize, n-k); signal_declarations.push_back(stringf(" uint%d_t value_%d_%d : %d;", max_uintsize, k+bits-1, k, bits)); } end_struct: signal_declarations.push_back(stringf("} signal%d_t;", n)); signal_declarations.push_back(stringf("#endif")); generated_sigtypes.insert(n); } return struct_name; } void util_ifdef_guard(string s) { for (int i = 0; i < GetSize(s); i++) if ('a' <= s[i] && s[i] <= 'z') s[i] -= 'a' - 'A'; util_declarations.push_back(""); util_declarations.push_back(stringf("#ifndef %s", s.c_str())); util_declarations.push_back(stringf("#define %s", s.c_str())); } string util_get_bit(const string &signame, int n, int idx) { if (n == 1 && idx == 0) return signame + ".value_0_0"; string util_name = stringf("yosys_simplec_get_bit_%d_of_%d", idx, n); if (generated_utils.count(util_name) == 0) { util_ifdef_guard(util_name); util_declarations.push_back(stringf("static inline bool %s(const %s *sig)", util_name.c_str(), sigtype(n).c_str())); util_declarations.push_back(stringf("{")); int word_idx = idx / max_uintsize, word_offset = idx % max_uintsize; string value_name = stringf("value_%d_%d", std::min(n-1, (word_idx+1)*max_uintsize-1), word_idx*max_uintsize); util_declarations.push_back(stringf(" return (sig->%s >> %d) & 1;", value_name.c_str(), word_offset)); util_declarations.push_back(stringf("}")); util_declarations.push_back(stringf("#endif")); generated_utils.insert(util_name); } return stringf("%s(&%s)", util_name.c_str(), signame.c_str()); } string util_set_bit(const string &signame, int n, int idx, const string &expr) { if (n == 1 && idx == 0) return stringf(" %s.value_0_0 = %s;", signame.c_str(), expr.c_str()); string util_name = stringf("yosys_simplec_set_bit_%d_of_%d", idx, n); if (generated_utils.count(util_name) == 0) { util_ifdef_guard(util_name); util_declarations.push_back(stringf("static inline void %s(%s *sig, bool value)", util_name.c_str(), sigtype(n).c_str())); util_declarations.push_back(stringf("{")); int word_idx = idx / max_uintsize, word_offset = idx % max_uintsize; string value_name = stringf("value_%d_%d", std::min(n-1, (word_idx+1)*max_uintsize-1), word_idx*max_uintsize); #if 0 util_declarations.push_back(stringf(" if (value)")); util_declarations.push_back(stringf(" sig->%s |= 1UL << %d;", value_name.c_str(), word_offset)); util_declarations.push_back(stringf(" else")); util_declarations.push_back(stringf(" sig->%s &= ~(1UL << %d);", value_name.c_str(), word_offset)); #else util_declarations.push_back(stringf(" sig->%s = (sig->%s & ~((uint%d_t)1 << %d)) | ((uint%d_t)value << %d);", value_name.c_str(), value_name.c_str(), max_uintsize, word_offset, max_uintsize, word_offset)); #endif util_declarations.push_back(stringf("}")); util_declarations.push_back(stringf("#endif")); generated_utils.insert(util_name); } return stringf(" %s(&%s, %s);", util_name.c_str(), signame.c_str(), expr.c_str()); } void create_module_struct(Module *mod) { if (generated_structs.count(mod->name)) return; generated_structs.insert(mod->name); sigmaps[mod].set(mod); for (Wire *w : mod->wires()) { if (w->port_output) for (auto bit : SigSpec(w)) bit2output[mod][sigmaps.at(mod)(bit)].insert(bit); } for (Cell *c : mod->cells()) { for (auto &conn : c->connections()) { if (!c->input(conn.first)) { for (auto bit : sigmaps.at(mod)(conn.second)) driven_bits[mod].insert(bit); continue; } int idx = 0; for (auto bit : sigmaps.at(mod)(conn.second)) bit2cell[mod][bit].insert(tuple(c, conn.first, idx++)); } if (design->module(c->type)) create_module_struct(design->module(c->type)); } TopoSort topo; for (Cell *c : mod->cells()) { topo.node(c->name); for (auto &conn : c->connections()) { if (!c->input(conn.first)) continue; for (auto bit : sigmaps.at(mod)(conn.second)) for (auto &it : bit2cell[mod][bit]) topo.edge(c->name, std::get<0>(it)->name); } } topo.analyze_loops = false; topo.sort(); for (int i = 0; i < GetSize(topo.sorted); i++) topoidx[mod->cell(topo.sorted[i])] = i; string ifdef_name = stringf("yosys_simplec_%s_state_t", cid(mod->name).c_str()); for (int i = 0; i < GetSize(ifdef_name); i++) if ('a' <= ifdef_name[i] && ifdef_name[i] <= 'z') ifdef_name[i] -= 'a' - 'A'; struct_declarations.push_back(""); struct_declarations.push_back(stringf("#ifndef %s", ifdef_name.c_str())); struct_declarations.push_back(stringf("#define %s", ifdef_name.c_str())); struct_declarations.push_back(stringf("struct %s_state_t", cid(mod->name).c_str())); struct_declarations.push_back("{"); struct_declarations.push_back(" // Input Ports"); for (Wire *w : mod->wires()) if (w->port_input) struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width).c_str(), cid(w->name).c_str(), log_id(w))); struct_declarations.push_back(""); struct_declarations.push_back(" // Output Ports"); for (Wire *w : mod->wires()) if (!w->port_input && w->port_output) struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width).c_str(), cid(w->name).c_str(), log_id(w))); struct_declarations.push_back(""); struct_declarations.push_back(" // Internal Wires"); for (Wire *w : mod->wires()) if (!w->port_input && !w->port_output) struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width).c_str(), cid(w->name).c_str(), log_id(w))); for (Cell *c : mod->cells()) if (design->module(c->type)) struct_declarations.push_back(stringf(" struct %s_state_t %s; // %s", cid(c->type).c_str(), cid(c->name).c_str(), log_id(c))); struct_declarations.push_back(stringf("};")); struct_declarations.push_back("#endif"); } void eval_cell(HierDirtyFlags *work, Cell *cell) { if (cell->type.in(ID($_BUF_), ID($_NOT_))) { SigBit a = sigmaps.at(work->module)(cell->getPort(ID::A)); SigBit y = sigmaps.at(work->module)(cell->getPort(ID::Y)); string a_expr = a.wire ? util_get_bit(work->prefix + cid(a.wire->name), a.wire->width, a.offset) : a.data ? "1" : "0"; string expr; if (cell->type == ID($_BUF_)) expr = a_expr; if (cell->type == ID($_NOT_)) expr = "!" + a_expr; log_assert(y.wire); funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) + stringf(" // %s (%s)", log_id(cell), log_id(cell->type))); work->set_dirty(y); return; } if (cell->type.in(ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_))) { SigBit a = sigmaps.at(work->module)(cell->getPort(ID::A)); SigBit b = sigmaps.at(work->module)(cell->getPort(ID::B)); SigBit y = sigmaps.at(work->module)(cell->getPort(ID::Y)); string a_expr = a.wire ? util_get_bit(work->prefix + cid(a.wire->name), a.wire->width, a.offset) : a.data ? "1" : "0"; string b_expr = b.wire ? util_get_bit(work->prefix + cid(b.wire->name), b.wire->width, b.offset) : b.data ? "1" : "0"; string expr; if (cell->type == ID($_AND_)) expr = stringf("%s & %s", a_expr.c_str(), b_expr.c_str()); if (cell->type == ID($_NAND_)) expr = stringf("!(%s & %s)", a_expr.c_str(), b_expr.c_str()); if (cell->type == ID($_OR_)) expr = stringf("%s | %s", a_expr.c_str(), b_expr.c_str()); if (cell->type == ID($_NOR_)) expr = stringf("!(%s | %s)", a_expr.c_str(), b_expr.c_str()); if (cell->type == ID($_XOR_)) expr = stringf("%s ^ %s", a_expr.c_str(), b_expr.c_str()); if (cell->type == ID($_XNOR_)) expr = stringf("!(%s ^ %s)", a_expr.c_str(), b_expr.c_str()); if (cell->type == ID($_ANDNOT_)) expr = stringf("%s & (!%s)", a_expr.c_str(), b_expr.c_str()); if (cell->type == ID($_ORNOT_)) expr = stringf("%s | (!%s)", a_expr.c_str(), b_expr.c_str()); log_assert(y.wire); funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) + stringf(" // %s (%s)", log_id(cell), log_id(cell->type))); work->set_dirty(y); return; } if (cell->type.in(ID($_AOI3_), ID($_OAI3_))) { SigBit a = sigmaps.at(work->module)(cell->getPort(ID::A)); SigBit b = sigmaps.at(work->module)(cell->getPort(ID::B)); SigBit c = sigmaps.at(work->module)(cell->getPort(ID::C)); SigBit y = sigmaps.at(work->module)(cell->getPort(ID::Y)); string a_expr = a.wire ? util_get_bit(work->prefix + cid(a.wire->name), a.wire->width, a.offset) : a.data ? "1" : "0"; string b_expr = b.wire ? util_get_bit(work->prefix + cid(b.wire->name), b.wire->width, b.offset) : b.data ? "1" : "0"; string c_expr = c.wire ? util_get_bit(work->prefix + cid(c.wire->name), c.wire->width, c.offset) : c.data ? "1" : "0"; string expr; if (cell->type == ID($_AOI3_)) expr = stringf("!((%s & %s) | %s)", a_expr.c_str(), b_expr.c_str(), c_expr.c_str()); if (cell->type == ID($_OAI3_)) expr = stringf("!((%s | %s) & %s)", a_expr.c_str(), b_expr.c_str(), c_expr.c_str()); log_assert(y.wire); funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) + stringf(" // %s (%s)", log_id(cell), log_id(cell->type))); work->set_dirty(y); return; } if (cell->type.in(ID($_AOI4_), ID($_OAI4_))) { SigBit a = sigmaps.at(work->module)(cell->getPort(ID::A)); SigBit b = sigmaps.at(work->module)(cell->getPort(ID::B)); SigBit c = sigmaps.at(work->module)(cell->getPort(ID::C)); SigBit d = sigmaps.at(work->module)(cell->getPort(ID::D)); SigBit y = sigmaps.at(work->module)(cell->getPort(ID::Y)); string a_expr = a.wire ? util_get_bit(work->prefix + cid(a.wire->name), a.wire->width, a.offset) : a.data ? "1" : "0"; string b_expr = b.wire ? util_get_bit(work->prefix + cid(b.wire->name), b.wire->width, b.offset) : b.data ? "1" : "0"; string c_expr = c.wire ? util_get_bit(work->prefix + cid(c.wire->name), c.wire->width, c.offset) : c.data ? "1" : "0"; string d_expr = d.wire ? util_get_bit(work->prefix + cid(d.wire->name), d.wire->width, d.offset) : d.data ? "1" : "0"; string expr; if (cell->type == ID($_AOI4_)) expr = stringf("!((%s & %s) | (%s & %s))", a_expr.c_str(), b_expr.c_str(), c_expr.c_str(), d_expr.c_str()); if (cell->type == ID($_OAI4_)) expr = stringf("!((%s | %s) & (%s | %s))", a_expr.c_str(), b_expr.c_str(), c_expr.c_str(), d_expr.c_str()); log_assert(y.wire); funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) + stringf(" // %s (%s)", log_id(cell), log_id(cell->type))); work->set_dirty(y); return; } if (cell->type.in(ID($_MUX_), ID($_NMUX_))) { SigBit a = sigmaps.at(work->module)(cell->getPort(ID::A)); SigBit b = sigmaps.at(work->module)(cell->getPort(ID::B)); SigBit s = sigmaps.at(work->module)(cell->getPort(ID::S)); SigBit y = sigmaps.at(work->module)(cell->getPort(ID::Y)); string a_expr = a.wire ? util_get_bit(work->prefix + cid(a.wire->name), a.wire->width, a.offset) : a.data ? "1" : "0"; string b_expr = b.wire ? util_get_bit(work->prefix + cid(b.wire->name), b.wire->width, b.offset) : b.data ? "1" : "0"; string s_expr = s.wire ? util_get_bit(work->prefix + cid(s.wire->name), s.wire->width, s.offset) : s.data ? "1" : "0"; // casts to bool are a workaround for CBMC bug (https://github.com/diffblue/cbmc/issues/933) string expr = stringf("%s ? %s(bool)%s : %s(bool)%s", s_expr.c_str(), cell->type == ID($_NMUX_) ? "!" : "", b_expr.c_str(), cell->type == ID($_NMUX_) ? "!" : "", a_expr.c_str()); log_assert(y.wire); funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) + stringf(" // %s (%s)", log_id(cell), log_id(cell->type))); work->set_dirty(y); return; } log_error("No C model for %s available at the moment (FIXME).\n", log_id(cell->type)); } void eval_dirty(HierDirtyFlags *work) { while (work->dirty) { if (verbose && (!work->dirty_bits.empty() || !work->dirty_cells.empty())) log(" In %s:\n", work->log_prefix.c_str()); while (!work->dirty_bits.empty() || !work->dirty_cells.empty()) { if (!work->dirty_bits.empty()) { SigSpec dirtysig(work->dirty_bits); dirtysig.sort_and_unify(); for (SigChunk chunk : dirtysig.chunks()) { if (chunk.wire == nullptr) continue; if (verbose) log(" Propagating %s.%s[%d:%d].\n", work->log_prefix.c_str(), log_id(chunk.wire), chunk.offset+chunk.width-1, chunk.offset); funct_declarations.push_back(stringf(" // Updated signal in %s: %s", work->log_prefix.c_str(), log_signal(chunk))); } for (SigBit bit : dirtysig) { if (bit2output[work->module].count(bit) && work->parent) for (auto outbit : bit2output[work->module][bit]) { Module *parent_mod = work->parent->module; Cell *parent_cell = parent_mod->cell(work->hiername); IdString port_name = outbit.wire->name; int port_offset = outbit.offset; SigBit parent_bit = sigmaps.at(parent_mod)(parent_cell->getPort(port_name)[port_offset]); log_assert(bit.wire && parent_bit.wire); funct_declarations.push_back(util_set_bit(work->parent->prefix + cid(parent_bit.wire->name), parent_bit.wire->width, parent_bit.offset, util_get_bit(work->prefix + cid(bit.wire->name), bit.wire->width, bit.offset))); work->parent->set_dirty(parent_bit); if (verbose) log(" Propagating %s.%s[%d] -> %s.%s[%d].\n", work->log_prefix.c_str(), log_id(bit.wire), bit.offset, work->parent->log_prefix.c_str(), log_id(parent_bit.wire), parent_bit.offset); } for (auto &port : bit2cell[work->module][bit]) { if (work->children.count(std::get<0>(port)->name)) { HierDirtyFlags *child = work->children.at(std::get<0>(port)->name); SigBit child_bit = sigmaps.at(child->module)(SigBit(child->module->wire(std::get<1>(port)), std::get<2>(port))); log_assert(bit.wire && child_bit.wire); funct_declarations.push_back(util_set_bit(work->prefix + cid(child->hiername) + "." + cid(child_bit.wire->name), child_bit.wire->width, child_bit.offset, util_get_bit(work->prefix + cid(bit.wire->name), bit.wire->width, bit.offset))); child->set_dirty(child_bit); if (verbose) log(" Propagating %s.%s[%d] -> %s.%s.%s[%d].\n", work->log_prefix.c_str(), log_id(bit.wire), bit.offset, work->log_prefix.c_str(), log_id(std::get<0>(port)), log_id(child_bit.wire), child_bit.offset); } else { if (verbose) log(" Marking cell %s.%s (via %s.%s[%d]).\n", work->log_prefix.c_str(), log_id(std::get<0>(port)), work->log_prefix.c_str(), log_id(bit.wire), bit.offset); work->set_dirty(std::get<0>(port)); } } work->unset_dirty(bit); } } if (!work->dirty_cells.empty()) { Cell *cell = nullptr; for (auto c : work->dirty_cells) if (cell == nullptr || topoidx.at(cell) < topoidx.at(c)) cell = c; string hiername = work->log_prefix + "." + log_id(cell); if (verbose) log(" Evaluating %s (%s, best of %d).\n", hiername.c_str(), log_id(cell->type), GetSize(work->dirty_cells)); if (activated_cells.count(hiername)) reactivated_cells.insert(hiername); activated_cells.insert(hiername); eval_cell(work, cell); work->unset_dirty(cell); } } for (auto &child : work->children) eval_dirty(child.second); } } void eval_sticky_dirty(HierDirtyFlags *work) { Module *mod = work->module; for (Wire *w : mod->wires()) for (SigBit bit : SigSpec(w)) { SigBit canonical_bit = sigmaps.at(mod)(bit); if (canonical_bit == bit) continue; if (work->sticky_dirty_bits.count(canonical_bit) == 0) continue; if (bit.wire == nullptr || canonical_bit.wire == nullptr) continue; funct_declarations.push_back(util_set_bit(work->prefix + cid(bit.wire->name), bit.wire->width, bit.offset, util_get_bit(work->prefix + cid(canonical_bit.wire->name), canonical_bit.wire->width, canonical_bit.offset).c_str())); if (verbose) log(" Propagating alias %s.%s[%d] -> %s.%s[%d].\n", work->log_prefix.c_str(), log_id(canonical_bit.wire), canonical_bit.offset, work->log_prefix.c_str(), log_id(bit.wire), bit.offset); } work->sticky_dirty_bits.clear(); for (auto &child : work->children) eval_sticky_dirty(child.second); } void make_func(HierDirtyFlags *work, const string &func_name, const vector &preamble) { log("Generating function %s():\n", func_name.c_str()); activated_cells.clear(); reactivated_cells.clear(); funct_declarations.push_back(""); funct_declarations.push_back(stringf("static void %s(struct %s_state_t *state)", func_name.c_str(), cid(work->module->name).c_str())); funct_declarations.push_back("{"); for (auto &line : preamble) funct_declarations.push_back(line); eval_dirty(work); eval_sticky_dirty(work); funct_declarations.push_back("}"); log(" Activated %d cells (%d activated more than once).\n", GetSize(activated_cells), GetSize(reactivated_cells)); } void eval_init(HierDirtyFlags *work, vector &preamble) { Module *module = work->module; for (Wire *w : module->wires()) { if (w->attributes.count(ID::init)) { SigSpec sig = sigmaps.at(module)(w); Const val = w->attributes.at(ID::init); val.bits.resize(GetSize(sig), State::Sx); for (int i = 0; i < GetSize(sig); i++) if (val[i] == State::S0 || val[i] == State::S1) { SigBit bit = sig[i]; preamble.push_back(util_set_bit(work->prefix + cid(bit.wire->name), bit.wire->width, bit.offset, val == State::S1 ? "true" : "false")); work->set_dirty(bit); } } for (SigBit bit : SigSpec(w)) { SigBit val = sigmaps.at(module)(bit); if (val == State::S0 || val == State::S1) preamble.push_back(util_set_bit(work->prefix + cid(bit.wire->name), bit.wire->width, bit.offset, val == State::S1 ? "true" : "false")); if (driven_bits.at(module).count(val) == 0) work->set_dirty(val); } } work->set_dirty(State::S0); work->set_dirty(State::S1); for (auto &child : work->children) eval_init(child.second, preamble); } void make_init_func(HierDirtyFlags *work) { vector preamble; eval_init(work, preamble); make_func(work, cid(work->module->name) + "_init", preamble); } void make_eval_func(HierDirtyFlags *work) { Module *mod = work->module; vector preamble; for (Wire *w : mod->wires()) { if (w->port_input) for (SigBit bit : sigmaps.at(mod)(w)) work->set_dirty(bit); } make_func(work, cid(work->module->name) + "_eval", preamble); } void make_tick_func(HierDirtyFlags* /* work */) { // FIXME } void run(Module *mod) { create_module_struct(mod); HierDirtyFlags work(mod, IdString(), nullptr, "state->", log_id(mod->name)); make_init_func(&work); make_eval_func(&work); make_tick_func(&work); } void write(std::ostream &f) { f << "#include " << std::endl; f << "#include " << std::endl; for (auto &line : signal_declarations) f << line << std::endl; for (auto &line : util_declarations) f << line << std::endl; for (auto &line : struct_declarations) f << line << std::endl; for (auto &line : funct_declarations) f << line << std::endl; } }; struct SimplecBackend : public Backend { SimplecBackend() : Backend("simplec", "convert design to simple C code") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" write_simplec [options] [filename]\n"); log("\n"); log("Write simple C code for simulating the design. The C code written can be used to\n"); log("simulate the design in a C environment, but the purpose of this command is to\n"); log("generate code that works well with C-based formal verification.\n"); log("\n"); log(" -verbose\n"); log(" this will print the recursive walk used to export the modules.\n"); log("\n"); log(" -i8, -i16, -i32, -i64\n"); log(" set the maximum integer bit width to use in the generated code.\n"); log("\n"); log("THIS COMMAND IS UNDER CONSTRUCTION\n"); log("\n"); } void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { reserved_cids.clear(); id2cid.clear(); SimplecWorker worker(design); log_header(design, "Executing SIMPLEC backend.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-verbose") { worker.verbose = true; continue; } if (args[argidx] == "-i8") { worker.max_uintsize = 8; continue; } if (args[argidx] == "-i16") { worker.max_uintsize = 16; continue; } if (args[argidx] == "-i32") { worker.max_uintsize = 32; continue; } if (args[argidx] == "-i64") { worker.max_uintsize = 64; continue; } break; } extra_args(f, filename, args, argidx); Module *topmod = design->top_module(); if (topmod == nullptr) log_error("Current design has no top module.\n"); worker.run(topmod); worker.write(*f); } } SimplecBackend; PRIVATE_NAMESPACE_END yosys-yosys-0.33/backends/simplec/test00.sh000066400000000000000000000002261447554276300207130ustar00rootroot00000000000000#!/bin/bash set -ex ../../yosys -p 'synth -top test; write_simplec -verbose -i8 test00_uut.c' test00_uut.v clang -o test00_tb test00_tb.c ./test00_tb yosys-yosys-0.33/backends/simplec/test00_tb.c000066400000000000000000000041251447554276300212120ustar00rootroot00000000000000#include #include #include "test00_uut.c" uint32_t xorshift32() { static uint32_t x32 = 314159265; x32 ^= x32 << 13; x32 ^= x32 >> 17; x32 ^= x32 << 5; return x32; } int main() { struct test_state_t state; uint32_t a, b, c, x, y, z, w; bool first_eval = true; for (int i = 0; i < 10; i++) { a = xorshift32(); b = xorshift32(); c = xorshift32(); x = (a & b) | c; y = a & (b | c); z = a ^ b ^ c; w = z; state.a.value_7_0 = a; state.a.value_15_8 = a >> 8; state.a.value_23_16 = a >> 16; state.a.value_31_24 = a >> 24; state.b.value_7_0 = b; state.b.value_15_8 = b >> 8; state.b.value_23_16 = b >> 16; state.b.value_31_24 = b >> 24; state.c.value_7_0 = c; state.c.value_15_8 = c >> 8; state.c.value_23_16 = c >> 16; state.c.value_31_24 = c >> 24; if (first_eval) { first_eval = false; test_init(&state); } else { test_eval(&state); } uint32_t uut_x = 0; uut_x |= (uint32_t)state.x.value_7_0; uut_x |= (uint32_t)state.x.value_15_8 << 8; uut_x |= (uint32_t)state.x.value_23_16 << 16; uut_x |= (uint32_t)state.x.value_31_24 << 24; uint32_t uut_y = 0; uut_y |= (uint32_t)state.y.value_7_0; uut_y |= (uint32_t)state.y.value_15_8 << 8; uut_y |= (uint32_t)state.y.value_23_16 << 16; uut_y |= (uint32_t)state.y.value_31_24 << 24; uint32_t uut_z = 0; uut_z |= (uint32_t)state.z.value_7_0; uut_z |= (uint32_t)state.z.value_15_8 << 8; uut_z |= (uint32_t)state.z.value_23_16 << 16; uut_z |= (uint32_t)state.z.value_31_24 << 24; uint32_t uut_w = 0; uut_w |= (uint32_t)state.w.value_7_0; uut_w |= (uint32_t)state.w.value_15_8 << 8; uut_w |= (uint32_t)state.w.value_23_16 << 16; uut_w |= (uint32_t)state.w.value_31_24 << 24; printf("---\n"); printf("A: 0x%08x\n", a); printf("B: 0x%08x\n", b); printf("C: 0x%08x\n", c); printf("X: 0x%08x 0x%08x\n", x, uut_x); printf("Y: 0x%08x 0x%08x\n", y, uut_y); printf("Z: 0x%08x 0x%08x\n", z, uut_z); printf("W: 0x%08x 0x%08x\n", w, uut_w); assert(x == uut_x); assert(y == uut_y); assert(z == uut_z); assert(w == uut_w); } return 0; } yosys-yosys-0.33/backends/simplec/test00_uut.v000066400000000000000000000006031447554276300214420ustar00rootroot00000000000000module test(input [31:0] a, b, c, output [31:0] x, y, z, w); unit_x unit_x_inst (.a(a), .b(b), .c(c), .x(x)); unit_y unit_y_inst (.a(a), .b(b), .c(c), .y(y)); assign z = a ^ b ^ c, w = z; endmodule module unit_x(input [31:0] a, b, c, output [31:0] x); assign x = (a & b) | c; endmodule module unit_y(input [31:0] a, b, c, output [31:0] y); assign y = a & (b | c); endmodule yosys-yosys-0.33/backends/smt2/000077500000000000000000000000001447554276300164715ustar00rootroot00000000000000yosys-yosys-0.33/backends/smt2/.gitignore000066400000000000000000000000131447554276300204530ustar00rootroot00000000000000test_cells yosys-yosys-0.33/backends/smt2/Makefile.inc000066400000000000000000000042401447554276300207010ustar00rootroot00000000000000 OBJS += backends/smt2/smt2.o ifneq ($(CONFIG),mxe) ifneq ($(CONFIG),emcc) # MSYS targets support yosys-smtbmc, but require a launcher script ifeq ($(CONFIG),$(filter $(CONFIG),msys2 msys2-64)) TARGETS += $(PROGRAM_PREFIX)yosys-smtbmc.exe $(PROGRAM_PREFIX)yosys-smtbmc-script.py TARGETS += $(PROGRAM_PREFIX)yosys-witness.exe $(PROGRAM_PREFIX)yosys-witness-script.py # Needed to find the Python interpreter for yosys-smtbmc scripts. # Override if necessary, it is only used for msys2 targets. PYTHON := $(shell cygpath -w -m $(PREFIX)/bin/python3) $(PROGRAM_PREFIX)yosys-smtbmc-script.py: backends/smt2/smtbmc.py $(P) sed -e 's|##yosys-sys-path##|sys.path += [os.path.dirname(os.path.realpath(__file__)) + p for p in ["/share/python3", "/../share/$(PROGRAM_PREFIX)yosys/python3"]]|;' \ -e "s|#!/usr/bin/env python3|#!$(PYTHON)|" < $< > $@ $(PROGRAM_PREFIX)yosys-witness-script.py: backends/smt2/witness.py $(P) sed -e 's|##yosys-sys-path##|sys.path += [os.path.dirname(os.path.realpath(__file__)) + p for p in ["/share/python3", "/../share/$(PROGRAM_PREFIX)yosys/python3"]]|;' \ -e "s|#!/usr/bin/env python3|#!$(PYTHON)|" < $< > $@ $(PROGRAM_PREFIX)yosys-smtbmc.exe: misc/launcher.c $(PROGRAM_PREFIX)yosys-smtbmc-script.py $(P) $(CXX) -DGUI=0 -O -s -o $@ $< $(PROGRAM_PREFIX)yosys-witness.exe: misc/launcher.c $(PROGRAM_PREFIX)yosys-witness-script.py $(P) $(CXX) -DGUI=0 -O -s -o $@ $< # Other targets else TARGETS += $(PROGRAM_PREFIX)yosys-smtbmc $(PROGRAM_PREFIX)yosys-witness $(PROGRAM_PREFIX)yosys-smtbmc: backends/smt2/smtbmc.py $(P) sed 's|##yosys-sys-path##|sys.path += [os.path.dirname(os.path.realpath(__file__)) + p for p in ["/share/python3", "/../share/$(PROGRAM_PREFIX)yosys/python3"]]|;' < $< > $@.new $(Q) chmod +x $@.new $(Q) mv $@.new $@ $(PROGRAM_PREFIX)yosys-witness: backends/smt2/witness.py $(P) sed 's|##yosys-sys-path##|sys.path += [os.path.dirname(os.path.realpath(__file__)) + p for p in ["/share/python3", "/../share/$(PROGRAM_PREFIX)yosys/python3"]]|;' < $< > $@.new $(Q) chmod +x $@.new $(Q) mv $@.new $@ endif $(eval $(call add_share_file,share/python3,backends/smt2/smtio.py)) $(eval $(call add_share_file,share/python3,backends/smt2/ywio.py)) endif endif yosys-yosys-0.33/backends/smt2/example.v000066400000000000000000000003531447554276300203140ustar00rootroot00000000000000module main(input clk); reg [3:0] counter = 0; always @(posedge clk) begin if (counter == 10) counter <= 0; else counter <= counter + 1; end assert property (counter != 15); // assert property (counter <= 10); endmodule yosys-yosys-0.33/backends/smt2/example.ys000066400000000000000000000001761447554276300205050ustar00rootroot00000000000000read_verilog -formal example.v hierarchy; proc; opt; memory -nordff -nomap; opt -fast write_smt2 -bv -mem -wires example.smt2 yosys-yosys-0.33/backends/smt2/smt2.cc000066400000000000000000002170441447554276300176750ustar00rootroot00000000000000/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ #include "kernel/rtlil.h" #include "kernel/register.h" #include "kernel/sigtools.h" #include "kernel/celltypes.h" #include "kernel/log.h" #include "kernel/mem.h" #include "libs/json11/json11.hpp" #include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct Smt2Worker { CellTypes ct; SigMap sigmap; RTLIL::Module *module; bool bvmode, memmode, wiresmode, verbose, statebv, statedt, forallmode; dict &mod_stbv_width; int idcounter = 0, statebv_width = 0; std::vector decls, trans, hier, dtmembers; std::map bit_driver; std::set exported_cells, hiercells, hiercells_queue; pool recursive_cells, registers; std::vector memories; dict mem_cells; std::set memory_queue; pool clock_posedge, clock_negedge; vector ex_state_eq, ex_input_eq; std::map> fcache; std::map memarrays; std::map bvsizes; dict ids; bool is_smtlib2_module; const char *get_id(IdString n) { if (ids.count(n) == 0) { std::string str = log_id(n); for (int i = 0; i < GetSize(str); i++) { if (str[i] == '\\') str[i] = '/'; } ids[n] = strdup(str.c_str()); } return ids[n]; } template const char *get_id(T *obj) { return get_id(obj->name); } void makebits(std::string name, int width = 0, std::string comment = std::string()) { std::string decl_str; if (statebv) { if (width == 0) { decl_str = stringf("(define-fun |%s| ((state |%s_s|)) Bool (= ((_ extract %d %d) state) #b1))", name.c_str(), get_id(module), statebv_width, statebv_width); statebv_width += 1; } else { decl_str = stringf("(define-fun |%s| ((state |%s_s|)) (_ BitVec %d) ((_ extract %d %d) state))", name.c_str(), get_id(module), width, statebv_width+width-1, statebv_width); statebv_width += width; } } else if (statedt) { if (width == 0) { decl_str = stringf(" (|%s| Bool)", name.c_str()); } else { decl_str = stringf(" (|%s| (_ BitVec %d))", name.c_str(), width); } } else { if (width == 0) { decl_str = stringf("(declare-fun |%s| (|%s_s|) Bool)", name.c_str(), get_id(module)); } else { decl_str = stringf("(declare-fun |%s| (|%s_s|) (_ BitVec %d))", name.c_str(), get_id(module), width); } } if (!comment.empty()) decl_str += " ; " + comment; if (statedt) dtmembers.push_back(decl_str + "\n"); else decls.push_back(decl_str + "\n"); } Smt2Worker(RTLIL::Module *module, bool bvmode, bool memmode, bool wiresmode, bool verbose, bool statebv, bool statedt, bool forallmode, dict &mod_stbv_width, dict>> &mod_clk_cache) : ct(module->design), sigmap(module), module(module), bvmode(bvmode), memmode(memmode), wiresmode(wiresmode), verbose(verbose), statebv(statebv), statedt(statedt), forallmode(forallmode), mod_stbv_width(mod_stbv_width), is_smtlib2_module(module->has_attribute(ID::smtlib2_module)) { pool noclock; makebits(stringf("%s_is", get_id(module))); dict mem_dict; memories = Mem::get_all_memories(module); for (auto &mem : memories) { if (is_smtlib2_module) log_error("Memory %s.%s not allowed in module with smtlib2_module attribute", get_id(module), mem.memid.c_str()); mem.narrow(); mem_dict[mem.memid] = &mem; for (auto &port : mem.wr_ports) { if (port.clk_enable) { SigSpec clk = sigmap(port.clk); for (int i = 0; i < GetSize(clk); i++) { if (clk[i].wire == nullptr) continue; if (port.clk_polarity) clock_posedge.insert(clk[i]); else clock_negedge.insert(clk[i]); } } for (auto bit : sigmap(port.en)) noclock.insert(bit); for (auto bit : sigmap(port.addr)) noclock.insert(bit); for (auto bit : sigmap(port.data)) noclock.insert(bit); } for (auto &port : mem.rd_ports) { if (port.clk_enable) { SigSpec clk = sigmap(port.clk); for (int i = 0; i < GetSize(clk); i++) { if (clk[i].wire == nullptr) continue; if (port.clk_polarity) clock_posedge.insert(clk[i]); else clock_negedge.insert(clk[i]); } } for (auto bit : sigmap(port.en)) noclock.insert(bit); for (auto bit : sigmap(port.addr)) noclock.insert(bit); for (auto bit : sigmap(port.data)) noclock.insert(bit); Cell *driver = port.cell ? port.cell : mem.cell; for (auto bit : sigmap(port.data)) { if (bit_driver.count(bit)) log_error("Found multiple drivers for %s.\n", log_signal(bit)); bit_driver[bit] = driver; } } } for (auto cell : module->cells()) for (auto &conn : cell->connections()) { if (GetSize(conn.second) == 0) continue; // Handled above. if (cell->is_mem_cell()) { mem_cells[cell] = mem_dict[cell->parameters.at(ID::MEMID).decode_string()]; continue; } bool is_input = ct.cell_input(cell->type, conn.first); bool is_output = ct.cell_output(cell->type, conn.first); if (is_output && !is_input) for (auto bit : sigmap(conn.second)) { if (bit_driver.count(bit)) log_error("Found multiple drivers for %s.\n", log_signal(bit)); bit_driver[bit] = cell; } else if (is_output || !is_input) log_error("Unsupported or unknown directionality on port %s of cell %s.%s (%s).\n", log_id(conn.first), log_id(module), log_id(cell), log_id(cell->type)); if (cell->type.in(ID($dff), ID($_DFF_P_), ID($_DFF_N_)) && conn.first.in(ID::CLK, ID::C)) { bool posedge = (cell->type == ID($_DFF_N_)) || (cell->type == ID($dff) && cell->getParam(ID::CLK_POLARITY).as_bool()); for (auto bit : sigmap(conn.second)) { if (posedge) clock_posedge.insert(bit); else clock_negedge.insert(bit); } } else if (mod_clk_cache.count(cell->type) && mod_clk_cache.at(cell->type).count(conn.first)) { for (auto bit : sigmap(conn.second)) { if (mod_clk_cache.at(cell->type).at(conn.first).first) clock_posedge.insert(bit); if (mod_clk_cache.at(cell->type).at(conn.first).second) clock_negedge.insert(bit); } } else { for (auto bit : sigmap(conn.second)) noclock.insert(bit); } } for (auto bit : noclock) { clock_posedge.erase(bit); clock_negedge.erase(bit); } for (auto wire : module->wires()) { auto gclk_attr = wire->attributes.find(ID::replaced_by_gclk); if (gclk_attr != wire->attributes.end()) { if (gclk_attr->second == State::S1) clock_posedge.insert(sigmap(wire)); else if (gclk_attr->second == State::S0) clock_negedge.insert(sigmap(wire)); } } for (auto wire : module->wires()) { if (!wire->port_input || GetSize(wire) != 1) continue; SigBit bit = sigmap(wire); if (clock_posedge.count(bit)) mod_clk_cache[module->name][wire->name].first = true; if (clock_negedge.count(bit)) mod_clk_cache[module->name][wire->name].second = true; } } ~Smt2Worker() { for (auto &it : ids) free(it.second); ids.clear(); } const char *get_id(Module *m) { return get_id(m->name); } const char *get_id(Cell *c) { return get_id(c->name); } const char *get_id(Wire *w) { return get_id(w->name); } void register_bool(RTLIL::SigBit bit, int id) { if (verbose) log("%*s-> register_bool: %s %d\n", 2+2*GetSize(recursive_cells), "", log_signal(bit), id); sigmap.apply(bit); log_assert(fcache.count(bit) == 0); fcache[bit] = std::pair(id, -1); } void register_bv(RTLIL::SigSpec sig, int id) { if (verbose) log("%*s-> register_bv: %s %d\n", 2+2*GetSize(recursive_cells), "", log_signal(sig), id); log_assert(bvmode); sigmap.apply(sig); log_assert(bvsizes.count(id) == 0); bvsizes[id] = GetSize(sig); for (int i = 0; i < GetSize(sig); i++) { log_assert(fcache.count(sig[i]) == 0); fcache[sig[i]] = std::pair(id, i); } } void register_boolvec(RTLIL::SigSpec sig, int id) { if (verbose) log("%*s-> register_boolvec: %s %d\n", 2+2*GetSize(recursive_cells), "", log_signal(sig), id); log_assert(bvmode); sigmap.apply(sig); register_bool(sig[0], id); for (int i = 1; i < GetSize(sig); i++) sigmap.add(sig[i], RTLIL::State::S0); } std::string get_bool(RTLIL::SigBit bit, const char *state_name = "state") { sigmap.apply(bit); if (bit.wire == nullptr) return bit == RTLIL::State::S1 ? "true" : "false"; if (bit_driver.count(bit)) export_cell(bit_driver.at(bit)); sigmap.apply(bit); if (fcache.count(bit) == 0) { if (verbose) log("%*s-> external bool: %s\n", 2+2*GetSize(recursive_cells), "", log_signal(bit)); makebits(stringf("%s#%d", get_id(module), idcounter), 0, log_signal(bit)); register_bool(bit, idcounter++); } auto f = fcache.at(bit); if (f.second >= 0) return stringf("(= ((_ extract %d %d) (|%s#%d| %s)) #b1)", f.second, f.second, get_id(module), f.first, state_name); return stringf("(|%s#%d| %s)", get_id(module), f.first, state_name); } std::string get_bool(RTLIL::SigSpec sig, const char *state_name = "state") { return get_bool(sig.as_bit(), state_name); } std::string get_bv(RTLIL::SigSpec sig, const char *state_name = "state") { log_assert(bvmode); sigmap.apply(sig); std::vector subexpr; SigSpec orig_sig; while (orig_sig != sig) { for (auto bit : sig) if (bit_driver.count(bit)) export_cell(bit_driver.at(bit)); orig_sig = sig; sigmap.apply(sig); } for (int i = 0, j = 1; i < GetSize(sig); i += j, j = 1) { if (sig[i].wire == nullptr) { while (i+j < GetSize(sig) && sig[i+j].wire == nullptr) j++; subexpr.push_back("#b"); for (int k = i+j-1; k >= i; k--) subexpr.back() += sig[k] == RTLIL::State::S1 ? "1" : "0"; continue; } if (fcache.count(sig[i]) && fcache.at(sig[i]).second == -1) { subexpr.push_back(stringf("(ite %s #b1 #b0)", get_bool(sig[i], state_name).c_str())); continue; } if (fcache.count(sig[i])) { auto t1 = fcache.at(sig[i]); while (i+j < GetSize(sig)) { if (fcache.count(sig[i+j]) == 0) break; auto t2 = fcache.at(sig[i+j]); if (t1.first != t2.first) break; if (t1.second+j != t2.second) break; j++; } if (t1.second == 0 && j == bvsizes.at(t1.first)) subexpr.push_back(stringf("(|%s#%d| %s)", get_id(module), t1.first, state_name)); else subexpr.push_back(stringf("((_ extract %d %d) (|%s#%d| %s))", t1.second + j - 1, t1.second, get_id(module), t1.first, state_name)); continue; } std::set seen_bits = { sig[i] }; while (i+j < GetSize(sig) && sig[i+j].wire && !fcache.count(sig[i+j]) && !seen_bits.count(sig[i+j])) seen_bits.insert(sig[i+j]), j++; if (verbose) log("%*s-> external bv: %s\n", 2+2*GetSize(recursive_cells), "", log_signal(sig.extract(i, j))); for (auto bit : sig.extract(i, j)) log_assert(bit_driver.count(bit) == 0); makebits(stringf("%s#%d", get_id(module), idcounter), j, log_signal(sig.extract(i, j))); subexpr.push_back(stringf("(|%s#%d| %s)", get_id(module), idcounter, state_name)); register_bv(sig.extract(i, j), idcounter++); } if (GetSize(subexpr) > 1) { std::string expr = "", end_str = ""; for (int i = GetSize(subexpr)-1; i >= 0; i--) { if (i > 0) expr += " (concat", end_str += ")"; expr += " " + subexpr[i]; } return expr.substr(1) + end_str; } else { log_assert(GetSize(subexpr) == 1); return subexpr[0]; } } void export_gate(RTLIL::Cell *cell, std::string expr) { RTLIL::SigBit bit = sigmap(cell->getPort(ID::Y).as_bit()); std::string processed_expr; for (char ch : expr) { if (ch == 'A') processed_expr += get_bool(cell->getPort(ID::A)); else if (ch == 'B') processed_expr += get_bool(cell->getPort(ID::B)); else if (ch == 'C') processed_expr += get_bool(cell->getPort(ID::C)); else if (ch == 'D') processed_expr += get_bool(cell->getPort(ID::D)); else if (ch == 'S') processed_expr += get_bool(cell->getPort(ID::S)); else processed_expr += ch; } if (verbose) log("%*s-> import cell: %s\n", 2+2*GetSize(recursive_cells), "", log_id(cell)); decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) Bool %s) ; %s\n", get_id(module), idcounter, get_id(module), processed_expr.c_str(), log_signal(bit))); register_bool(bit, idcounter++); recursive_cells.erase(cell); } void export_bvop(RTLIL::Cell *cell, std::string expr, char type = 0) { RTLIL::SigSpec sig_a, sig_b; RTLIL::SigSpec sig_y = sigmap(cell->getPort(ID::Y)); bool is_signed = type == 'U' ? false : cell->getParam(ID::A_SIGNED).as_bool(); int width = GetSize(sig_y); if (type == 's' || type == 'S' || type == 'd' || type == 'b') { if (type == 'b') width = GetSize(cell->getPort(ID::A)); else width = max(width, GetSize(cell->getPort(ID::A))); if (cell->hasPort(ID::B)) width = max(width, GetSize(cell->getPort(ID::B))); } if (cell->hasPort(ID::A)) { sig_a = cell->getPort(ID::A); sig_a.extend_u0(width, is_signed); } if (cell->hasPort(ID::B)) { sig_b = cell->getPort(ID::B); sig_b.extend_u0(width, (type == 'S') || (is_signed && !(type == 's'))); } std::string processed_expr; for (char ch : expr) { if (ch == 'A') processed_expr += get_bv(sig_a); else if (ch == 'B') processed_expr += get_bv(sig_b); else if (ch == 'P') processed_expr += get_bv(cell->getPort(ID::B)); else if (ch == 'S') processed_expr += get_bv(cell->getPort(ID::S)); else if (ch == 'L') processed_expr += is_signed ? "a" : "l"; else if (ch == 'U') processed_expr += is_signed ? "s" : "u"; else processed_expr += ch; } if (width != GetSize(sig_y) && type != 'b') processed_expr = stringf("((_ extract %d 0) %s)", GetSize(sig_y)-1, processed_expr.c_str()); if (verbose) log("%*s-> import cell: %s\n", 2+2*GetSize(recursive_cells), "", log_id(cell)); if (type == 'b') { decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) Bool %s) ; %s\n", get_id(module), idcounter, get_id(module), processed_expr.c_str(), log_signal(sig_y))); register_boolvec(sig_y, idcounter++); } else { decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n", get_id(module), idcounter, get_id(module), GetSize(sig_y), processed_expr.c_str(), log_signal(sig_y))); register_bv(sig_y, idcounter++); } recursive_cells.erase(cell); } void export_reduce(RTLIL::Cell *cell, std::string expr, bool identity_val) { RTLIL::SigSpec sig_y = sigmap(cell->getPort(ID::Y)); std::string processed_expr; for (char ch : expr) if (ch == 'A' || ch == 'B') { RTLIL::SigSpec sig = sigmap(cell->getPort(stringf("\\%c", ch))); for (auto bit : sig) processed_expr += " " + get_bool(bit); if (GetSize(sig) == 1) processed_expr += identity_val ? " true" : " false"; } else processed_expr += ch; if (verbose) log("%*s-> import cell: %s\n", 2+2*GetSize(recursive_cells), "", log_id(cell)); decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) Bool %s) ; %s\n", get_id(module), idcounter, get_id(module), processed_expr.c_str(), log_signal(sig_y))); register_boolvec(sig_y, idcounter++); recursive_cells.erase(cell); } void export_cell(RTLIL::Cell *cell) { if (verbose) log("%*s=> export_cell %s (%s) [%s]\n", 2+2*GetSize(recursive_cells), "", log_id(cell), log_id(cell->type), exported_cells.count(cell) ? "old" : "new"); if (recursive_cells.count(cell)) log_error("Found logic loop in module %s! See cell %s.\n", get_id(module), get_id(cell)); if (exported_cells.count(cell)) return; exported_cells.insert(cell); recursive_cells.insert(cell); if (cell->type == ID($initstate)) { SigBit bit = sigmap(cell->getPort(ID::Y).as_bit()); decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) Bool (|%s_is| state)) ; %s\n", get_id(module), idcounter, get_id(module), get_id(module), log_signal(bit))); register_bool(bit, idcounter++); recursive_cells.erase(cell); return; } if (cell->type.in(ID($_FF_), ID($_DFF_P_), ID($_DFF_N_))) { registers.insert(cell); SigBit q_bit = cell->getPort(ID::Q); if (q_bit.is_wire()) decls.push_back(witness_signal("reg", 1, 0, "", idcounter, q_bit.wire)); makebits(stringf("%s#%d", get_id(module), idcounter), 0, log_signal(cell->getPort(ID::Q))); register_bool(cell->getPort(ID::Q), idcounter++); recursive_cells.erase(cell); return; } if (cell->type == ID($_BUF_)) return export_gate(cell, "A"); if (cell->type == ID($_NOT_)) return export_gate(cell, "(not A)"); if (cell->type == ID($_AND_)) return export_gate(cell, "(and A B)"); if (cell->type == ID($_NAND_)) return export_gate(cell, "(not (and A B))"); if (cell->type == ID($_OR_)) return export_gate(cell, "(or A B)"); if (cell->type == ID($_NOR_)) return export_gate(cell, "(not (or A B))"); if (cell->type == ID($_XOR_)) return export_gate(cell, "(xor A B)"); if (cell->type == ID($_XNOR_)) return export_gate(cell, "(not (xor A B))"); if (cell->type == ID($_ANDNOT_)) return export_gate(cell, "(and A (not B))"); if (cell->type == ID($_ORNOT_)) return export_gate(cell, "(or A (not B))"); if (cell->type == ID($_MUX_)) return export_gate(cell, "(ite S B A)"); if (cell->type == ID($_NMUX_)) return export_gate(cell, "(not (ite S B A))"); if (cell->type == ID($_AOI3_)) return export_gate(cell, "(not (or (and A B) C))"); if (cell->type == ID($_OAI3_)) return export_gate(cell, "(not (and (or A B) C))"); if (cell->type == ID($_AOI4_)) return export_gate(cell, "(not (or (and A B) (and C D)))"); if (cell->type == ID($_OAI4_)) return export_gate(cell, "(not (and (or A B) (or C D)))"); // FIXME: $lut if (bvmode) { if (cell->type.in(ID($ff), ID($dff))) { registers.insert(cell); int smtoffset = 0; for (auto chunk : cell->getPort(ID::Q).chunks()) { if (chunk.is_wire()) decls.push_back(witness_signal("reg", chunk.width, chunk.offset, "", idcounter, chunk.wire, smtoffset)); smtoffset += chunk.width; } makebits(stringf("%s#%d", get_id(module), idcounter), GetSize(cell->getPort(ID::Q)), log_signal(cell->getPort(ID::Q))); register_bv(cell->getPort(ID::Q), idcounter++); recursive_cells.erase(cell); return; } if (cell->type.in(ID($anyconst), ID($anyseq), ID($anyinit), ID($allconst), ID($allseq))) { auto QY = cell->type == ID($anyinit) ? ID::Q : ID::Y; registers.insert(cell); string infostr = cell->attributes.count(ID::src) ? cell->attributes.at(ID::src).decode_string().c_str() : get_id(cell); if (cell->attributes.count(ID::reg)) infostr += " " + cell->attributes.at(ID::reg).decode_string(); decls.push_back(stringf("; yosys-smt2-%s %s#%d %d %s\n", cell->type.c_str() + 1, get_id(module), idcounter, GetSize(cell->getPort(QY)), infostr.c_str())); if (cell->getPort(QY).is_wire() && cell->getPort(QY).as_wire()->get_bool_attribute(ID::maximize)){ decls.push_back(stringf("; yosys-smt2-maximize %s#%d\n", get_id(module), idcounter)); log("Wire %s is maximized\n", cell->getPort(QY).as_wire()->name.str().c_str()); } else if (cell->getPort(QY).is_wire() && cell->getPort(QY).as_wire()->get_bool_attribute(ID::minimize)){ decls.push_back(stringf("; yosys-smt2-minimize %s#%d\n", get_id(module), idcounter)); log("Wire %s is minimized\n", cell->getPort(QY).as_wire()->name.str().c_str()); } bool init_only = cell->type.in(ID($anyconst), ID($anyinit), ID($allconst)); bool clk2fflogic = cell->type == ID($anyinit) && cell->get_bool_attribute(ID(clk2fflogic)); int smtoffset = 0; for (auto chunk : cell->getPort(clk2fflogic ? ID::D : QY).chunks()) { if (chunk.is_wire()) decls.push_back(witness_signal(init_only ? "init" : "seq", chunk.width, chunk.offset, "", idcounter, chunk.wire, smtoffset)); smtoffset += chunk.width; } makebits(stringf("%s#%d", get_id(module), idcounter), GetSize(cell->getPort(QY)), log_signal(cell->getPort(QY))); if (cell->type == ID($anyseq)) ex_input_eq.push_back(stringf(" (= (|%s#%d| state) (|%s#%d| other_state))", get_id(module), idcounter, get_id(module), idcounter)); register_bv(cell->getPort(QY), idcounter++); recursive_cells.erase(cell); return; } if (cell->type == ID($and)) return export_bvop(cell, "(bvand A B)"); if (cell->type == ID($or)) return export_bvop(cell, "(bvor A B)"); if (cell->type == ID($xor)) return export_bvop(cell, "(bvxor A B)"); if (cell->type == ID($xnor)) return export_bvop(cell, "(bvxnor A B)"); if (cell->type == ID($bweqx)) return export_bvop(cell, "(bvxnor A B)", 'U'); if (cell->type == ID($bwmux)) return export_bvop(cell, "(bvor (bvand A (bvnot S)) (bvand B S))", 'U'); if (cell->type == ID($shl)) return export_bvop(cell, "(bvshl A B)", 's'); if (cell->type == ID($shr)) return export_bvop(cell, "(bvlshr A B)", 's'); if (cell->type == ID($sshl)) return export_bvop(cell, "(bvshl A B)", 's'); if (cell->type == ID($sshr)) return export_bvop(cell, "(bvLshr A B)", 's'); if (cell->type.in(ID($shift), ID($shiftx))) { if (cell->getParam(ID::B_SIGNED).as_bool()) { return export_bvop(cell, stringf("(ite (bvsge P #b%0*d) " "(bvlshr A B) (bvshl A (bvneg B)))", GetSize(cell->getPort(ID::B)), 0), 'S'); // type 'S' sign extends B } else { return export_bvop(cell, "(bvlshr A B)", 's'); } } if (cell->type == ID($lt)) return export_bvop(cell, "(bvUlt A B)", 'b'); if (cell->type == ID($le)) return export_bvop(cell, "(bvUle A B)", 'b'); if (cell->type == ID($ge)) return export_bvop(cell, "(bvUge A B)", 'b'); if (cell->type == ID($gt)) return export_bvop(cell, "(bvUgt A B)", 'b'); if (cell->type == ID($ne)) return export_bvop(cell, "(distinct A B)", 'b'); if (cell->type == ID($nex)) return export_bvop(cell, "(distinct A B)", 'b'); if (cell->type == ID($eq)) return export_bvop(cell, "(= A B)", 'b'); if (cell->type == ID($eqx)) return export_bvop(cell, "(= A B)", 'b'); if (cell->type == ID($not)) return export_bvop(cell, "(bvnot A)"); if (cell->type == ID($pos)) return export_bvop(cell, "A"); if (cell->type == ID($neg)) return export_bvop(cell, "(bvneg A)"); if (cell->type == ID($add)) return export_bvop(cell, "(bvadd A B)"); if (cell->type == ID($sub)) return export_bvop(cell, "(bvsub A B)"); if (cell->type == ID($mul)) return export_bvop(cell, "(bvmul A B)"); if (cell->type == ID($div)) return export_bvop(cell, "(bvUdiv A B)", 'd'); // "rem" = truncating modulo if (cell->type == ID($mod)) return export_bvop(cell, "(bvUrem A B)", 'd'); // "mod" = flooring modulo if (cell->type == ID($modfloor)) { // bvumod doesn't exist because it's the same as bvurem if (cell->getParam(ID::A_SIGNED).as_bool()) { return export_bvop(cell, "(bvsmod A B)", 'd'); } else { return export_bvop(cell, "(bvurem A B)", 'd'); } } // "div" = flooring division if (cell->type == ID($divfloor)) { if (cell->getParam(ID::A_SIGNED).as_bool()) { // bvsdiv is truncating division, so we can't use it here. int width = max(GetSize(cell->getPort(ID::A)), GetSize(cell->getPort(ID::B))); width = max(width, GetSize(cell->getPort(ID::Y))); auto expr = stringf("(let (" "(a_neg (bvslt A #b%0*d)) " "(b_neg (bvslt B #b%0*d))) " "(let ((abs_a (ite a_neg (bvneg A) A)) " "(abs_b (ite b_neg (bvneg B) B))) " "(let ((u (bvudiv abs_a abs_b)) " "(adj (ite (= #b%0*d (bvurem abs_a abs_b)) #b%0*d #b%0*d))) " "(ite (= a_neg b_neg) u " "(bvneg (bvadd u adj))))))", width, 0, width, 0, width, 0, width, 0, width, 1); return export_bvop(cell, expr, 'd'); } else { return export_bvop(cell, "(bvudiv A B)", 'd'); } } if (cell->type.in(ID($reduce_and), ID($reduce_or), ID($reduce_bool)) && 2*GetSize(cell->getPort(ID::A).chunks()) < GetSize(cell->getPort(ID::A))) { bool is_and = cell->type == ID($reduce_and); string bits(GetSize(cell->getPort(ID::A)), is_and ? '1' : '0'); return export_bvop(cell, stringf("(%s A #b%s)", is_and ? "=" : "distinct", bits.c_str()), 'b'); } if (cell->type == ID($reduce_and)) return export_reduce(cell, "(and A)", true); if (cell->type == ID($reduce_or)) return export_reduce(cell, "(or A)", false); if (cell->type == ID($reduce_xor)) return export_reduce(cell, "(xor A)", false); if (cell->type == ID($reduce_xnor)) return export_reduce(cell, "(not (xor A))", false); if (cell->type == ID($reduce_bool)) return export_reduce(cell, "(or A)", false); if (cell->type == ID($logic_not)) return export_reduce(cell, "(not (or A))", false); if (cell->type == ID($logic_and)) return export_reduce(cell, "(and (or A) (or B))", false); if (cell->type == ID($logic_or)) return export_reduce(cell, "(or A B)", false); if (cell->type.in(ID($mux), ID($pmux))) { int width = GetSize(cell->getPort(ID::Y)); std::string processed_expr = get_bv(cell->getPort(ID::A)); RTLIL::SigSpec sig_b = cell->getPort(ID::B); RTLIL::SigSpec sig_s = cell->getPort(ID::S); get_bv(sig_b); get_bv(sig_s); for (int i = 0; i < GetSize(sig_s); i++) processed_expr = stringf("(ite %s %s %s)", get_bool(sig_s[i]).c_str(), get_bv(sig_b.extract(i*width, width)).c_str(), processed_expr.c_str()); if (verbose) log("%*s-> import cell: %s\n", 2+2*GetSize(recursive_cells), "", log_id(cell)); RTLIL::SigSpec sig = sigmap(cell->getPort(ID::Y)); decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n", get_id(module), idcounter, get_id(module), width, processed_expr.c_str(), log_signal(sig))); register_bv(sig, idcounter++); recursive_cells.erase(cell); return; } // FIXME: $slice $concat } if (memmode && cell->is_mem_cell()) { Mem *mem = mem_cells[cell]; if (memarrays.count(mem)) { recursive_cells.erase(cell); return; } int arrayid = idcounter++; memarrays[mem] = arrayid; int abits = max(1, ceil_log2(mem->size)); bool has_sync_wr = false; bool has_async_wr = false; for (auto &port : mem->wr_ports) { if (port.clk_enable) has_sync_wr = true; else has_async_wr = true; } if (has_async_wr && has_sync_wr) log_error("Memory %s.%s has mixed clocked/nonclocked write ports. This is not supported by \"write_smt2\".\n", log_id(cell), log_id(module)); decls.push_back(stringf("; yosys-smt2-memory %s %d %d %d %d %s\n", get_id(mem->memid), abits, mem->width, GetSize(mem->rd_ports), GetSize(mem->wr_ports), has_async_wr ? "async" : "sync")); decls.push_back(witness_memory(get_id(mem->memid), cell, mem)); string memstate; if (has_async_wr) { memstate = stringf("%s#%d#final", get_id(module), arrayid); } else { memstate = stringf("%s#%d#0", get_id(module), arrayid); } if (statebv) { makebits(memstate, mem->width*mem->size, get_id(mem->memid)); decls.push_back(stringf("(define-fun |%s_m %s| ((state |%s_s|)) (_ BitVec %d) (|%s| state))\n", get_id(module), get_id(mem->memid), get_id(module), mem->width*mem->size, memstate.c_str())); for (int i = 0; i < GetSize(mem->rd_ports); i++) { auto &port = mem->rd_ports[i]; SigSpec addr_sig = port.addr; addr_sig.extend_u0(abits); std::string addr = get_bv(addr_sig); if (port.clk_enable) log_error("Read port %d (%s) of memory %s.%s is clocked. This is not supported by \"write_smt2\"! " "Call \"memory\" with -nordff to avoid this error.\n", i, log_signal(port.data), log_id(mem->memid), log_id(module)); decls.push_back(stringf("(define-fun |%s_m:R%dA %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n", get_id(module), i, get_id(mem->memid), get_id(module), abits, addr.c_str(), log_signal(addr_sig))); std::string read_expr = "#b"; for (int k = 0; k < mem->width; k++) read_expr += "0"; for (int k = 0; k < mem->size; k++) read_expr = stringf("(ite (= (|%s_m:R%dA %s| state) #b%s) ((_ extract %d %d) (|%s| state))\n %s)", get_id(module), i, get_id(mem->memid), Const(k+mem->start_offset, abits).as_string().c_str(), mem->width*(k+1)-1, mem->width*k, memstate.c_str(), read_expr.c_str()); decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) (_ BitVec %d)\n %s) ; %s\n", get_id(module), idcounter, get_id(module), mem->width, read_expr.c_str(), log_signal(port.data))); decls.push_back(stringf("(define-fun |%s_m:R%dD %s| ((state |%s_s|)) (_ BitVec %d) (|%s#%d| state))\n", get_id(module), i, get_id(mem->memid), get_id(module), mem->width, get_id(module), idcounter)); register_bv(port.data, idcounter++); } } else { if (statedt) dtmembers.push_back(stringf(" (|%s| (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n", memstate.c_str(), abits, mem->width, get_id(mem->memid))); else decls.push_back(stringf("(declare-fun |%s| (|%s_s|) (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n", memstate.c_str(), get_id(module), abits, mem->width, get_id(mem->memid))); decls.push_back(stringf("(define-fun |%s_m %s| ((state |%s_s|)) (Array (_ BitVec %d) (_ BitVec %d)) (|%s| state))\n", get_id(module), get_id(mem->memid), get_id(module), abits, mem->width, memstate.c_str())); for (int i = 0; i < GetSize(mem->rd_ports); i++) { auto &port = mem->rd_ports[i]; SigSpec addr_sig = port.addr; addr_sig.extend_u0(abits); std::string addr = get_bv(addr_sig); if (port.clk_enable) log_error("Read port %d (%s) of memory %s.%s is clocked. This is not supported by \"write_smt2\"! " "Call \"memory\" with -nordff to avoid this error.\n", i, log_signal(port.data), log_id(mem->memid), log_id(module)); decls.push_back(stringf("(define-fun |%s_m:R%dA %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n", get_id(module), i, get_id(mem->memid), get_id(module), abits, addr.c_str(), log_signal(addr_sig))); decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) (_ BitVec %d) (select (|%s| state) (|%s_m:R%dA %s| state))) ; %s\n", get_id(module), idcounter, get_id(module), mem->width, memstate.c_str(), get_id(module), i, get_id(mem->memid), log_signal(port.data))); decls.push_back(stringf("(define-fun |%s_m:R%dD %s| ((state |%s_s|)) (_ BitVec %d) (|%s#%d| state))\n", get_id(module), i, get_id(mem->memid), get_id(module), mem->width, get_id(module), idcounter)); register_bv(port.data, idcounter++); } } memory_queue.insert(mem); recursive_cells.erase(cell); return; } Module *m = module->design->module(cell->type); if (m != nullptr) { decls.push_back(stringf("; yosys-smt2-cell %s %s\n", get_id(cell->type), get_id(cell->name))); decls.push_back(witness_cell(get_id(cell->name), cell)); string cell_state = stringf("(|%s_h %s| state)", get_id(module), get_id(cell->name)); for (auto &conn : cell->connections()) { if (GetSize(conn.second) == 0) continue; Wire *w = m->wire(conn.first); SigSpec sig = sigmap(conn.second); if (w->port_output && !w->port_input) { if (GetSize(w) > 1) { if (bvmode) { makebits(stringf("%s#%d", get_id(module), idcounter), GetSize(w), log_signal(sig)); register_bv(sig, idcounter++); } else { for (int i = 0; i < GetSize(w); i++) { makebits(stringf("%s#%d", get_id(module), idcounter), 0, log_signal(sig[i])); register_bool(sig[i], idcounter++); } } } else { makebits(stringf("%s#%d", get_id(module), idcounter), 0, log_signal(sig)); register_bool(sig, idcounter++); } } } if (statebv) makebits(stringf("%s_h %s", get_id(module), get_id(cell->name)), mod_stbv_width.at(cell->type)); else if (statedt) dtmembers.push_back(stringf(" (|%s_h %s| |%s_s|)\n", get_id(module), get_id(cell->name), get_id(cell->type))); else decls.push_back(stringf("(declare-fun |%s_h %s| (|%s_s|) |%s_s|)\n", get_id(module), get_id(cell->name), get_id(module), get_id(cell->type))); hiercells.insert(cell); hiercells_queue.insert(cell); recursive_cells.erase(cell); return; } if (cell->type.in(ID($dffe), ID($sdff), ID($sdffe), ID($sdffce)) || cell->type.str().substr(0, 6) == "$_SDFF" || (cell->type.str().substr(0, 6) == "$_DFFE" && cell->type.str().size() == 10)) { log_error("Unsupported cell type %s for cell %s.%s -- please run `dffunmap` before `write_smt2`.\n", log_id(cell->type), log_id(module), log_id(cell)); } if (cell->type.in(ID($adff), ID($adffe), ID($aldff), ID($aldffe), ID($dffsr), ID($dffsre)) || cell->type.str().substr(0, 5) == "$_DFF" || cell->type.str().substr(0, 7) == "$_ALDFF") { log_error("Unsupported cell type %s for cell %s.%s -- please run `async2sync; dffunmap` or `clk2fflogic` before `write_smt2`.\n", log_id(cell->type), log_id(module), log_id(cell)); } if (cell->type.in(ID($sr), ID($dlatch), ID($adlatch), ID($dlatchsr)) || cell->type.str().substr(0, 8) == "$_DLATCH" || cell->type.str().substr(0, 5) == "$_SR_") { log_error("Unsupported cell type %s for cell %s.%s -- please run `clk2fflogic` before `write_smt2`.\n", log_id(cell->type), log_id(module), log_id(cell)); } log_error("Unsupported cell type %s for cell %s.%s.\n", log_id(cell->type), log_id(module), log_id(cell)); } void verify_smtlib2_module() { if (!module->get_blackbox_attribute()) log_error("Module %s with smtlib2_module attribute must also have blackbox attribute.\n", log_id(module)); if (module->cells().size() > 0) log_error("Module %s with smtlib2_module attribute must not have any cells inside it.\n", log_id(module)); for (auto wire : module->wires()) if (!wire->port_id) log_error("Wire %s.%s must be input or output since module has smtlib2_module attribute.\n", log_id(module), log_id(wire)); } void run() { if (verbose) log("=> export logic driving outputs\n"); if (is_smtlib2_module) verify_smtlib2_module(); pool reg_bits; for (auto cell : module->cells()) if (cell->type.in(ID($ff), ID($dff), ID($_FF_), ID($_DFF_P_), ID($_DFF_N_), ID($anyinit))) { // not using sigmap -- we want the net directly at the dff output for (auto bit : cell->getPort(ID::Q)) reg_bits.insert(bit); } std::string smtlib2_inputs; std::vector smtlib2_decls; if (is_smtlib2_module) { for (auto wire : module->wires()) { if (!wire->port_input) continue; smtlib2_inputs += stringf("(|%s| (|%s_n %s| state))\n", get_id(wire), get_id(module), get_id(wire)); } } for (auto wire : module->wires()) { bool is_register = false; bool contains_clock = false; for (auto bit : SigSpec(wire)) { if (reg_bits.count(bit)) is_register = true; auto sig_bit = sigmap(bit); if (clock_posedge.count(sig_bit) || clock_negedge.count(sig_bit)) contains_clock = true; } bool is_smtlib2_comb_expr = wire->has_attribute(ID::smtlib2_comb_expr); if (is_smtlib2_comb_expr && !is_smtlib2_module) log_error("smtlib2_comb_expr is only valid in a module with the smtlib2_module attribute: wire %s.%s", log_id(module), log_id(wire)); if (wire->port_id || is_register || contains_clock || wire->get_bool_attribute(ID::keep) || (wiresmode && wire->name.isPublic())) { RTLIL::SigSpec sig = sigmap(wire); std::vector comments; if (wire->port_input) comments.push_back(stringf("; yosys-smt2-input %s %d\n", get_id(wire), wire->width)); if (wire->port_output) comments.push_back(stringf("; yosys-smt2-output %s %d\n", get_id(wire), wire->width)); if (is_register) comments.push_back(stringf("; yosys-smt2-register %s %d\n", get_id(wire), wire->width)); if (wire->get_bool_attribute(ID::keep) || (wiresmode && wire->name.isPublic())) comments.push_back(stringf("; yosys-smt2-wire %s %d\n", get_id(wire), wire->width)); if (contains_clock && GetSize(wire) == 1 && (clock_posedge.count(sig) || clock_negedge.count(sig))) comments.push_back(stringf("; yosys-smt2-clock %s%s%s\n", get_id(wire), clock_posedge.count(sig) ? " posedge" : "", clock_negedge.count(sig) ? " negedge" : "")); if (wire->port_input && contains_clock) { for (int i = 0; i < GetSize(sig); i++) { bool is_posedge = clock_posedge.count(sig[i]); bool is_negedge = clock_negedge.count(sig[i]); if (is_posedge != is_negedge) comments.push_back(witness_signal( is_posedge ? "posedge" : "negedge", 1, i, get_id(wire), -1, wire)); } } if (wire->port_input) comments.push_back(witness_signal("input", wire->width, 0, get_id(wire), -1, wire)); std::string smtlib2_comb_expr; if (is_smtlib2_comb_expr) { smtlib2_comb_expr = "(let (\n" + smtlib2_inputs + ")\n" + wire->get_string_attribute(ID::smtlib2_comb_expr) + "\n)"; if (wire->port_input || !wire->port_output) log_error("smtlib2_comb_expr is only valid on output: wire %s.%s", log_id(module), log_id(wire)); if (!bvmode && GetSize(sig) > 1) log_error("smtlib2_comb_expr is unsupported on multi-bit wires when -nobv is specified: wire %s.%s", log_id(module), log_id(wire)); comments.push_back(witness_signal("blackbox", wire->width, 0, get_id(wire), -1, wire)); } auto &out_decls = is_smtlib2_comb_expr ? smtlib2_decls : decls; if (bvmode && GetSize(sig) > 1) { std::string sig_bv = is_smtlib2_comb_expr ? smtlib2_comb_expr : get_bv(sig); if (!comments.empty()) out_decls.insert(out_decls.end(), comments.begin(), comments.end()); out_decls.push_back(stringf("(define-fun |%s_n %s| ((state |%s_s|)) (_ BitVec %d) %s)\n", get_id(module), get_id(wire), get_id(module), GetSize(sig), sig_bv.c_str())); if (wire->port_input) ex_input_eq.push_back(stringf(" (= (|%s_n %s| state) (|%s_n %s| other_state))", get_id(module), get_id(wire), get_id(module), get_id(wire))); } else { std::vector sig_bool; for (int i = 0; i < GetSize(sig); i++) { sig_bool.push_back(is_smtlib2_comb_expr ? smtlib2_comb_expr : get_bool(sig[i])); } if (!comments.empty()) out_decls.insert(out_decls.end(), comments.begin(), comments.end()); for (int i = 0; i < GetSize(sig); i++) { if (GetSize(sig) > 1) { out_decls.push_back(stringf("(define-fun |%s_n %s %d| ((state |%s_s|)) Bool %s)\n", get_id(module), get_id(wire), i, get_id(module), sig_bool[i].c_str())); if (wire->port_input) ex_input_eq.push_back(stringf(" (= (|%s_n %s %d| state) (|%s_n %s %d| other_state))", get_id(module), get_id(wire), i, get_id(module), get_id(wire), i)); } else { out_decls.push_back(stringf("(define-fun |%s_n %s| ((state |%s_s|)) Bool %s)\n", get_id(module), get_id(wire), get_id(module), sig_bool[i].c_str())); if (wire->port_input) ex_input_eq.push_back(stringf(" (= (|%s_n %s| state) (|%s_n %s| other_state))", get_id(module), get_id(wire), get_id(module), get_id(wire))); } } } } } decls.insert(decls.end(), smtlib2_decls.begin(), smtlib2_decls.end()); if (verbose) log("=> export logic associated with the initial state\n"); vector init_list; for (auto wire : module->wires()) if (wire->attributes.count(ID::init)) { if (is_smtlib2_module) log_error("init attribute not allowed on wires in module with smtlib2_module attribute: wire %s.%s", log_id(module), log_id(wire)); RTLIL::SigSpec sig = sigmap(wire); Const val = wire->attributes.at(ID::init); 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; use_mask = true; } if (use_mask) init_list.push_back(stringf("(= (bvand %s #b%s) #b%s) ; %s", get_bv(sig).c_str(), mask.as_string().c_str(), val.as_string().c_str(), get_id(wire))); else init_list.push_back(stringf("(= %s #b%s) ; %s", get_bv(sig).c_str(), val.as_string().c_str(), get_id(wire))); } else { for (int i = 0; i < GetSize(sig); i++) if (val[i] == State::S0 || val[i] == State::S1) init_list.push_back(stringf("(= %s %s) ; %s", get_bool(sig[i]).c_str(), val[i] == State::S1 ? "true" : "false", get_id(wire))); } } if (verbose) log("=> export logic driving asserts\n"); int assert_id = 0, assume_id = 0, cover_id = 0; vector assert_list, assume_list, cover_list; for (auto cell : module->cells()) { if (cell->type.in(ID($assert), ID($assume), ID($cover))) { int &id = cell->type == ID($assert) ? assert_id : cell->type == ID($assume) ? assume_id : cell->type == ID($cover) ? cover_id : *(int*)nullptr; char postfix = cell->type == ID($assert) ? 'a' : cell->type == ID($assume) ? 'u' : cell->type == ID($cover) ? 'c' : 0; string name_a = get_bool(cell->getPort(ID::A)); string name_en = get_bool(cell->getPort(ID::EN)); if (cell->name[0] == '$' && cell->attributes.count(ID::src)) decls.push_back(stringf("; yosys-smt2-%s %d %s %s\n", cell->type.c_str() + 1, id, get_id(cell), cell->attributes.at(ID::src).decode_string().c_str())); else decls.push_back(stringf("; yosys-smt2-%s %d %s\n", cell->type.c_str() + 1, id, get_id(cell))); if (cell->type == ID($cover)) decls.push_back(stringf("(define-fun |%s_%c %d| ((state |%s_s|)) Bool (and %s %s)) ; %s\n", get_id(module), postfix, id, get_id(module), name_a.c_str(), name_en.c_str(), get_id(cell))); else decls.push_back(stringf("(define-fun |%s_%c %d| ((state |%s_s|)) Bool (or %s (not %s))) ; %s\n", get_id(module), postfix, id, get_id(module), name_a.c_str(), name_en.c_str(), get_id(cell))); if (cell->type == ID($assert)) assert_list.push_back(stringf("(|%s_a %d| state)", get_id(module), id)); else if (cell->type == ID($assume)) assume_list.push_back(stringf("(|%s_u %d| state)", get_id(module), id)); id++; } } if (verbose) log("=> export logic driving hierarchical cells\n"); for (auto cell : module->cells()) if (module->design->module(cell->type) != nullptr) export_cell(cell); while (!hiercells_queue.empty()) { std::set queue; queue.swap(hiercells_queue); for (auto cell : queue) { string cell_state = stringf("(|%s_h %s| state)", get_id(module), get_id(cell->name)); Module *m = module->design->module(cell->type); log_assert(m != nullptr); hier.push_back(stringf(" (= (|%s_is| state) (|%s_is| %s))\n", get_id(module), get_id(cell->type), cell_state.c_str())); for (auto &conn : cell->connections()) { if (GetSize(conn.second) == 0) continue; Wire *w = m->wire(conn.first); SigSpec sig = sigmap(conn.second); if (bvmode || GetSize(w) == 1) { hier.push_back(stringf(" (= %s (|%s_n %s| %s)) ; %s.%s\n", (GetSize(w) > 1 ? get_bv(sig) : get_bool(sig)).c_str(), get_id(cell->type), get_id(w), cell_state.c_str(), get_id(cell->type), get_id(w))); } else { for (int i = 0; i < GetSize(w); i++) hier.push_back(stringf(" (= %s (|%s_n %s %d| %s)) ; %s.%s[%d]\n", get_bool(sig[i]).c_str(), get_id(cell->type), get_id(w), i, cell_state.c_str(), get_id(cell->type), get_id(w), i)); } } } } for (int iter = 1; !registers.empty() || !memory_queue.empty(); iter++) { pool this_regs; this_regs.swap(registers); if (verbose) log("=> export logic driving registers [iteration %d]\n", iter); for (auto cell : this_regs) { if (cell->type.in(ID($_FF_), ID($_DFF_P_), ID($_DFF_N_))) { std::string expr_d = get_bool(cell->getPort(ID::D)); std::string expr_q = get_bool(cell->getPort(ID::Q), "next_state"); trans.push_back(stringf(" (= %s %s) ; %s %s\n", expr_d.c_str(), expr_q.c_str(), get_id(cell), log_signal(cell->getPort(ID::Q)))); ex_state_eq.push_back(stringf("(= %s %s)", get_bool(cell->getPort(ID::Q)).c_str(), get_bool(cell->getPort(ID::Q), "other_state").c_str())); } if (cell->type.in(ID($ff), ID($dff), ID($anyinit))) { std::string expr_d = get_bv(cell->getPort(ID::D)); std::string expr_q = get_bv(cell->getPort(ID::Q), "next_state"); trans.push_back(stringf(" (= %s %s) ; %s %s\n", expr_d.c_str(), expr_q.c_str(), get_id(cell), log_signal(cell->getPort(ID::Q)))); ex_state_eq.push_back(stringf("(= %s %s)", get_bv(cell->getPort(ID::Q)).c_str(), get_bv(cell->getPort(ID::Q), "other_state").c_str())); } if (cell->type.in(ID($anyconst), ID($allconst))) { std::string expr_d = get_bv(cell->getPort(ID::Y)); std::string expr_q = get_bv(cell->getPort(ID::Y), "next_state"); trans.push_back(stringf(" (= %s %s) ; %s %s\n", expr_d.c_str(), expr_q.c_str(), get_id(cell), log_signal(cell->getPort(ID::Y)))); if (cell->type == ID($anyconst)) ex_state_eq.push_back(stringf("(= %s %s)", get_bv(cell->getPort(ID::Y)).c_str(), get_bv(cell->getPort(ID::Y), "other_state").c_str())); } } std::set this_mems; this_mems.swap(memory_queue); for (auto mem : this_mems) { int arrayid = memarrays.at(mem); int abits = max(1, ceil_log2(mem->size)); bool has_sync_wr = false; bool has_async_wr = false; for (auto &port : mem->wr_ports) { if (port.clk_enable) has_sync_wr = true; else has_async_wr = true; } string initial_memstate, final_memstate; if (has_async_wr) { log_assert(!has_sync_wr); initial_memstate = stringf("%s#%d#0", get_id(module), arrayid); final_memstate = stringf("%s#%d#final", get_id(module), arrayid); } if (statebv) { if (has_async_wr) { makebits(final_memstate, mem->width*mem->size, get_id(mem->memid)); } for (int i = 0; i < GetSize(mem->wr_ports); i++) { auto &port = mem->wr_ports[i]; SigSpec addr_sig = port.addr; addr_sig.extend_u0(abits); std::string addr = get_bv(addr_sig); std::string data = get_bv(port.data); std::string mask = get_bv(port.en); decls.push_back(stringf("(define-fun |%s_m:W%dA %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n", get_id(module), i, get_id(mem->memid), get_id(module), abits, addr.c_str(), log_signal(addr_sig))); addr = stringf("(|%s_m:W%dA %s| state)", get_id(module), i, get_id(mem->memid)); decls.push_back(stringf("(define-fun |%s_m:W%dD %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n", get_id(module), i, get_id(mem->memid), get_id(module), mem->width, data.c_str(), log_signal(port.data))); data = stringf("(|%s_m:W%dD %s| state)", get_id(module), i, get_id(mem->memid)); decls.push_back(stringf("(define-fun |%s_m:W%dM %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n", get_id(module), i, get_id(mem->memid), get_id(module), mem->width, mask.c_str(), log_signal(port.en))); mask = stringf("(|%s_m:W%dM %s| state)", get_id(module), i, get_id(mem->memid)); std::string data_expr; for (int k = mem->size-1; k >= 0; k--) { std::string new_data = stringf("(bvor (bvand %s %s) (bvand ((_ extract %d %d) (|%s#%d#%d| state)) (bvnot %s)))", data.c_str(), mask.c_str(), mem->width*(k+1)-1, mem->width*k, get_id(module), arrayid, i, mask.c_str()); data_expr += stringf("\n (ite (= %s #b%s) %s ((_ extract %d %d) (|%s#%d#%d| state)))", addr.c_str(), Const(k+mem->start_offset, abits).as_string().c_str(), new_data.c_str(), mem->width*(k+1)-1, mem->width*k, get_id(module), arrayid, i); } decls.push_back(stringf("(define-fun |%s#%d#%d| ((state |%s_s|)) (_ BitVec %d) (concat%s)) ; %s\n", get_id(module), arrayid, i+1, get_id(module), mem->width*mem->size, data_expr.c_str(), get_id(mem->memid))); } } else { if (has_async_wr) { if (statedt) dtmembers.push_back(stringf(" (|%s| (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n", initial_memstate.c_str(), abits, mem->width, get_id(mem->memid))); else decls.push_back(stringf("(declare-fun |%s| (|%s_s|) (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n", initial_memstate.c_str(), get_id(module), abits, mem->width, get_id(mem->memid))); } for (int i = 0; i < GetSize(mem->wr_ports); i++) { auto &port = mem->wr_ports[i]; SigSpec addr_sig = port.addr; addr_sig.extend_u0(abits); std::string addr = get_bv(addr_sig); std::string data = get_bv(port.data); std::string mask = get_bv(port.en); decls.push_back(stringf("(define-fun |%s_m:W%dA %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n", get_id(module), i, get_id(mem->memid), get_id(module), abits, addr.c_str(), log_signal(addr_sig))); addr = stringf("(|%s_m:W%dA %s| state)", get_id(module), i, get_id(mem->memid)); decls.push_back(stringf("(define-fun |%s_m:W%dD %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n", get_id(module), i, get_id(mem->memid), get_id(module), mem->width, data.c_str(), log_signal(port.data))); data = stringf("(|%s_m:W%dD %s| state)", get_id(module), i, get_id(mem->memid)); decls.push_back(stringf("(define-fun |%s_m:W%dM %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n", get_id(module), i, get_id(mem->memid), get_id(module), mem->width, mask.c_str(), log_signal(port.en))); mask = stringf("(|%s_m:W%dM %s| state)", get_id(module), i, get_id(mem->memid)); data = stringf("(bvor (bvand %s %s) (bvand (select (|%s#%d#%d| state) %s) (bvnot %s)))", data.c_str(), mask.c_str(), get_id(module), arrayid, i, addr.c_str(), mask.c_str()); string empty_mask(mem->width, '0'); decls.push_back(stringf("(define-fun |%s#%d#%d| ((state |%s_s|)) (Array (_ BitVec %d) (_ BitVec %d)) " "(ite (= %s #b%s) (|%s#%d#%d| state) (store (|%s#%d#%d| state) %s %s))) ; %s\n", get_id(module), arrayid, i+1, get_id(module), abits, mem->width, mask.c_str(), empty_mask.c_str(), get_id(module), arrayid, i, get_id(module), arrayid, i, addr.c_str(), data.c_str(), get_id(mem->memid))); } } std::string expr_d = stringf("(|%s#%d#%d| state)", get_id(module), arrayid, GetSize(mem->wr_ports)); std::string expr_q = stringf("(|%s#%d#0| next_state)", get_id(module), arrayid); trans.push_back(stringf(" (= %s %s) ; %s\n", expr_d.c_str(), expr_q.c_str(), get_id(mem->memid))); ex_state_eq.push_back(stringf("(= (|%s#%d#0| state) (|%s#%d#0| other_state))", get_id(module), arrayid, get_id(module), arrayid)); if (has_async_wr) hier.push_back(stringf(" (= %s (|%s| state)) ; %s\n", expr_d.c_str(), final_memstate.c_str(), get_id(mem->memid))); Const init_data = mem->get_init_data(); for (int i = 0; i < mem->size; i++) { if (i*mem->width >= GetSize(init_data)) break; Const initword = init_data.extract(i*mem->width, mem->width, State::Sx); Const initmask = initword; bool gen_init_constr = false; 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; } else { initmask[k] = State::S0; initword[k] = State::S0; } } if (gen_init_constr) { if (statebv) /* FIXME */; else init_list.push_back(stringf("(= (bvand (select (|%s#%d#0| state) #b%s) #b%s) #b%s) ; %s[%d]", get_id(module), arrayid, Const(i, abits).as_string().c_str(), initmask.as_string().c_str(), initword.as_string().c_str(), get_id(mem->memid), i)); } } } } if (verbose) log("=> finalizing SMT2 representation of %s.\n", log_id(module)); for (auto c : hiercells) { assert_list.push_back(stringf("(|%s_a| (|%s_h %s| state))", get_id(c->type), get_id(module), get_id(c->name))); assume_list.push_back(stringf("(|%s_u| (|%s_h %s| state))", get_id(c->type), get_id(module), get_id(c->name))); init_list.push_back(stringf("(|%s_i| (|%s_h %s| state))", get_id(c->type), get_id(module), get_id(c->name))); hier.push_back(stringf(" (|%s_h| (|%s_h %s| state))\n", get_id(c->type), get_id(module), get_id(c->name))); trans.push_back(stringf(" (|%s_t| (|%s_h %s| state) (|%s_h %s| next_state))\n", get_id(c->type), get_id(module), get_id(c->name), get_id(module), get_id(c->name))); ex_state_eq.push_back(stringf("(|%s_ex_state_eq| (|%s_h %s| state) (|%s_h %s| other_state))\n", get_id(c->type), get_id(module), get_id(c->name), get_id(module), get_id(c->name))); } if (forallmode) { string expr = ex_state_eq.empty() ? "true" : "(and"; if (!ex_state_eq.empty()) { if (GetSize(ex_state_eq) == 1) { expr = "\n " + ex_state_eq.front() + "\n"; } else { for (auto &str : ex_state_eq) expr += stringf("\n %s", str.c_str()); expr += "\n)"; } } decls.push_back(stringf("(define-fun |%s_ex_state_eq| ((state |%s_s|) (other_state |%s_s|)) Bool %s)\n", get_id(module), get_id(module), get_id(module), expr.c_str())); expr = ex_input_eq.empty() ? "true" : "(and"; if (!ex_input_eq.empty()) { if (GetSize(ex_input_eq) == 1) { expr = "\n " + ex_input_eq.front() + "\n"; } else { for (auto &str : ex_input_eq) expr += stringf("\n %s", str.c_str()); expr += "\n)"; } } decls.push_back(stringf("(define-fun |%s_ex_input_eq| ((state |%s_s|) (other_state |%s_s|)) Bool %s)\n", get_id(module), get_id(module), get_id(module), expr.c_str())); } string assert_expr = assert_list.empty() ? "true" : "(and"; if (!assert_list.empty()) { if (GetSize(assert_list) == 1) { assert_expr = "\n " + assert_list.front() + "\n"; } else { for (auto &str : assert_list) assert_expr += stringf("\n %s", str.c_str()); assert_expr += "\n)"; } } decls.push_back(stringf("(define-fun |%s_a| ((state |%s_s|)) Bool %s)\n", get_id(module), get_id(module), assert_expr.c_str())); string assume_expr = assume_list.empty() ? "true" : "(and"; if (!assume_list.empty()) { if (GetSize(assume_list) == 1) { assume_expr = "\n " + assume_list.front() + "\n"; } else { for (auto &str : assume_list) assume_expr += stringf("\n %s", str.c_str()); assume_expr += "\n)"; } } decls.push_back(stringf("(define-fun |%s_u| ((state |%s_s|)) Bool %s)\n", get_id(module), get_id(module), assume_expr.c_str())); string init_expr = init_list.empty() ? "true" : "(and"; if (!init_list.empty()) { if (GetSize(init_list) == 1) { init_expr = "\n " + init_list.front() + "\n"; } else { for (auto &str : init_list) init_expr += stringf("\n %s", str.c_str()); init_expr += "\n)"; } } decls.push_back(stringf("(define-fun |%s_i| ((state |%s_s|)) Bool %s)\n", get_id(module), get_id(module), init_expr.c_str())); } void write(std::ostream &f) { f << stringf("; yosys-smt2-module %s\n", get_id(module)); if (statebv) { f << stringf("(define-sort |%s_s| () (_ BitVec %d))\n", get_id(module), statebv_width); mod_stbv_width[module->name] = statebv_width; } else if (statedt) { f << stringf("(declare-datatype |%s_s| ((|%s_mk|\n", get_id(module), get_id(module)); for (auto it : dtmembers) f << it; f << stringf(")))\n"); } else f << stringf("(declare-sort |%s_s| 0)\n", get_id(module)); for (auto it : decls) f << it; f << stringf("(define-fun |%s_h| ((state |%s_s|)) Bool ", get_id(module), get_id(module)); if (GetSize(hier) > 1) { f << "(and\n"; for (auto it : hier) f << it; f << "))\n"; } else if (GetSize(hier) == 1) f << "\n" + hier.front() + ")\n"; else f << "true)\n"; f << stringf("(define-fun |%s_t| ((state |%s_s|) (next_state |%s_s|)) Bool ", get_id(module), get_id(module), get_id(module)); if (GetSize(trans) > 1) { f << "(and\n"; for (auto it : trans) f << it; f << "))"; } else if (GetSize(trans) == 1) f << "\n" + trans.front() + ")"; else f << "true)"; f << stringf(" ; end of module %s\n", get_id(module)); } template static std::vector witness_path(T *obj) { std::vector path; if (obj->name.isPublic()) { auto hdlname = obj->get_string_attribute(ID::hdlname); for (auto token : split_tokens(hdlname)) path.push_back("\\" + token); } if (path.empty()) path.push_back(obj->name.str()); return path; } std::string witness_signal(const char *type, int width, int offset, const std::string &smtname, int smtid, RTLIL::Wire *wire, int smtoffset = 0) { std::vector hiername; const char *wire_name = wire->name.c_str(); if (wire_name[0] == '\\') { auto hdlname = wire->get_string_attribute(ID::hdlname); for (auto token : split_tokens(hdlname)) hiername.push_back("\\" + token); } if (hiername.empty()) hiername.push_back(wire->name.str()); std::string line = "; yosys-smt2-witness "; (json11::Json { json11::Json::object { { "type", type }, { "offset", offset }, { "width", width }, { "smtname", smtname.empty() ? json11::Json(smtid) : json11::Json(smtname) }, { "smtoffset", smtoffset }, { "path", witness_path(wire) }, }}).dump(line); line += "\n"; return line; } std::string witness_cell(const char *smtname, RTLIL::Cell *cell) { std::string line = "; yosys-smt2-witness "; (json11::Json {json11::Json::object { { "type", "cell" }, { "smtname", smtname }, { "path", witness_path(cell) }, }}).dump(line); line += "\n"; return line; } std::string witness_memory(const char *smtname, RTLIL::Cell *cell, Mem *mem) { json11::Json::array uninitialized; auto init_data = mem->get_init_data(); int cursor = 0; while (cursor < init_data.size()) { while (cursor < init_data.size() && init_data[cursor] != State::Sx) cursor++; int offset = cursor; while (cursor < init_data.size() && init_data[cursor] == State::Sx) cursor++; int width = cursor - offset; if (width) uninitialized.push_back(json11::Json::object { {"width", width}, {"offset", offset}, }); } std::string line = "; yosys-smt2-witness "; (json11::Json { json11::Json::object { { "type", "mem" }, { "width", mem->width }, { "size", mem->size }, { "rom", mem->wr_ports.empty() }, { "statebv", statebv }, { "smtname", smtname }, { "uninitialized", uninitialized }, { "path", witness_path(cell) }, }}).dump(line); line += "\n"; return line; } }; struct Smt2Backend : public Backend { Smt2Backend() : Backend("smt2", "write design to SMT-LIBv2 file") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" write_smt2 [options] [filename]\n"); log("\n"); log("Write a SMT-LIBv2 [1] description of the current design. For a module with name\n"); log("'' this will declare the sort '_s' (state of the module) and will\n"); log("define and declare functions operating on that state.\n"); log("\n"); log("The following SMT2 functions are generated for a module with name ''.\n"); log("Some declarations/definitions are printed with a special comment. A prover\n"); log("using the SMT2 files can use those comments to collect all relevant metadata\n"); log("about the design.\n"); log("\n"); log(" ; yosys-smt2-module \n"); log(" (declare-sort |_s| 0)\n"); log(" The sort representing a state of module .\n"); log("\n"); log(" (define-fun |_h| ((state |_s|)) Bool (...))\n"); log(" This function must be asserted for each state to establish the\n"); log(" design hierarchy.\n"); log("\n"); log(" ; yosys-smt2-input \n"); log(" ; yosys-smt2-output \n"); log(" ; yosys-smt2-register \n"); log(" ; yosys-smt2-wire \n"); log(" (define-fun |_n | (|_s|) (_ BitVec ))\n"); log(" (define-fun |_n | (|_s|) Bool)\n"); log(" For each port, register, and wire with the 'keep' attribute set an\n"); log(" accessor function is generated. Single-bit wires are returned as Bool,\n"); log(" multi-bit wires as BitVec.\n"); log("\n"); log(" ; yosys-smt2-cell \n"); log(" (declare-fun |_h | (|_s|) |_s|)\n"); log(" There is a function like that for each hierarchical instance. It\n"); log(" returns the sort that represents the state of the sub-module that\n"); log(" implements the instance.\n"); log("\n"); log(" (declare-fun |_is| (|_s|) Bool)\n"); log(" This function must be asserted 'true' for initial states, and 'false'\n"); log(" otherwise.\n"); log("\n"); log(" (define-fun |_i| ((state |_s|)) Bool (...))\n"); log(" This function must be asserted 'true' for initial states. For\n"); log(" non-initial states it must be left unconstrained.\n"); log("\n"); log(" (define-fun |_t| ((state |_s|) (next_state |_s|)) Bool (...))\n"); log(" This function evaluates to 'true' if the states 'state' and\n"); log(" 'next_state' form a valid state transition.\n"); log("\n"); log(" (define-fun |_a| ((state |_s|)) Bool (...))\n"); log(" This function evaluates to 'true' if all assertions hold in the state.\n"); log("\n"); log(" (define-fun |_u| ((state |_s|)) Bool (...))\n"); log(" This function evaluates to 'true' if all assumptions hold in the state.\n"); log("\n"); log(" ; yosys-smt2-assert \n"); log(" (define-fun |_a | ((state |_s|)) Bool (...))\n"); log(" Each $assert cell is converted into one of this functions. The function\n"); log(" evaluates to 'true' if the assert statement holds in the state.\n"); log("\n"); log(" ; yosys-smt2-assume \n"); log(" (define-fun |_u | ((state |_s|)) Bool (...))\n"); log(" Each $assume cell is converted into one of this functions. The function\n"); log(" evaluates to 'true' if the assume statement holds in the state.\n"); log("\n"); log(" ; yosys-smt2-cover \n"); log(" (define-fun |_c | ((state |_s|)) Bool (...))\n"); log(" Each $cover cell is converted into one of this functions. The function\n"); log(" evaluates to 'true' if the cover statement is activated in the state.\n"); log("\n"); log("Options:\n"); log("\n"); log(" -verbose\n"); log(" this will print the recursive walk used to export the modules.\n"); log("\n"); log(" -stbv\n"); log(" Use a BitVec sort to represent a state instead of an uninterpreted\n"); log(" sort. As a side-effect this will prevent use of arrays to model\n"); log(" memories.\n"); log("\n"); log(" -stdt\n"); log(" Use SMT-LIB 2.6 style datatypes to represent a state instead of an\n"); log(" uninterpreted sort.\n"); log("\n"); log(" -nobv\n"); log(" disable support for BitVec (FixedSizeBitVectors theory). without this\n"); log(" option multi-bit wires are represented using the BitVec sort and\n"); log(" support for coarse grain cells (incl. arithmetic) is enabled.\n"); log("\n"); log(" -nomem\n"); log(" disable support for memories (via ArraysEx theory). this option is\n"); log(" implied by -nobv. only $mem cells without merged registers in\n"); log(" read ports are supported. call \"memory\" with -nordff to make sure\n"); log(" that no registers are merged into $mem read ports. '_m' functions\n"); log(" will be generated for accessing the arrays that are used to represent\n"); log(" memories.\n"); log("\n"); log(" -wires\n"); log(" create '_n' functions for all public wires. by default only ports,\n"); log(" registers, and wires with the 'keep' attribute are exported.\n"); log("\n"); log(" -tpl \n"); log(" use the given template file. the line containing only the token '%%%%'\n"); log(" is replaced with the regular output of this command.\n"); log("\n"); log(" -solver-option