├── .gitattributes ├── .github ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── ci.yml │ ├── codeql.yml │ └── get_markdown.py ├── .gitignore ├── .gittag ├── .readthedocs.yaml ├── COPYING ├── Makefile ├── README.md ├── docs ├── .gitignore ├── Makefile ├── examples │ └── quickstart │ │ └── README.md ├── source │ ├── conf.diff │ ├── conf.py │ ├── config.rst │ ├── index.rst │ ├── install.rst │ ├── license.rst │ ├── outdir.rst │ ├── quickstart.rst │ ├── requirements.txt │ ├── strategies.rst │ └── xprop.rst └── static │ ├── custom.css │ ├── favico.png │ ├── logo.png │ └── yosyshq.css ├── examples ├── nerv │ ├── .gitignore │ ├── Makefile │ ├── README.md │ ├── nerv.sv │ ├── nerv_change.sv │ ├── nerv_change_fail.eqy │ ├── nerv_change_pass.eqy │ ├── nerv_synth.eqy │ ├── nerv_synth.v │ └── nerv_synth.ys ├── picorv32 │ ├── .gitignore │ ├── Makefile │ ├── README.md │ ├── firmware.hex │ ├── firmware.patch │ ├── init.v │ ├── picorv32.v │ ├── picorv32_modified.eqy │ ├── picorv32_modified.v │ ├── picorv32_vivado.eqy │ ├── picorv32_vivado.log │ ├── picorv32_vivado.tcl │ ├── picorv32_vivado.v │ ├── testbench.v │ ├── tryamend.sh │ └── vcdmatch.py ├── risc16f84 │ ├── .gitignore │ ├── Makefile │ ├── PIC16F84-T300.pdf │ ├── README.md │ ├── risc16f84-free.v │ ├── risc16f84-in.v │ ├── risc16f84-th-free.v │ ├── risc16f84-th-in.v │ └── risc16f84.eqy ├── simple │ ├── .gitignore │ ├── Makefile │ ├── aliases.eqy │ ├── aliases.sv │ ├── combine.eqy │ ├── combine.sv │ ├── counter.eqy │ ├── counter.sv │ ├── ex_amend.eqy │ ├── ex_amend.sv │ ├── ex_bind.eqy │ ├── ex_bind.sv │ ├── ex_group.eqy │ ├── ex_group.sv │ ├── ex_join.eqy │ ├── ex_join.sv │ ├── fsm.eqy │ ├── fsm.sv │ ├── hierarchy.eqy │ ├── hierarchy.sv │ ├── submodules.eqy │ └── submodules.sv └── spm │ ├── .gitignore │ ├── Makefile │ ├── README.md │ ├── formal_pdk_proc.py │ ├── primitives.v │ ├── sky130_fd_sc_hd.v │ ├── spm.eqy │ ├── spm.nl.v │ └── spm.v ├── extern └── launcher.c ├── llvm-gcov.sh ├── requirements.txt ├── src ├── .gitignore ├── eqy.py ├── eqy_combine.cc ├── eqy_job.py ├── eqy_partition.cc └── eqy_recode.cc └── tests ├── plugin ├── .gitignore ├── combine_extra_argument.ys ├── combine_gate_create_error.ys ├── combine_gate_missing.ys ├── combine_gold_create_error.ys ├── combine_gold_missing.ys ├── combine_top_gate_missing.ys ├── combine_top_gold_missing.ys ├── combine_top_not_match.ys ├── data │ ├── counter_combined.il │ ├── counter_matched.ids │ ├── counter_partition.ids │ ├── empty.ids │ ├── gate_module_error.ids │ ├── gate_value_width_error.ids │ ├── gate_wire_missing.ids │ ├── gold_module_error.ids │ ├── gold_value_width_error.ids │ ├── gold_wire_missing.ids │ ├── malformed.ids │ ├── matched_empty.ids │ ├── matched_malformed.ids │ ├── matched_ok.ids │ └── partition_malformed.ids ├── help.ys ├── partition_additional_module.ys ├── partition_extra_argument.ys ├── partition_matched_malformed.ys ├── partition_matched_not_found.ys ├── partition_matching_gate_not_found.ys ├── partition_mismatched.ys ├── partition_no_matched.ys ├── partition_partition_malformed.ys ├── partition_partition_not_found.ys ├── partition_required_params.ys ├── partition_write_fragments.ys ├── recode_empty_list.ys ├── recode_extra_argument.ys ├── recode_filename_bad.ys ├── recode_filename_malformed.ys ├── recode_filename_missing.ys ├── recode_gate_module_error.ys ├── recode_gate_value_width_error.ys ├── recode_gate_wire_missing.ys ├── recode_gold_missing.ys ├── recode_gold_module_error.ys ├── recode_gold_value_width_error.ys ├── recode_gold_wire_missing.ys ├── recode_top_gate_missing.ys ├── recode_top_gold_missing.ys ├── recode_top_not_match.ys └── run-test.sh └── python ├── .gitignore ├── Makefile ├── counter.eqy ├── counter.sv ├── counter_cells.eqy ├── dupl_section.eqy ├── equivalence_problem.eqy ├── error_verilog.eqy ├── error_verilog.sv ├── expected_opt_bool_on_off.eqy ├── expected_opt_bool_val.eqy ├── expected_opt_int_integer.eqy ├── expected_opt_int_val.eqy ├── expected_opt_str_val.eqy ├── failed_partition.eqy ├── missing_gate_section.eqy ├── missing_gold_section.eqy ├── no_use_line.eqy ├── picorv32_vivado.eqy ├── problem_occured.eqy ├── read_source_fail.eqy ├── repeated_option_bool.eqy ├── repeated_option_int.eqy ├── repeated_option_str.eqy ├── repeated_option_use.eqy ├── splitnets.eqy ├── syntax_error.eqy ├── syntax_error_collect.eqy ├── syntax_error_options.eqy ├── syntax_error_partition.eqy ├── timeout.eqy ├── unknown_opt_other.eqy ├── unknown_option.eqy └── unknown_strategy.eqy /.gitattributes: -------------------------------------------------------------------------------- 1 | /.gittag export-subst 2 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | _What are the reasons/motivation for this change?_ 2 | 3 | _Explain how this is achieved._ 4 | 5 | _If applicable, please suggest to reviewers how they can test the change._ 6 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: "Build" 2 | 3 | on: 4 | push: 5 | pull_request: 6 | schedule: 7 | - cron: '0 1 * * *' 8 | 9 | jobs: 10 | build_oss: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout repository 15 | uses: actions/checkout@v4 16 | 17 | - name: Install OSS-Cad-Suite 18 | uses: YosysHQ/setup-oss-cad-suite@v3 19 | with: 20 | github-token: ${{ secrets.GITHUB_TOKEN }} 21 | 22 | - name: Build 23 | run: docker run --rm -v $(pwd):/work -v /home/runner/work/_temp/oss-cad-suite:/tabby yosyshq/plugin_build_x64:22.04 make 24 | 25 | - name: Install 26 | run: | 27 | cp /home/runner/work/_temp/oss-cad-suite/bin/eqy . 28 | make install PREFIX=/home/runner/work/_temp/oss-cad-suite 29 | cp -f /home/runner/work/_temp/oss-cad-suite/bin/eqy /home/runner/work/_temp/oss-cad-suite/libexec/. 30 | cp -f eqy /home/runner/work/_temp/oss-cad-suite/bin/. 31 | 32 | - name: Test 33 | run: make test 34 | 35 | build_verific: 36 | runs-on: [self-hosted, linux, x64, fast] 37 | steps: 38 | - name: Checkout EQY 39 | uses: actions/checkout@v4 40 | 41 | - name: Checkout Yosys 42 | uses: actions/checkout@v4 43 | with: 44 | repository: 'YosysHQ/yosys' 45 | path: 'yosys' 46 | submodules: true 47 | 48 | - name: Runtime environment 49 | run: | 50 | echo "procs=$(nproc)" >> $GITHUB_ENV 51 | 52 | - name: Build Yosys 53 | run: | 54 | cd yosys 55 | make config-clang 56 | echo "ENABLE_VERIFIC := 1" >> Makefile.conf 57 | echo "ENABLE_VERIFIC_EDIF := 1" >> Makefile.conf 58 | echo "ENABLE_VERIFIC_LIBERTY := 1" >> Makefile.conf 59 | echo "ENABLE_VERIFIC_YOSYSHQ_EXTENSIONS := 1" >> Makefile.conf 60 | echo "ENABLE_CCACHE := 1" >> Makefile.conf 61 | make -j${{ env.procs }} 62 | make install DESTDIR=${GITHUB_WORKSPACE}/.local PREFIX= 63 | sed -i 's,/usr/local/share,${GITHUB_WORKSPACE}/.local/share,g' ${GITHUB_WORKSPACE}/.local/bin/yosys-config 64 | sed -i 's,/usr/local/include,${GITHUB_WORKSPACE}/.local/include,g' ${GITHUB_WORKSPACE}/.local/bin/yosys-config 65 | sed -i 's,/usr/local/lib,${GITHUB_WORKSPACE}/.local/lib,g' ${GITHUB_WORKSPACE}/.local/bin/yosys-config 66 | 67 | - name: Checkout SBY 68 | uses: actions/checkout@v4 69 | with: 70 | repository: 'YosysHQ/sby' 71 | path: 'sby' 72 | 73 | - name: Build SBY 74 | run: | 75 | make -C sby install DESTDIR=${GITHUB_WORKSPACE}/.local PREFIX= 76 | 77 | - name: EQY Coverage 78 | if: (github.event_name != 'schedule') 79 | run: | 80 | sed -i 's,clang,g,g' ${GITHUB_WORKSPACE}/.local/bin/yosys-config 81 | sed -i 's,--gcov-tool $$PWD/llvm-gcov.sh,,g' Makefile 82 | sed -i "s,--no-external,--no-external --exclude '*/.local/share/*',g" Makefile 83 | make COVERAGE=1 84 | make coverage 85 | lcov_cobertura coverage.info --excludes .local.share --demangle 86 | 87 | - name: EQY Tests 88 | if: (github.event_name == 'schedule') 89 | run: | 90 | make 91 | make test EQY="python3 $(pwd)/src/eqy.py" 92 | 93 | - name: Report 94 | if: (github.event_name != 'schedule') 95 | run: | 96 | python3 .github/workflows/get_markdown.py coverage.xml 90 97 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: '0 3 * * *' 7 | 8 | jobs: 9 | analyze: 10 | name: Analyze 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Install deps 14 | run: sudo apt-get install bison flex libreadline-dev tcl-dev libffi-dev 15 | 16 | - name: Checkout repository 17 | uses: actions/checkout@v4 18 | 19 | - name: Install OSS-Cad-Suite 20 | uses: YosysHQ/setup-oss-cad-suite@v3 21 | with: 22 | github-token: ${{ secrets.GITHUB_TOKEN }} 23 | 24 | - name: Initialize CodeQL 25 | uses: github/codeql-action/init@v3 26 | with: 27 | languages: cpp, python 28 | queries: security-extended,security-and-quality 29 | 30 | - name: Build 31 | run: make 32 | 33 | - name: Perform CodeQL Analysis 34 | uses: github/codeql-action/analyze@v3 35 | -------------------------------------------------------------------------------- /.github/workflows/get_markdown.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import sys 4 | import xml.dom.minidom 5 | 6 | if len(sys.argv)<3: 7 | print("Not enough parameters") 8 | sys.exit(-1) 9 | 10 | with open(os.environ['GITHUB_STEP_SUMMARY'], 'a') as fh: 11 | print("| File | Coverage | |", file=fh) 12 | print("|---------------|:--------:|:------------------:|", file=fh) 13 | 14 | min_perc = int(sys.argv[2]) 15 | 16 | with open(sys.argv[1], "r") as f: 17 | xml_doc = xml.dom.minidom.parse(f) 18 | perc_total = int(round(float(xml_doc.documentElement.getAttribute('line-rate'))*1000)) 19 | sign_total = ":white_check_mark:" if (perc_total>=(min_perc*10)) else ":x:" 20 | print(f"| **All files** | `{perc_total/10}%` | {sign_total} |", file=fh) 21 | 22 | for cl in xml_doc.getElementsByTagName('class'): 23 | perc = int(round(float(cl.getAttribute('line-rate'))*1000)) 24 | sign = ":white_check_mark:" if (perc>=(min_perc*10)) else ":x:" 25 | escape_chars = r'_*[]()~`>#+-=|{}.!' 26 | text = re.sub(f'([{re.escape(escape_chars)}])', r'\\\1', cl.getAttribute('filename')) 27 | print(f"| {text} | `{perc/10}%` | {sign} |", file=fh) 28 | 29 | print("", file=fh) 30 | if (perc_total 4 | Copyright (C) 2020 N. Engelhardt 5 | Copyright (C) 2022 National Technology and Engineering Solutions of Sandia, LLC 6 | 7 | Permission to use, copy, modify, and/or distribute this software for any 8 | purpose with or without fee is hereby granted, provided that the above 9 | copyright notice and this permission notice appear in all copies. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | DESTDIR = 3 | PREFIX = /usr/local 4 | PROGRAM_PREFIX = 5 | YOSYS_CFGFLAGS = 6 | 7 | YOSYS_CONFIG ?= yosys-config 8 | 9 | # On Windows, manually setting absolute path to Python binary may be required 10 | # for launcher executable to work. From MSYS2, this can be done using the 11 | # following command: "which python3 | cygpath -w -m -f -". 12 | ifeq ($(OS), Windows_NT) 13 | PYTHON = $(shell cygpath -w -m $(PREFIX)/bin/python3) 14 | endif 15 | 16 | ifeq ($(file < .gittag),$$Format:%(describe)$$) 17 | YOSYS_RELEASE_VERSION := EQY $(shell git describe --dirty) 18 | else 19 | YOSYS_RELEASE_VERSION := EQY $(file < .gittag) 20 | endif 21 | 22 | build: src/eqy_combine.so src/eqy_partition.so src/eqy_recode.so 23 | 24 | DEBUG_CXXFLAGS := 25 | #DEBUG_CXXFLAGS += -Og 26 | COVERAGE := 0 27 | ifeq ($(COVERAGE),1) 28 | YOSYS_CFGFLAGS += --coverage 29 | endif 30 | 31 | src/eqy_combine.so: src/eqy_combine.cc 32 | $(YOSYS_CONFIG) --build $@ $(DEBUG_CXXFLAGS) $^ $(YOSYS_CFGFLAGS) 33 | 34 | src/eqy_partition.so: src/eqy_partition.cc 35 | $(YOSYS_CONFIG) --build $@ $(DEBUG_CXXFLAGS) $^ $(YOSYS_CFGFLAGS) 36 | 37 | src/eqy_recode.so: src/eqy_recode.cc 38 | $(YOSYS_CONFIG) --build $@ $(DEBUG_CXXFLAGS) $^ $(YOSYS_CFGFLAGS) 39 | 40 | install: src/eqy_combine.so src/eqy_partition.so src/eqy_recode.so 41 | mkdir -p $(DESTDIR)$(PREFIX)/bin 42 | mkdir -p $(DESTDIR)$(PREFIX)/share/yosys/python3 43 | mkdir -p $(DESTDIR)$(PREFIX)/share/yosys/plugins 44 | cp src/eqy_job.py $(DESTDIR)$(PREFIX)/share/yosys/python3/ 45 | cp src/eqy_combine.so $(DESTDIR)$(PREFIX)/share/yosys/plugins/ 46 | cp src/eqy_partition.so $(DESTDIR)$(PREFIX)/share/yosys/plugins/ 47 | cp src/eqy_recode.so $(DESTDIR)$(PREFIX)/share/yosys/plugins/ 48 | ifeq ($(OS), Windows_NT) 49 | sed -e 's|##yosys-sys-path##|sys.path += [os.path.dirname(__file__) + p for p in ["/share/python3", "/../share/yosys/python3"]]|;' \ 50 | -e "s|##yosys-release-version##|release_version = '$(YOSYS_RELEASE_VERSION)'|;" \ 51 | -e "s|#!/usr/bin/env python3|#!$(PYTHON)|" < src/eqy.py > $(DESTDIR)$(PREFIX)/bin/eqy-script.py 52 | gcc -DGUI=0 -O -s -o $(DESTDIR)$(PREFIX)/bin/eqy.exe extern/launcher.c 53 | else 54 | sed -e 's|##yosys-sys-path##|sys.path += [os.path.dirname(__file__) + p for p in ["/share/python3", "/../share/yosys/python3"]]|;' \ 55 | -e "s|##yosys-release-version##|release_version = '$(YOSYS_RELEASE_VERSION)'|;" < src/eqy.py > $(DESTDIR)$(PREFIX)/bin/eqy 56 | chmod +x $(DESTDIR)$(PREFIX)/bin/eqy 57 | endif 58 | 59 | html: 60 | $(MAKE) -C docs html 61 | 62 | test: 63 | $(MAKE) -C examples/simple clean 64 | $(MAKE) -C examples/simple 65 | $(MAKE) -C tests/python clean 66 | $(MAKE) -C tests/python 67 | +cd tests/plugin && bash run-test.sh 68 | 69 | coverage: 70 | rm -rf coverage.info coverage_html .coverage coverage.lcov 71 | $(MAKE) COVERAGE_FILE="$$PWD/.coverage" EQY="coverage run -a $$PWD/src/eqy.py" -C examples/simple clean test 72 | $(MAKE) COVERAGE_FILE="$$PWD/.coverage" EQY="coverage run -a $$PWD/src/eqy.py" -C examples/nerv clean test 73 | $(MAKE) COVERAGE_FILE="$$PWD/.coverage" EQY="coverage run -a $$PWD/src/eqy.py" -C examples/risc16f84 clean test 74 | $(MAKE) COVERAGE_FILE="$$PWD/.coverage" EQY="coverage run -a $$PWD/src/eqy.py" -C tests/python clean test 75 | +cd tests/plugin && bash run-test.sh 76 | lcov --capture -d . --no-external -o coverage.info --gcov-tool $$PWD/llvm-gcov.sh 77 | coverage report --omit=*/dist-packages/* 78 | coverage lcov --omit=*/dist-packages/* 79 | cat coverage.lcov >> coverage.info 80 | genhtml coverage.info --output-directory coverage_html 81 | 82 | clean: 83 | $(MAKE) -C docs clean 84 | $(MAKE) -C examples/simple clean 85 | $(MAKE) -C examples/nerv clean 86 | find . -name "*.gcda" -type f -delete 87 | find . -name "*.gcno" -type f -delete 88 | rm -rf docs/build src/eqy_combine.so src/eqy_partition.so src/eqy_recode.so src/__pycache__ coverage.info coverage_html .coverage coverage.lcov 89 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Equivalence Checking with Yosys (EQY) 2 | 3 | Equivalence Checking with Yosys (EQY) is a front-end driver program for 4 | [Yosys](https://yosyshq.net/yosys/)-based formal hardware verification flows. 5 | 6 | ``` 7 | Copyright (C) 2020 Claire Xenia Wolf 8 | Copyright (C) 2020 N. Engelhardt 9 | Copyright (C) 2022 National Technology and Engineering Solutions of Sandia, LLC 10 | 11 | Permission to use, copy, modify, and/or distribute this software for any 12 | purpose with or without fee is hereby granted, provided that the above 13 | copyright notice and this permission notice appear in all copies. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 16 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 17 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 18 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 20 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 21 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22 | ``` 23 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = build 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 14 | # the i18n builder cannot share the environment and doctrees with the others 15 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 16 | 17 | .PHONY: help 18 | help: 19 | @echo "Please use \`make ' where is one of" 20 | @echo " html to make standalone HTML files" 21 | @echo " dirhtml to make HTML files named index.html in directories" 22 | @echo " singlehtml to make a single large HTML file" 23 | @echo " pickle to make pickle files" 24 | @echo " json to make JSON files" 25 | @echo " htmlhelp to make HTML files and a HTML help project" 26 | @echo " qthelp to make HTML files and a qthelp project" 27 | @echo " applehelp to make an Apple Help Book" 28 | @echo " devhelp to make HTML files and a Devhelp project" 29 | @echo " epub to make an epub" 30 | @echo " epub3 to make an epub3" 31 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 32 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 33 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 34 | @echo " text to make text files" 35 | @echo " man to make manual pages" 36 | @echo " texinfo to make Texinfo files" 37 | @echo " info to make Texinfo files and run them through makeinfo" 38 | @echo " gettext to make PO message catalogs" 39 | @echo " changes to make an overview of all changed/added/deprecated items" 40 | @echo " xml to make Docutils-native XML files" 41 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 42 | @echo " linkcheck to check all external links for integrity" 43 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 44 | @echo " coverage to run coverage check of the documentation (if enabled)" 45 | @echo " dummy to check syntax errors of document sources" 46 | 47 | .PHONY: clean 48 | clean: 49 | rm -rf $(BUILDDIR)/* 50 | 51 | .PHONY: html 52 | html: 53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 54 | @echo 55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 56 | 57 | .PHONY: dirhtml 58 | dirhtml: 59 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 60 | @echo 61 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 62 | 63 | .PHONY: singlehtml 64 | singlehtml: 65 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 66 | @echo 67 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 68 | 69 | .PHONY: pickle 70 | pickle: 71 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 72 | @echo 73 | @echo "Build finished; now you can process the pickle files." 74 | 75 | .PHONY: json 76 | json: 77 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 78 | @echo 79 | @echo "Build finished; now you can process the JSON files." 80 | 81 | .PHONY: htmlhelp 82 | htmlhelp: 83 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 84 | @echo 85 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 86 | ".hhp project file in $(BUILDDIR)/htmlhelp." 87 | 88 | .PHONY: qthelp 89 | qthelp: 90 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 91 | @echo 92 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 93 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 94 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/SymbiYosys.qhcp" 95 | @echo "To view the help file:" 96 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/SymbiYosys.qhc" 97 | 98 | .PHONY: applehelp 99 | applehelp: 100 | $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp 101 | @echo 102 | @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." 103 | @echo "N.B. You won't be able to view it unless you put it in" \ 104 | "~/Library/Documentation/Help or install it in your application" \ 105 | "bundle." 106 | 107 | .PHONY: devhelp 108 | devhelp: 109 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 110 | @echo 111 | @echo "Build finished." 112 | @echo "To view the help file:" 113 | @echo "# mkdir -p $$HOME/.local/share/devhelp/SymbiYosys" 114 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/SymbiYosys" 115 | @echo "# devhelp" 116 | 117 | .PHONY: epub 118 | epub: 119 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 120 | @echo 121 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 122 | 123 | .PHONY: epub3 124 | epub3: 125 | $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3 126 | @echo 127 | @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3." 128 | 129 | .PHONY: latex 130 | latex: 131 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 132 | @echo 133 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 134 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 135 | "(use \`make latexpdf' here to do that automatically)." 136 | 137 | .PHONY: latexpdf 138 | latexpdf: 139 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 140 | @echo "Running LaTeX files through pdflatex..." 141 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 142 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 143 | 144 | .PHONY: latexpdfja 145 | latexpdfja: 146 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 147 | @echo "Running LaTeX files through platex and dvipdfmx..." 148 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 149 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 150 | 151 | .PHONY: text 152 | text: 153 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 154 | @echo 155 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 156 | 157 | .PHONY: man 158 | man: 159 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 160 | @echo 161 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 162 | 163 | .PHONY: texinfo 164 | texinfo: 165 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 166 | @echo 167 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 168 | @echo "Run \`make' in that directory to run these through makeinfo" \ 169 | "(use \`make info' here to do that automatically)." 170 | 171 | .PHONY: info 172 | info: 173 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 174 | @echo "Running Texinfo files through makeinfo..." 175 | make -C $(BUILDDIR)/texinfo info 176 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 177 | 178 | .PHONY: gettext 179 | gettext: 180 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 181 | @echo 182 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 183 | 184 | .PHONY: changes 185 | changes: 186 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 187 | @echo 188 | @echo "The overview file is in $(BUILDDIR)/changes." 189 | 190 | .PHONY: linkcheck 191 | linkcheck: 192 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 193 | @echo 194 | @echo "Link check complete; look for any errors in the above output " \ 195 | "or in $(BUILDDIR)/linkcheck/output.txt." 196 | 197 | .PHONY: doctest 198 | doctest: 199 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 200 | @echo "Testing of doctests in the sources finished, look at the " \ 201 | "results in $(BUILDDIR)/doctest/output.txt." 202 | 203 | .PHONY: coverage 204 | coverage: 205 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage 206 | @echo "Testing of coverage in the sources finished, look at the " \ 207 | "results in $(BUILDDIR)/coverage/python.txt." 208 | 209 | .PHONY: xml 210 | xml: 211 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 212 | @echo 213 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 214 | 215 | .PHONY: pseudoxml 216 | pseudoxml: 217 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 218 | @echo 219 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 220 | 221 | .PHONY: dummy 222 | dummy: 223 | $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy 224 | @echo 225 | @echo "Build finished. Dummy builder generates no files." 226 | -------------------------------------------------------------------------------- /docs/examples/quickstart/README.md: -------------------------------------------------------------------------------- 1 | TBD 2 | -------------------------------------------------------------------------------- /docs/source/conf.diff: -------------------------------------------------------------------------------- 1 | --- a/docs/source/conf.py 2 | +++ b/docs/source/conf.py 3 | @@ -1,5 +1,5 @@ 4 | #!/usr/bin/env python3 5 | -project = 'YosysHQ Docs' 6 | +project = 'YosysHQ EQY' 7 | author = 'YosysHQ GmbH' 8 | copyright ='2021 YosysHQ GmbH' 9 | 10 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | project = 'YosysHQ EQY' 3 | author = 'YosysHQ GmbH' 4 | copyright ='2021 YosysHQ GmbH' 5 | 6 | # select HTML theme 7 | html_theme = 'press' 8 | html_logo = '../static/logo.png' 9 | html_favicon = '../static/favico.png' 10 | html_css_files = ['yosyshq.css', 'custom.css'] 11 | html_sidebars = {'**': ['util/searchbox.html', 'util/sidetoc.html']} 12 | 13 | # These folders are copied to the documentation's HTML output 14 | html_static_path = ['../static', "../images"] 15 | 16 | # code blocks style 17 | pygments_style = 'colorful' 18 | highlight_language = 'systemverilog' 19 | 20 | html_theme_options = { 21 | 'external_links' : [ 22 | ('YosysHQ Docs', 'https://yosyshq.readthedocs.io'), 23 | ('Blog', 'https://blog.yosyshq.com'), 24 | ('Website', 'https://www.yosyshq.com'), 25 | ], 26 | } 27 | 28 | extensions = ['sphinx.ext.autosectionlabel'] 29 | -------------------------------------------------------------------------------- /docs/source/config.rst: -------------------------------------------------------------------------------- 1 | 2 | Reference for .eqy file format 3 | ============================== 4 | 5 | A ``.eqy`` file consists of sections. Each section start with a single-line 6 | section header in square brackets. 7 | 8 | Options section 9 | --------------- 10 | 11 | The optional ``[options]`` section contains one config setting per line, in the 12 | form of key-value pairs. 13 | 14 | .. code-block:: text 15 | 16 | [options] 17 | splitnets on 18 | 19 | Supported options are: 20 | 21 | =============== ================== ========================================================================== 22 | Option Values Description 23 | =============== ================== ========================================================================== 24 | ``splitnets`` ``on``/``off`` Split vector nets and ports into individual bits when reading the designs 25 | =============== ================== ========================================================================== 26 | 27 | Gold and gate sections 28 | ---------------------- 29 | 30 | The mandatory ``[gold]`` and ``gate`` sections contains the Yosys script that 31 | reads and elaborates the "gold" and "gate" versions of the design under test. 32 | For example, for a simple project contained in a single design file 33 | ``mytest.sv``, and a synthesis output file ``synth.v``, with the 34 | top-module ``mytest``: 35 | 36 | .. code-block:: text 37 | 38 | [gold] 39 | read -sv mytest.sv 40 | prep -top mytest 41 | 42 | [gate] 43 | read -sv synth.v 44 | prep -top mytest 45 | 46 | Run ``yosys`` in a terminal window and enter ``help`` on the Yosys prompt 47 | for a command list. Run ``help `` for a detailed description of the 48 | command, for example ``help prep``. 49 | 50 | The contents of ``[script]`` sections are added to the gold and gate scripts. 51 | This simplifies the configuration file in cases where both scripts share 52 | larger sequences of commands. 53 | 54 | Recode sections 55 | --------------- 56 | 57 | Recode sections contain encodings for FSM state registers, for the cases when 58 | the sythesis tool changes those encodings. (Alternatively the state registers 59 | can be excluded as match points and strategies for sequential equivalence 60 | checking can be employed for proving equivalence of the FSM as a whole.) 61 | 62 | These sections start with a header that contains two arguments: A module 63 | name from the (gold) design and the name of a wire within that module. (Those 64 | arguments are actually both regular expressions, but are rarely used to match 65 | more than one entity.) 66 | 67 | The section itself listst binary representation of values in the gold design 68 | and their corresponding gate encodings. Values not listed should never be 69 | produced by the gold design. 70 | 71 | .. code-block:: text 72 | 73 | [recode mytest state] 74 | 00 001 75 | 01 010 76 | 10 100 77 | 78 | Match sections 79 | -------------- 80 | 81 | Match sections contain rules for matching net names in the gold design to net 82 | names in the gate design. The match section header contains an optional pattern 83 | matching module names. The lines within the match section are only applied to 84 | modules matching that pattern, or all modules if the pattern is omitted. 85 | 86 | .. code-block:: text 87 | 88 | [match axi_xbar_*] 89 | gold-match *_ff[] \2_reg[\3].Q 90 | 91 | TBD: gold-match, gate-match, final-gold-match, final-gate-match, gold-nomatch, gate-nomatch 92 | 93 | Collect sections 94 | ---------------- 95 | 96 | Collect sections contain rules for the first step of the partition algorithm, 97 | in which individual bits from matched nets are grouped into /fragments/, i.e. 98 | small circuits that are closely related and thus should be put in the same 99 | partition. 100 | 101 | .. code-block:: text 102 | 103 | [collect axi_xbar_*] 104 | bind partial_sum_* 105 | 106 | solo and nosolo 107 | ............... 108 | 109 | The ``solo `` command prevents the auto-grouping mechanism to 110 | group this net with other nets into the same fragment. Per default 111 | auto-grouping does group nets when 112 | 113 | - they are driven by the same cell, either in the gold or gate design, or 114 | - gold nets are aliases, i.e. are implemented by the same gate net. 115 | 116 | The ``nosolo `` command prevents all further ``solo`` commands 117 | from matching the specified nets. 118 | 119 | group and nogroup 120 | ................. 121 | 122 | The ``group `` command is used to group everything matching the given 123 | pattern into the same fragment. 124 | 125 | The ``group `` command is pairing matches for the first pattern with the sets 126 | of nets matching the (dependent) second pattern, and is grouping them accordingly. 127 | 128 | The ``nogroup `` command prevents all further ``group`` commands from 129 | matching the specified nets. 130 | 131 | bind and nobind 132 | ............... 133 | 134 | Marking a net as ``bind`` prevents that net from becoming a primary input of 135 | a fragment. (This has no effect on nets that are module inputs.) 136 | 137 | The ``nobind `` command prevents all further ``bind`` commands from 138 | matching the speciefied nets. 139 | 140 | join and nojoin 141 | ................. 142 | 143 | The ``join`` command operates on a multi-bit wires and groups all bits 144 | from such a net into the same fragment. 145 | 146 | The ``nojoin`` command prevents further ``join`` commands from matching 147 | the given nets. 148 | 149 | solo-group and solo-join 150 | ........................ 151 | 152 | The ``solo-group `` command is a convenient shortcut for running 153 | both ``solo`` and ``group`` on the same pattern. 154 | 155 | The ``solo-join `` command is a convenient shortcut for running 156 | both ``solo`` and ``join`` on the same pattern. 157 | 158 | Partition sections 159 | ------------------ 160 | 161 | Partition sections contain rules for creating matching partitions in the gold 162 | and gate designs. The partition section header contains an optional pattern 163 | matching module names. The lines within the partition section are only applied 164 | to modules matching that pattern, or all modules if the pattern is omitted. 165 | 166 | .. code-block:: text 167 | 168 | [partition axi_xbar_*] 169 | name pipeline_\1 /^(reader|buffer|arbiter|writer)_([0-7])$/ 170 | 171 | name and noname 172 | ............... 173 | 174 | The ``name `` command is looking for nets matching the given pattern, 175 | and then applies the given name to the partition that contains that net as primary 176 | output. 177 | 178 | If multiple ``name`` commands assign the same name to different entities, then the 179 | corresponding partitions will be merged into one partition with the given name. If 180 | multiple ``name`` commands apply to the same partition, then the earlier name command 181 | will be used to name the partition. (Both names are used for merging partitions tho.) 182 | 183 | The ``noname `` command can be used to prevent further name commands from 184 | mathing the given nets. 185 | 186 | merge and nomerge 187 | ................. 188 | 189 | The ``merge`` and ``nomerge`` commands work similar to ``group`` and ``nogroup``, 190 | but creates non-fragment partitions by merging the fragment partitions generated 191 | by the grouping commands. 192 | 193 | path statements 194 | ............... 195 | 196 | The ``path `` command will determine the shortest path from the 197 | first net to the second net, and then merge all partitions along that path. 198 | 199 | If the first pattern matches a net name then the partition generating that net is 200 | not included in the path itself. If the first pattern matches a partition name then 201 | that partition is included in the path. 202 | 203 | If the second pattern matches a net name then partitions consuming that signal 204 | are not included in the path itself. If the second pattern matches a partition name 205 | then that partition is included in the path. 206 | 207 | sticky and nosticky 208 | ................... 209 | 210 | The ``sticky `` command marks nets as sticky. The partition generating the 211 | sticky net as primary output will then be merged with any partition using the 212 | sticky net as primary input. 213 | 214 | The ``nosticky `` command preents further ``sticky`` commands from matching 215 | the given net. 216 | 217 | amend, ramend, and noamend 218 | .......................... 219 | 220 | The ``amend `` command finds the fragment(s) generating the 221 | specified net(s), and amends all partitions using those signals with the gold 222 | definition of that signal. 223 | 224 | The ``amend `` command only amends partitions that are 225 | generating signals matching the second pattern. 226 | 227 | The ``amend`` command only amends partitions that are currently consuming 228 | the specified signal. The ``ramend`` command amends the target partition 229 | unconditionally. This is useful when the signals being amended are themself 230 | only used by other partitions that are being amended to the target partition. 231 | 232 | The ``noamend `` command preents further ``amend`` and ``ramend`` 233 | commands from matching the given net. 234 | 235 | final statements 236 | ................ 237 | 238 | The ``final `` command marks the partitions generating the 239 | nets matching the pattern as final. No furter statements will have any 240 | effect on a partition after it is marked as final. 241 | 242 | Strategy sections 243 | ----------------- 244 | 245 | Each strategy section creates a verification strategy used to prove 246 | partitions to be equal. The section header contains the name of the 247 | strategy as an argument. 248 | 249 | .. code-block:: text 250 | 251 | [strategy simple] 252 | apply axi_xbar_* 253 | use sat 254 | depth 10 255 | 256 | use statements 257 | .............. 258 | 259 | The ``use strategy_type`` command selects a strategy type for this strategy. Each 260 | strategy type defines its own custom commands for the strategy section. For example, 261 | the ``depth`` command in the example above is a custom command only understood by 262 | the ``sat`` strategy type. See the :doc:`EQY Strategies reference page 263 | ` for details on the individual strategy types. 264 | 265 | apply and noapply 266 | ................. 267 | 268 | The ``apply []`` command is used to enable the given strategy 269 | in modules matching the first pattern, for partitions matching 270 | the second pattern. The ``noapply []`` command prevents 271 | further ``apply`` commands in the same strategy section from matching the 272 | specified partitions. If the second pattern is omitted, then the strategy will 273 | be applied to all partitions in the modules matching the first pattern. 274 | 275 | Pattern Syntax 276 | -------------- 277 | 278 | Patterns are comma-seperated lists of any combinations of the following 279 | types of expressions. 280 | 281 | - names of modules or nets, or shell wildcard pattern matching those names, 282 | - regular expressions matching enity names, enclosed in forward slashes, 283 | - at-sign (@) followed by an attribute name, matching all entities with that attribute set, 284 | - at-sign and attribute name, followed by an equal sign (=) and an attribute value, 285 | - or ampercent-sign (&) followed by a partition name. 286 | 287 | A regular expression can be enclosed in ``//i`` instead of ``//``, in which 288 | case it is evaluated case-insensitive. 289 | 290 | Attribute names and partition names can also be shell wildcard patterns, or 291 | regular expressions. 292 | 293 | The partition name syntax is only available in ``path``, ``final``, and ``apply`` 294 | statements. 295 | 296 | In commands that accept pairs of patterns, numeric backreferences (\0, \1, \2) and 297 | named backreferences (\g<1>, \g) are replaced in the second pattern by 298 | the contents of the corresponding group from the first pattern. 299 | 300 | In shell wildcard patterns, the entire name is stored in \0 and each wildcard 301 | char (``?``, ``*``, or ``[abcd]`` groups) stores the matching text in a numbered 302 | group. The special string ``[]`` in shell wildcard patterns match to an integer 303 | in square brackets, storing the text preceding the square brackets in \1 and the 304 | integer in the group corresponding to the ``[]`` token. 305 | 306 | If the first pattern in a pair used the at-sign syntax for attributes, then \g 307 | in the second pattern is replaced with the attribute name and \g with 308 | the corresponding attribute value. 309 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | 2 | Equivalence Checking with Yosys (EQY) Documentation 3 | =================================================== 4 | 5 | EQY is a front-end driver program for Yosys-based formal 6 | hardware equivalence checking. 7 | 8 | .. toctree:: 9 | :maxdepth: 3 10 | 11 | install.rst 12 | quickstart.rst 13 | config.rst 14 | strategies.rst 15 | outdir.rst 16 | xprop.rst 17 | license.rst 18 | 19 | -------------------------------------------------------------------------------- /docs/source/install.rst: -------------------------------------------------------------------------------- 1 | Installing 2 | ========== 3 | 4 | Follow the instructions below to install EQY and its dependencies. 5 | 6 | CAD suite(s) 7 | ************ 8 | 9 | EQY is part of the `Tabby CAD Suite 10 | `_ and the `OSS CAD Suite 11 | `_! The easiest way to use sby 12 | is to install the binary software suite, which contains all required 13 | dependencies, including Yosys, SBY, and all its solvers. 14 | 15 | * `Contact YosysHQ `_ for a `Tabby CAD Suite 16 | `_ Evaluation License and 17 | download link 18 | * OR go to https://github.com/YosysHQ/oss-cad-suite-build/releases to download 19 | the free OSS CAD Suite 20 | * Follow the `Install Instructions on GitHub 21 | `_ 22 | 23 | Make sure to get a Tabby CAD Suite Evaluation License for industry-grade 24 | SystemVerilog and VHDL parsers! 25 | 26 | For more information about the difference between Tabby CAD Suite and the OSS 27 | CAD Suite, please visit https://www.yosyshq.com/tabby-cad-datasheet. 28 | 29 | Installing from source 30 | ********************** 31 | 32 | Prerequisites 33 | ------------- 34 | 35 | EQY requires a matching version of `Yosys `_. 36 | We use `GitHub tags `_ to indicate that a 37 | commit has been confirmed to work with that Yosys release version. 38 | 39 | Parts of EQY are Yosys plugins. To be able to load the plugins, they must be 40 | compiled in the same environment as the Yosys binary you are using. If you 41 | would like to build EQY from source but use a precompiled Yosys binary, you can 42 | use the `Tabby CAD Plugin Build docker environment 43 | `_ to compile the EQY 44 | plugins (the environment works for both Tabby and OSS CAD Suite Yosys). 45 | 46 | To use ``[strategy sby]``, you also need to have a 47 | `matching version `_ of 48 | `SBY `_ installed, as well as the solvers you 49 | want to use. 50 | 51 | EQY 52 | --- 53 | 54 | Use these commands to build and install EQY: 55 | 56 | .. code-block:: text 57 | 58 | git clone https://github.com/YosysHQ/eqy.git eqy 59 | cd eqy 60 | make 61 | sudo make install 62 | 63 | -------------------------------------------------------------------------------- /docs/source/license.rst: -------------------------------------------------------------------------------- 1 | 2 | EQY License 3 | =========== 4 | 5 | EQY itself is licensed under the ISC license: 6 | 7 | .. literalinclude:: ../../COPYING 8 | :language: text 9 | 10 | Note that the solvers and other components used by EQY come with their 11 | own license terms. 12 | 13 | -------------------------------------------------------------------------------- /docs/source/outdir.rst: -------------------------------------------------------------------------------- 1 | 2 | EQY Output Directory Format 3 | =========================== 4 | 5 | Main log and status files 6 | ------------------------- 7 | 8 | The EQY terminal output is also written to the file ``logfile.txt`` 9 | in the output directory. 10 | 11 | Further, when the EQY run successfully proved equivalence the entire design, a 12 | file ``PASS`` is created in the output directory, and a file ``FAIL`` is 13 | generated otherwise. 14 | 15 | Gate and gold designs 16 | --------------------- 17 | 18 | The file ``gold.il`` contains the compiled gold design. The log 19 | for compiling it is written to ``gold.log`` and the yosys 20 | script compiling it is ``gold.ys``. Finally, the list of 21 | gold entities going into the matching algorithm is stored 22 | in ``gold.ids``. 23 | 24 | Similarly there are ``gate.il``, ``gate.log``, ``gate.ys``, 25 | and ``gate.ids`` for the gate design. 26 | 27 | Combined gold and gate designs 28 | ------------------------------ 29 | 30 | EQY stores a "combined" gold and gate design, that contains 31 | all modules from the gold design, their names prefixed with ``gold_``, 32 | and all modules from the gate design, using the ``gate_`` name 33 | prefix. 34 | 35 | The combined design is stored in the output file ``combined.il``. 36 | The script that performs the task of combining the gold and 37 | gate designs is stored in ``combine.ys``, and the log messages 38 | generated by that script are stored in ``combine.log``. 39 | 40 | The combine operation partially flattens the gold and gate designs, 41 | so that the hierarchies of both designs match, thus getting to a 42 | state of matching module names in both design hierarchies. 43 | 44 | Matched gold and gate names 45 | --------------------------- 46 | 47 | The list of matched gold and gate entity names is stored in 48 | the file ``matched.ids``. Each line of this file contains 49 | a module name, followed by a gold net name, followed by 50 | a gate net name. 51 | 52 | Design partition files 53 | ---------------------- 54 | 55 | The file ``partition.ids`` contains partitioning rules generated from the 56 | ``[collect ...]`` and ``[partition ...]`` configuration sections. These rules guide the partition 57 | generation algorithm. The following statements are valid in ``partition.ids`` 58 | files. 59 | 60 | .. code-block:: text 61 | 62 | solo 63 | group 64 | bind 65 | join 66 | 67 | name 68 | merge 69 | path 70 | sticky 71 | final 72 | 73 | amend 74 | amend 75 | 76 | The file ``partition.list`` contains a list of all generated design partitions, 77 | and the primary output bits and primary input bits of each partition. 78 | 79 | The file ``partition.ys`` contains the script running the partitioning 80 | algorithm, and the file ``partition.log`` contains the log output 81 | from running that script. 82 | 83 | Finally, the ``partitions/`` directory contains the generated partitions, 84 | one file for each partition, each file containing a ``gold_`` and a ``gate_`` 85 | module. 86 | 87 | Strategies input and output files 88 | --------------------------------- 89 | 90 | The directory ``strategies/`` contains a sub-directory for each partition, 91 | and a sub-sub-directory for each strategy applied to that partition. 92 | 93 | Each of those sub-sub-directories contains a ``run.sh`` script that will 94 | run the strategy, and a ``run.log`` script with the log output from running 95 | that script. 96 | 97 | Lastly, a file ``status`` is generated when the strategy is being run, 98 | and the first word in that file is ``PASS`` when the strategy proved the 99 | partition to be equivalent successfully. The other possible status values for a 100 | strategy are ``UNKNOWN``, ``FAIL``, and ``ERROR``. 101 | 102 | The file ``strategies.mk`` is a Makefile that runs all the strategies 103 | within the ``strategies/`` directory. 104 | 105 | -------------------------------------------------------------------------------- /docs/source/quickstart.rst: -------------------------------------------------------------------------------- 1 | 2 | Getting Started 3 | =============== 4 | 5 | EQY - EQuivalence checking with Yosys - is a tool designed to perform 6 | formal verification that two designs are equivalent, such as ensuring 7 | that a synthesis tool has not introduced functional changes into a 8 | design, or ensuring that a design refactor preserves correctness in all 9 | conditions. 10 | 11 | In this example, a very simple CPU called NERV is used to demonstrate 12 | checking that a refactor does not introduce bugs. 13 | 14 | The example files used in this chapter can be downloaded from `here 15 | `_. 16 | 17 | In the ``nerv.sv`` starter code, each RISC-V shift opcode (SLL, SRL, 18 | SRA, SLLI, SRLI, SRAI) has its own shifter, and while the synthesis tool 19 | may be able to combine some of these shifters together, it would be more 20 | efficient to have a single shifter unit that all of these instructions 21 | use together. 22 | 23 | ``nerv_change.sv`` contains a refactored version, where the shifts have 24 | been extracted to a separate shifter unit, which can perform logical and 25 | arithmetic right shifts, and by bit-reversing the input and output 26 | perform left shifts as well. 27 | 28 | .. code:: systemverilog 29 | 30 | // new shifter code 31 | function [31:0] bitreverse(input [31:0] arg); 32 | for (integer i = 0; i < 32; i++) bitreverse[i] = arg[31-i]; 33 | endfunction 34 | wire [32:0] shift_t1 = {insn_funct7[5] && rs1_value[31], 35 | insn_funct3 == 3'b001 ? bitreverse(rs1_value) : rs1_value}; 36 | `ifdef SHIFTER_BUG 37 | wire [31:0] shift_t2 = $signed(shift_t1) >>> 38 | (insn_opcode == OPCODE_OP_IMM ? insn[24:20] : rs2_value[5:0]); 39 | `else 40 | wire [31:0] shift_t2 = $signed(shift_t1) >>> 41 | (insn_opcode == OPCODE_OP_IMM ? insn[24:20] : rs2_value[4:0]); 42 | `endif 43 | wire [31:0] shift_out = 44 | insn_funct3 == 3'b001 ? bitreverse(shift_t2) : shift_t2; 45 | 46 | Imagine that we accidentally write an error in the code by using 47 | ``rs2_value[5:0]`` instead of ``rs2_value[4:0]``. This can be tested 48 | using the ``SHIFTER_BUG`` ifdef in NERV. 49 | 50 | To test if this design is equivalent to the original, we need to write a 51 | ``.eqy`` file to tell EQY how to process the two designs. 52 | 53 | First, we’ll give the Yosys commands to process the known-good (“gold”) 54 | design: 55 | 56 | .. code-block:: text 57 | 58 | [gold] 59 | read_verilog -sv nerv.sv 60 | prep -top nerv 61 | memory_map 62 | 63 | We use ``read_verilog -sv`` to load the file, ``prep -top nerv`` to 64 | synthesise the design with ``nerv`` as top module, and then we use 65 | ``memory_map`` to turn the register file into DFFs. 66 | 67 | The design to be checked for equivalence (the “gate” design) has a 68 | similar script. 69 | 70 | .. code-block:: text 71 | 72 | [gate] 73 | read_verilog -sv -DSHIFTER_BUG nerv_change.sv 74 | prep -top nerv 75 | memory_map 76 | 77 | Then we need to tell EQY to collect sections that should be proven 78 | together, like the register file and some logic known to be identical. 79 | 80 | .. code-block:: text 81 | 82 | [collect *] 83 | group regfile* 84 | join imm_* 85 | join insn* 86 | 87 | Finally, we need to tell EQY how to prove these designs equivalent. 88 | 89 | .. code-block:: text 90 | 91 | [strategy sby] 92 | use sby 93 | depth 2 94 | engine smtbmc bitwuzla 95 | 96 | These are included in ``nerv_change_fail.eqy`` for convenience. Now we 97 | can check equivalence through ``eqy nerv_change_fail.eqy``. 98 | 99 | EQY will give output that ends like this: 100 | 101 | .. code-block:: text 102 | 103 | EQY [nerv_change_fail] Warning: Failed to prove equivalence for 1/44 partitions: 104 | EQY [nerv_change_fail] Failed to prove equivalence of partition nerv.next_rd 105 | EQY [nerv_change_fail] summary: Elapsed clock time [H:MM:SS (secs)]: 0:00:18 (18) 106 | EQY [nerv_change_fail] summary: Elapsed process time [H:MM:SS (secs)]: 0:00:22 (22) 107 | EQY [nerv_change_fail] DONE (FAIL, rc=2) 108 | 109 | And we can see that these designs are not equivalent. EQY will create a 110 | trace of the failing case in 111 | ``nerv_change_fail/strategies/nerv.next_rd/sby/nerv.next_rd/engine_0/trace.vcd``. 112 | 113 | Examining the trace, we can see that the failing instruction is an 114 | ``SLL`` with ``rs1_value`` equal to ``0xE0000000``, and an ``rs2_value`` 115 | equal to ``0x1FFFFFE2``. Since RISC-V mandates that ``SLL`` only looks 116 | at the lower 5 bits of ``rs2_value``, this is a left-shift of 117 | ``0xE0000000`` by 2, which should equal ``0x80000000``. However, due to 118 | the shifter bug, the shifter looks at the lower 6 bits of ``rs2_value``, 119 | and performs a left-shift of ``0xE0000000`` by 34, resulting in zero. 120 | 121 | Since the bug has been found and fixed, we can run another equivalence 122 | check to ensure that the new design is now equivalent. For NERV, this 123 | consists of just removing the ``SHIFTER_BUG`` ifdef, which can be found 124 | in ``nerv_change_pass.eqy``. Running ``eqy nerv_change_pass.eqy``, the 125 | output ends in 126 | 127 | .. code-block:: text 128 | 129 | EQY [nerv_change_pass] Successfully proved designs equivalent 130 | EQY [nerv_change_pass] summary: Elapsed clock time [H:MM:SS (secs)]: 0:00:21 (21) 131 | EQY [nerv_change_pass] summary: Elapsed process time [H:MM:SS (secs)]: 0:00:28 (28) 132 | EQY [nerv_change_pass] DONE (PASS, rc=0) 133 | 134 | and EQY has proven the designs equivalent. 135 | -------------------------------------------------------------------------------- /docs/source/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx-press-theme 2 | -------------------------------------------------------------------------------- /docs/source/strategies.rst: -------------------------------------------------------------------------------- 1 | 2 | EQY Strategies 3 | ============== 4 | 5 | When proving partitions equal, EQY can use different strategies. Strategies are 6 | configured using strategy sections in an ``.eqy`` file. This page documents the 7 | currently supported strategies and the corresponding strategy specific options. 8 | See :doc:`the .eqy file format reference ` for general information on 9 | how to specify the strategies to use for each partition. 10 | 11 | 12 | The ``sat`` Strategy 13 | -------------------- 14 | 15 | The ``sat`` strategy uses Yosys's builtin ``sat`` command in the 16 | ``-tempinduct`` mode to perform a k-induction proof (see the `Yosys command 17 | reference`_ for more details). 18 | 19 | .. _Yosys command reference: https://yosyshq.readthedocs.io/projects/yosys/en/latest/cmd/sat.html 20 | 21 | This strategy can be quite fast for small and simple partitions, but is less 22 | effective for larger or more complex partitions. Being limited to k-induction 23 | means that this strategy is unable to prove some partitions equal. 24 | 25 | Yosys's ``sat`` command also lacks support for memories, and therefore this 26 | startegy will not apply to any partitions that contain memory. This is done 27 | automatically and does not require excluding these partitions using the 28 | ``noapply`` option. 29 | 30 | Strategy Options 31 | ................ 32 | 33 | ``depth `` 34 | The maximal depth to try. The ``sat`` strategy will only be able to prove 35 | partitions equivalent if the partitions produce equal outputs for the 36 | initial depth steps and also produce equal outputs in any step for 37 | which the depth preceding steps did so. (default value: 5) 38 | 39 | Example Configuration 40 | ..................... 41 | 42 | .. code-block:: text 43 | 44 | [strategy simple] 45 | use sat 46 | depth 10 47 | 48 | The ``sby`` Strategy 49 | -------------------- 50 | 51 | The ``sby`` strategy uses SBY_, the front-end for Yosys-based formal 52 | verification flows. SBY provides a common interface for using different 53 | backend-tools. This makes the ``sby`` stratagy a very versatile strategy and a 54 | good default choice. 55 | 56 | The ``sby`` strategy always uses SBY's ``prove`` mode. The partitions it can 57 | prove equal and the runtime it takes to do so depend on which engine and solver 58 | are used. See the `SBY reference`_ for details on the supported engines and 59 | solvers in ``prove`` mode. 60 | 61 | .. _SBY: https://yosyshq.readthedocs.io/projects/sby 62 | .. _SBY reference: https://yosyshq.readthedocs.io/projects/sby 63 | 64 | Strategy Options 65 | ................ 66 | 67 | ``engine `` 68 | The SBY engine to use. This can include the solver to be used by the engine 69 | as well as engine and solver options. Here ```` is used 70 | verbatim in the generated ``.sby`` files. (default value: ``smtbmc``) 71 | 72 | ``depth `` 73 | The depth to use for engine configurations that require a depth bound. 74 | Ignored by engines that do not take a depth bound. (default value: 5) 75 | 76 | ``timeout `` 77 | Limits the amount of time spent trying to prove a partition equivalent. If 78 | the timout is reached before a proof was found, the next strategy is tried. 79 | (not enabled by default) 80 | 81 | ``xprop `` 82 | Do not use formal x-propagation. Disabling this avoids using the Yosys 83 | ``xprop`` pass, but may prevent proving partitions equivalent when they 84 | contain uninitialized state or don't-care values. (default value: on) 85 | 86 | ``option `` 87 | Set arbitrary option in the generated ``.sby`` files. Can be used multiple 88 | times. 89 | 90 | Example Configurations 91 | ...................... 92 | 93 | Using the ``smtbmc`` engine with the ``bitwuzla`` solver to perform a depth 10 94 | k-induction equivalence proof: 95 | 96 | 97 | .. code-block:: text 98 | 99 | [strategy induction] 100 | use sby 101 | engine smtbmc bitwuzla 102 | depth 10 103 | 104 | Using the ``abc`` engine with the ``pdr`` solver to prove equivalence using the 105 | PDR/IC3 algorithm: 106 | 107 | .. code-block:: text 108 | 109 | [strategy induction] 110 | use sby 111 | engine abc pdr 112 | 113 | The ``dummy`` Strategy 114 | ---------------------- 115 | 116 | The dummy strategy does not try to prove partitions equivalent and instead 117 | immediately gives up, falling through to the next strategy or failing the 118 | equivalence prove if no other strategy remains. 119 | -------------------------------------------------------------------------------- /docs/source/xprop.rst: -------------------------------------------------------------------------------- 1 | 2 | Equivalence and X-Propagation 3 | ============================== 4 | 5 | For a circuit described without any don't-care values and with all flip-flops 6 | initialized to a fixed value, an equivalence check can be expressed as 7 | safety-property using a miter circuit that compares the output of the "gold" 8 | and "gate" circuit, each receiving the same inputs. 9 | 10 | Safe-Replacement Equivalence 11 | ............................ 12 | 13 | The situation is not as simple when flip-flops start with an unknown initial 14 | value or when synthesis is allowed to turn don't-care bits into arbitrary 15 | values. There are different notions of circuit equivalence in such a setting. 16 | EQY implements a form of safe-replacement equivalence. This means that every 17 | output sequence that can be observed for the "gate" circuit for some input 18 | sequence must correspond to a compatible output sequence of the "gold" circuit 19 | when using the same input sequence. 20 | 21 | It makes sense to think of this as a refinement as it is not a symmetric 22 | equivalence. Requireing that any behavior of the "gate" circuit is present for 23 | the "gold" circuit ensures that properties validated for the "gold" circuit 24 | also hold for the "gate" circuit. This does not hold when reversing the roles 25 | of "gate" and "gold" as chosing different initial values for uninitialized 26 | flip-flops or making different choices for don't-care values within the "gold" 27 | circuit may result in an output sequence that cannot occur for a given 28 | equivalent "gate" circuit. 29 | 30 | 31 | Equivalence using Formal X-Propagation 32 | ....................................................... 33 | 34 | EQY's strategies interpret the "gold" circuit using Yosys's 3-valued (0, 1 and 35 | x) semantics (essentially the SystemVerilog x-propagation rules with the 36 | exception that ``if`` statements behave like ``s ? a : b`` expressions, 37 | producing x-bits when the two branches produce incompatible assignments). The 38 | "gate" circuit is interpreted using 2-valued semantics where any x-bit is 39 | replaced with an arbitrary unconstrained value (matching SBY's formal 40 | verification flows). The outputs are then compared using a miter that considers 41 | an x output on the "gold" side equal to any value of the "gate" side. 42 | 43 | Using a 3-valued interpretation for the "gold" side makes it possible to check 44 | an indivudal "gate" trace against a single "gold" trace, turning the 45 | equivalence check with don't-cares and uninitialized flip-flops into a 46 | safety-property which in turn makes it possible to have EQY strategies 47 | integrating a large number of well performing solvers. This would not be 48 | possible when using a 2-valued interpretation of the "gold" side which would 49 | require an extra level of quantification to capture the possible choices for 50 | uninitialized or don't-care bits. 51 | 52 | When using EQY with a design that is not fully initialized or contains 53 | don't-care values, it is important to keep in mind that the "gold" circuit is 54 | interpreted using 3-valued semantics. Properties of the "gold" circuit that are 55 | expected to hold for equivalent "gate" circuit should be verified against a 56 | 3-valued interpretation of the "gold" circuit. 57 | 58 | Avoiding X-Bits 59 | ............... 60 | 61 | When a 3-valued interpretation of the "gold" circuit exhibits unwanted 62 | behavior, it is possible to use Yosys's passes to fully or partially replace 63 | some x-bits with defined values in the ``[gate]``, ``[gold]`` or ``[script]`` 64 | section of an ``.eqy`` file. The ``setundef`` pass (`setundef help`_) replaces 65 | x-bits with constant, random or unconstrained values while the ``sim -r`` pass 66 | (`sim help`_) can simulate a reset sequence, writing back the resulting state 67 | as new initial state. 68 | 69 | .. _`setundef help`: https://yosyshq.readthedocs.io/projects/yosys/en/latest/cmd/setundef.html 70 | .. _`sim help`: https://yosyshq.readthedocs.io/projects/yosys/en/latest/cmd/sim.html 71 | -------------------------------------------------------------------------------- /docs/static/custom.css: -------------------------------------------------------------------------------- 1 | /* empty */ 2 | -------------------------------------------------------------------------------- /docs/static/favico.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YosysHQ/eqy/91b2178dc5b141aba3794d1d088740f9d1a48a54/docs/static/favico.png -------------------------------------------------------------------------------- /docs/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YosysHQ/eqy/91b2178dc5b141aba3794d1d088740f9d1a48a54/docs/static/logo.png -------------------------------------------------------------------------------- /docs/static/yosyshq.css: -------------------------------------------------------------------------------- 1 | h1, h3, p.topic-title, .content li.toctree-l1 > a { 2 | color: #d6368f !important; 3 | } 4 | 5 | h2, p.admonition-title, dt, .content li.toctree-l2 > a { 6 | color: #4b72b8; 7 | } 8 | 9 | a { 10 | color: #8857a3; 11 | } 12 | 13 | a.current, a:hover, a.external { 14 | color: #d6368f !important; 15 | } 16 | 17 | a.external:hover { 18 | text-decoration: underline; 19 | } 20 | 21 | p { 22 | text-align: justify; 23 | } 24 | 25 | .vp-sidebar a { 26 | color: #d6368f; 27 | } 28 | 29 | .vp-sidebar li li a { 30 | color: #4b72b8; 31 | } 32 | 33 | .vp-sidebar li li li a { 34 | color: #2c3e50; 35 | font-weight: 400; 36 | } 37 | 38 | .vp-sidebar h3 { 39 | padding-left: 1.5rem !important; 40 | } 41 | 42 | .vp-sidebar ul a { 43 | padding-left: 1.5rem !important; 44 | } 45 | 46 | .vp-sidebar ul ul a { 47 | padding-left: 3rem !important; 48 | } 49 | 50 | .vp-sidebar ul ul ul a { 51 | padding-left: 4.5rem !important; 52 | } 53 | 54 | .vp-sidebar .toctree-l1.current a { 55 | border-left: 0.5rem solid #6ecbd7; 56 | } 57 | 58 | .vp-sidebar .toctree-l1 a.current { 59 | border-left: 0.5rem solid #8857a3; 60 | } 61 | 62 | .injected .rst-current-version, .injected dt { 63 | color: #6ecbd7 !important; 64 | } 65 | 66 | dd { /* use the same indentation as for code blocks */ 67 | margin-inline-start: 1.5rem; 68 | } 69 | -------------------------------------------------------------------------------- /examples/nerv/.gitignore: -------------------------------------------------------------------------------- 1 | /nerv_synth/ 2 | /nerv_change_pass/ 3 | /nerv_change_fail/ 4 | -------------------------------------------------------------------------------- /examples/nerv/Makefile: -------------------------------------------------------------------------------- 1 | EQY := eqy 2 | 3 | test: nerv_synth/PASS nerv_change_fail/FAIL nerv_change_pass/PASS 4 | 5 | nerv_synth/PASS: nerv_synth.eqy nerv.sv nerv_synth.v 6 | $(EQY) -f nerv_synth.eqy 7 | 8 | nerv_change_fail/FAIL: nerv_change_fail.eqy nerv.sv nerv_change.sv 9 | -$(EQY) -f nerv_change_fail.eqy 10 | 11 | nerv_change_pass/PASS: nerv_change_pass.eqy nerv.sv nerv_change.sv 12 | $(EQY) -f nerv_change_pass.eqy 13 | 14 | clean: 15 | rm -rf nerv_synth/ nerv_change_fail/ nerv_change_pass/ 16 | 17 | .PHONY: test clean 18 | -------------------------------------------------------------------------------- /examples/nerv/README.md: -------------------------------------------------------------------------------- 1 | # Checking Refactor Correctness With EQY 2 | 3 | EQY - EQuivalence checking with Yosys - is a tool designed to perform formal 4 | verification that two designs are equivalent, such as ensuring that a synthesis 5 | tool has not introduced functional changes into a design, or ensuring that a 6 | design refactor preserves correctness in all conditions. 7 | 8 | In this example, a very simple CPU called NERV is used to demonstrate checking 9 | that a refactor does not introduce bugs. 10 | 11 | In the `nerv.sv` starter code, each RISC-V shift opcode (SLL, SRL, SRA, SLLI, 12 | SRLI, SRAI) has its own shifter, and while the synthesis tool may be able to 13 | combine some of these shifters together, it would be more efficient to have a 14 | single shifter unit that all of these instructions use together. 15 | 16 | `nerv_change.sv` contains a refactored version, where the shifts have been 17 | extracted to a separate shifter unit, which can perform logical and arithmetic 18 | right shifts, and by bit-reversing the input and output perform left shifts as 19 | well. 20 | 21 | ```systemverilog 22 | // new shifter code 23 | function [31:0] bitreverse(input [31:0] arg); 24 | for (integer i = 0; i < 32; i++) bitreverse[i] = arg[31-i]; 25 | endfunction 26 | wire [32:0] shift_t1 = {insn_funct7[5] && rs1_value[31], insn_funct3 == 3'b001 ? bitreverse(rs1_value) : rs1_value}; 27 | `ifdef SHIFTER_BUG 28 | wire [31:0] shift_t2 = $signed(shift_t1) >>> (insn_opcode == OPCODE_OP_IMM ? insn[24:20] : rs2_value[5:0]); 29 | `else 30 | wire [31:0] shift_t2 = $signed(shift_t1) >>> (insn_opcode == OPCODE_OP_IMM ? insn[24:20] : rs2_value[4:0]); 31 | `endif 32 | wire [31:0] shift_out = insn_funct3 == 3'b001 ? bitreverse(shift_t2) : shift_t2; 33 | ``` 34 | 35 | Imagine that we accidentally write an error in the code by using 36 | `rs2_value[5:0]` instead of `rs2_value[4:0]`. This can be tested using the 37 | `SHIFTER_BUG` ifdef in NERV. 38 | 39 | To test if this design is equivalent to the original, we need to write a `.eqy` 40 | file to tell EQY how to process the two designs. 41 | 42 | First, we'll give the Yosys commands to process the known-good ("gold") design: 43 | 44 | ``` 45 | [gold] 46 | read_verilog -sv nerv.sv 47 | prep -top nerv 48 | memory_map 49 | ``` 50 | 51 | We use `read_verilog -sv` to load the file, `prep -top nerv` to synthesise the 52 | design with `nerv` as top module, and then we use `memory_map` to turn the 53 | register file into DFFs. 54 | 55 | The design to be checked for equivalence (the "gate" design) has a similar 56 | script. 57 | 58 | ``` 59 | [gate] 60 | read_verilog -sv -DSHIFTER_BUG nerv_change.sv 61 | prep -top nerv 62 | memory_map 63 | ``` 64 | 65 | Then we need to tell EQY to collect sections that should be proven together, 66 | like the register file and some logic known to be identical. 67 | 68 | ``` 69 | [collect *] 70 | group regfile* 71 | join imm_* 72 | join insn* 73 | ``` 74 | 75 | Finally, we need to tell EQY how to prove these designs equivalent. 76 | 77 | ``` 78 | [strategy sby] 79 | use sby 80 | depth 2 81 | engine smtbmc bitwuzla 82 | ``` 83 | 84 | These are included in `nerv_change_fail.eqy` for convenience. Now we can check 85 | equivalence through `eqy nerv_change_fail.eqy`. 86 | 87 | EQY will give output that ends like this: 88 | 89 | ``` 90 | EQY 16:33:44 [nerv_change_fail] Warning: Failed to prove equivalence for 1/44 partitions: 91 | EQY 16:33:44 [nerv_change_fail] Failed to prove equivalence of partition nerv.next_rd 92 | EQY 16:33:44 [nerv_change_fail] summary: Elapsed clock time [H:MM:SS (secs)]: 0:00:18 (18) 93 | EQY 16:33:44 [nerv_change_fail] summary: Elapsed process time [H:MM:SS (secs)]: 0:00:22 (22) 94 | EQY 16:33:44 [nerv_change_fail] DONE (FAIL, rc=2) 95 | ``` 96 | 97 | And we can see that these designs are not equivalent. EQY will create a trace 98 | of the failing case in `nerv_change_fail/strategies/nerv.next_rd/sby/nerv.next_rd/engine_0/trace.vcd`. 99 | 100 | Examining the trace, we can see that the failing instruction is an `SLL` 101 | with `rs1_value` equal to `0xE0000000`, and an `rs2_value` equal to 102 | `0x1FFFFFE2`. Since RISC-V mandates that `SLL` only looks at the lower 5 bits 103 | of `rs2_value`, this is a left-shift of `0xE0000000` by 2, which should equal 104 | `0x80000000`. However, due to the shifter bug, the shifter looks at the lower 6 105 | bits of `rs2_value`, and performs a left-shift of `0xE0000000` by 34, resulting 106 | in zero. 107 | 108 | Since the bug has been found and fixed, we can run another equivalence check to 109 | ensure that the new design is now equivalent. For NERV, this consists of just 110 | removing the `SHIFTER_BUG` ifdef, which can be found in `nerv_change_pass.eqy`. 111 | Running `eqy nerv_change_pass.eqy`, the output ends in 112 | 113 | ``` 114 | EQY 18:40:56 [nerv_change_pass] Successfully proved designs equivalent 115 | EQY 18:40:56 [nerv_change_pass] summary: Elapsed clock time [H:MM:SS (secs)]: 0:00:21 (21) 116 | EQY 18:40:56 [nerv_change_pass] summary: Elapsed process time [H:MM:SS (secs)]: 0:00:28 (28) 117 | EQY 18:40:56 [nerv_change_pass] DONE (PASS, rc=0) 118 | ``` 119 | 120 | and EQY has proven the designs equivalent. 121 | -------------------------------------------------------------------------------- /examples/nerv/nerv_change_fail.eqy: -------------------------------------------------------------------------------- 1 | [gold] 2 | read_verilog -sv nerv.sv 3 | prep -top nerv 4 | memory_map 5 | 6 | [gate] 7 | read_verilog -sv -DSHIFTER_BUG nerv_change.sv 8 | prep -top nerv 9 | memory_map 10 | 11 | [collect *] 12 | group regfile* 13 | join imm_* 14 | join insn* 15 | 16 | [strategy sby] 17 | use sby 18 | depth 2 19 | engine smtbmc bitwuzla 20 | -------------------------------------------------------------------------------- /examples/nerv/nerv_change_pass.eqy: -------------------------------------------------------------------------------- 1 | [gold] 2 | read_verilog -sv nerv.sv 3 | prep -top nerv 4 | memory_map 5 | 6 | [gate] 7 | read_verilog -sv nerv_change.sv 8 | prep -top nerv 9 | memory_map 10 | 11 | [collect *] 12 | group regfile* 13 | join imm_* 14 | join insn* 15 | 16 | [strategy sby] 17 | use sby 18 | depth 2 19 | engine smtbmc bitwuzla 20 | -------------------------------------------------------------------------------- /examples/nerv/nerv_synth.eqy: -------------------------------------------------------------------------------- 1 | [gold] 2 | read_verilog -formal nerv.sv 3 | hierarchy -check -top nerv 4 | proc 5 | memory -nomap 6 | prep -top nerv 7 | 8 | memory_map 9 | 10 | # only keep FF outputs, avoids partitioning on signals that were changed by optimizations 11 | rename -hide t:$dff %co x:* %% w:* %D 12 | 13 | 14 | [gate] 15 | read_verilog -formal -icells nerv_synth.v 16 | prep -top nerv 17 | 18 | [collect *] 19 | # grouping all of the register file makes things a lot faster 20 | group regfile* 21 | 22 | # would be among the unused signals of the regfile partition which fails without this 23 | bind dmem_wdata 24 | 25 | [strategy sby] 26 | use sby 27 | depth 2 28 | engine smtbmc bitwuzla 29 | -------------------------------------------------------------------------------- /examples/nerv/nerv_synth.ys: -------------------------------------------------------------------------------- 1 | read_verilog -sv nerv.sv 2 | synth -top nerv 3 | write_verilog nerv_synth.v 4 | -------------------------------------------------------------------------------- /examples/picorv32/.gitignore: -------------------------------------------------------------------------------- 1 | /picorv32_modified/ 2 | /picorv32_vivado/ 3 | /init_gate.vcd 4 | /init_gold.vcd 5 | /test_gate.vcd 6 | /test_gold.vcd 7 | /test_gate_uut.v 8 | /test_gold_uut.v 9 | /test_gate 10 | /test_gold 11 | /tryamend_*/ 12 | /tryamend_*.eqy 13 | /tryamend.mk 14 | -------------------------------------------------------------------------------- /examples/picorv32/Makefile: -------------------------------------------------------------------------------- 1 | help: 2 | @echo "" 3 | @echo "make prove_vivado" 4 | @echo "make prove_modified" 5 | @echo " run the formal equivalence proofs" 6 | @echo "" 7 | @echo "make vcdmatch" 8 | @echo " re-generate vcdmatch.out" 9 | @echo "" 10 | @echo "make vivado" 11 | @echo " re-run vivado synthesis" 12 | @echo "" 13 | @echo "make clean" 14 | @echo " remove temporary output files" 15 | @echo "" 16 | 17 | prove_vivado: picorv32_vivado.eqy picorv32_vivado.v picorv32_modified.v 18 | eqy -fj6 picorv32_vivado.eqy 19 | 20 | prove_modified: picorv32_modified.eqy picorv32_modified.v picorv32.v 21 | eqy -fj6 picorv32_modified.eqy 22 | 23 | test_gold.vcd: test_gold firmware.hex 24 | vvp -N test_gold 25 | 26 | test_gate.vcd: test_gate firmware.hex 27 | vvp -N test_gate 28 | 29 | netlists.d/partition.log: picorv32_modified.v picorv32_vivado.v picorv32_vivado.eqy 30 | eqy -fmj6 -d netlists.d picorv32_vivado.eqy 31 | 32 | test_gold_uut.v: netlists.d/partition.log 33 | yosys -qo test_gold_uut.v netlists.d/gold.il 34 | 35 | test_gate_uut.v: netlists.d/partition.log 36 | yosys -qo test_gate_uut.v netlists.d/gate.il 37 | 38 | test_gold: testbench.v test_gold_uut.v 39 | iverilog -o test_gold -D GOLD testbench.v test_gold_uut.v 40 | 41 | test_gate: testbench.v test_gate_uut.v 42 | iverilog -o test_gate testbench.v test_gate_uut.v 43 | 44 | vcdmatch: test_gold.vcd test_gate.vcd 45 | python3 vcdmatch.py | tee vcdmatch.out 46 | 47 | vivado: 48 | rm -f picorv32_vivado.log 49 | bash -c ". /opt/Xilinx/Vivado/2021.2/settings64.sh; vivado -mode batch -nojournal -log picorv32_vivado.log -source picorv32_vivado.tcl" 50 | 51 | check_module: netlists.d/partition.log 52 | cp -r netlists.d/. $@.d 53 | sed '/Fragment-Partition-Matrix:/,/^$$/!d;' $@.d/partition.log 54 | cd $@.d/modules && sby -f picorv32.sby 55 | 56 | check_%: netlists.d/partition.log 57 | cp -r netlists.d/. $@.d 58 | sed '/Fragment-Partition-Matrix:/,/^$$/!d;' $@.d/partition.log 59 | cd $@.d/partitions && sby -f picorv32.$(subst check_,,$@).sby 60 | 61 | prove_%: netlists.d/partition.log 62 | cp -r netlists.d/. $@.d 63 | sed '/Fragment-Partition-Matrix:/,/^$$/!d;' $@.d/partition.log 64 | cd $@.d/strategies/picorv32.$(subst prove_,,$@)/pdr* && sby -f picorv32.$(subst prove_,,$@).sby 65 | 66 | watch: 67 | @while \ 68 | files=`sh -c 'fuser */strategies/*/*/*/logfile.txt' 2>&1 | cut -f1 -d: | sed "s,^$$PWD/,,"`; \ 69 | test -n "$$files"; \ 70 | do \ 71 | clear; \ 72 | ! timeout 30 tail -vfn4 `ls -rt $$files`; \ 73 | done 74 | 75 | 76 | show_partition_list: 77 | sed 's/ : / :\n/' picorv32_vivado/partition.list | fold -s | sed '/ :/ ! s/^/ /' 78 | 79 | show_partition_matrix: 80 | sed '/^Partition.Summary:/,/^$$/!d;' picorv32_vivado/partition.log 81 | 82 | show_partition_sizes: 83 | jq -c '{(input_filename): {gold_cells: .gold_module.cellcount, gate_cells: .gate_module.cellcount}}' picorv32_vivado/modules/picorv32.json picorv32_vivado/partitions/*.json 84 | 85 | show_partition_runtimes: 86 | grep -h Elapsed.clock picorv32_vivado/strategies/*/*/*/logfile.txt | gawk '{print$$10,$$3;}' | sort 87 | grep -h Elapsed.proc picorv32_vivado/strategies/*/*/*/logfile.txt | gawk '{print$$10,$$3;}' | sort 88 | 89 | clean: 90 | rm -f test_gold.vcd test_gate.vcd test_gold test_gate 91 | rm -f init_gold.vcd init_gate.vcd vcdmatch.out 92 | rm -rf picorv32_vivado picorv32_modified 93 | -------------------------------------------------------------------------------- /examples/picorv32/README.md: -------------------------------------------------------------------------------- 1 | # Using EQY to get a formally verified synthesis result from Xilinx Vivado 2 | 3 | This directory contains an example project that demonstrates a methodology for 4 | creating formally verified netlist using EQY and Xilinx Vivado. The example 5 | design we are using is PicoRV32 in its default configuration, and the 6 | architecture we are targetting is Kintex-7. 7 | 8 | The file [picorv32.v](picorv32.v) contains the unmodified original PicoRV32 9 | Verilog HDL code. From this we first created a functionally equivalent 10 | [picorv32_modified.v](picorv32_modified.v) that eliminates some of the 11 | X-propagation from the design. The EQY project [picorv32_modified.eqy](picorv32_modified.eqy) 12 | proves equivalence for the two versions of the design: 13 | 14 | ``` 15 | $ eqy -j6 picorv32_modified.eqy 16 | ... 17 | EQY 17:43:02 [picorv32_modified] Successfully proved designs equivalent 18 | EQY 17:43:02 [picorv32_modified] summary: Elapsed clock time [H:MM:SS (secs)]: 0:00:16 (16) 19 | EQY 17:43:02 [picorv32_modified] summary: Elapsed process time [H:MM:SS (secs)]: 0:00:49 (49) 20 | EQY 17:43:02 [picorv32_modified] DONE (PASS, rc=0) 21 | ``` 22 | 23 | Next we synthesized that modified design with Xilinx Vivado with [picorv32_vivado.tcl](picorv32_vivado.tcl) 24 | into the Verilog netlist [picorv32_vivado.v](picorv32_vivado.v). Equivalence of the synthesis output 25 | with the modified design is proven via the EQY project [picorv32_vivado.eqy](picorv32_vivado.eqy): 26 | 27 | ``` 28 | $ eqy -j6 picorv32_vivado.eqy 29 | ... 30 | EQY 17:50:43 [picorv32_vivado] Successfully proved designs equivalent 31 | EQY 17:50:43 [picorv32_vivado] summary: Elapsed clock time [H:MM:SS (secs)]: 0:04:16 (256) 32 | EQY 17:50:43 [picorv32_vivado] summary: Elapsed process time [H:MM:SS (secs)]: 0:21:52 (1312) 33 | EQY 17:50:43 [picorv32_vivado] DONE (PASS, rc=0) 34 | ``` 35 | 36 | The following sections describe the techniques we employed to create the [picorv32_vivado.eqy](picorv32_vivado.eqy) file. 37 | 38 | ## Initializing the design 39 | 40 | After reading the gold and gate HDL files, the testbench in [init.v](init.v) is run on both 41 | designs, initializing the registers in both designs to the same state, thus eliminating some 42 | x-bits present in the un-initialzed versions of both design. 43 | 44 | This is achieved by the `sim` commands in the `[gold]` and `[gate]` sections at the top 45 | of the EQY configuration file. 46 | 47 | ## Eliminating false matched points 48 | 49 | By default, EQY assumes that nets in the gold and gate designs with the same 50 | name are meant to be equivalent, and the proof will fail if that is an 51 | incorrect assumption. 52 | 53 | The EQY output directory contains a per-module SBY file to run a bounded model 54 | check for the assumed-equivalent nets: 55 | 56 | ``` 57 | $ eqy -fm picorv32_vivado.eqy 58 | $ cd picorv32_vivado/modules/ 59 | $ sby picorv32.sby 60 | ``` 61 | 62 | The nets found not to be equivalent by this check have then been excluded from 63 | matching each other using `gate-nomatch` statements in the EQY configuration file. 64 | 65 | ## Finding additional matching points 66 | 67 | Additional equivalent points in the design are identified by comparing simulation 68 | traces from running the same testbench on both designs. 69 | 70 | First, the gold and gate netlists are extracted from the EQY output directory: 71 | 72 | ``` 73 | $ yosys -qo test_gold_uut.v picorv32_vivado/gold.il 74 | $ yosys -qo test_gate_uut.v picorv32_vivado/gate.il 75 | ``` 76 | 77 | Next we compile and run the testbench on both designs: 78 | 79 | ``` 80 | $ iverilog -o test_gold -D GOLD testbench.v test_gold_uut.v 81 | $ iverilog -o test_gate testbench.v test_gate_uut.v 82 | $ vvp -N test_gold 83 | $ vvp -N test_gate 84 | ``` 85 | 86 | Then we run [vcdmatch.py](vcdmatch.py) to extract candidates for equivalent 87 | nets: 88 | 89 | ``` 90 | python3 vcdmatch.py | tee vcdmatch.out 91 | ``` 92 | 93 | After that we added additional rules to the EQY configuration file for the 94 | candidates identified by `vcdmatch.py`. 95 | 96 | Finally we re-generated the EQY output directory and re-ran the module-level 97 | bounded model check from the previous section above, to verify we have not 98 | accidentally added false equivalent points. 99 | 100 | ## Generating partitions and amend-rules 101 | 102 | Next we added rules to group the *fragments* EQY extraced from gold and gate 103 | designs into *partitions*, using `group` and `merge` rules in the EQY configuration 104 | file. For this we select the fragments that we want to merge into the same partition 105 | based on shared logic and signals, and related logic function. 106 | 107 | Finally we needed to figure out which fragments of the gold design we need 108 | to add to which EQY partition as additional constraints. The easiest way 109 | of doing that is by first amending and excessive amount of fragments, and 110 | then narrowing down that list. 111 | 112 | The partitions with more fragments amended than needed are usually too complex 113 | to PASS a complete proof. But EQY generates a per-partition bounded model check 114 | for each partition, that can be used to test a configuration. For example: 115 | 116 | ``` 117 | $ eqy -fm picorv32_vivado.eqy 118 | $ cd picorv32_vivado/partitions/ 119 | $ sby picorv32.3_cpu_state.sby 120 | ``` 121 | 122 | The script [tryamend.sh](tryamend.sh) can be useful for the task of stripping 123 | down amend rules. First, we prefix the individual rules we want to test with 124 | tags in the form `{name}`, for example: 125 | 126 | ``` 127 | {a} amend mem_do_prefetch cpu_state[] 128 | {b} amend mem_do_rdata cpu_state[] 129 | {c} amend mem_do_wdata cpu_state[] 130 | {d} amend decoded_imm_j[] cpu_state[] 131 | {e} amend is_rdcycle_* cpu_state[] 132 | {f} amend instr_trap cpu_state[] 133 | ``` 134 | 135 | Running `bash tryamend.sh 3_cpu_state 12` will create copies of the config 136 | file, each with one of the above lines omitted, and then run the per-partition 137 | bounded model check for the `3_cpu_state` partition. 138 | 139 | A failing test for a line indicates that the fragment was required for the 140 | partition to pass verification. A passing test for a line indicates that 141 | the line can be removed from the configuration file, as the fragment it 142 | amends to the partition generating the `cpu_state` bits seems not needed 143 | to verify the given partition. 144 | -------------------------------------------------------------------------------- /examples/picorv32/firmware.patch: -------------------------------------------------------------------------------- 1 | diff --git a/Makefile b/Makefile 2 | index dd2fbce..5178d90 100644 3 | --- a/Makefile 4 | +++ b/Makefile 5 | @@ -16,7 +16,7 @@ FIRMWARE_OBJS = firmware/start.o firmware/irq.o firmware/print.o firmware/hello. 6 | GCC_WARNS = -Werror -Wall -Wextra -Wshadow -Wundef -Wpointer-arith -Wcast-qual -Wcast-align -Wwrite-strings 7 | GCC_WARNS += -Wredundant-decls -Wstrict-prototypes -Wmissing-prototypes -pedantic # -Wconversion 8 | TOOLCHAIN_PREFIX = $(RISCV_GNU_TOOLCHAIN_INSTALL_PREFIX)i/bin/riscv32-unknown-elf- 9 | -COMPRESSED_ISA = C 10 | +COMPRESSED_ISA = 11 | 12 | # Add things like "export http_proxy=... https_proxy=..." here 13 | GIT_ENV = true 14 | diff --git a/firmware/start.S b/firmware/start.S 15 | index d95f04c..6bcb936 100644 16 | --- a/firmware/start.S 17 | +++ b/firmware/start.S 18 | @@ -5,15 +5,15 @@ 19 | // binary, for any purpose, commercial or non-commercial, and by any 20 | // means. 21 | 22 | -#define ENABLE_QREGS 23 | +//#define ENABLE_QREGS 24 | #define ENABLE_HELLO 25 | #define ENABLE_RVTST 26 | #define ENABLE_SIEVE 27 | -#define ENABLE_MULTST 28 | +//#define ENABLE_MULTST 29 | #define ENABLE_STATS 30 | 31 | #ifndef ENABLE_QREGS 32 | -# undef ENABLE_RVTST 33 | +//# undef ENABLE_RVTST 34 | #endif 35 | 36 | // Only save registers in IRQ wrapper that are to be saved by the caller in 37 | @@ -40,8 +40,8 @@ 38 | 39 | reset_vec: 40 | // no more than 16 bytes here ! 41 | - picorv32_waitirq_insn(zero) 42 | - picorv32_maskirq_insn(zero, zero) 43 | + /* picorv32_waitirq_insn(zero) */ 44 | + /* picorv32_maskirq_insn(zero, zero) */ 45 | j start 46 | 47 | 48 | @@ -344,6 +344,47 @@ irq_stack: 49 | **********************************/ 50 | 51 | start: 52 | + /* uniquely tag all register bits */ 53 | +#define set_all_regs(v) \ 54 | + li x1, v; \ 55 | + mv x2,x1; \ 56 | + mv x3,x1; \ 57 | + mv x4,x1; \ 58 | + mv x5,x1; \ 59 | + mv x6,x1; \ 60 | + mv x7,x1; \ 61 | + mv x8,x1; \ 62 | + mv x9,x1; \ 63 | + mv x10,x1; \ 64 | + mv x11,x1; \ 65 | + mv x12,x1; \ 66 | + mv x13,x1; \ 67 | + mv x14,x1; \ 68 | + mv x15,x1; \ 69 | + mv x16,x1; \ 70 | + mv x17,x1; \ 71 | + mv x18,x1; \ 72 | + mv x19,x1; \ 73 | + mv x20,x1; \ 74 | + mv x21,x1; \ 75 | + mv x22,x1; \ 76 | + mv x23,x1; \ 77 | + mv x24,x1; \ 78 | + mv x25,x1; \ 79 | + mv x26,x1; \ 80 | + mv x27,x1; \ 81 | + mv x28,x1; \ 82 | + mv x29,x1; \ 83 | + mv x30,x1; \ 84 | + mv x31,x1; \ 85 | + mv x0,x1 86 | + set_all_regs(0xFFFFFFFF) 87 | + set_all_regs(0x0000FFFF) 88 | + set_all_regs(0x00FF00FF) 89 | + set_all_regs(0x0F0F0F0F) 90 | + set_all_regs(0x33333333) 91 | + set_all_regs(0x55555555) 92 | + 93 | /* zero-initialize all registers */ 94 | 95 | addi x1, zero, 0 96 | @@ -392,7 +433,7 @@ start: 97 | # define TEST(n) \ 98 | .global n; \ 99 | addi x1, zero, 1000; \ 100 | - picorv32_timer_insn(zero, x1); \ 101 | + /* picorv32_timer_insn(zero, x1); */ \ 102 | jal zero,n; \ 103 | .global n ## _ret; \ 104 | n ## _ret: 105 | @@ -444,6 +485,7 @@ start: 106 | TEST(or) 107 | TEST(and) 108 | 109 | + j skip_mul_div 110 | TEST(mulh) 111 | TEST(mulhsu) 112 | TEST(mulhu) 113 | @@ -454,6 +496,7 @@ start: 114 | TEST(rem) 115 | TEST(remu) 116 | 117 | +skip_mul_div: 118 | TEST(simple) 119 | 120 | /* set stack pointer */ 121 | diff --git a/testbench.v b/testbench.v 122 | index 9d8e249..d9dfad3 100644 123 | --- a/testbench.v 124 | +++ b/testbench.v 125 | @@ -161,18 +161,18 @@ module picorv32_wrapper #( 126 | `endif 127 | 128 | picorv32_axi #( 129 | -`ifndef SYNTH_TEST 130 | -`ifdef SP_TEST 131 | - .ENABLE_REGS_DUALPORT(0), 132 | -`endif 133 | -`ifdef COMPRESSED_ISA 134 | - .COMPRESSED_ISA(1), 135 | -`endif 136 | - .ENABLE_MUL(1), 137 | - .ENABLE_DIV(1), 138 | - .ENABLE_IRQ(1), 139 | - .ENABLE_TRACE(1) 140 | -`endif 141 | +//`ifndef SYNTH_TEST 142 | +//`ifdef SP_TEST 143 | +// .ENABLE_REGS_DUALPORT(0), 144 | +//`endif 145 | +//`ifdef COMPRESSED_ISA 146 | +// .COMPRESSED_ISA(1), 147 | +//`endif 148 | +// .ENABLE_MUL(1), 149 | +// .ENABLE_DIV(1), 150 | +// .ENABLE_IRQ(1), 151 | +// .ENABLE_TRACE(1) 152 | +//`endif 153 | ) uut ( 154 | .clk (clk ), 155 | .resetn (resetn ), 156 | -------------------------------------------------------------------------------- /examples/picorv32/init.v: -------------------------------------------------------------------------------- 1 | module testbench ( 2 | input clk, resetn, 3 | output mem_valid, 4 | output mem_instr, 5 | output reg mem_ready, 6 | output [31:0] mem_addr, 7 | output [31:0] mem_wdata, 8 | output [ 3:0] mem_wstrb, 9 | output reg [31:0] mem_rdata 10 | ); 11 | picorv32 uut ( 12 | .clk (clk ), 13 | .resetn (resetn ), 14 | .mem_valid (mem_valid), 15 | .mem_instr (mem_instr), 16 | .mem_ready (mem_ready), 17 | .mem_addr (mem_addr ), 18 | .mem_wdata (mem_wdata), 19 | .mem_wstrb (mem_wstrb), 20 | .mem_rdata (mem_rdata) 21 | ); 22 | 23 | always @* begin 24 | mem_ready = mem_valid; 25 | mem_rdata = 32'b 0000000_00000_00000_000_00000_0010011; // ADDI x0,x0,0 26 | case (mem_addr) 27 | 32'h 0000_0000: mem_rdata = 32'b 0000001_00001_00000_000_00001_0010011; // ADDI x1,x0,33 28 | 32'h 0000_0004: mem_rdata = 32'b 0000010_00001_00000_000_00010_0010011; // ADDI x2,x0,65 29 | 32'h 0000_0008: mem_rdata = 32'b 0000100_00001_00000_000_00011_0010011; // ADDI x3,x0,129 30 | 32'h 0000_000C: mem_rdata = 32'b 0001000_00001_00000_000_00100_0010011; // ADDI x4,x0,257 31 | 32'h 0000_0010: mem_rdata = 32'b 0010000_00001_00000_000_00101_0010011; // ADDI x5,x0,513 32 | 32'h 0000_0014: mem_rdata = 32'b 0100000_00001_00000_000_00110_0010011; // ADDI x6,x0,1025 33 | 32'h 0000_0018: mem_rdata = 32'b 0000001_00010_00000_000_00111_0010011; // ADDI x7,x0,34 34 | 32'h 0000_001C: mem_rdata = 32'b 0000010_00010_00000_000_01000_0010011; // ADDI x8,x0,66 35 | 32'h 0000_0020: mem_rdata = 32'b 0000100_00010_00000_000_01001_0010011; // ADDI x9,x0,130 36 | 32'h 0000_0024: mem_rdata = 32'b 0001000_00010_00000_000_01010_0010011; // ADDI x10,x0,258 37 | 32'h 0000_0028: mem_rdata = 32'b 0010000_00010_00000_000_01011_0010011; // ADDI x11,x0,514 38 | 32'h 0000_002C: mem_rdata = 32'b 0100000_00010_00000_000_01100_0010011; // ADDI x12,x0,1026 39 | 32'h 0000_0030: mem_rdata = 32'b 0000001_00100_00000_000_01101_0010011; // ADDI x13,x0,36 40 | 32'h 0000_0034: mem_rdata = 32'b 0000010_00100_00000_000_01110_0010011; // ADDI x14,x0,68 41 | 32'h 0000_0038: mem_rdata = 32'b 0000100_00100_00000_000_01111_0010011; // ADDI x15,x0,132 42 | 32'h 0000_003C: mem_rdata = 32'b 0001000_00100_00000_000_10000_0010011; // ADDI x16,x0,260 43 | 32'h 0000_0040: mem_rdata = 32'b 0010000_00100_00000_000_10001_0010011; // ADDI x17,x0,516 44 | 32'h 0000_0044: mem_rdata = 32'b 0100000_00100_00000_000_10010_0010011; // ADDI x18,x0,1028 45 | 32'h 0000_0048: mem_rdata = 32'b 0000001_01000_00000_000_10011_0010011; // ADDI x19,x0,40 46 | 32'h 0000_004C: mem_rdata = 32'b 0000010_01000_00000_000_10100_0010011; // ADDI x20,x0,72 47 | 32'h 0000_0050: mem_rdata = 32'b 0000100_01000_00000_000_10101_0010011; // ADDI x21,x0,136 48 | 32'h 0000_0054: mem_rdata = 32'b 0001000_01000_00000_000_10110_0010011; // ADDI x22,x0,264 49 | 32'h 0000_0058: mem_rdata = 32'b 0010000_01000_00000_000_10111_0010011; // ADDI x23,x0,520 50 | 32'h 0000_005C: mem_rdata = 32'b 0100000_01000_00000_000_11000_0010011; // ADDI x24,x0,1032 51 | 32'h 0000_0060: mem_rdata = 32'b 0000001_10000_00000_000_11001_0010011; // ADDI x25,x0,48 52 | 32'h 0000_0064: mem_rdata = 32'b 0000010_10000_00000_000_11010_0010011; // ADDI x26,x0,80 53 | 32'h 0000_0068: mem_rdata = 32'b 0000100_10000_00000_000_11011_0010011; // ADDI x27,x0,144 54 | 32'h 0000_006C: mem_rdata = 32'b 0001000_10000_00000_000_11100_0010011; // ADDI x28,x0,272 55 | 32'h 0000_0070: mem_rdata = 32'b 0010000_10000_00000_000_11101_0010011; // ADDI x29,x0,528 56 | 32'h 0000_0074: mem_rdata = 32'b 0100000_10000_00000_000_11110_0010011; // ADDI x30,x0,1040 57 | 32'h 0000_0078: mem_rdata = 32'b 1111111_11111_00000_000_11111_0010011; // ADDI x31,x0,-1 58 | endcase 59 | end 60 | endmodule 61 | -------------------------------------------------------------------------------- /examples/picorv32/picorv32_modified.eqy: -------------------------------------------------------------------------------- 1 | ############################################################## 2 | # 3 | # Gold: Original PicoRV32 Verilog HDL 4 | # Gate: Modified PicoRV32 Verilog HDL 5 | 6 | [options] 7 | 8 | [gold] 9 | read_verilog picorv32.v 10 | prep -top picorv32 11 | memory_map 12 | 13 | [gate] 14 | read_verilog picorv32_modified.v 15 | prep -top picorv32 16 | memory_map 17 | 18 | [collect picorv32] 19 | join * 20 | group is_* 21 | group reg_* 22 | group mem_* 23 | group alu_* 24 | group dbg_* 25 | group pcpi_* 26 | group instr_* 27 | group count_* 28 | group latched_* 29 | group decoded_* 30 | group cpuregs[] 31 | 32 | [strategy simple] 33 | use sat 34 | depth 3 35 | -------------------------------------------------------------------------------- /examples/picorv32/picorv32_vivado.tcl: -------------------------------------------------------------------------------- 1 | # Run with: 2 | # bash -c ". /opt/Xilinx/Vivado/2021.2/settings64.sh; vivado -mode batch -nojournal -log picorv32_vivado.log -source picorv32_vivado.tcl" 3 | 4 | set synth_opts "-verbose" 5 | lappend synth_opts -part xc7k70t-fbg676 6 | lappend synth_opts -max_bram 0 7 | lappend synth_opts -max_uram 0 8 | lappend synth_opts -max_dsp 0 9 | lappend synth_opts -no_srlextract 10 | lappend synth_opts -fsm_extraction off 11 | lappend synth_opts -resource_sharing off 12 | # lappend synth_opts -retiming 13 | 14 | set opt_opts "-verbose" 15 | lappend opt_opts -propconst 16 | lappend opt_opts -merge_equivalent_drivers 17 | # lappend opt_opts -remap 18 | # lappend opt_opts -aggressive_remap 19 | # lappend opt_opts -resynth_remap 20 | # lappend opt_opts -resynth_area 21 | # lappend opt_opts -resynth_seq_area 22 | # lappend opt_opts -muxf_remap 23 | # lappend opt_opts -control_set_merge 24 | 25 | set write_opts "-force" 26 | # lappend write_opts -include_xilinx_libs 27 | # lappend write_opts -include_unisim 28 | # lappend write_opts -mode funcsim 29 | 30 | read_verilog picorv32_modified.v 31 | 32 | synth_design -rtl -top picorv32 33 | # set keep_nets [get_nets cpu_state[*]] 34 | # set_property KEEP true $keep_nets 35 | # set_property DONT_TOUCH true $keep_nets 36 | 37 | synth_design {*}$synth_opts 38 | opt_design {*}$opt_opts 39 | 40 | report_utilization 41 | report_timing 42 | 43 | write_verilog {*}$write_opts picorv32_vivado.v 44 | -------------------------------------------------------------------------------- /examples/picorv32/testbench.v: -------------------------------------------------------------------------------- 1 | // This is free and unencumbered software released into the public domain. 2 | // 3 | // Anyone is free to copy, modify, publish, use, compile, sell, or 4 | // distribute this software, either in source code form or as a compiled 5 | // binary, for any purpose, commercial or non-commercial, and by any 6 | // means. 7 | 8 | `timescale 1 ns / 1 ps 9 | 10 | module testbench; 11 | reg clk = 1; 12 | reg resetn = 0; 13 | reg okay = 0; 14 | wire trap; 15 | 16 | always #5 clk = ~clk; 17 | 18 | initial begin 19 | `ifdef GOLD 20 | $dumpfile("test_gold.vcd"); 21 | $dumpvars(0, testbench); 22 | `else 23 | $dumpfile("test_gate.vcd"); 24 | $dumpvars(0, testbench); 25 | `endif 26 | repeat (5) @(posedge clk); 27 | resetn <= 1; 28 | if (0) begin 29 | // shorter trace for vcdmatch.py development 30 | repeat (1000) @(posedge clk); 31 | $finish; 32 | end 33 | repeat (120000) @(posedge clk); 34 | $stop; 35 | end 36 | 37 | wire mem_valid; 38 | wire mem_instr; 39 | reg mem_ready; 40 | wire [31:0] mem_addr; 41 | wire [31:0] mem_wdata; 42 | wire [3:0] mem_wstrb; 43 | reg [31:0] mem_rdata; 44 | 45 | picorv32 uut ( 46 | .clk (clk ), 47 | .resetn (resetn ), 48 | .trap (trap ), 49 | .mem_valid (mem_valid ), 50 | .mem_instr (mem_instr ), 51 | .mem_ready (mem_ready ), 52 | .mem_addr (mem_addr ), 53 | .mem_wdata (mem_wdata ), 54 | .mem_wstrb (mem_wstrb ), 55 | .mem_rdata (mem_rdata ) 56 | ); 57 | 58 | reg [10:0] hickup_state = 0; 59 | wire hickup = !hickup_state[10:8] && (hickup_state[7] != (hickup_state[6:0] < 7 || (hickup_state[6:0] % 7 == 0) || (hickup_state[6:0] % 5 == 0))); 60 | always @(posedge clk) hickup_state <= hickup_state + 1; 61 | 62 | reg [31:0] memory [0:128*1024/4-1]; 63 | initial $readmemh("firmware.hex", memory); 64 | 65 | always @* begin 66 | mem_ready = 0; 67 | mem_rdata = 0; 68 | if (resetn && mem_valid && !hickup) begin 69 | if (mem_addr < 128*1024) begin 70 | mem_ready = 1; 71 | mem_rdata = memory[mem_addr >> 2]; 72 | end 73 | if (mem_addr == 32'h1000_0000 || mem_addr == 32'h2000_0000) begin 74 | mem_ready = 1; 75 | end 76 | end 77 | end 78 | 79 | always @(posedge clk) begin 80 | if (resetn && mem_valid && mem_ready && mem_wstrb) begin 81 | if (mem_addr < 128*1024) begin 82 | if (mem_wstrb[0]) memory[mem_addr >> 2][ 7: 0] <= mem_wdata[ 7: 0]; 83 | if (mem_wstrb[1]) memory[mem_addr >> 2][15: 8] <= mem_wdata[15: 8]; 84 | if (mem_wstrb[2]) memory[mem_addr >> 2][23:16] <= mem_wdata[23:16]; 85 | if (mem_wstrb[3]) memory[mem_addr >> 2][31:24] <= mem_wdata[31:24]; 86 | end 87 | if (mem_addr == 32'h1000_0000) begin 88 | $write("%c", mem_wdata); 89 | $fflush(); 90 | end 91 | if (mem_addr == 32'h2000_0000) begin 92 | okay <= 1; 93 | end 94 | end 95 | if (resetn && trap) begin 96 | if (okay) $finish; else $stop; 97 | end 98 | end 99 | endmodule 100 | -------------------------------------------------------------------------------- /examples/picorv32/tryamend.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Utility script for finding the right fragments to amend to a partition. 4 | # Add lines with "{tag}" prefixes to picorv32_vivado.eqy, for example: 5 | # {foo} amend foo_reg 6 | # {bar} amend bar_reg 7 | # 8 | # This script will create a derivative .eqy config file for each unique 9 | # tag, with the lines for this tag removed. And it creates a makefile for 10 | # running tests on those derivative .eqy files. 11 | # 12 | # Usage example: 13 | # bash tryamend.sh decoder_trigger 12 14 | 15 | set -ex 16 | 17 | rm -rf tryamend_*.eqy tryamend_*/ tryamend.mk 18 | variants=$(echo $(grep '^{' picorv32_vivado.eqy | sed -e 's/^.//; s/. .*//;' | sort -u)) 19 | 20 | { 21 | echo -n "all:" 22 | for v in $variants; do 23 | echo -n " tryamend_${v}/tryamend.done" 24 | done; echo 25 | } > tryamend.mk 26 | 27 | for v in $variants; do 28 | awk "/^{/ {if (\$1 == \"{$v}\") next; \$1=\"\"; } { print; }" picorv32_vivado.eqy > tryamend_${v}.eqy 29 | { 30 | echo; echo "tryamend_${v}/tryamend.done:" 31 | echo " -eqy -fm tryamend_${v}.eqy" 32 | echo " sed -i 's/ --keep-going / /; /^depth / s/.*/depth ${2:-20}/;' tryamend_${v}/partitions/picorv32.${1}.sby" 33 | echo " -cd tryamend_${v}/partitions; sby -f -d tryamend_${v} picorv32.${1}.sby | tee ../tryamend.log" 34 | echo " touch tryamend_${v}/tryamend.done" 35 | } >> tryamend.mk 36 | done 37 | 38 | make -j6 -f tryamend.mk 39 | tail -n4 tryamend_*/tryamend.log 40 | -------------------------------------------------------------------------------- /examples/picorv32/vcdmatch.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import re 4 | from vcdvcd import VCDVCD 5 | 6 | cpuregs_only_mode = False 7 | exclude_cpuregs = True 8 | 9 | def vcd_extend(v, n): 10 | extbit = v[-1] 11 | if extbit == "1": extbit="0" 12 | return extbit*(n-len(v)) + v 13 | 14 | class VcdData: 15 | def __init__(self): 16 | self.data = dict() 17 | self.refbits = set() 18 | self.onebits = set() 19 | self.zerobits = set() 20 | 21 | self.currentPrefix = None 22 | self.currentTop = None 23 | self.references = None 24 | self.bitnames = None 25 | self.framecnt = None 26 | 27 | def enddefinitions(self, vcd, signals, cur_sig_vals): 28 | """Called by VCDVCD at $enddefinitions""" 29 | for key in vcd.data: 30 | for name in vcd.data[key].references: 31 | if "BUF" in name or "._" in name: continue 32 | if "dbg" in name: continue 33 | if name.startswith(self.currentTop): 34 | name = name[len(self.currentTop):] 35 | if name.startswith("\\"): name = name[1:] 36 | if name.startswith("_"): continue 37 | if name.endswith("_"): continue 38 | if exclude_cpuregs: 39 | if name.startswith("cpuregs_reg_r") and name.endswith("]"): continue 40 | if name.startswith("cpuregs["): continue 41 | if cpuregs_only_mode: 42 | if name.startswith("cpuregs_reg_r") and name.endswith("]"): break 43 | if name.startswith("cpuregs["): break 44 | else: 45 | break 46 | else: 47 | continue 48 | if self.currentDepth == 0 or self.currentDepth > name.count(".") or ".\\mem" in name: 49 | name = self.currentPrefix + name 50 | self.references[key] = name 51 | if vcd.data[key].size == "1": 52 | self.bitnames[name, 0] = name 53 | else: 54 | for idx in range(int(vcd.data[key].size)): 55 | self.bitnames[name, idx] = f"{name}<{idx}>" 56 | print(f"Signal: {name}") 57 | print(f" found {len(self.references)} signals with {len(self.bitnames)} bits", flush=True) 58 | 59 | def time(self, vcd, time, cur_sig_vals): 60 | """Called by VCDVCD whenever a new time is found.""" 61 | if cur_sig_vals[vcd.references_to_ids["testbench.clk"]] == '0': 62 | return 63 | 64 | self.framecnt += 1 65 | if self.framecnt % 10000 == 0: 66 | print(f" frame #{self.framecnt} at {time}", flush=True) 67 | 68 | if time not in self.data: 69 | self.data[time] = dict() 70 | 71 | frame = self.data[time] 72 | for key, name in self.references.items(): 73 | bits = vcd_extend(cur_sig_vals[key], int(vcd.data[key].size)) 74 | for idx, bit in enumerate(reversed(bits)): 75 | refbit = self.bitnames[name, idx] 76 | if bit == "1": 77 | self.onebits.add(refbit) 78 | elif bit == "0": 79 | self.zerobits.add(refbit) 80 | self.refbits.add(refbit) 81 | frame[refbit] = bit 82 | 83 | def value(self, vcd, time, value, identifier_code, cur_sig_vals): 84 | """Called by VCDVCD whenever the value of a signal changes.""" 85 | pass 86 | 87 | def parse(self, vcdfile, prefix, top, depth, signals): 88 | self.currentPrefix = prefix 89 | self.currentTop = top 90 | self.currentDepth = depth 91 | self.references = dict() 92 | self.bitnames = dict() 93 | self.framecnt = 0 94 | 95 | vcd = VCDVCD(vcdfile, signals=signals, callbacks=self, store_tvs=False) 96 | 97 | self.currentPrefix = None 98 | self.currentTop = None 99 | self.currentDepth = None 100 | self.references = None 101 | self.bitnames = None 102 | self.framecnt = None 103 | 104 | 105 | class SignalGroup: 106 | def __init__(self): 107 | self.members = set() 108 | self.zeros = set() 109 | self.ones = set() 110 | self.undefs = set() 111 | self.trace = "" 112 | 113 | def __str__(self): 114 | def show_first_n(s, n): 115 | if len(s) <= n: 116 | return ",".join(sorted(s)) 117 | return ",".join(sorted(s)[0:n])+f"+{len(s)-n}" 118 | return f"""G({show_first_n(self.members, 10)}): 119 | zeros: [{show_first_n(self.zeros, 5)}], 120 | ones: [{show_first_n(self.ones, 5)}], 121 | undefs: [{show_first_n(self.undefs, 5)}], 122 | trace: {self.trace} 123 | """ 124 | 125 | 126 | class SignalDatabase: 127 | def __init__(self): 128 | self.vcdData = VcdData() 129 | 130 | def parse(self, vcdfile, prefix, top, signals=None): 131 | print(f"Reading {top} from {vcdfile} as {prefix}..", flush=True) 132 | self.vcdData.parse(vcdfile, prefix+".", top+".", 1, signals) 133 | 134 | def mk(self): 135 | self.groups.add(group := SignalGroup()) 136 | self.dirtyGroups.add(group) 137 | return group 138 | 139 | def add(self, group, member): 140 | if member not in self.members: 141 | self.members[member] = set() 142 | self.members[member].add(group) 143 | group.members.add(member) 144 | 145 | def rem(self, group, member=None): 146 | if member is None: 147 | for member in group.members: 148 | self.members[member].remove(group) 149 | self.groups.remove(group) 150 | else: 151 | self.dirtyGroups.add(group) 152 | self.members[member].remove(group) 153 | group.members.remove(member) 154 | 155 | def cleanup(self): 156 | for group in self.dirtyGroups: 157 | hasGold = any([m.startswith("gold.") for m in group.members]) 158 | hasGate = any([m.startswith("gate.") for m in group.members]) 159 | if not hasGold or not hasGate: self.rem(group) 160 | self.dirtyGroups = set() 161 | 162 | def process(self): 163 | self.groups = set() 164 | self.dirtyGroups = set() 165 | self.members = dict() 166 | self.const_one = set() 167 | self.const_zero = set() 168 | 169 | rootGroup = self.mk() 170 | for name in self.vcdData.refbits: 171 | found_one = name in self.vcdData.onebits 172 | found_zero = name in self.vcdData.zerobits 173 | if found_one and not found_zero: self.const_one.add(name) 174 | if found_zero and not found_one: self.const_zero.add(name) 175 | if not found_one or not found_zero: continue 176 | self.add(rootGroup, name) 177 | 178 | somethingHappened = True 179 | for time, frame in self.vcdData.data.items(): 180 | if somethingHappened: 181 | maxsizes = list(reversed(sorted([len(g.members) for g in self.groups]))) 182 | print(f"At {time}: {len(self.groups)} groups, max sizes are {maxsizes[:5]}", flush=True) 183 | somethingHappened = False 184 | 185 | for name, bit in frame.items(): 186 | if name not in self.members: continue 187 | for group in self.members[name]: 188 | if bit == "0": 189 | group.zeros.add(name) 190 | elif bit == "1": 191 | group.ones.add(name) 192 | elif bit == "x" or bit == "z": 193 | group.undefs.add(name) 194 | else: 195 | assert False 196 | 197 | for group in list(self.groups): 198 | if len(group.zeros) and len(group.ones): 199 | somethingHappened = True 200 | newGroup = self.mk() 201 | for name in group.ones: 202 | self.add(newGroup, name) 203 | self.rem(group, name) 204 | for name in group.undefs: 205 | self.add(newGroup, name) 206 | newGroup.trace = group.trace + f"1" 207 | group.trace += f"0" 208 | else: 209 | group.trace += "_" if len(group.zeros) else "-" if len(group.ones) else "x" 210 | 211 | group.zeros = set() 212 | group.ones = set() 213 | group.undefs = set() 214 | 215 | self.cleanup() 216 | 217 | def print(self): 218 | print(f"Found {len(self.const_zero)} constant zero signals:") 219 | for name in sorted(self.const_zero): 220 | print(f" {name}") 221 | print(f"Found {len(self.const_one)} constant one signals:") 222 | for name in sorted(self.const_one): 223 | print(f" {name}") 224 | 225 | if True: 226 | G = set() 227 | stmts = list() 228 | for group in self.groups: 229 | M = tuple(sorted(group.members)) 230 | for name in M: 231 | if name.startswith("gold."): 232 | n = "gate" + name[4:] 233 | if n in group.members: break 234 | 235 | if name.startswith("gold.cpu_state["): break 236 | if name.startswith("gold.cpuregs_rs1["): break 237 | if name.startswith("gold.next_pc["): break 238 | if name.startswith("gold.mem_rdata_latched[["): break 239 | if name.startswith("gold.cpuregs_rs2[["): break 240 | 241 | if name == "gold.instr_trap": break 242 | if name == "gold.is_rdcycle_rdcycleh_rdinstr_rdinstrh": break 243 | 244 | if name.startswith("gold.alu_out["): break 245 | if name.startswith("gold.count_instr["): break 246 | if name.startswith("gold.mem_rdata_q["): break 247 | if name.startswith("gold.mem_state["): break 248 | if name.startswith("gold.mem_wordsize["): break 249 | if name.startswith("gold.reg_next_pc["): break 250 | if name.startswith("gold.reg_out["): break 251 | if name.startswith("gold.reg_pc["): break 252 | if name.startswith("gold.reg_sh["): break 253 | 254 | if name == "gold.decoder_pseudo_trigger": break 255 | if name == "gold.decoder_trigger": break 256 | if name == "gold.latched_branch": break 257 | if name == "gold.latched_is_lb": break 258 | if name == "gold.latched_is_lh": break 259 | if name == "gold.latched_is_lu": break 260 | if name == "gold.latched_stalu": break 261 | if name == "gold.latched_store": break 262 | if name == "gold.mem_do_prefetch": break 263 | if name == "gold.mem_do_rinst": break 264 | else: 265 | if len(M) == 2: 266 | gate, gold = M 267 | gate = re.sub(r"\[\d+:\d+\]<(\d+)>$", r"[\1]", gate) 268 | gold = re.sub(r"\[\d+:\d+\]<(\d+)>$", r"[\1]", gold) 269 | stmts.append(f"final-gold-match {gold[5:]:30} {gate[5:]}") 270 | else: 271 | G.add(M) 272 | 273 | print(f"Left with {len(G)} groups after sorting and filtering{':' if G else '.'}") 274 | for idx, group in enumerate(sorted(G)): 275 | print(f" Group {idx}:") 276 | for name in group: 277 | print(f" {name}") 278 | for stmt in sorted(stmts): 279 | print(stmt) 280 | else: 281 | for idx, group in enumerate(self.groups): 282 | for name in sorted(group.members): 283 | if name.startswith("gold."): 284 | n = "gate" + name[4:] 285 | if n in group.members: break 286 | print(f" Group {idx}:") 287 | for name in group.members: 288 | print(f" {name}") 289 | 290 | cpuregs = dict() 291 | for group in self.groups: 292 | m = tuple(sorted(group.members)) 293 | if len(m) == 3 and m[0].startswith("gate.cpuregs_reg_r1_") and m[1].startswith("gate.cpuregs_reg_r2_") and m[2].startswith("gold.cpuregs["): 294 | match = re.match(r"gold.cpuregs\[(\d+)\]\[(\d+)\]", m[2]) 295 | cpuregs[int(match[1]), int(match[2])] = (m[2], m[0], m[1]) 296 | if cpuregs: 297 | print(f"Matched {len(cpuregs)} CPU Register bits:") 298 | for k in sorted(cpuregs): 299 | gold_1 = cpuregs[k][0][5:] 300 | gold_2 = re.sub(r"cpuregs\[(\d+)\]", r"dbg_reg_x\1", gold_1) 301 | gate_1 = cpuregs[k][1][5:].replace(".\\", ".") 302 | gate_2 = cpuregs[k][2][5:].replace(".\\", ".") 303 | print(f"gold-match {gold_1} {gate_1}") 304 | print(f"gold-match {gold_2} {gate_2}") 305 | 306 | db = SignalDatabase() 307 | db.parse("test_gold.vcd", "gold", "testbench.uut") 308 | db.parse("test_gate.vcd", "gate", "testbench.uut") 309 | db.process() 310 | db.print() 311 | 312 | -------------------------------------------------------------------------------- /examples/risc16f84/.gitignore: -------------------------------------------------------------------------------- 1 | /risc16f84 2 | -------------------------------------------------------------------------------- /examples/risc16f84/Makefile: -------------------------------------------------------------------------------- 1 | EQY := eqy 2 | 3 | test: risc16f84/FAIL 4 | 5 | risc16f84/FAIL: risc16f84.eqy risc16f84-free.v risc16f84-in.v 6 | -$(EQY) -f risc16f84.eqy 7 | 8 | clean: 9 | rm -rf risc16f84/ 10 | 11 | .PHONY: test clean 12 | -------------------------------------------------------------------------------- /examples/risc16f84/PIC16F84-T300.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YosysHQ/eqy/91b2178dc5b141aba3794d1d088740f9d1a48a54/examples/risc16f84/PIC16F84-T300.pdf -------------------------------------------------------------------------------- /examples/risc16f84/README.md: -------------------------------------------------------------------------------- 1 | PIC16F84-T300 benchmark from [trust-hub](https://trust-hub.org/#/benchmarks/chip-level-trojan) (with the obvious bugfix). 2 | -------------------------------------------------------------------------------- /examples/risc16f84/risc16f84.eqy: -------------------------------------------------------------------------------- 1 | [gold] 2 | read -sv risc16f84-free.v 3 | prep -top pic_16f84_core 4 | async2sync 5 | memory_map 6 | 7 | [gate] 8 | read -sv risc16f84-in.v 9 | prep -top pic_16f84_core 10 | async2sync 11 | memory_map 12 | 13 | [strategy sby] 14 | use sby 15 | depth 2 16 | engine smtbmc bitwuzla 17 | 18 | [collect pic_16f84_core] 19 | join * 20 | group intc* 21 | group wdt_* 22 | group inst_* 23 | group addr_* 24 | group stack_* 25 | group rb*_int 26 | group ram* 27 | group porta* 28 | group portb* 29 | group alu* 30 | group inc_* 31 | group write* 32 | -------------------------------------------------------------------------------- /examples/simple/.gitignore: -------------------------------------------------------------------------------- 1 | /aliases/ 2 | /combine/ 3 | /counter/ 4 | /ex_bind/ 5 | /ex_group/ 6 | /ex_join/ 7 | /ex_amend/ 8 | /fsm/ 9 | /hierarchy/ 10 | /submodules/ 11 | -------------------------------------------------------------------------------- /examples/simple/Makefile: -------------------------------------------------------------------------------- 1 | EQY := eqy 2 | 3 | test: aliases/PASS combine/PASS counter/PASS ex_amend/PASS ex_bind/PASS ex_group/PASS ex_join/PASS fsm/PASS hierarchy/PASS submodules/PASS 4 | 5 | aliases/PASS: aliases.eqy aliases.sv 6 | $(EQY) -f aliases.eqy 7 | 8 | combine/PASS: combine.eqy combine.sv 9 | $(EQY) -f combine.eqy 10 | 11 | counter/PASS: counter.eqy counter.sv 12 | $(EQY) -f counter.eqy 13 | 14 | ex_amend/PASS: ex_amend.eqy ex_amend.sv 15 | $(EQY) -f ex_amend.eqy 16 | 17 | ex_bind/PASS: ex_bind.eqy ex_bind.sv 18 | $(EQY) -f ex_bind.eqy 19 | 20 | ex_group/PASS: ex_group.eqy ex_group.sv 21 | $(EQY) -f ex_group.eqy 22 | 23 | ex_join/PASS: ex_join.eqy ex_join.sv 24 | $(EQY) -f ex_join.eqy 25 | 26 | fsm/PASS: fsm.eqy fsm.sv 27 | $(EQY) -f fsm.eqy 28 | 29 | hierarchy/PASS: hierarchy.eqy hierarchy.sv 30 | $(EQY) -f hierarchy.eqy 31 | 32 | submodules/PASS: submodules.eqy submodules.sv 33 | $(EQY) -f submodules.eqy 34 | 35 | clean: 36 | rm -rf aliases/ combine/ counter/ ex_amend/ ex_bind/ ex_group/ ex_join/ fsm/ hierarchy/ submodules/ 37 | 38 | .PHONY: test clean 39 | -------------------------------------------------------------------------------- /examples/simple/aliases.eqy: -------------------------------------------------------------------------------- 1 | [gold] 2 | read_verilog -DGOLD aliases.sv 3 | prep -top aliases 4 | 5 | [gate] 6 | read_verilog -DGATE aliases.sv 7 | prep -top aliases 8 | 9 | [strategy simple] 10 | use sat 11 | depth 10 12 | -------------------------------------------------------------------------------- /examples/simple/aliases.sv: -------------------------------------------------------------------------------- 1 | `ifdef GOLD 2 | module aliases(input [7:0] A, output [7:0] X, Y, Z); 3 | assign X = A && !A ? 0 : A; 4 | assign Y = X; 5 | assign Z = A && !A ? 23 : 42; 6 | endmodule 7 | `endif 8 | `ifdef GATE 9 | module aliases(input [7:0] A, output [7:0] X, Y, Z); 10 | assign X = A; 11 | assign Y = A; 12 | assign Z = 42; 13 | endmodule 14 | `endif 15 | -------------------------------------------------------------------------------- /examples/simple/combine.eqy: -------------------------------------------------------------------------------- 1 | [options] 2 | 3 | [gold] 4 | read_verilog combine.sv 5 | setattr -set keep_hierarchy 1 */t:pipeline 6 | flatten 7 | prep -top top 8 | 9 | [gate] 10 | read_verilog combine.sv 11 | setattr -set keep_hierarchy 1 */t:multiplier 12 | flatten 13 | prep -top top 14 | 15 | [collect top] 16 | group y *.y,c 17 | group clk *.clk 18 | 19 | [strategy simple] 20 | use sat 21 | depth 10 22 | -------------------------------------------------------------------------------- /examples/simple/combine.sv: -------------------------------------------------------------------------------- 1 | module multiplier(input clk, input [3:0] a, input [3:0] b, output reg [3:0] y); 2 | always @(posedge clk) 3 | y <= a * b; 4 | endmodule 5 | 6 | module pipeline(input clk, input [3:0] a, input [3:0] b, input [3:0] c, output [3:0] y); 7 | wire [3:0] ab; 8 | reg [3:0] c_q; 9 | 10 | always @(posedge clk) 11 | c_q <= c; 12 | 13 | multiplier mult_1(.clk(clk), .a(a), .b(b), .y(ab)); 14 | multiplier mult_2(.clk(clk), .a(ab), .b(c_q), .y(y)); 15 | endmodule 16 | 17 | module top(input clk, input reset, output [3:0] y); 18 | reg [3:0] a; 19 | reg [3:0] b; 20 | reg [3:0] c; 21 | 22 | always @(posedge clk) begin 23 | if (reset) begin 24 | a <= 0; 25 | b <= 0; 26 | c <= 0; 27 | end else begin 28 | a <= a + 1; 29 | b <= b + 1; 30 | c <= c + y; 31 | end 32 | end 33 | 34 | pipeline pipeline(clk, a, b, c, y); 35 | endmodule 36 | -------------------------------------------------------------------------------- /examples/simple/counter.eqy: -------------------------------------------------------------------------------- 1 | [options] 2 | 3 | [gold] 4 | read_verilog counter.sv 5 | prep -top counter 6 | 7 | [gate] 8 | read_verilog counter.sv 9 | synth -top counter 10 | 11 | [strategy simple] 12 | use sat 13 | depth 10 14 | -------------------------------------------------------------------------------- /examples/simple/counter.sv: -------------------------------------------------------------------------------- 1 | module counter(input clk, rst, en, output reg [7:0] state); 2 | always @(posedge clk) 3 | state <= rst ? 0 : state + en; 4 | endmodule 5 | -------------------------------------------------------------------------------- /examples/simple/ex_amend.eqy: -------------------------------------------------------------------------------- 1 | [options] 2 | 3 | [gold] 4 | read_verilog -DGOLD ex_amend.sv 5 | prep -top top 6 | 7 | [gate] 8 | read_verilog -DGATE ex_amend.sv 9 | prep -top top 10 | 11 | [collect top] 12 | join X 13 | join Y 14 | 15 | [partition top] 16 | amend X 17 | 18 | [strategy simple] 19 | use sat 20 | depth 10 21 | -------------------------------------------------------------------------------- /examples/simple/ex_amend.sv: -------------------------------------------------------------------------------- 1 | `ifdef GOLD 2 | module top(input A, B, C, output [1:0] X, Y); 3 | assign X = 1 << (A^B); 4 | assign Y = X ^ {2{C}}; 5 | endmodule 6 | `endif 7 | 8 | `ifdef GATE 9 | module top(input A, B, C, output [1:0] X, Y); 10 | assign X = {A^B, !(A^B)}; 11 | assign Y = 2'b 01 ^ {2{C^X[1]}}; 12 | endmodule 13 | `endif 14 | -------------------------------------------------------------------------------- /examples/simple/ex_bind.eqy: -------------------------------------------------------------------------------- 1 | [options] 2 | 3 | [gold] 4 | read_verilog -DGOLD ex_bind.sv 5 | prep -top top 6 | 7 | [gate] 8 | read_verilog -DGATE ex_bind.sv 9 | prep -top top 10 | 11 | [collect top] 12 | bind X 13 | bind Y 14 | solo * 15 | 16 | [strategy simple] 17 | use sat 18 | depth 10 19 | -------------------------------------------------------------------------------- /examples/simple/ex_bind.sv: -------------------------------------------------------------------------------- 1 | `ifdef GOLD 2 | module top(input A, B, C, output X, Y, Z); 3 | assign X = A ^ B; 4 | assign Y = B ^ C; 5 | assign Z = X ^ Y; 6 | endmodule 7 | `endif 8 | 9 | `ifdef GATE 10 | module top(input A, B, C, output X, Y, Z); 11 | assign X = A ^ B; 12 | assign Y = B ^ C; 13 | assign Z = A ^ C; 14 | endmodule 15 | `endif 16 | -------------------------------------------------------------------------------- /examples/simple/ex_group.eqy: -------------------------------------------------------------------------------- 1 | [options] 2 | 3 | [gold] 4 | read_verilog -DGOLD ex_group.sv 5 | prep -top top 6 | 7 | [gate] 8 | read_verilog -DGATE ex_group.sv 9 | prep -top top 10 | 11 | [collect top] 12 | solo * 13 | group X Y 14 | 15 | [strategy simple] 16 | use sat 17 | depth 10 18 | -------------------------------------------------------------------------------- /examples/simple/ex_group.sv: -------------------------------------------------------------------------------- 1 | `ifdef GOLD 2 | module top(input A, B, C, output X, Y, Z); 3 | assign X = A + B; 4 | assign Y = X + C; 5 | assign Z = B + C; 6 | endmodule 7 | `endif 8 | 9 | `ifdef GATE 10 | module top(input A, B, C, output X, Y, Z); 11 | assign X = A ^ B; 12 | assign Y = X ^ C; 13 | assign Z = B ^ C; 14 | endmodule 15 | `endif 16 | -------------------------------------------------------------------------------- /examples/simple/ex_join.eqy: -------------------------------------------------------------------------------- 1 | [options] 2 | 3 | [gold] 4 | read_verilog -DGOLD ex_join.sv 5 | prep -top top 6 | 7 | [gate] 8 | read_verilog -DGATE ex_join.sv 9 | prep -top top 10 | 11 | [collect top] 12 | solo * 13 | join X 14 | 15 | [strategy simple] 16 | use sat 17 | depth 10 18 | -------------------------------------------------------------------------------- /examples/simple/ex_join.sv: -------------------------------------------------------------------------------- 1 | `ifdef GOLD 2 | module top(input A, B, C, output [1:0] X, Y); 3 | assign X = A + B, Y = A + C; 4 | endmodule 5 | `endif 6 | 7 | `ifdef GATE 8 | module top(input A, B, C, output [1:0] X, Y); 9 | assign X[0] = A ^ B; 10 | assign X[1] = A & B; 11 | assign Y[0] = A ^ C; 12 | assign Y[1] = A & C; 13 | endmodule 14 | `endif 15 | -------------------------------------------------------------------------------- /examples/simple/fsm.eqy: -------------------------------------------------------------------------------- 1 | [options] 2 | 3 | [gold] 4 | read_verilog -DGOLD fsm.sv 5 | prep -top top 6 | 7 | [gate] 8 | read_verilog -DGATE fsm.sv 9 | prep -top top 10 | 11 | [match top] 12 | 13 | [collect top] 14 | 15 | [partition top] 16 | 17 | [recode top currentstate] 18 | 0001 00 19 | 0010 01 20 | 0100 10 21 | 1000 11 22 | 23 | [strategy simple] 24 | use sat 25 | depth 10 26 | -------------------------------------------------------------------------------- /examples/simple/fsm.sv: -------------------------------------------------------------------------------- 1 | module top ( 2 | input clk, 3 | output reg [1:0] o 4 | ); 5 | 6 | `ifdef GOLD 7 | (*keep *) reg [3:0] currentstate = 4'b0001; 8 | always @ (posedge clk) 9 | begin 10 | case (currentstate) 11 | 4'b0001 : begin 12 | o <= 2'b01; 13 | currentstate <= 4'b0010; 14 | end 15 | 4'b0010 : begin 16 | o <= 2'b10; 17 | currentstate <= 4'b0100; 18 | end 19 | 4'b0100: begin 20 | o <= 2'b11; 21 | currentstate <= 4'b1000; 22 | end 23 | default : begin 24 | o <= 2'b00; 25 | currentstate <= 4'b0001; 26 | end 27 | endcase 28 | end 29 | `endif 30 | 31 | `ifdef GATE 32 | (*keep *) reg [1:0] currentstate = 2'b00; 33 | always @ (posedge clk) 34 | begin 35 | case (currentstate) 36 | 2'b00 : begin 37 | o <= 2'b01; 38 | currentstate <= 2'b01; 39 | end 40 | 2'b01 : begin 41 | o <= 2'b10; 42 | currentstate <= 2'b10; 43 | end 44 | 2'b10: begin 45 | o <= 2'b11; 46 | currentstate <= 2'b11; 47 | end 48 | default : begin 49 | o <= 2'b00; 50 | currentstate <= 2'b00; 51 | end 52 | endcase 53 | end 54 | `endif 55 | endmodule 56 | 57 | -------------------------------------------------------------------------------- /examples/simple/hierarchy.eqy: -------------------------------------------------------------------------------- 1 | [options] 2 | 3 | [gold] 4 | read_verilog hierarchy.sv 5 | prep -top top 6 | 7 | [gate] 8 | read_verilog hierarchy.sv 9 | synth -top top 10 | 11 | [match top] 12 | gold-nomatch b 13 | gold-nomatch a 14 | 15 | [strategy simple] 16 | use sat 17 | depth 10 18 | -------------------------------------------------------------------------------- /examples/simple/hierarchy.sv: -------------------------------------------------------------------------------- 1 | module multiplier(input clk, input [3:0] a, input [3:0] b, output reg [3:0] y); 2 | always @(posedge clk) 3 | y <= a * b; 4 | endmodule 5 | 6 | module pipeline(input clk, input [3:0] a, input [3:0] b, input [3:0] c, output [3:0] y); 7 | wire [3:0] ab; 8 | reg [3:0] c_q; 9 | 10 | always @(posedge clk) 11 | c_q <= c; 12 | 13 | multiplier mult_1(.clk(clk), .a(a), .b(b), .y(ab)); 14 | multiplier mult_2(.clk(clk), .a(ab), .b(c_q), .y(y)); 15 | endmodule 16 | 17 | module top(input clk, input reset, output [3:0] y); 18 | reg [3:0] a; 19 | reg [3:0] b; 20 | reg [3:0] c; 21 | 22 | always @(posedge clk) begin 23 | if (reset) begin 24 | a <= 0; 25 | b <= 0; 26 | c <= 0; 27 | end else begin 28 | a <= a + 1; 29 | b <= b + 1; 30 | c <= c + y; 31 | end 32 | end 33 | 34 | pipeline pipeline(clk, a, b, c, y); 35 | endmodule 36 | -------------------------------------------------------------------------------- /examples/simple/submodules.eqy: -------------------------------------------------------------------------------- 1 | [gold] 2 | read_verilog -sv -DGOLD submodules.sv 3 | [gate] 4 | read_verilog -sv -DGATE submodules.sv 5 | 6 | [script] 7 | prep -top top 8 | 9 | [strategy sat] 10 | use sat 11 | depth 5 12 | 13 | [strategy pdr] 14 | use sby 15 | engine abc pdr -rfi 16 | -------------------------------------------------------------------------------- /examples/simple/submodules.sv: -------------------------------------------------------------------------------- 1 | `ifdef GOLD 2 | 3 | 4 | module submod(input [7:0] a, input [7:0] b, output [7:0] outp); 5 | assign outp = a + b; 6 | endmodule 7 | 8 | module top(input clk, input [7:0] a, output [7:0] b); 9 | 10 | reg [15:0] counter = 0; 11 | 12 | always @(posedge clk) 13 | counter <= counter + a; 14 | 15 | 16 | wire [7:0] tmp; 17 | 18 | submod submod(.a(counter[7:0]), .b(counter[15:8]), .outp(tmp)); 19 | 20 | reg [7:0] counter2 = 0; 21 | 22 | always @(posedge clk) 23 | counter2 <= counter2 + tmp; 24 | 25 | assign b = counter2; 26 | 27 | endmodule 28 | `endif 29 | 30 | 31 | `ifdef GATE 32 | 33 | module submod(input [7:0] a, input [7:0] b, output [7:0] outp); 34 | assign outp = ((a * 7) + (b * 7)) * 183; 35 | endmodule 36 | 37 | 38 | module top(input clk, input [7:0] a, output [7:0] b); 39 | reg [15:0] counter_late = 0; 40 | reg [7:0] a_d = 0; 41 | 42 | 43 | always @(posedge clk) 44 | a_d <= a; 45 | 46 | always @(posedge clk) 47 | counter_late <= counter_late + a_d; 48 | 49 | wire [15:0] counter = counter_late + a_d; 50 | 51 | wire [7:0] tmp; 52 | 53 | submod submod(.a(counter[7:0]), .b(counter[15:8]), .outp(tmp)); 54 | 55 | reg [7:0] neg_counter2 = 0; 56 | 57 | always @(posedge clk) 58 | neg_counter2 <= neg_counter2 - tmp; 59 | 60 | assign b = -neg_counter2; 61 | endmodule 62 | 63 | `endif 64 | -------------------------------------------------------------------------------- /examples/spm/.gitignore: -------------------------------------------------------------------------------- 1 | spm/ 2 | -------------------------------------------------------------------------------- /examples/spm/Makefile: -------------------------------------------------------------------------------- 1 | EQY := eqy 2 | 3 | test: spm/PASS 4 | 5 | spm/PASS: spm.eqy spm.v spm.nl.v 6 | -$(EQY) -f spm.eqy 7 | 8 | clean: 9 | rm -rf spm/ 10 | 11 | .PHONY: test clean 12 | -------------------------------------------------------------------------------- /examples/spm/README.md: -------------------------------------------------------------------------------- 1 | # Pre- and Post-Synthesis Equivalence Checking of an OpenLANE SkyWater130 Netlist with EQY 2 | 3 | In this example, the RTL of a very small design is compared to the post-synthesis netlist output by the SkyWater130 OpenLANE flow. 4 | 5 | - ``spm.v``: the original RTL 6 | - ``spm.nl.v``: the final post-synthesis netlist found in ``results/final/verilog`` 7 | - ``primitives.v`` and ``sky130_fd_sc_hd.v``: the cell library simulation models from the SkyWater130 PDK 8 | - ``formal_pdk_proc.py``: a small preprocessing script that makes the simulation models parsable for yosys 9 | -------------------------------------------------------------------------------- /examples/spm/formal_pdk_proc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Preprocessor for formal-friendly Verilog models for the SkyWater PDK 4 | # 5 | # Copyright (C) 2023 Jannis Harder 6 | # 7 | # Permission to use, copy, modify, and/or distribute this software for any 8 | # purpose with or without fee is hereby granted, provided that the above 9 | # copyright notice and this permission notice appear in all copies. 10 | # 11 | # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 | # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | # 19 | 20 | import argparse 21 | import re 22 | import itertools 23 | import sys 24 | 25 | description = "Preprocessor for formal-friendly Verilog models for the SkyWater PDK." 26 | description += """ 27 | This script takes the primitives.v and .v files from the SkyWater PDK and 28 | preprocesses them into a synthesizable Yosys-compatible form, allowing Yosys based 29 | formal verification of Verilog netlists using PDK cells. 30 | 31 | This script isn't very smart and will probably need to be updated when there are 32 | significant changes to the PDK. 33 | 34 | It performs the following actions: 35 | 36 | * Resolves `ifdefs (with an implicit `FUNCTIONAL define) and removes `UNIT_DELAY. This 37 | isn't a full Verilog preprocessor, but it's enough to handle the PDK Verilog files. 38 | 39 | * Adds (* noblackbox *) attributes to all modules, as the PDK contains some modules 40 | without logic. 41 | 42 | * Replaces pullup and pulldown primitives which Yosys doesn't support with mod_pullup 43 | and mod_pulldown instances. 44 | 45 | * Replaces pwrgood with primitives that assume the power is always good. 46 | 47 | * Automatically replaces combinational UDPs with a casez-based module implementation. 48 | 49 | * Replaces the few remaining stateful UDPs with manually written synthesizable modules. 50 | 51 | """ 52 | 53 | parser = argparse.ArgumentParser( 54 | formatter_class=argparse.RawDescriptionHelpFormatter, 55 | description=description, 56 | ) 57 | 58 | parser.add_argument( 59 | "sources", 60 | type=argparse.FileType("r"), 61 | nargs="+", 62 | help="paths to the primitives.v and .v files", 63 | ) 64 | 65 | parser.add_argument( 66 | "--output", 67 | "-o", 68 | type=argparse.FileType("w"), 69 | help="path to the synthesizable verilog output file", 70 | ) 71 | parser.add_argument( 72 | "--define", 73 | "-D", 74 | help="add additional defines (does not support defined values)", 75 | action="append", 76 | default=[], 77 | ) 78 | 79 | args = parser.parse_args() 80 | 81 | 82 | def out(*out_args, **kwds): 83 | print(*out_args, **kwds, file=args.output) 84 | 85 | 86 | defines = set(["FUNCTIONAL", *args.define]) 87 | 88 | 89 | def handle_ifdefs(lines): 90 | ifdef_stack = [] 91 | 92 | current_lines_active = True 93 | 94 | for line in lines: 95 | if "//" in line: 96 | line = line[: line.index("//")] + "\n" 97 | words = line.split() 98 | 99 | if words and words[0] == "`ifdef": 100 | name = words[1] 101 | ifdef_stack.append((name, name in defines)) 102 | current_lines_active = all(cond for name, cond in ifdef_stack) 103 | continue 104 | elif words and words[0] == "`ifndef": 105 | name = words[1] 106 | ifdef_stack.append((name, name not in defines)) 107 | current_lines_active = all(cond for name, cond in ifdef_stack) 108 | continue 109 | elif words and words[0] == "`else": 110 | last_name, last_cond = ifdef_stack.pop() 111 | ifdef_stack.append((last_name, not last_cond)) 112 | current_lines_active = all(cond for name, cond in ifdef_stack) 113 | continue 114 | elif words and words[0] == "`endif": 115 | ifdef_stack.pop() 116 | current_lines_active = all(cond for name, cond in ifdef_stack) 117 | continue 118 | if not current_lines_active: 119 | continue 120 | 121 | if words and words[0] == "`define": 122 | name = words[1] 123 | defines.add(name) 124 | continue 125 | 126 | if current_lines_active: 127 | yield line 128 | 129 | 130 | source = "".join(handle_ifdefs(itertools.chain.from_iterable(args.sources))) 131 | 132 | item_re = re.compile( 133 | r"""(?Pmodule|primitive)\s*(?P\S+)\s*\((?P.*?)\)\s*;""" 134 | r"""(?P.*?)""" 135 | r"""\bend(?P=kind)""", 136 | re.DOTALL, 137 | ) 138 | 139 | supply_re = re.compile(r"supply\s*\((?P.*?)\)\s*;") 140 | 141 | pull_re = re.compile(r"\bpull(up|down)\b") 142 | 143 | 144 | def port_re(direction, name): 145 | return re.compile(rf"\b{re.escape(direction)}\s+{re.escape(name)}\s*;") 146 | 147 | 148 | for found in item_re.finditer(source): 149 | kind = found["kind"] 150 | name = found["name"] 151 | ports = found["ports"].strip() 152 | contents = found["contents"] 153 | 154 | contents = pull_re.subn(lambda match: "mod_pull" + match[1], contents)[0] 155 | 156 | ports = [port.strip() for port in ports.split(",")] if ports else [] 157 | 158 | if kind == "module": 159 | contents = contents.replace("`UNIT_DELAY", "") 160 | 161 | out(f"(* noblackbox *) module {name} ({','.join(ports)});") 162 | out(contents) 163 | out("endmodule") 164 | else: 165 | if "$PG" in name: 166 | continue 167 | 168 | outputs = [] 169 | inputs = [] 170 | regs = [] 171 | table_lines = [] 172 | 173 | lines = iter(contents.split(";")) 174 | 175 | for line in lines: 176 | words = line.split() 177 | 178 | if words and words[0] == "output": 179 | outputs.append(words[1].rstrip(";")) 180 | elif words and words[0] == "input": 181 | inputs.append(words[1].rstrip(";")) 182 | elif words and words[0] == "reg": 183 | regs.append(words[1].rstrip(";")) 184 | elif words and words[0] == "table": 185 | table_lines.append("".join(words[1:])) 186 | for line in lines: 187 | words = line.split() 188 | if words and words[0] == "endtable": 189 | break 190 | table_lines.append("".join(words)) 191 | 192 | assert ports == outputs + inputs 193 | 194 | def start_module(out_regs=False): 195 | out(f"(* noblackbox *) module {name} ({','.join(ports)});") 196 | for port in outputs: 197 | out(f"output {port};") 198 | 199 | if out_regs: 200 | for reg in outputs: 201 | out(f"reg {reg};") 202 | 203 | for port in inputs: 204 | out(f"input {port};") 205 | for reg in regs: 206 | out(f"reg {reg};") 207 | 208 | if "pwrgood" in name and "UDP_IN" in ports and "UDP_OUT" in ports: 209 | start_module() 210 | out("assign UDP_OUT = UDP_IN;") 211 | out("endmodule") 212 | elif not regs: 213 | start_module(out_regs=True) 214 | 215 | out(f"always @* casez ({{{','.join(inputs)}}})") 216 | for line in table_lines: 217 | inbits, outbits = line.split(":") 218 | out( 219 | f" {len(inbits)}'b{inbits}: " 220 | f"{{{','.join(outputs)}}} = {len(outbits)}'b{outbits};" 221 | ) 222 | out("endcase;") 223 | out("endmodule") 224 | elif name.endswith("__udp_dff$P"): 225 | start_module() 226 | 227 | out("always @(posedge CLK) Q <= D;") 228 | out("endmodule") 229 | elif name.endswith("__udp_dff$PR"): 230 | start_module() 231 | 232 | out("always @(posedge CLK or posedge RESET)") 233 | out(" if (RESET) Q <= 1'b0;") 234 | out(" else Q <= D;") 235 | out("endmodule") 236 | elif name.endswith("__udp_dff$PS"): 237 | start_module() 238 | 239 | out("always @(posedge CLK or posedge SET)") 240 | out(" if (SET) Q <= 1'b1;") 241 | out(" else Q <= D;") 242 | out("endmodule") 243 | elif name.endswith("__udp_dff$NSR"): 244 | start_module() 245 | 246 | out("wire AD = SET;") 247 | out("wire AL = SET | RESET;") 248 | 249 | out("always @(negedge CLK_N or posedge AL)") 250 | out(" if (AL) Q <= AD;") 251 | out(" else Q <= D;") 252 | out("endmodule") 253 | elif name.endswith("__udp_dlatch$P") or name.endswith("__udp_dlatch$lP"): 254 | start_module() 255 | 256 | out("always @(GATE or D)") 257 | out(" if (GATE) Q <= D;") 258 | out("endmodule") 259 | elif name.endswith("__udp_dlatch$PR"): 260 | start_module() 261 | 262 | out("wire AG = GATE | RESET;") 263 | out("wire AD = (~RESET) & D;") 264 | 265 | out("always @(AG or AD)") 266 | out(" if (AG) Q <= AD;") 267 | out("endmodule") 268 | else: 269 | print("unknown primitive", name, ports, file=sys.stderr) 270 | print(contents, file=sys.stderr) 271 | 272 | out("(* noblackbox *) module mod_pullup (Y);") 273 | out("output Y;") 274 | out("assign Y = 1'b1;") 275 | out("endmodule") 276 | out("(* noblackbox *) module mod_pulldown (Y);") 277 | out("output Y;") 278 | out("assign Y = 1'b0;") 279 | out("endmodule") 280 | -------------------------------------------------------------------------------- /examples/spm/spm.eqy: -------------------------------------------------------------------------------- 1 | [gold] 2 | read_verilog -formal spm.v 3 | 4 | [gate] 5 | exec -- python3 formal_pdk_proc.py primitives.v sky130_fd_sc_hd.v -o spm/formal_pdk.v 6 | read -sv spm/formal_pdk.v spm.nl.v 7 | 8 | [script] 9 | hierarchy -check -top spm 10 | prep 11 | async2sync 12 | 13 | [strategy sat] 14 | use sat 15 | depth 2 16 | 17 | [strategy sby] 18 | use sby 19 | depth 2 20 | engine smtbmc bitwuzla 21 | -------------------------------------------------------------------------------- /examples/spm/spm.v: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Efabless Corporation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | module spm(clk, rst, x, y, p); 16 | parameter size = 32; 17 | input clk, rst; 18 | input y; 19 | input[size-1:0] x; 20 | output p; 21 | 22 | wire[size-1:1] pp; 23 | wire[size-1:0] xy; 24 | 25 | genvar i; 26 | 27 | CSADD csa0 (.clk(clk), .rst(rst), .x(x[0]&y), .y(pp[1]), .sum(p)); 28 | generate for(i=1; i 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | this software and associated documentation files (the "Software"), to deal in 9 | the Software without restriction, including without limitation the rights to 10 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 11 | of the Software, and to permit persons to whom the Software is furnished to do 12 | so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. */ 24 | 25 | /* Setuptools Script Launcher for Windows 26 | 27 | This is a stub executable for Windows that functions somewhat like 28 | Effbot's "exemaker", in that it runs a script with the same name but 29 | a .py extension, using information from a #! line. It differs in that 30 | it spawns the actual Python executable, rather than attempting to 31 | hook into the Python DLL. This means that the script will run with 32 | sys.executable set to the Python executable, where exemaker ends up with 33 | sys.executable pointing to itself. (Which means it won't work if you try 34 | to run another Python process using sys.executable.) 35 | 36 | To build/rebuild with mingw32, do this in the setuptools project directory: 37 | 38 | gcc -DGUI=0 -mno-cygwin -O -s -o setuptools/cli.exe launcher.c 39 | gcc -DGUI=1 -mwindows -mno-cygwin -O -s -o setuptools/gui.exe launcher.c 40 | 41 | To build for Windows RT, install both Visual Studio Express for Windows 8 42 | and for Windows Desktop (both freeware), create "win32" application using 43 | "Windows Desktop" version, create new "ARM" target via 44 | "Configuration Manager" menu and modify ".vcxproj" file by adding 45 | "true" tag 46 | as child of "PropertyGroup" tags that has "Debug|ARM" and "Release|ARM" 47 | properties. 48 | 49 | It links to msvcrt.dll, but this shouldn't be a problem since it doesn't 50 | actually run Python in the same process. Note that using 'exec' instead 51 | of 'spawn' doesn't work, because on Windows this leads to the Python 52 | executable running in the *background*, attached to the same console 53 | window, meaning you get a command prompt back *before* Python even finishes 54 | starting. So, we have to use spawnv() and wait for Python to exit before 55 | continuing. :( 56 | */ 57 | 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | 66 | int child_pid=0; 67 | 68 | int fail(const char *format, const char *data) { 69 | /* Print error message to stderr and return 2 */ 70 | fprintf(stderr, format, data); 71 | return 2; 72 | } 73 | 74 | char *quoted(char *data) { 75 | int i, ln = strlen(data), nb; 76 | 77 | /* We allocate twice as much space as needed to deal with worse-case 78 | of having to escape everything. */ 79 | char *result = (char *)calloc(ln*2+3, sizeof(char)); 80 | char *presult = result; 81 | 82 | *presult++ = '"'; 83 | for (nb=0, i=0; i < ln; i++) 84 | { 85 | if (data[i] == '\\') 86 | nb += 1; 87 | else if (data[i] == '"') 88 | { 89 | for (; nb > 0; nb--) 90 | *presult++ = '\\'; 91 | *presult++ = '\\'; 92 | } 93 | else 94 | nb = 0; 95 | *presult++ = data[i]; 96 | } 97 | 98 | for (; nb > 0; nb--) /* Deal w trailing slashes */ 99 | *presult++ = '\\'; 100 | 101 | *presult++ = '"'; 102 | *presult++ = 0; 103 | return result; 104 | } 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | char *loadable_exe(char *exename) { 116 | /* HINSTANCE hPython; DLL handle for python executable */ 117 | char *result; 118 | 119 | /* hPython = LoadLibraryEx(exename, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); 120 | if (!hPython) return NULL; */ 121 | 122 | /* Return the absolute filename for spawnv */ 123 | result = (char *)calloc(MAX_PATH, sizeof(char)); 124 | strncpy(result, exename, MAX_PATH); 125 | /*if (result) GetModuleFileNameA(hPython, result, MAX_PATH); 126 | 127 | FreeLibrary(hPython); */ 128 | return result; 129 | } 130 | 131 | 132 | char *find_exe(char *exename, char *script) { 133 | char drive[_MAX_DRIVE], dir[_MAX_DIR], fname[_MAX_FNAME], ext[_MAX_EXT]; 134 | char path[_MAX_PATH], c, *result; 135 | 136 | /* convert slashes to backslashes for uniform search below */ 137 | result = exename; 138 | while (c = *result++) if (c=='/') result[-1] = '\\'; 139 | 140 | _splitpath(exename, drive, dir, fname, ext); 141 | if (drive[0] || dir[0]=='\\') { 142 | return loadable_exe(exename); /* absolute path, use directly */ 143 | } 144 | /* Use the script's parent directory, which should be the Python home 145 | (This should only be used for bdist_wininst-installed scripts, because 146 | easy_install-ed scripts use the absolute path to python[w].exe 147 | */ 148 | _splitpath(script, drive, dir, fname, ext); 149 | result = dir + strlen(dir) -1; 150 | if (*result == '\\') result--; 151 | while (*result != '\\' && result>=dir) *result-- = 0; 152 | _makepath(path, drive, dir, exename, NULL); 153 | return loadable_exe(path); 154 | } 155 | 156 | 157 | char **parse_argv(char *cmdline, int *argc) 158 | { 159 | /* Parse a command line in-place using MS C rules */ 160 | 161 | char **result = (char **)calloc(strlen(cmdline), sizeof(char *)); 162 | char *output = cmdline; 163 | char c; 164 | int nb = 0; 165 | int iq = 0; 166 | *argc = 0; 167 | 168 | result[0] = output; 169 | while (isspace(*cmdline)) cmdline++; /* skip leading spaces */ 170 | 171 | do { 172 | c = *cmdline++; 173 | if (!c || (isspace(c) && !iq)) { 174 | while (nb) {*output++ = '\\'; nb--; } 175 | *output++ = 0; 176 | result[++*argc] = output; 177 | if (!c) return result; 178 | while (isspace(*cmdline)) cmdline++; /* skip leading spaces */ 179 | if (!*cmdline) return result; /* avoid empty arg if trailing ws */ 180 | continue; 181 | } 182 | if (c == '\\') 183 | ++nb; /* count \'s */ 184 | else { 185 | if (c == '"') { 186 | if (!(nb & 1)) { iq = !iq; c = 0; } /* skip " unless odd # of \ */ 187 | nb = nb >> 1; /* cut \'s in half */ 188 | } 189 | while (nb) {*output++ = '\\'; nb--; } 190 | if (c) *output++ = c; 191 | } 192 | } while (1); 193 | } 194 | 195 | void pass_control_to_child(DWORD control_type) { 196 | /* 197 | * distribute-issue207 198 | * passes the control event to child process (Python) 199 | */ 200 | if (!child_pid) { 201 | return; 202 | } 203 | GenerateConsoleCtrlEvent(child_pid,0); 204 | } 205 | 206 | BOOL control_handler(DWORD control_type) { 207 | /* 208 | * distribute-issue207 209 | * control event handler callback function 210 | */ 211 | switch (control_type) { 212 | case CTRL_C_EVENT: 213 | pass_control_to_child(0); 214 | break; 215 | } 216 | return TRUE; 217 | } 218 | 219 | int create_and_wait_for_subprocess(char* command) { 220 | /* 221 | * distribute-issue207 222 | * launches child process (Python) 223 | */ 224 | DWORD return_value = 0; 225 | LPSTR commandline = command; 226 | STARTUPINFOA s_info; 227 | PROCESS_INFORMATION p_info; 228 | ZeroMemory(&p_info, sizeof(p_info)); 229 | ZeroMemory(&s_info, sizeof(s_info)); 230 | s_info.cb = sizeof(STARTUPINFO); 231 | // set-up control handler callback funciotn 232 | SetConsoleCtrlHandler((PHANDLER_ROUTINE) control_handler, TRUE); 233 | if (!CreateProcessA(NULL, commandline, NULL, NULL, TRUE, 0, NULL, NULL, &s_info, &p_info)) { 234 | fprintf(stderr, "failed to create process.\n"); 235 | return 0; 236 | } 237 | child_pid = p_info.dwProcessId; 238 | // wait for Python to exit 239 | WaitForSingleObject(p_info.hProcess, INFINITE); 240 | if (!GetExitCodeProcess(p_info.hProcess, &return_value)) { 241 | fprintf(stderr, "failed to get exit code from process.\n"); 242 | return 0; 243 | } 244 | return return_value; 245 | } 246 | 247 | char* join_executable_and_args(char *executable, char **args, int argc) 248 | { 249 | /* 250 | * distribute-issue207 251 | * CreateProcess needs a long string of the executable and command-line arguments, 252 | * so we need to convert it from the args that was built 253 | */ 254 | int len,counter; 255 | char* cmdline; 256 | 257 | len=strlen(executable)+2; 258 | for (counter=1; counterscript && *end != '.') 289 | *end-- = '\0'; 290 | *end-- = '\0'; 291 | strcat(script, (GUI ? "-script.pyw" : "-script.py")); 292 | 293 | /* figure out the target python executable */ 294 | 295 | scriptf = open(script, O_RDONLY); 296 | if (scriptf == -1) { 297 | return fail("Cannot open %s\n", script); 298 | } 299 | end = python + read(scriptf, python, sizeof(python)); 300 | close(scriptf); 301 | 302 | ptr = python-1; 303 | while(++ptr < end && *ptr && *ptr!='\n' && *ptr!='\r') {;} 304 | 305 | *ptr-- = '\0'; 306 | 307 | if (strncmp(python, "#!", 2)) { 308 | /* default to python.exe if no #! header */ 309 | strcpy(python, "#!python.exe"); 310 | } 311 | 312 | parsedargs = parse_argv(python+2, &parsedargc); 313 | 314 | /* Using spawnv() can fail strangely if you e.g. find the Cygwin 315 | Python, so we'll make sure Windows can find and load it */ 316 | 317 | ptr = find_exe(parsedargs[0], script); 318 | if (!ptr) { 319 | return fail("Cannot find Python executable %s\n", parsedargs[0]); 320 | } 321 | 322 | /* printf("Python executable: %s\n", ptr); */ 323 | 324 | /* Argument array needs to be 325 | parsedargc + argc, plus 1 for null sentinel */ 326 | 327 | newargs = (char **)calloc(parsedargc + argc + 1, sizeof(char *)); 328 | newargsp = newargs; 329 | 330 | *newargsp++ = quoted(ptr); 331 | for (i = 1; i 5 | Copyright (C) 2020 N. Engelhardt 6 | 7 | Permission to use, copy, modify, and/or distribute this software for any 8 | purpose with or without fee is hereby granted, provided that the above 9 | copyright notice and this permission notice appear in all copies. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | */ 19 | 20 | #include "kernel/yosys.h" 21 | 22 | USING_YOSYS_NAMESPACE 23 | PRIVATE_NAMESPACE_BEGIN 24 | 25 | struct EqyCombinePass : public Pass 26 | { 27 | EqyCombinePass() : Pass("eqy_combine", "combine gate and gold designs for eqy") { } 28 | 29 | void help() override 30 | { 31 | // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| 32 | log("\n"); 33 | log(" eqy_combine -gold_ids file -gate_ids file\n"); 34 | log("\n"); 35 | log("Combine gate and gold designs, partially flattening each design until their\n"); 36 | log("hierarchies match. Designs \"gate\" and \"gold\" need to have been previously\n"); 37 | log("saved under these names. For use by EQY.\n"); 38 | log("\n"); 39 | log(" -gold_ids \n"); 40 | log(" Dump IDs of public cells and wires in gold module to file\n"); 41 | log("\n"); 42 | log(" -gate_ids \n"); 43 | log(" Dump IDs of public cells and wires in gate module to file\n"); 44 | log("\n"); 45 | log(" -nocombine\n"); 46 | log(" Skip generating miter, just generate IDs\n"); 47 | log("\n"); 48 | } 49 | 50 | void co_flatten_worker(RTLIL::Selection &sel, Design *work, Design *other, Module *mod) 51 | { 52 | // TODO: check that the interfaces of both modules are compatible 53 | // recoding should be done before this so the interfaces match already 54 | for (auto cell : mod->cells()) 55 | { 56 | Module *work_submod = work->module(cell->type); 57 | Module *other_submod = other->module(cell->type); 58 | if (work_submod) 59 | { 60 | if(other_submod) 61 | co_flatten_worker(sel, work, other, work_submod); 62 | else 63 | sel.select(mod, cell); 64 | } 65 | } 66 | } 67 | 68 | bool co_flatten(Design *work, Design *other) 69 | { 70 | RTLIL::Selection sel(false); 71 | co_flatten_worker(sel, work, other, work->top_module()); 72 | if (sel.empty()) 73 | return false; 74 | Pass::call_on_selection(work, sel, "flatten"); 75 | Pass::call(work, "hierarchy"); 76 | return true; 77 | } 78 | 79 | void print_ids(FILE *file, Module *m) 80 | { 81 | fprintf(file, "%s .", unescape_id(m->name).c_str()); 82 | for (string name : m->get_hdlname_attribute()) 83 | if (unescape_id(m->name) != name) 84 | fprintf(file, " N=%s", name.c_str()); 85 | for (auto &a : m->attributes) { 86 | if (a.first == ID::hdlname) 87 | continue; 88 | if (a.second.flags == RTLIL::CONST_FLAG_STRING) 89 | fprintf(file, " A:%s=\"%s\"", unescape_id(a.first).c_str(), a.second.decode_string().c_str()); 90 | else 91 | fprintf(file, " A:%s=%d", unescape_id(a.first).c_str(), a.second.as_int()); 92 | } 93 | fprintf(file, "\n"); 94 | 95 | for (auto c : m->cells()) 96 | if (c->name.isPublic()) 97 | { 98 | fprintf(file, "%s %s c=%s", unescape_id(m->name).c_str(), unescape_id(c->name).c_str(), unescape_id(c->type).c_str()); 99 | for (string name : c->get_hdlname_attribute()) 100 | if (unescape_id(c->name) != name) 101 | fprintf(file, " N=%s", name.c_str()); 102 | for (auto &a : c->attributes) { 103 | if (a.first == ID::hdlname) 104 | continue; 105 | if (a.second.flags == RTLIL::CONST_FLAG_STRING) 106 | fprintf(file, " A:%s=\"%s\"", unescape_id(a.first).c_str(), a.second.decode_string().c_str()); 107 | else 108 | fprintf(file, " A:%s=%d", unescape_id(a.first).c_str(), a.second.as_int()); 109 | } 110 | fprintf(file, "\n"); 111 | } 112 | 113 | for (auto w : m->wires()) 114 | if (w->name.isPublic()) 115 | { 116 | fprintf(file, "%s %s w=%d:%d", unescape_id(m->name).c_str(), unescape_id(w->name).c_str(), w->width - 1 + w->start_offset, w->start_offset); 117 | for (string name : w->get_hdlname_attribute()) 118 | if (unescape_id(w->name) != name) 119 | fprintf(file, " N=%s", name.c_str()); 120 | for (auto &a : w->attributes) { 121 | if (a.first == ID::hdlname) 122 | continue; 123 | if (a.second.flags == RTLIL::CONST_FLAG_STRING) 124 | fprintf(file, " A:%s=\"%s\"", unescape_id(a.first).c_str(), a.second.decode_string().c_str()); 125 | else 126 | fprintf(file, " A:%s=%d", unescape_id(a.first).c_str(), a.second.as_int()); 127 | } 128 | if (w->port_id) 129 | fprintf(file, " P=%s%s", w->port_input ? "I" : "", w->port_output ? "O" : ""); 130 | fprintf(file, "\n"); 131 | } 132 | } 133 | 134 | void execute(std::vector args, Design *design) override 135 | { 136 | FILE *gold_ids = nullptr; 137 | FILE *gate_ids = nullptr; 138 | 139 | size_t argidx; 140 | bool nocombine = false; 141 | for (argidx = 1; argidx < args.size(); argidx++) 142 | { 143 | if ((args[argidx] == "-gold_ids") && argidx+1 < args.size()) { 144 | gold_ids = fopen(args[++argidx].c_str(), "w"); 145 | if (!gold_ids) 146 | log_cmd_error("Can't create file %s.\n", args[argidx].c_str()); 147 | continue; 148 | } 149 | if ((args[argidx] == "-gate_ids") && argidx+1 < args.size()) { 150 | gate_ids = fopen(args[++argidx].c_str(), "w"); 151 | if (!gate_ids) 152 | log_cmd_error("Can't create file %s.\n", args[argidx].c_str()); 153 | continue; 154 | } 155 | if (args[argidx] == "-nocombine") { 156 | nocombine = true; 157 | continue; 158 | } 159 | break; 160 | } 161 | extra_args(args, argidx, design, false); 162 | 163 | if (saved_designs.find("gold") == saved_designs.end()) 164 | log_error("Design \"gold\" not found in saved designs.\n"); 165 | if (saved_designs.find("gate") == saved_designs.end()) 166 | log_error("Design \"gate\" not found in saved designs.\n"); 167 | Design *gold_design = saved_designs.at("gold"); 168 | Design *gate_design = saved_designs.at("gate"); 169 | 170 | if (gold_design->top_module() == nullptr) 171 | log_cmd_error("No \"gold\" top module found!\n"); 172 | if (gate_design->top_module() == nullptr) 173 | log_cmd_error("No \"gate\" top module found!\n"); 174 | IdString gold_top = gold_design->top_module()->name; 175 | IdString gate_top = gate_design->top_module()->name; 176 | if (gold_top != gate_top) 177 | log_error("Top modules of gold and gate do not have the same name.\n"); 178 | 179 | Pass::call(gold_design, "setattr -unset keep_hierarchy"); 180 | Pass::call(gate_design, "setattr -unset keep_hierarchy"); 181 | Pass::call(gold_design, "setattr -unset keep_hierarchy -mod"); 182 | Pass::call(gate_design, "setattr -unset keep_hierarchy -mod"); 183 | 184 | bool did_something = true; 185 | while (did_something) 186 | { 187 | did_something = co_flatten(gold_design, gate_design); 188 | did_something |= co_flatten(gate_design, gold_design); 189 | } 190 | 191 | for (auto m : gold_design->modules()) 192 | { 193 | Module *gold_m = m->clone(); 194 | gold_m->attributes.erase("\\top"); 195 | gold_m->name = "\\gold." + unescape_id(gold_m->name); 196 | design->add(gold_m); 197 | if (!gate_design->module(m->name)) 198 | log_error("Unmatched module exists in gold that does not exist in gate. This should not happen. Please report this bug.\n"); 199 | Module *gate_m = gate_design->module(m->name)->clone(); 200 | gate_m->attributes.erase("\\top"); 201 | gate_m->name = "\\gate." + unescape_id(gate_m->name); 202 | design->add(gate_m); 203 | if (gold_ids) print_ids(gold_ids, m); 204 | } 205 | 206 | for (auto m : gate_design->modules()) 207 | { 208 | if (!gold_design->module(m->name)) 209 | log_error("Unmatched module exists in gate that does not exist in gold. This should not happen. Please report this bug.\n"); 210 | if (gate_ids) 211 | print_ids(gate_ids, m); 212 | } 213 | 214 | if (nocombine) 215 | return; 216 | 217 | for (auto mod : design->modules()) 218 | { 219 | for (auto cell : mod->cells()) 220 | { 221 | IdString new_type = mod->name.substr(0, 6) + unescape_id(cell->type); //no comment 222 | if (mod->design->module(new_type)) 223 | cell->type = new_type; 224 | } 225 | } 226 | 227 | Pass::call(design, std::vector({"miter", "-equiv", "-ignore_gold_x", "-make_assert", "\\gold." + unescape_id(gold_top), "\\gate." + unescape_id(gold_top), "miter"})); 228 | 229 | design->module("\\miter")->set_bool_attribute("\\top"); 230 | 231 | } 232 | } EqyCombinePass; 233 | 234 | PRIVATE_NAMESPACE_END 235 | -------------------------------------------------------------------------------- /src/eqy_recode.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Equivalence Checking with Yosys (eqy) 3 | 4 | Copyright (C) 2022 Miodrag Milanovic 5 | 6 | Permission to use, copy, modify, and/or distribute this software for any 7 | purpose with or without fee is hereby granted, provided that the above 8 | copyright notice and this permission notice appear in all copies. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include "kernel/yosys.h" 20 | 21 | USING_YOSYS_NAMESPACE 22 | PRIVATE_NAMESPACE_BEGIN 23 | 24 | struct EqyRecodePass : public Pass 25 | { 26 | EqyRecodePass() : Pass("eqy_recode", "recode gate design FSM for eqy") { } 27 | 28 | dict>>> read_recode_data(std::string filename) 29 | { 30 | dict>>> matched_ids; 31 | std::ifstream matched_file(filename.c_str()); 32 | if (!matched_file) 33 | log_error("Cannot open file '%s'\n", filename.c_str()); 34 | std::string line; 35 | for (int linenr = 1; std::getline(matched_file, line); linenr++) { 36 | std::vector things = split_tokens(line); 37 | if (things.size() != 4) 38 | log_error("Malformed line %d in file %s\n", linenr, filename.c_str()); 39 | matched_ids[things[0]][things[1]].push_back(std::make_pair(things[2], things[3])); 40 | } 41 | return matched_ids; 42 | } 43 | 44 | void help() override 45 | { 46 | // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| 47 | log("\n"); 48 | log(" eqy_recode -recode file\n"); 49 | log("\n"); 50 | log("Update gate design and add logic for recoding specified states.\n"); 51 | log("For use by EQY.\n"); 52 | log("\n"); 53 | log(" -recode \n"); 54 | log(" Recode data mapping per module entity values.\n"); 55 | log("\n"); 56 | } 57 | 58 | void execute(std::vector args, Design *design) override 59 | { 60 | std::string recode_filename; 61 | 62 | size_t argidx; 63 | for (argidx = 1; argidx < args.size(); argidx++) 64 | { 65 | if ((args[argidx] == "-recode") && argidx+1 < args.size()) { 66 | recode_filename = args[++argidx]; 67 | continue; 68 | } 69 | break; 70 | } 71 | 72 | extra_args(args, argidx, design, false); 73 | 74 | log_header(design, "Executing EQY RECODE task.\n"); 75 | 76 | if (saved_designs.find("gold") == saved_designs.end()) 77 | log_error("Design \"gold\" not found in saved designs.\n"); 78 | 79 | Design *gold_design = saved_designs.at("gold"); 80 | 81 | if (gold_design->top_module() == nullptr) 82 | log_cmd_error("No \"gold\" top module found!\n"); 83 | if (design->top_module() == nullptr) 84 | log_cmd_error("No \"gate\" top module found!\n"); 85 | 86 | IdString gold_top = gold_design->top_module()->name; 87 | IdString gate_top = design->top_module()->name; 88 | if (gold_top != gate_top) 89 | log_error("Top modules of gold and gate do not have the same name.\n"); 90 | 91 | if (recode_filename.empty()) 92 | log_error("Recode data mapping file must be specified.\n"); 93 | 94 | auto recode_data = read_recode_data(recode_filename); 95 | 96 | if (recode_data.empty()) { 97 | log_warning("No recode data found in mapping file.\n"); 98 | return; 99 | } 100 | 101 | for (auto mod : recode_data) { 102 | IdString module_name = RTLIL::escape_id(mod.first); 103 | Module *gate_m = design->module(module_name); 104 | if (!gate_m) 105 | log_error("Module '%s' not found in gate.\n", mod.first.c_str()); 106 | Module *gold_m = gold_design->module(module_name); 107 | if (!gold_m) 108 | log_error("Module '%s' not found in gold.\n", mod.first.c_str()); 109 | for (auto entity : mod.second) { 110 | IdString name = RTLIL::escape_id(entity.first); 111 | IdString new_name = NEW_ID; 112 | Wire *gate_w = gate_m->wire(name); 113 | if (!gate_w) 114 | log_error("Wire '%s' not found in gate.\n", entity.first.c_str()); 115 | Wire *gold_w = gold_m->wire(name); 116 | if (!gold_w) 117 | log_error("Wire '%s' not found in gold.\n", entity.first.c_str()); 118 | gate_m->rename(gate_w, new_name); 119 | gate_w = gate_m->wire(new_name); 120 | Wire *new_wire = gate_m->addWire(name, gold_w->width); 121 | new_wire->start_offset = gold_w->start_offset; 122 | new_wire->upto = gold_w->upto; 123 | 124 | SigSpec sig; 125 | std::string val; 126 | size_t gate_size = gate_w->width; 127 | size_t gold_size = gold_w->width; 128 | for (auto map : entity.second) { 129 | if (gate_size != map.second.size()) 130 | log_error("Mapping gate value '%s' not proper width.\n", map.second.c_str()); 131 | if (gold_size != map.first.size()) 132 | log_error("Mapping gold value '%s' not proper width.\n", map.first.c_str()); 133 | RTLIL::Wire *y_wire = gate_m->addWire(NEW_ID); 134 | gate_m->addEq(NEW_ID, gate_w, Const::from_string(map.second), y_wire); 135 | val.append(map.first); 136 | sig.append(y_wire); 137 | } 138 | sig.reverse(); 139 | 140 | RTLIL::Wire *a_wire = gate_m->addWire(NEW_ID); 141 | gate_m->addEq(NEW_ID, sig, Const(State::S0, sig.size()), a_wire); 142 | 143 | std::string assert_name = stringf("$recode_missing$%s", log_id(new_name)); 144 | auto assert_cell = gate_m->addAssert(assert_name, gate_m->Not(NEW_ID,a_wire), State::S1); 145 | assert_cell->set_bool_attribute(ID::keep); 146 | 147 | gate_m->addPmux(NEW_ID, RTLIL::Const(State::Sx, gold_size), Const::from_string(val), sig, new_wire); 148 | } 149 | } 150 | } 151 | } EqyRecodePass; 152 | 153 | PRIVATE_NAMESPACE_END 154 | -------------------------------------------------------------------------------- /tests/plugin/.gitignore: -------------------------------------------------------------------------------- 1 | /*.log 2 | /*.out 3 | /run-test.mk 4 | -------------------------------------------------------------------------------- /tests/plugin/combine_extra_argument.ys: -------------------------------------------------------------------------------- 1 | logger -expect error "Command syntax error: Extra argument." 1 2 | eqy_combine top 3 | -------------------------------------------------------------------------------- /tests/plugin/combine_gate_create_error.ys: -------------------------------------------------------------------------------- 1 | logger -expect error "Can't create file not_exist/dummy.ids." 1 2 | eqy_combine -gate_ids not_exist/dummy.ids 3 | -------------------------------------------------------------------------------- /tests/plugin/combine_gate_missing.ys: -------------------------------------------------------------------------------- 1 | logger -expect error "Design .gate. not found in saved designs." 1 2 | read_verilog << EOF 3 | module top(); 4 | endmodule 5 | EOF 6 | design -save gold 7 | eqy_combine 8 | -------------------------------------------------------------------------------- /tests/plugin/combine_gold_create_error.ys: -------------------------------------------------------------------------------- 1 | logger -expect error "Can't create file not_exist/dummy.ids." 1 2 | eqy_combine -gold_ids not_exist/dummy.ids 3 | -------------------------------------------------------------------------------- /tests/plugin/combine_gold_missing.ys: -------------------------------------------------------------------------------- 1 | logger -expect error "Design .gold. not found in saved designs." 1 2 | eqy_combine 3 | -------------------------------------------------------------------------------- /tests/plugin/combine_top_gate_missing.ys: -------------------------------------------------------------------------------- 1 | logger -expect error "No .gate. top module found!" 1 2 | 3 | read_verilog << EOF 4 | module top(input i, output o); 5 | assign o = i; 6 | endmodule 7 | EOF 8 | uniquify 9 | hierarchy 10 | design -stash gold 11 | 12 | read_verilog << EOF 13 | module top_new(input i, output o); 14 | endmodule 15 | EOF 16 | uniquify 17 | hierarchy 18 | design -stash gate 19 | 20 | eqy_combine 21 | -------------------------------------------------------------------------------- /tests/plugin/combine_top_gold_missing.ys: -------------------------------------------------------------------------------- 1 | logger -expect error "No .gold. top module found!" 1 2 | 3 | read_verilog << EOF 4 | module top(input i, output o); 5 | endmodule 6 | EOF 7 | uniquify 8 | hierarchy 9 | design -stash gold 10 | 11 | read_verilog << EOF 12 | module top_new(input i, output o); 13 | assign o = i; 14 | endmodule 15 | EOF 16 | uniquify 17 | hierarchy 18 | design -stash gate 19 | 20 | eqy_combine 21 | -------------------------------------------------------------------------------- /tests/plugin/combine_top_not_match.ys: -------------------------------------------------------------------------------- 1 | logger -expect error "Top modules of gold and gate do not have the same name." 1 2 | 3 | read_verilog << EOF 4 | module top(input i, output o); 5 | assign o = i; 6 | endmodule 7 | EOF 8 | uniquify 9 | hierarchy 10 | design -stash gold 11 | 12 | read_verilog << EOF 13 | module top_new(input i, output o); 14 | assign o = i; 15 | endmodule 16 | EOF 17 | uniquify 18 | hierarchy 19 | design -stash gate 20 | 21 | eqy_combine 22 | -------------------------------------------------------------------------------- /tests/plugin/data/counter_combined.il: -------------------------------------------------------------------------------- 1 | # Generated by Yosys 0.25+83 (git sha1 755b753e1, clang 15.0.7 -fPIC -Os) 2 | autoidx 241 3 | attribute \src "counter.sv:1.1-4.10" 4 | module \gate.counter 5 | wire $abc$205$new_n18_ 6 | wire $abc$205$new_n19_ 7 | wire $abc$205$new_n21_ 8 | wire $abc$205$new_n23_ 9 | wire $abc$205$new_n25_ 10 | wire $abc$205$new_n26_ 11 | wire $abc$205$new_n28_ 12 | wire $abc$205$new_n30_ 13 | wire $abc$205$new_n31_ 14 | wire $abc$205$new_n33_ 15 | attribute \force_downto 1 16 | attribute \src "counter.sv:3.22-3.32|/usr/local/bin/../share/yosys/techmap.v:270.23-270.24" 17 | wire width 8 $auto$alumacc.cc:485:replace_alu$7.X 18 | attribute \force_downto 1 19 | attribute \src "counter.sv:3.22-3.32|/usr/local/bin/../share/yosys/techmap.v:270.26-270.27" 20 | wire width 8 $auto$alumacc.cc:485:replace_alu$7.Y 21 | attribute \src "counter.sv:1.22-1.25" 22 | wire input 1 \clk 23 | attribute \src "counter.sv:1.32-1.34" 24 | wire input 3 \en 25 | attribute \src "counter.sv:1.27-1.30" 26 | wire input 2 \rst 27 | attribute \src "counter.sv:1.53-1.58" 28 | wire width 8 output 4 \state 29 | cell $_NOT_ $abc$205$auto$blifparse.cc:386:parse_blif$206 30 | connect \A \state [1] 31 | connect \Y $abc$205$new_n18_ 32 | end 33 | cell $_NAND_ $abc$205$auto$blifparse.cc:386:parse_blif$207 34 | connect \A \state [0] 35 | connect \B \en 36 | connect \Y $abc$205$new_n19_ 37 | end 38 | cell $_XOR_ $abc$205$auto$blifparse.cc:386:parse_blif$208 39 | connect \A $abc$205$new_n19_ 40 | connect \B $abc$205$new_n18_ 41 | connect \Y $auto$alumacc.cc:485:replace_alu$7.Y [1] 42 | end 43 | cell $_OR_ $abc$205$auto$blifparse.cc:386:parse_blif$209 44 | connect \A $abc$205$new_n19_ 45 | connect \B $abc$205$new_n18_ 46 | connect \Y $abc$205$new_n21_ 47 | end 48 | cell $_XNOR_ $abc$205$auto$blifparse.cc:386:parse_blif$210 49 | connect \A $abc$205$new_n21_ 50 | connect \B \state [2] 51 | connect \Y $auto$alumacc.cc:485:replace_alu$7.Y [2] 52 | end 53 | cell $_ANDNOT_ $abc$205$auto$blifparse.cc:386:parse_blif$211 54 | connect \A \state [2] 55 | connect \B $abc$205$new_n21_ 56 | connect \Y $abc$205$new_n23_ 57 | end 58 | cell $_XOR_ $abc$205$auto$blifparse.cc:386:parse_blif$212 59 | connect \A $abc$205$new_n23_ 60 | connect \B \state [3] 61 | connect \Y $auto$alumacc.cc:485:replace_alu$7.Y [3] 62 | end 63 | cell $_NAND_ $abc$205$auto$blifparse.cc:386:parse_blif$213 64 | connect \A \state [3] 65 | connect \B \state [2] 66 | connect \Y $abc$205$new_n25_ 67 | end 68 | cell $_OR_ $abc$205$auto$blifparse.cc:386:parse_blif$214 69 | connect \A $abc$205$new_n25_ 70 | connect \B $abc$205$new_n21_ 71 | connect \Y $abc$205$new_n26_ 72 | end 73 | cell $_XNOR_ $abc$205$auto$blifparse.cc:386:parse_blif$215 74 | connect \A $abc$205$new_n26_ 75 | connect \B \state [4] 76 | connect \Y $auto$alumacc.cc:485:replace_alu$7.Y [4] 77 | end 78 | cell $_ANDNOT_ $abc$205$auto$blifparse.cc:386:parse_blif$216 79 | connect \A \state [4] 80 | connect \B $abc$205$new_n26_ 81 | connect \Y $abc$205$new_n28_ 82 | end 83 | cell $_XOR_ $abc$205$auto$blifparse.cc:386:parse_blif$217 84 | connect \A $abc$205$new_n28_ 85 | connect \B \state [5] 86 | connect \Y $auto$alumacc.cc:485:replace_alu$7.Y [5] 87 | end 88 | cell $_NAND_ $abc$205$auto$blifparse.cc:386:parse_blif$218 89 | connect \A \state [5] 90 | connect \B \state [4] 91 | connect \Y $abc$205$new_n30_ 92 | end 93 | cell $_OR_ $abc$205$auto$blifparse.cc:386:parse_blif$219 94 | connect \A $abc$205$new_n30_ 95 | connect \B $abc$205$new_n26_ 96 | connect \Y $abc$205$new_n31_ 97 | end 98 | cell $_XNOR_ $abc$205$auto$blifparse.cc:386:parse_blif$220 99 | connect \A $abc$205$new_n31_ 100 | connect \B \state [6] 101 | connect \Y $auto$alumacc.cc:485:replace_alu$7.Y [6] 102 | end 103 | cell $_ANDNOT_ $abc$205$auto$blifparse.cc:386:parse_blif$221 104 | connect \A \state [6] 105 | connect \B $abc$205$new_n31_ 106 | connect \Y $abc$205$new_n33_ 107 | end 108 | cell $_XOR_ $abc$205$auto$blifparse.cc:386:parse_blif$222 109 | connect \A $abc$205$new_n33_ 110 | connect \B \state [7] 111 | connect \Y $auto$alumacc.cc:485:replace_alu$7.Y [7] 112 | end 113 | cell $_XOR_ $abc$205$auto$blifparse.cc:386:parse_blif$223 114 | connect \A \state [0] 115 | connect \B \en 116 | connect \Y $auto$alumacc.cc:485:replace_alu$7.X [0] 117 | end 118 | attribute \src "counter.sv:2.2-3.33" 119 | cell $_SDFF_PP0_ $auto$ff.cc:266:slice$87 120 | connect \C \clk 121 | connect \D $auto$alumacc.cc:485:replace_alu$7.X [0] 122 | connect \Q \state [0] 123 | connect \R \rst 124 | end 125 | attribute \src "counter.sv:2.2-3.33" 126 | cell $_SDFF_PP0_ $auto$ff.cc:266:slice$88 127 | connect \C \clk 128 | connect \D $auto$alumacc.cc:485:replace_alu$7.Y [1] 129 | connect \Q \state [1] 130 | connect \R \rst 131 | end 132 | attribute \src "counter.sv:2.2-3.33" 133 | cell $_SDFF_PP0_ $auto$ff.cc:266:slice$89 134 | connect \C \clk 135 | connect \D $auto$alumacc.cc:485:replace_alu$7.Y [2] 136 | connect \Q \state [2] 137 | connect \R \rst 138 | end 139 | attribute \src "counter.sv:2.2-3.33" 140 | cell $_SDFF_PP0_ $auto$ff.cc:266:slice$90 141 | connect \C \clk 142 | connect \D $auto$alumacc.cc:485:replace_alu$7.Y [3] 143 | connect \Q \state [3] 144 | connect \R \rst 145 | end 146 | attribute \src "counter.sv:2.2-3.33" 147 | cell $_SDFF_PP0_ $auto$ff.cc:266:slice$91 148 | connect \C \clk 149 | connect \D $auto$alumacc.cc:485:replace_alu$7.Y [4] 150 | connect \Q \state [4] 151 | connect \R \rst 152 | end 153 | attribute \src "counter.sv:2.2-3.33" 154 | cell $_SDFF_PP0_ $auto$ff.cc:266:slice$92 155 | connect \C \clk 156 | connect \D $auto$alumacc.cc:485:replace_alu$7.Y [5] 157 | connect \Q \state [5] 158 | connect \R \rst 159 | end 160 | attribute \src "counter.sv:2.2-3.33" 161 | cell $_SDFF_PP0_ $auto$ff.cc:266:slice$93 162 | connect \C \clk 163 | connect \D $auto$alumacc.cc:485:replace_alu$7.Y [6] 164 | connect \Q \state [6] 165 | connect \R \rst 166 | end 167 | attribute \src "counter.sv:2.2-3.33" 168 | cell $_SDFF_PP0_ $auto$ff.cc:266:slice$94 169 | connect \C \clk 170 | connect \D $auto$alumacc.cc:485:replace_alu$7.Y [7] 171 | connect \Q \state [7] 172 | connect \R \rst 173 | end 174 | connect $auto$alumacc.cc:485:replace_alu$7.X [7:1] \state [7:1] 175 | connect $auto$alumacc.cc:485:replace_alu$7.Y [0] $auto$alumacc.cc:485:replace_alu$7.X [0] 176 | end 177 | attribute \src "counter.sv:1.1-4.10" 178 | module \gold.counter 179 | attribute \src "counter.sv:2.2-3.33" 180 | wire width 8 $0\state[7:0] 181 | wire width 8 $add$counter.sv:3$2_Y 182 | attribute \src "counter.sv:1.22-1.25" 183 | wire input 1 \clk 184 | attribute \src "counter.sv:1.32-1.34" 185 | wire input 3 \en 186 | attribute \src "counter.sv:1.27-1.30" 187 | wire input 2 \rst 188 | attribute \src "counter.sv:1.53-1.58" 189 | wire width 8 output 4 \state 190 | attribute \src "counter.sv:3.22-3.32" 191 | cell $add $add$counter.sv:3$2 192 | parameter \A_SIGNED 0 193 | parameter \A_WIDTH 8 194 | parameter \B_SIGNED 0 195 | parameter \B_WIDTH 1 196 | parameter \Y_WIDTH 8 197 | connect \A \state 198 | connect \B \en 199 | connect \Y $add$counter.sv:3$2_Y 200 | end 201 | attribute \src "counter.sv:2.2-3.33" 202 | cell $dff $procdff$4 203 | parameter \CLK_POLARITY 1'1 204 | parameter \WIDTH 8 205 | connect \CLK \clk 206 | connect \D $0\state[7:0] 207 | connect \Q \state 208 | end 209 | attribute \src "counter.sv:3.12-3.32" 210 | cell $mux $ternary$counter.sv:3$3 211 | parameter \WIDTH 8 212 | connect \A $add$counter.sv:3$2_Y 213 | connect \B 8'00000000 214 | connect \S \rst 215 | connect \Y $0\state[7:0] 216 | end 217 | end 218 | attribute \top 1 219 | module \miter 220 | wire width 8 $auto$miter.cc:185:create_miter_equiv$224 221 | wire width 8 $auto$miter.cc:198:create_miter_equiv$233 222 | wire width 8 $auto$miter.cc:199:create_miter_equiv$234 223 | wire $auto$miter.cc:229:create_miter_equiv$238 224 | wire width 8 \gate_state 225 | wire width 8 \gold_state 226 | wire input 3 \in_clk 227 | wire input 2 \in_en 228 | wire input 1 \in_rst 229 | wire output 4 \trigger 230 | cell $eqx $auto$miter.cc:187:create_miter_equiv$225 231 | parameter \A_SIGNED 0 232 | parameter \A_WIDTH 1 233 | parameter \B_SIGNED 0 234 | parameter \B_WIDTH 1 235 | parameter \Y_WIDTH 1 236 | connect \A \gold_state [0] 237 | connect \B 1'x 238 | connect \Y $auto$miter.cc:185:create_miter_equiv$224 [0] 239 | end 240 | cell $eqx $auto$miter.cc:187:create_miter_equiv$226 241 | parameter \A_SIGNED 0 242 | parameter \A_WIDTH 1 243 | parameter \B_SIGNED 0 244 | parameter \B_WIDTH 1 245 | parameter \Y_WIDTH 1 246 | connect \A \gold_state [1] 247 | connect \B 1'x 248 | connect \Y $auto$miter.cc:185:create_miter_equiv$224 [1] 249 | end 250 | cell $eqx $auto$miter.cc:187:create_miter_equiv$227 251 | parameter \A_SIGNED 0 252 | parameter \A_WIDTH 1 253 | parameter \B_SIGNED 0 254 | parameter \B_WIDTH 1 255 | parameter \Y_WIDTH 1 256 | connect \A \gold_state [2] 257 | connect \B 1'x 258 | connect \Y $auto$miter.cc:185:create_miter_equiv$224 [2] 259 | end 260 | cell $eqx $auto$miter.cc:187:create_miter_equiv$228 261 | parameter \A_SIGNED 0 262 | parameter \A_WIDTH 1 263 | parameter \B_SIGNED 0 264 | parameter \B_WIDTH 1 265 | parameter \Y_WIDTH 1 266 | connect \A \gold_state [3] 267 | connect \B 1'x 268 | connect \Y $auto$miter.cc:185:create_miter_equiv$224 [3] 269 | end 270 | cell $eqx $auto$miter.cc:187:create_miter_equiv$229 271 | parameter \A_SIGNED 0 272 | parameter \A_WIDTH 1 273 | parameter \B_SIGNED 0 274 | parameter \B_WIDTH 1 275 | parameter \Y_WIDTH 1 276 | connect \A \gold_state [4] 277 | connect \B 1'x 278 | connect \Y $auto$miter.cc:185:create_miter_equiv$224 [4] 279 | end 280 | cell $eqx $auto$miter.cc:187:create_miter_equiv$230 281 | parameter \A_SIGNED 0 282 | parameter \A_WIDTH 1 283 | parameter \B_SIGNED 0 284 | parameter \B_WIDTH 1 285 | parameter \Y_WIDTH 1 286 | connect \A \gold_state [5] 287 | connect \B 1'x 288 | connect \Y $auto$miter.cc:185:create_miter_equiv$224 [5] 289 | end 290 | cell $eqx $auto$miter.cc:187:create_miter_equiv$231 291 | parameter \A_SIGNED 0 292 | parameter \A_WIDTH 1 293 | parameter \B_SIGNED 0 294 | parameter \B_WIDTH 1 295 | parameter \Y_WIDTH 1 296 | connect \A \gold_state [6] 297 | connect \B 1'x 298 | connect \Y $auto$miter.cc:185:create_miter_equiv$224 [6] 299 | end 300 | cell $eqx $auto$miter.cc:187:create_miter_equiv$232 301 | parameter \A_SIGNED 0 302 | parameter \A_WIDTH 1 303 | parameter \B_SIGNED 0 304 | parameter \B_WIDTH 1 305 | parameter \Y_WIDTH 1 306 | connect \A \gold_state [7] 307 | connect \B 1'x 308 | connect \Y $auto$miter.cc:185:create_miter_equiv$224 [7] 309 | end 310 | cell $or $auto$miter.cc:201:create_miter_equiv$235 311 | parameter \A_SIGNED 0 312 | parameter \A_WIDTH 8 313 | parameter \B_SIGNED 0 314 | parameter \B_WIDTH 8 315 | parameter \Y_WIDTH 8 316 | connect \A \gold_state 317 | connect \B $auto$miter.cc:185:create_miter_equiv$224 318 | connect \Y $auto$miter.cc:198:create_miter_equiv$233 319 | end 320 | cell $or $auto$miter.cc:211:create_miter_equiv$236 321 | parameter \A_SIGNED 0 322 | parameter \A_WIDTH 8 323 | parameter \B_SIGNED 0 324 | parameter \B_WIDTH 8 325 | parameter \Y_WIDTH 8 326 | connect \A \gate_state 327 | connect \B $auto$miter.cc:185:create_miter_equiv$224 328 | connect \Y $auto$miter.cc:199:create_miter_equiv$234 329 | end 330 | cell $eqx $auto$miter.cc:221:create_miter_equiv$237 331 | parameter \A_SIGNED 0 332 | parameter \A_WIDTH 8 333 | parameter \B_SIGNED 0 334 | parameter \B_WIDTH 8 335 | parameter \Y_WIDTH 1 336 | connect \A $auto$miter.cc:198:create_miter_equiv$233 337 | connect \B $auto$miter.cc:199:create_miter_equiv$234 338 | connect \Y $auto$miter.cc:229:create_miter_equiv$238 339 | end 340 | cell $assert $auto$miter.cc:274:create_miter_equiv$239 341 | connect \A $auto$miter.cc:229:create_miter_equiv$238 342 | connect \EN 1'1 343 | end 344 | cell $not $auto$miter.cc:282:create_miter_equiv$240 345 | parameter \A_SIGNED 0 346 | parameter \A_WIDTH 1 347 | parameter \Y_WIDTH 1 348 | connect \A $auto$miter.cc:229:create_miter_equiv$238 349 | connect \Y \trigger 350 | end 351 | cell \gate.counter \gate 352 | connect \clk \in_clk 353 | connect \en \in_en 354 | connect \rst \in_rst 355 | connect \state \gate_state 356 | end 357 | cell \gold.counter \gold 358 | connect \clk \in_clk 359 | connect \en \in_en 360 | connect \rst \in_rst 361 | connect \state \gold_state 362 | end 363 | end 364 | -------------------------------------------------------------------------------- /tests/plugin/data/counter_matched.ids: -------------------------------------------------------------------------------- 1 | # [*] gold-match * 2 | counter clk clk 3 | counter en en 4 | counter rst rst 5 | counter state state 6 | -------------------------------------------------------------------------------- /tests/plugin/data/counter_partition.ids: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YosysHQ/eqy/91b2178dc5b141aba3794d1d088740f9d1a48a54/tests/plugin/data/counter_partition.ids -------------------------------------------------------------------------------- /tests/plugin/data/empty.ids: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YosysHQ/eqy/91b2178dc5b141aba3794d1d088740f9d1a48a54/tests/plugin/data/empty.ids -------------------------------------------------------------------------------- /tests/plugin/data/gate_module_error.ids: -------------------------------------------------------------------------------- 1 | top2 currentstate 0001 00 2 | -------------------------------------------------------------------------------- /tests/plugin/data/gate_value_width_error.ids: -------------------------------------------------------------------------------- 1 | top i 0001 00 2 | -------------------------------------------------------------------------------- /tests/plugin/data/gate_wire_missing.ids: -------------------------------------------------------------------------------- 1 | top i1 0001 00 2 | -------------------------------------------------------------------------------- /tests/plugin/data/gold_module_error.ids: -------------------------------------------------------------------------------- 1 | a currentstate 0001 00 2 | -------------------------------------------------------------------------------- /tests/plugin/data/gold_value_width_error.ids: -------------------------------------------------------------------------------- 1 | top i 0001 00 2 | -------------------------------------------------------------------------------- /tests/plugin/data/gold_wire_missing.ids: -------------------------------------------------------------------------------- 1 | top i1 0001 00 2 | -------------------------------------------------------------------------------- /tests/plugin/data/malformed.ids: -------------------------------------------------------------------------------- 1 | top currentstate 0001 00 2 | top currentstate 0010 01 2 3 | -------------------------------------------------------------------------------- /tests/plugin/data/matched_empty.ids: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YosysHQ/eqy/91b2178dc5b141aba3794d1d088740f9d1a48a54/tests/plugin/data/matched_empty.ids -------------------------------------------------------------------------------- /tests/plugin/data/matched_malformed.ids: -------------------------------------------------------------------------------- 1 | # [*] gold-match * 2 | aliases A A extra 3 | -------------------------------------------------------------------------------- /tests/plugin/data/matched_ok.ids: -------------------------------------------------------------------------------- 1 | # [*] gold-match * 2 | aliases A A 3 | -------------------------------------------------------------------------------- /tests/plugin/data/partition_malformed.ids: -------------------------------------------------------------------------------- 1 | amend top X 1 extra 2 | -------------------------------------------------------------------------------- /tests/plugin/help.ys: -------------------------------------------------------------------------------- 1 | help eqy_combine 2 | help eqy_partition 3 | help eqy_recode 4 | -------------------------------------------------------------------------------- /tests/plugin/partition_additional_module.ys: -------------------------------------------------------------------------------- 1 | logger -expect error "Found module top that is neither gate nor gold nor miter." 1 2 | read_verilog << EOF 3 | module top(input i, output o); 4 | assign o = i; 5 | endmodule 6 | EOF 7 | eqy_partition -matched_ids data/counter_matched.ids -partition_ids data/counter_partition.ids 8 | -------------------------------------------------------------------------------- /tests/plugin/partition_extra_argument.ys: -------------------------------------------------------------------------------- 1 | logger -expect error "Command syntax error: Extra argument." 1 2 | eqy_partition top 3 | -------------------------------------------------------------------------------- /tests/plugin/partition_matched_malformed.ys: -------------------------------------------------------------------------------- 1 | logger -expect error "Malformed line" 1 2 | eqy_partition -matched_ids data/matched_malformed.ids -partition_ids notfound.ids 3 | -------------------------------------------------------------------------------- /tests/plugin/partition_matched_not_found.ys: -------------------------------------------------------------------------------- 1 | logger -expect error "Cannot open file" 1 2 | eqy_partition -matched_ids notfound.ids -partition_ids notfound.ids 3 | -------------------------------------------------------------------------------- /tests/plugin/partition_matching_gate_not_found.ys: -------------------------------------------------------------------------------- 1 | logger -expect error "Could not find matching gate for module gold.counter" 1 2 | read_rtlil data/counter_combined.il 3 | insbuf -chain 4 | delete gate.counter 5 | eqy_partition -matched_ids data/counter_matched.ids -partition_ids data/counter_partition.ids -write_fragments 6 | -------------------------------------------------------------------------------- /tests/plugin/partition_mismatched.ys: -------------------------------------------------------------------------------- 1 | logger -expect error "Mismatched number of gate and gold modules" 1 2 | eqy_partition -matched_ids data/counter_matched.ids -partition_ids data/counter_partition.ids 3 | -------------------------------------------------------------------------------- /tests/plugin/partition_no_matched.ys: -------------------------------------------------------------------------------- 1 | logger -expect error "No matched IDs for module counter." 1 2 | read_rtlil data/counter_combined.il 3 | insbuf -chain 4 | eqy_partition -matched_ids data/matched_empty.ids -partition_ids data/counter_partition.ids 5 | -------------------------------------------------------------------------------- /tests/plugin/partition_partition_malformed.ys: -------------------------------------------------------------------------------- 1 | logger -expect error "Malformed line" 1 2 | eqy_partition -matched_ids data/matched_ok.ids -partition_ids data/partition_malformed.ids 3 | -------------------------------------------------------------------------------- /tests/plugin/partition_partition_not_found.ys: -------------------------------------------------------------------------------- 1 | logger -expect error "Cannot open file" 1 2 | eqy_partition -matched_ids data/matched_ok.ids -partition_ids notfound.ids 3 | -------------------------------------------------------------------------------- /tests/plugin/partition_required_params.ys: -------------------------------------------------------------------------------- 1 | logger -expect error "Both matched ids and partition ids parameters are required." 1 2 | eqy_partition -matched_ids notfound.ids 3 | -------------------------------------------------------------------------------- /tests/plugin/partition_write_fragments.ys: -------------------------------------------------------------------------------- 1 | exec -- rm -rf fragments/ modules/ partitions/ partition.list fragment.list 2 | exec -- mkdir -p fragments modules partitions 3 | read_rtlil data/counter_combined.il 4 | insbuf -chain 5 | eqy_partition -matched_ids data/counter_matched.ids -partition_ids data/counter_partition.ids -write_fragments 6 | exec -- rm -rf fragments/ modules/ partitions/ partition.list fragment.list 7 | -------------------------------------------------------------------------------- /tests/plugin/recode_empty_list.ys: -------------------------------------------------------------------------------- 1 | logger -expect warning "No recode data found in mapping file." 1 2 | 3 | read_verilog << EOF 4 | module top(input i, output o); 5 | assign o = i; 6 | endmodule 7 | EOF 8 | uniquify 9 | hierarchy 10 | design -stash gold 11 | 12 | read_verilog << EOF 13 | module top(input i1, output o); 14 | assign o = i1; 15 | endmodule 16 | EOF 17 | uniquify 18 | hierarchy 19 | 20 | eqy_recode -recode data/empty.ids 21 | -------------------------------------------------------------------------------- /tests/plugin/recode_extra_argument.ys: -------------------------------------------------------------------------------- 1 | logger -expect error "Command syntax error: Extra argument." 1 2 | eqy_recode top 3 | -------------------------------------------------------------------------------- /tests/plugin/recode_filename_bad.ys: -------------------------------------------------------------------------------- 1 | logger -expect error "Cannot open file" 1 2 | 3 | read_verilog << EOF 4 | module top(input i, output o); 5 | assign o = i; 6 | endmodule 7 | EOF 8 | uniquify 9 | hierarchy 10 | design -stash gold 11 | 12 | read_verilog << EOF 13 | module top(input i, output o); 14 | assign o = i; 15 | endmodule 16 | EOF 17 | uniquify 18 | hierarchy 19 | 20 | eqy_recode -recode bad.file 21 | -------------------------------------------------------------------------------- /tests/plugin/recode_filename_malformed.ys: -------------------------------------------------------------------------------- 1 | logger -expect error "Malformed line" 1 2 | 3 | read_verilog << EOF 4 | module top(input i, output o); 5 | assign o = i; 6 | endmodule 7 | EOF 8 | uniquify 9 | hierarchy 10 | design -stash gold 11 | 12 | read_verilog << EOF 13 | module top(input i, output o); 14 | assign o = i; 15 | endmodule 16 | EOF 17 | uniquify 18 | hierarchy 19 | 20 | eqy_recode -recode data/malformed.ids 21 | -------------------------------------------------------------------------------- /tests/plugin/recode_filename_missing.ys: -------------------------------------------------------------------------------- 1 | logger -expect error "Recode data mapping file must be specified." 1 2 | 3 | read_verilog << EOF 4 | module top(input i, output o); 5 | assign o = i; 6 | endmodule 7 | EOF 8 | uniquify 9 | hierarchy 10 | design -stash gold 11 | 12 | read_verilog << EOF 13 | module top(input i, output o); 14 | assign o = i; 15 | endmodule 16 | EOF 17 | uniquify 18 | hierarchy 19 | 20 | eqy_recode 21 | -------------------------------------------------------------------------------- /tests/plugin/recode_gate_module_error.ys: -------------------------------------------------------------------------------- 1 | logger -expect error "Module 'top2' not found in gate." 1 2 | 3 | read_verilog << EOF 4 | module top(input i, output o); 5 | assign o = i; 6 | endmodule 7 | EOF 8 | uniquify 9 | hierarchy 10 | design -stash gold 11 | 12 | read_verilog << EOF 13 | module top(input i, output o); 14 | assign o = i; 15 | endmodule 16 | EOF 17 | uniquify 18 | hierarchy 19 | 20 | eqy_recode -recode data/gate_module_error.ids 21 | -------------------------------------------------------------------------------- /tests/plugin/recode_gate_value_width_error.ys: -------------------------------------------------------------------------------- 1 | logger -expect error "Mapping gate value '00' not proper width." 1 2 | 3 | read_verilog << EOF 4 | module top(input i, output o); 5 | assign o = i; 6 | endmodule 7 | EOF 8 | uniquify 9 | hierarchy 10 | design -stash gold 11 | 12 | read_verilog << EOF 13 | module top(input i, output o); 14 | assign o = i; 15 | endmodule 16 | EOF 17 | uniquify 18 | hierarchy 19 | 20 | eqy_recode -recode data/gate_value_width_error.ids 21 | -------------------------------------------------------------------------------- /tests/plugin/recode_gate_wire_missing.ys: -------------------------------------------------------------------------------- 1 | logger -expect error "Wire 'i1' not found in gate." 1 2 | 3 | read_verilog << EOF 4 | module top(input i, output o); 5 | assign o = i; 6 | endmodule 7 | EOF 8 | uniquify 9 | hierarchy 10 | design -stash gold 11 | 12 | read_verilog << EOF 13 | module top(input i, output o); 14 | assign o = i; 15 | endmodule 16 | EOF 17 | uniquify 18 | hierarchy 19 | 20 | eqy_recode -recode data/gate_wire_missing.ids 21 | -------------------------------------------------------------------------------- /tests/plugin/recode_gold_missing.ys: -------------------------------------------------------------------------------- 1 | logger -expect error "Design .gold. not found in saved designs." 1 2 | eqy_recode 3 | -------------------------------------------------------------------------------- /tests/plugin/recode_gold_module_error.ys: -------------------------------------------------------------------------------- 1 | logger -expect error "Module 'a' not found in gold." 1 2 | 3 | read_verilog << EOF 4 | module top(input i, output o); 5 | assign o = i; 6 | endmodule 7 | EOF 8 | uniquify 9 | hierarchy 10 | design -stash gold 11 | 12 | read_verilog << EOF 13 | module a(input i, output o); 14 | assign o = i; 15 | endmodule 16 | 17 | module top(input i, output o); 18 | a t(.i(i),.o(o)); 19 | endmodule 20 | EOF 21 | uniquify 22 | hierarchy -top top 23 | 24 | eqy_recode -recode data/gold_module_error.ids 25 | -------------------------------------------------------------------------------- /tests/plugin/recode_gold_value_width_error.ys: -------------------------------------------------------------------------------- 1 | logger -expect error "Mapping gold value '0001' not proper width." 1 2 | 3 | read_verilog << EOF 4 | module top(input i, output o); 5 | assign o = i; 6 | endmodule 7 | EOF 8 | uniquify 9 | hierarchy 10 | design -stash gold 11 | 12 | read_verilog << EOF 13 | module top(input [1:0] i, output o); 14 | assign o = i; 15 | endmodule 16 | EOF 17 | uniquify 18 | hierarchy 19 | 20 | eqy_recode -recode data/gold_value_width_error.ids 21 | -------------------------------------------------------------------------------- /tests/plugin/recode_gold_wire_missing.ys: -------------------------------------------------------------------------------- 1 | logger -expect error "Wire 'i1' not found in gold." 1 2 | 3 | read_verilog << EOF 4 | module top(input i, output o); 5 | assign o = i; 6 | endmodule 7 | EOF 8 | uniquify 9 | hierarchy 10 | design -stash gold 11 | 12 | read_verilog << EOF 13 | module top(input i1, output o); 14 | assign o = i1; 15 | endmodule 16 | EOF 17 | uniquify 18 | hierarchy 19 | 20 | eqy_recode -recode data/gold_wire_missing.ids 21 | -------------------------------------------------------------------------------- /tests/plugin/recode_top_gate_missing.ys: -------------------------------------------------------------------------------- 1 | logger -expect error "No .gate. top module found!" 1 2 | 3 | read_verilog << EOF 4 | module top(input i, output o); 5 | assign o = i; 6 | endmodule 7 | EOF 8 | uniquify 9 | hierarchy 10 | design -stash gold 11 | 12 | read_verilog << EOF 13 | module top_new(input i, output o); 14 | endmodule 15 | EOF 16 | uniquify 17 | hierarchy 18 | 19 | eqy_recode 20 | -------------------------------------------------------------------------------- /tests/plugin/recode_top_gold_missing.ys: -------------------------------------------------------------------------------- 1 | logger -expect error "No .gold. top module found!" 1 2 | 3 | read_verilog << EOF 4 | module top(input i, output o); 5 | endmodule 6 | EOF 7 | uniquify 8 | hierarchy 9 | design -stash gold 10 | 11 | read_verilog << EOF 12 | module top_new(input i, output o); 13 | assign o = i; 14 | endmodule 15 | EOF 16 | uniquify 17 | hierarchy 18 | 19 | eqy_recode 20 | -------------------------------------------------------------------------------- /tests/plugin/recode_top_not_match.ys: -------------------------------------------------------------------------------- 1 | logger -expect error "Top modules of gold and gate do not have the same name." 1 2 | 3 | read_verilog << EOF 4 | module top(input i, output o); 5 | assign o = i; 6 | endmodule 7 | EOF 8 | uniquify 9 | hierarchy 10 | design -stash gold 11 | 12 | read_verilog << EOF 13 | module top_new(input i, output o); 14 | assign o = i; 15 | endmodule 16 | EOF 17 | uniquify 18 | hierarchy 19 | 20 | eqy_recode 21 | -------------------------------------------------------------------------------- /tests/plugin/run-test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eu 3 | 4 | EQY_BASEDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")"/../../ >/dev/null 2>&1 && pwd)" 5 | 6 | # $ generate_target target_name test_command 7 | generate_target() { 8 | target_name=$1 9 | test_command=$2 10 | echo "all: $target_name" 11 | echo ".PHONY: $target_name" 12 | echo "$target_name:" 13 | printf "\t@%s\n" "$test_command" 14 | printf "\t@echo 'Passed %s'\n" "$target_name" 15 | } 16 | 17 | # $ generate_ys_test ys_file [yosys_args] 18 | generate_ys_test() { 19 | ys_file=$1 20 | yosys_args=${2:-} 21 | generate_target "$ys_file" "yosys -m $EQY_BASEDIR/src/eqy_combine.so -m $EQY_BASEDIR/src/eqy_partition.so -m $EQY_BASEDIR/src/eqy_recode.so -ql ${ys_file%.*}.log $yosys_args $ys_file" 22 | } 23 | 24 | # $ generate_bash_test bash_file 25 | generate_bash_test() { 26 | bash_file=$1 27 | generate_target "$bash_file" "bash -v $bash_file >${bash_file%.*}.log 2>&1" 28 | } 29 | 30 | # $ generate_tests [-y|--yosys-scripts] [-s|--prove-sv] [-b|--bash] [-a|--yosys-args yosys_args] 31 | generate_tests() { 32 | do_ys=false 33 | do_sv=false 34 | do_sh=false 35 | yosys_args="" 36 | 37 | while [[ $# -gt 0 ]]; do 38 | arg="$1" 39 | case "$arg" in 40 | -y|--yosys-scripts) 41 | do_ys=true 42 | shift 43 | ;; 44 | -s|--prove-sv) 45 | do_sv=true 46 | shift 47 | ;; 48 | -b|--bash) 49 | do_sh=true 50 | shift 51 | ;; 52 | -a|--yosys-args) 53 | yosys_args+="$2" 54 | shift 55 | shift 56 | ;; 57 | *) 58 | echo >&2 "Unknown argument: $1" 59 | exit 1 60 | esac 61 | done 62 | 63 | if [[ ! ( $do_ys = true || $do_sv = true || $do_sh = true ) ]]; then 64 | echo >&2 "Error: No file types selected" 65 | exit 1 66 | fi 67 | 68 | echo ".PHONY: all" 69 | echo "all:" 70 | 71 | if [[ $do_ys = true ]]; then 72 | for x in *.ys; do 73 | generate_ys_test "$x" "$yosys_args" 74 | done 75 | fi; 76 | if [[ $do_sv = true ]]; then 77 | for x in *.sv; do 78 | if [ ! -f "${x%.sv}.ys" ]; then 79 | generate_ys_test "$x" "-p \"prep -top top; sat -verify -prove-asserts\" $yosys_args" 80 | fi; 81 | done 82 | fi; 83 | if [[ $do_sh == true ]]; then 84 | for s in *.sh; do 85 | if [ "$s" != "run-test.sh" ]; then 86 | generate_bash_test "$s" 87 | fi 88 | done 89 | fi 90 | } 91 | 92 | run_tests() { 93 | generate_tests "$@" > run-test.mk 94 | exec ${MAKE:-make} -f run-test.mk 95 | } 96 | 97 | run_tests --yosys-scripts --bash 98 | -------------------------------------------------------------------------------- /tests/python/.gitignore: -------------------------------------------------------------------------------- 1 | generate.eqy 2 | /read_source_fail/ 3 | /error_verilog/ 4 | /counter/ 5 | /counter.bak*/ 6 | /counter_cells/ 7 | /splitnets/ 8 | /picorv32_vivado/ 9 | /timeout/ 10 | /problem_occured/ 11 | /failed_partition/ 12 | /equivalence_problem/ 13 | /syntax_error_partition/ 14 | /syntax_error_collect/ 15 | -------------------------------------------------------------------------------- /tests/python/Makefile: -------------------------------------------------------------------------------- 1 | EQY := eqy 2 | PYTHON_TEMPDIR := $(shell python3 -c "import tempfile; print(tempfile.gettempdir())") 3 | 4 | test: failing_tests passing_tests 5 | 6 | passing_tests: 7 | @$(EQY) -f splitnets.eqy > /dev/null 8 | @$(EQY) --init-config-file generate.eqy counter counter.sv counter.sv 2>&1 | grep -i "template config written to" 9 | @rm -rf counter/ counter.bak*/; $(EQY) counter.eqy > /dev/null 10 | @$(EQY) -b counter.eqy > /dev/null # 1st counter 11 | @$(EQY) -b counter.eqy > /dev/null # 2nd counter 12 | @$(EQY) -t counter.eqy | grep -i "make -C $(PYTHON_TEMPDIR)" # Use temp 13 | @rm -rf counter/ counter.bak*/; $(EQY) counter.eqy -d counter | grep -i "make -C counter -f strategies.mk" # setting workdir 14 | @$(EQY) -c counter.eqy --purge "counter.state/simple" 2>&1 | grep -i "Removing" # continue with purge 15 | @$(EQY) -f counter_cells.eqy 2>&1 | grep -i "counter.state__SDFF_PP0__Q" # matching cells 16 | @$(EQY) - < counter.eqy 2>&1 | grep -i "Cannot derive workdir name from config file name" # stdin 17 | @$(EQY) -f counter.eqy --setup | grep -i "DONE (UNKNOWN, rc=0)" # setup only 18 | @$(EQY) -f failed_partition.eqy > /dev/null 19 | 20 | failing_tests: 21 | @$(EQY) 2>&1 | grep -i "No config file given" 22 | @$(EQY) -f dupl_section.eqy 2>&1 | grep -i "duplicated strategy section" 23 | @$(EQY) -f error_verilog.eqy 2>&1 | grep -i "Reading sources failed" 24 | @$(EQY) -f read_source_fail.eqy --yosys /tmp/yosys 2>&1 | grep -i "Reading sources failed" 25 | @timeout 2s $(EQY) -f timeout.eqy 2>&1 | grep -i "Keyboard interrupt or external termination signal" 26 | @$(EQY) counter.eqy -t -d counter 2>&1 | grep -i "Cannot use -d with -t" 27 | @mkdir -p counter; $(EQY) counter.eqy 2>&1 | grep -i "Directory 'counter' already exists" 28 | @$(EQY) counter.eqy -d counter 2>&1 | grep -i "Directory 'counter' already exists" # setting workdir when exist 29 | @$(EQY) -f counter.eqy -p stat_error counter.state 2>&1 | grep -i "returned a non-zero exit code" # non-working command 30 | @$(EQY) -c counter.eqy -d counter.eqy 2>&1 | grep -i "Cannot continue in 'counter.eqy': not a directory" 31 | @rm -rf counter/; $(EQY) -c counter.eqy 2>&1 | grep -i "Cannot continue in" # Cannot continue 32 | @$(EQY) --debug -f picorv32_vivado.eqy 2>&1 | grep -i "DONE (FAIL, rc=2)" 33 | @$(EQY) -f syntax_error.eqy 2>&1 | grep -i "syntax error in syntax_error.eqy line 16" 34 | @$(EQY) -f syntax_error_options.eqy 2>&1 | grep -i "syntax error in options section" 35 | @$(EQY) -f syntax_error_partition.eqy 2>&1 | grep -i "Syntax error in partition command" 36 | @$(EQY) -f syntax_error_collect.eqy 2>&1 | grep -i "Syntax error in collect command" 37 | @$(EQY) -f unknown_option.eqy 2>&1 | grep -i "unknown option" 38 | @$(EQY) -f unknown_strategy.eqy 2>&1 | grep -i "Unknown strategy type" 39 | @$(EQY) -f expected_opt_bool_val.eqy 2>&1 | grep -i "expected option value" 40 | @$(EQY) -f expected_opt_str_val.eqy 2>&1 | grep -i "expected option value" 41 | @$(EQY) -f expected_opt_int_val.eqy 2>&1 | grep -i "expected option value" 42 | @$(EQY) -f repeated_option_bool.eqy 2>&1 | grep -i "repeated option" 43 | @$(EQY) -f repeated_option_str.eqy 2>&1 | grep -i "repeated option" 44 | @$(EQY) -f repeated_option_int.eqy 2>&1 | grep -i "repeated option" 45 | @$(EQY) -f repeated_option_use.eqy 2>&1 | grep -i "repeated option" 46 | @$(EQY) -f no_use_line.eqy 2>&1 | grep -i "has no 'use' line" 47 | @$(EQY) -f expected_opt_bool_on_off.eqy 2>&1 | grep -i "expected one of" 48 | @$(EQY) -f expected_opt_int_integer.eqy 2>&1 | grep -i "expected integer option" 49 | @$(EQY) -f unknown_opt_other.eqy 2>&1 | grep -i "unknown option 'unknown'" 50 | @$(EQY) -f equivalence_problem.eqy 2>&1 | grep -i "A problem occurred during equivalence check." 51 | @$(EQY) -f problem_occured.eqy 2>&1 | grep -i "No configured strategy supports partition" 52 | @$(EQY) -f missing_gate_section.eqy 2>&1 | grep -i "section \[gate\] missing" 53 | @$(EQY) -f missing_gold_section.eqy 2>&1 | grep -i "section \[gold\] missing" 54 | 55 | clean: 56 | @rm -rf counter/ counter.bak*/ counter_cells/ error_verilog/ generate.eqy picorv32_vivado/ read_source_fail/ timeout/ splitnets/ problem_occured/ failed_partition/ equivalence_problem/ syntax_error_partition/ syntax_error_collect/ 57 | 58 | .PHONY: test clean 59 | -------------------------------------------------------------------------------- /tests/python/counter.eqy: -------------------------------------------------------------------------------- 1 | [options] 2 | 3 | [gold] 4 | read_verilog counter.sv 5 | prep -top counter 6 | 7 | [gate] 8 | read_verilog counter.sv 9 | synth -top counter 10 | 11 | [strategy simple] 12 | use sby 13 | depth 10 14 | engine smtbmc bitwuzla 15 | xprop on 16 | timeout 100 17 | -------------------------------------------------------------------------------- /tests/python/counter.sv: -------------------------------------------------------------------------------- 1 | module counter(input clk, rst, en, output reg [7:0] state); 2 | always @(posedge clk) 3 | state <= rst ? 0 : state + en; 4 | endmodule 5 | -------------------------------------------------------------------------------- /tests/python/counter_cells.eqy: -------------------------------------------------------------------------------- 1 | [options] 2 | 3 | [gold] 4 | read_verilog counter.sv 5 | synth -top counter 6 | autoname 7 | 8 | [gate] 9 | read_verilog counter.sv 10 | synth -top counter 11 | autoname 12 | 13 | [strategy simple] 14 | use sat 15 | depth 10 16 | -------------------------------------------------------------------------------- /tests/python/dupl_section.eqy: -------------------------------------------------------------------------------- 1 | [options] 2 | 3 | [gold] 4 | read_verilog counter.sv 5 | prep -top counter 6 | 7 | [gate] 8 | read_verilog counter.sv 9 | synth -top counter 10 | 11 | [strategy simple] 12 | use sat 13 | depth 10 14 | 15 | [strategy simple] 16 | use sat 17 | depth 10 18 | -------------------------------------------------------------------------------- /tests/python/equivalence_problem.eqy: -------------------------------------------------------------------------------- 1 | [gold] 2 | read_verilog -sv ../../examples/nerv/nerv.sv 3 | prep -top nerv 4 | memory_map 5 | 6 | [gate] 7 | read_verilog -sv ../../examples/nerv/nerv_change.sv 8 | prep -top nerv 9 | 10 | [collect *] 11 | group regfile* 12 | join imm_* 13 | join insn* 14 | 15 | [strategy sby] 16 | use sby 17 | depth 2 18 | engine smtbmc bitwuzla 19 | -------------------------------------------------------------------------------- /tests/python/error_verilog.eqy: -------------------------------------------------------------------------------- 1 | [options] 2 | 3 | [gold] 4 | read_verilog error_verilog.sv 5 | prep -top counter 6 | 7 | [gate] 8 | read_verilog error_verilog.sv 9 | synth -top counter 10 | 11 | [strategy simple] 12 | use sat 13 | depth 10 14 | -------------------------------------------------------------------------------- /tests/python/error_verilog.sv: -------------------------------------------------------------------------------- 1 | module counter(input clk, rst, en, output reg state [7:0]); 2 | always @(posedge clk) 3 | state <= rst ? 0 : state + en; 4 | endmodule 5 | -------------------------------------------------------------------------------- /tests/python/expected_opt_bool_on_off.eqy: -------------------------------------------------------------------------------- 1 | [options] 2 | 3 | [gold] 4 | read_verilog counter.sv 5 | prep -top counter 6 | exec -- sleep 10 7 | 8 | [gate] 9 | read_verilog counter.sv 10 | synth -top counter 11 | 12 | [strategy simple] 13 | use sby 14 | xprop 123 15 | -------------------------------------------------------------------------------- /tests/python/expected_opt_bool_val.eqy: -------------------------------------------------------------------------------- 1 | [options] 2 | 3 | [gold] 4 | read_verilog counter.sv 5 | prep -top counter 6 | exec -- sleep 10 7 | 8 | [gate] 9 | read_verilog counter.sv 10 | synth -top counter 11 | 12 | [strategy simple] 13 | use sby 14 | xprop 15 | -------------------------------------------------------------------------------- /tests/python/expected_opt_int_integer.eqy: -------------------------------------------------------------------------------- 1 | [options] 2 | 3 | [gold] 4 | read_verilog counter.sv 5 | prep -top counter 6 | exec -- sleep 10 7 | 8 | [gate] 9 | read_verilog counter.sv 10 | synth -top counter 11 | 12 | [strategy simple] 13 | use sby 14 | xprop on 15 | timeout dummy 16 | -------------------------------------------------------------------------------- /tests/python/expected_opt_int_val.eqy: -------------------------------------------------------------------------------- 1 | [options] 2 | 3 | [gold] 4 | read_verilog counter.sv 5 | prep -top counter 6 | exec -- sleep 10 7 | 8 | [gate] 9 | read_verilog counter.sv 10 | synth -top counter 11 | 12 | [strategy simple] 13 | use sby 14 | depth 15 | -------------------------------------------------------------------------------- /tests/python/expected_opt_str_val.eqy: -------------------------------------------------------------------------------- 1 | [options] 2 | 3 | [gold] 4 | read_verilog counter.sv 5 | prep -top counter 6 | exec -- sleep 10 7 | 8 | [gate] 9 | read_verilog counter.sv 10 | synth -top counter 11 | 12 | [strategy simple] 13 | use sby 14 | engine 15 | -------------------------------------------------------------------------------- /tests/python/failed_partition.eqy: -------------------------------------------------------------------------------- 1 | [gold] 2 | read_verilog -sv ../../examples/nerv/nerv.sv 3 | prep -top nerv 4 | 5 | [gate] 6 | read_verilog -sv ../../examples/nerv/nerv_change.sv 7 | prep -top nerv 8 | 9 | [collect *] 10 | group regfile* 11 | 12 | [strategy simple] 13 | use sat 14 | depth 10 15 | -------------------------------------------------------------------------------- /tests/python/missing_gate_section.eqy: -------------------------------------------------------------------------------- 1 | [options] 2 | 3 | [gold] 4 | read_verilog counter.sv 5 | prep -top counter 6 | exec -- sleep 10 7 | 8 | [strategy simple] 9 | use sat 10 | depth 10 11 | -------------------------------------------------------------------------------- /tests/python/missing_gold_section.eqy: -------------------------------------------------------------------------------- 1 | [options] 2 | 3 | [gate] 4 | read_verilog counter.sv 5 | synth -top counter 6 | 7 | [strategy simple] 8 | use sat 9 | depth 10 10 | -------------------------------------------------------------------------------- /tests/python/no_use_line.eqy: -------------------------------------------------------------------------------- 1 | [options] 2 | 3 | [gold] 4 | read_verilog counter.sv 5 | prep -top counter 6 | exec -- sleep 10 7 | 8 | [gate] 9 | read_verilog counter.sv 10 | synth -top counter 11 | 12 | [strategy simple] 13 | use 14 | depth 10 15 | -------------------------------------------------------------------------------- /tests/python/problem_occured.eqy: -------------------------------------------------------------------------------- 1 | [gold] 2 | read_verilog -sv ../../examples/nerv/nerv.sv 3 | prep -top nerv 4 | memory_map 5 | 6 | [gate] 7 | read_verilog -sv ../../examples/nerv/nerv_change.sv 8 | prep -top nerv 9 | 10 | [collect *] 11 | #group regfile* 12 | join imm_* 13 | join insn* 14 | 15 | [strategy simple] 16 | use sat 17 | depth 10 18 | -------------------------------------------------------------------------------- /tests/python/read_source_fail.eqy: -------------------------------------------------------------------------------- 1 | [options] 2 | 3 | [gold] 4 | read_verilog counter.sv 5 | prep -top counter 6 | 7 | [gate] 8 | read_verilog counter.sv 9 | synth -top counter 10 | 11 | [strategy simple] 12 | use sat 13 | depth 10 14 | -------------------------------------------------------------------------------- /tests/python/repeated_option_bool.eqy: -------------------------------------------------------------------------------- 1 | [options] 2 | 3 | [gold] 4 | read_verilog counter.sv 5 | prep -top counter 6 | exec -- sleep 10 7 | 8 | [gate] 9 | read_verilog counter.sv 10 | synth -top counter 11 | 12 | [strategy simple] 13 | use sby 14 | xprop on 15 | xprop on 16 | -------------------------------------------------------------------------------- /tests/python/repeated_option_int.eqy: -------------------------------------------------------------------------------- 1 | [options] 2 | 3 | [gold] 4 | read_verilog counter.sv 5 | prep -top counter 6 | exec -- sleep 10 7 | 8 | [gate] 9 | read_verilog counter.sv 10 | synth -top counter 11 | 12 | [strategy simple] 13 | use sby 14 | xprop on 15 | depth 10 16 | depth 10 17 | -------------------------------------------------------------------------------- /tests/python/repeated_option_str.eqy: -------------------------------------------------------------------------------- 1 | [options] 2 | 3 | [gold] 4 | read_verilog counter.sv 5 | prep -top counter 6 | exec -- sleep 10 7 | 8 | [gate] 9 | read_verilog counter.sv 10 | synth -top counter 11 | 12 | [strategy simple] 13 | use sby 14 | engine smtbmc bitwuzla 15 | engine smtbmc bitwuzla 16 | xprop on 17 | -------------------------------------------------------------------------------- /tests/python/repeated_option_use.eqy: -------------------------------------------------------------------------------- 1 | [options] 2 | 3 | [gold] 4 | read_verilog counter.sv 5 | prep -top counter 6 | exec -- sleep 10 7 | 8 | [gate] 9 | read_verilog counter.sv 10 | synth -top counter 11 | 12 | [strategy simple] 13 | use sby 14 | use sby 15 | engine smtbmc bitwuzla 16 | xprop on 17 | -------------------------------------------------------------------------------- /tests/python/splitnets.eqy: -------------------------------------------------------------------------------- 1 | [options] 2 | splitnets on 3 | 4 | [gold] 5 | read_verilog counter.sv 6 | prep -top counter 7 | 8 | [gate] 9 | read_verilog counter.sv 10 | synth -top counter 11 | 12 | [script] 13 | stat 14 | 15 | [strategy simple] 16 | use sat 17 | depth 10 18 | -------------------------------------------------------------------------------- /tests/python/syntax_error.eqy: -------------------------------------------------------------------------------- 1 | [options] 2 | 3 | [gold] 4 | read_verilog counter.sv 5 | prep -top counter 6 | exec -- sleep 10 7 | 8 | [gate] 9 | read_verilog counter.sv 10 | synth -top counter 11 | 12 | [strategy simple] 13 | use sat 14 | depth 10 15 | 16 | [syntax_error] 17 | -------------------------------------------------------------------------------- /tests/python/syntax_error_collect.eqy: -------------------------------------------------------------------------------- 1 | [options] 2 | 3 | [gold] 4 | read_verilog counter.sv 5 | prep -top counter 6 | exec -- sleep 10 7 | 8 | [gate] 9 | read_verilog counter.sv 10 | synth -top counter 11 | 12 | 13 | [collect test] 14 | unknown 15 | 16 | [strategy simple] 17 | use sat 18 | depth 10 19 | -------------------------------------------------------------------------------- /tests/python/syntax_error_options.eqy: -------------------------------------------------------------------------------- 1 | [options] 2 | test 3 | 4 | [gold] 5 | read_verilog counter.sv 6 | prep -top counter 7 | exec -- sleep 10 8 | 9 | [gate] 10 | read_verilog counter.sv 11 | synth -top counter 12 | 13 | [strategy simple] 14 | use sat 15 | depth 10 16 | -------------------------------------------------------------------------------- /tests/python/syntax_error_partition.eqy: -------------------------------------------------------------------------------- 1 | [options] 2 | 3 | [gold] 4 | read_verilog counter.sv 5 | prep -top counter 6 | exec -- sleep 10 7 | 8 | [gate] 9 | read_verilog counter.sv 10 | synth -top counter 11 | 12 | 13 | [partition test] 14 | unknown 15 | 16 | [strategy simple] 17 | use sat 18 | depth 10 19 | -------------------------------------------------------------------------------- /tests/python/timeout.eqy: -------------------------------------------------------------------------------- 1 | [options] 2 | 3 | [gold] 4 | read_verilog counter.sv 5 | prep -top counter 6 | exec -- sleep 10 7 | 8 | [gate] 9 | read_verilog counter.sv 10 | synth -top counter 11 | 12 | [strategy simple] 13 | use sat 14 | depth 10 15 | -------------------------------------------------------------------------------- /tests/python/unknown_opt_other.eqy: -------------------------------------------------------------------------------- 1 | [options] 2 | 3 | [gold] 4 | read_verilog counter.sv 5 | prep -top counter 6 | exec -- sleep 10 7 | 8 | [gate] 9 | read_verilog counter.sv 10 | synth -top counter 11 | 12 | [strategy simple] 13 | use sat 14 | depth 10 15 | unknown 16 | -------------------------------------------------------------------------------- /tests/python/unknown_option.eqy: -------------------------------------------------------------------------------- 1 | [options] 2 | syntax_error 1 3 | 4 | [gold] 5 | read_verilog counter.sv 6 | prep -top counter 7 | exec -- sleep 10 8 | 9 | [gate] 10 | read_verilog counter.sv 11 | synth -top counter 12 | 13 | [strategy simple] 14 | use sat 15 | depth 10 16 | -------------------------------------------------------------------------------- /tests/python/unknown_strategy.eqy: -------------------------------------------------------------------------------- 1 | [options] 2 | 3 | [gold] 4 | read_verilog counter.sv 5 | prep -top counter 6 | exec -- sleep 10 7 | 8 | [gate] 9 | read_verilog counter.sv 10 | synth -top counter 11 | 12 | [strategy simple] 13 | use unknown 14 | depth 10 15 | --------------------------------------------------------------------------------