[
  {
    "path": ".gitattributes",
    "content": "/.gittag export-subst\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "_What are the reasons/motivation for this change?_\n\n_Explain how this is achieved._\n\n_If applicable, please suggest to reviewers how they can test the change._\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: ci\non:\n  push:\n  pull_request:\n  schedule:\n    - cron: '30 0 * * *'\n\njobs:\n  build_oss:\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout SBY\n        uses: actions/checkout@v5\n\n      - name: Install oss-cad-suite\n        uses: YosysHQ/setup-oss-cad-suite@v4\n        with:\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n      - name: Run SBY tests\n        run: pip install xmlschema && make ci\n\n  build_verific:\n    runs-on: [self-hosted, linux, x64, fast]\n    steps:\n      - name: Checkout SBY\n        uses: actions/checkout@v4\n\n      - name: Checkout Yosys\n        uses: actions/checkout@v4\n        with:\n          repository: 'YosysHQ/yosys'\n          path: 'yosys'\n          submodules: true\n\n      - name: Runtime environment\n        run: |\n          echo \"procs=$(nproc)\" >> $GITHUB_ENV\n\n      - name: Build Yosys\n        run: |\n          cd yosys\n          make config-clang\n          echo \"ENABLE_VERIFIC := 1\" >> Makefile.conf\n          echo \"ENABLE_VERIFIC_EDIF := 1\" >> Makefile.conf\n          echo \"ENABLE_VERIFIC_LIBERTY := 1\" >> Makefile.conf\n          echo \"ENABLE_VERIFIC_YOSYSHQ_EXTENSIONS := 1\" >> Makefile.conf\n          echo \"ENABLE_CCACHE := 1\" >> Makefile.conf\n          make -j${{ env.procs }}\n          make install DESTDIR=${GITHUB_WORKSPACE}/.local PREFIX=\n\n      - name: Build SBY\n        run: |\n          make install DESTDIR=${GITHUB_WORKSPACE}/.local PREFIX=\n\n      - name: Run SBY tests\n        run: |\n          make run_ci\n"
  },
  {
    "path": ".github/workflows/codeql.yml",
    "content": "name: \"CodeQL\"\n\non:\n  workflow_dispatch:\n  schedule:\n    - cron: '0 2 * * *'\n\njobs:\n  analyze:\n    name: Analyze\n    runs-on: ubuntu-latest\n    permissions:\n      actions: read\n      contents: read\n      security-events: write\n\n    steps:\n    - name: Checkout repository\n      uses: actions/checkout@v5\n\n    - name: Initialize CodeQL\n      uses: github/codeql-action/init@v4\n      with:\n        languages: python\n        queries: security-extended,security-and-quality\n\n    - name: Perform CodeQL Analysis\n      uses: github/codeql-action/analyze@v4\n"
  },
  {
    "path": ".gitignore",
    "content": "/docs/build\n/sbysrc/demo[0-9]\n/sbysrc/__pycache__\n\n"
  },
  {
    "path": ".gittag",
    "content": "$Format:%(describe)$\n"
  },
  {
    "path": ".readthedocs.yaml",
    "content": "# .readthedocs.yaml\n# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details\n\nversion: 2\n\nbuild:\n    os: ubuntu-22.04\n    tools:\n        python: '3.11'\n\nformats:\n  - pdf\n\nsphinx:\n  configuration: docs/source/conf.py\n  fail_on_warning: true\n\npython:\n  install:\n    - requirements: docs/source/requirements.txt\n"
  },
  {
    "path": "COPYING",
    "content": "SymbiYosys (sby) -- Front-end for Yosys-based formal verification flows\n\nCopyright (C) 2016  Claire Xenia Wolf <claire@yosyshq.com>\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted, provided that the above\ncopyright notice and this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\nWITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\nANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\nWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\nACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\nOR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": "\nDESTDIR =\nPREFIX ?= /usr/local\nPROGRAM_PREFIX =\n\n# On Windows, manually setting absolute path to Python binary may be required\n# for launcher executable to work. From MSYS2, this can be done using the\n# following command: \"which python3 | cygpath -w -m -f -\".\nifeq ($(OS), Windows_NT)\nPYTHON = $(shell cygpath -w -m $(PREFIX)/bin/python3)\nendif\n\nifeq ($(file < .gittag),$$Format:%(describe)$$)\nYOSYS_RELEASE_VERSION := SBY $(shell git describe --dirty)\nelse\nYOSYS_RELEASE_VERSION := SBY $(file < .gittag)\nendif\n\n.PHONY: help install ci test html clean\n\nhelp:\n\t@echo \"\"\n\t@echo \"sudo make install\"\n\t@echo \"    build and install SymbiYosys (sby)\"\n\t@echo \"\"\n\t@echo \"make html\"\n\t@echo \"    build documentation in docs/build/html/\"\n\t@echo \"\"\n\t@echo \"make test\"\n\t@echo \"    run tests, skipping tests with missing dependencies\"\n\t@echo \"\"\n\t@echo \"make ci\"\n\t@echo \"    run all tests, failing tests with missing dependencies\"\n\t@echo \"    note: this requires a full Tabby CAD Suite or OSS CAD Suite install\"\n\t@echo \"\"\n\t@echo \"make clean\"\n\t@echo \"    cleanup\"\n\t@echo \"\"\n\ninstall:\n\tmkdir -p $(DESTDIR)$(PREFIX)/bin\n\tmkdir -p $(DESTDIR)$(PREFIX)/share/yosys/python3\n\tcp sbysrc/sby_*.py $(DESTDIR)$(PREFIX)/share/yosys/python3/\n\tsed -e 's|##yosys-program-prefix##|\"'$(PROGRAM_PREFIX)'\"|' < sbysrc/sby_core.py > $(DESTDIR)$(PREFIX)/share/yosys/python3/sby_core.py\nifeq ($(OS), Windows_NT)\n\tsed -e 's|##yosys-sys-path##|sys.path += [os.path.dirname(__file__) + p for p in [\"/share/python3\", \"/../share/yosys/python3\"]]|;' \\\n\t\t-e \"s|##yosys-release-version##|release_version = '$(YOSYS_RELEASE_VERSION)'|;\" \\\n\t\t-e \"s|#!/usr/bin/env python3|#!$(PYTHON)|\" < sbysrc/sby.py > $(DESTDIR)$(PREFIX)/bin/sby-script.py\n\tgcc -DGUI=0 -O -s -o $(DESTDIR)$(PREFIX)/bin/sby.exe extern/launcher.c\nelse\n\tsed -e 's|##yosys-sys-path##|sys.path += [os.path.dirname(__file__) + p for p in [\"/share/python3\", \"/../share/yosys/python3\"]]|;' \\\n\t\t-e \"s|##yosys-release-version##|release_version = '$(YOSYS_RELEASE_VERSION)'|;\" < sbysrc/sby.py > $(DESTDIR)$(PREFIX)/bin/sby\n\tchmod +x $(DESTDIR)$(PREFIX)/bin/sby\nendif\n\n.PHONY: check_cad_suite run_ci\n\nci: check_cad_suite\n\t@$(MAKE) run_ci\n\nrun_ci:\n\t$(MAKE) test NOSKIP=1\n\tif yosys -qp 'read -verific' 2> /dev/null; then set -x; \\\n\t\tYOSYS_NOVERIFIC=1 $(MAKE) run_ci; \\\n\tfi\n\ncheck_cad_suite:\n\t@if ! which tabbypip >/dev/null 2>&1; then \\\n\t\techo \"'make ci' requries the Tabby CAD Suite or the OSS CAD Suite\"; \\\n\t\techo \"try 'make test' instead or run 'make run_ci' to proceed anyway.\"; \\\n\t\texit 1; \\\n\tfi\n\ntest_demo1:\n\tcd sbysrc && python3 sby.py -f demo1.sby\n\ntest_demo2:\n\tcd sbysrc && python3 sby.py -f demo2.sby\n\ntest_demo3:\n\tcd sbysrc && python3 sby.py -f demo3.sby\n\ntest:\n\t$(MAKE) -C tests test\n\nhtml:\n\t$(MAKE) -C docs html\n\nclean:\n\t$(MAKE) -C docs clean\n\trm -rf docs/build sbysrc/sby sbysrc/__pycache__\n"
  },
  {
    "path": "README.md",
    "content": "SymbiYosys (sby) is a front-end driver program for [Yosys](https://yosyshq.net/yosys/)-based formal hardware verification flows. See [https://yosyshq.readthedocs.io/projects/sby/](https://yosyshq.readthedocs.io/projects/sby/) for documentation on how to use SymbiYosys.\n\nSymbiYosys (sby) itself is licensed under the ISC license, note that the solvers and other components used by SymbiYosys come with their own license terms. There is some more details in the [\"Selecting the right engine\" section of the documentation](https://yosyshq.readthedocs.io/projects/sby/en/latest/quickstart.html#selecting-the-right-engine).\n\n---\n\nSymbiYosys (sby) is part of the [Tabby CAD Suite](https://www.yosyshq.com/tabby-cad-datasheet) and the [OSS CAD Suite](https://github.com/YosysHQ/oss-cad-suite-build)! The easiest way to use sby is to install the binary software suite, which contains all required dependencies, including all supported solvers.\n\n* [Contact YosysHQ](https://www.yosyshq.com/contact) for a [Tabby CAD Suite](https://www.yosyshq.com/tabby-cad-datasheet) Evaluation License and download link\n* OR go to https://github.com/YosysHQ/oss-cad-suite-build/releases to download the free OSS CAD Suite\n* Follow the [Install Instructions on GitHub](https://github.com/YosysHQ/oss-cad-suite-build#installation)\n\nMake sure to get a Tabby CAD Suite Evaluation License for extensive SystemVerilog Assertion (SVA) support, as well as industry-grade SystemVerilog and VHDL parsers!\n\nFor more information about the difference between Tabby CAD Suite and the OSS CAD Suite, please visit https://www.yosyshq.com/tabby-cad-datasheet.\n"
  },
  {
    "path": "docs/.gitignore",
    "content": "/build/\n"
  },
  {
    "path": "docs/Makefile",
    "content": "# Makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line.\nSPHINXOPTS    =\nSPHINXBUILD   = sphinx-build\nPAPER         =\nBUILDDIR      = build\n\n# Internal variables.\nPAPEROPT_a4     = -D latex_paper_size=a4\nPAPEROPT_letter = -D latex_paper_size=letter\nALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source\n# the i18n builder cannot share the environment and doctrees with the others\nI18NSPHINXOPTS  = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source\n\n.PHONY: help\nhelp:\n\t@echo \"Please use \\`make <target>' where <target> is one of\"\n\t@echo \"  html       to make standalone HTML files\"\n\t@echo \"  dirhtml    to make HTML files named index.html in directories\"\n\t@echo \"  singlehtml to make a single large HTML file\"\n\t@echo \"  pickle     to make pickle files\"\n\t@echo \"  json       to make JSON files\"\n\t@echo \"  htmlhelp   to make HTML files and a HTML help project\"\n\t@echo \"  qthelp     to make HTML files and a qthelp project\"\n\t@echo \"  applehelp  to make an Apple Help Book\"\n\t@echo \"  devhelp    to make HTML files and a Devhelp project\"\n\t@echo \"  epub       to make an epub\"\n\t@echo \"  epub3      to make an epub3\"\n\t@echo \"  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter\"\n\t@echo \"  latexpdf   to make LaTeX files and run them through pdflatex\"\n\t@echo \"  latexpdfja to make LaTeX files and run them through platex/dvipdfmx\"\n\t@echo \"  text       to make text files\"\n\t@echo \"  man        to make manual pages\"\n\t@echo \"  texinfo    to make Texinfo files\"\n\t@echo \"  info       to make Texinfo files and run them through makeinfo\"\n\t@echo \"  gettext    to make PO message catalogs\"\n\t@echo \"  changes    to make an overview of all changed/added/deprecated items\"\n\t@echo \"  xml        to make Docutils-native XML files\"\n\t@echo \"  pseudoxml  to make pseudoxml-XML files for display purposes\"\n\t@echo \"  linkcheck  to check all external links for integrity\"\n\t@echo \"  doctest    to run all doctests embedded in the documentation (if enabled)\"\n\t@echo \"  coverage   to run coverage check of the documentation (if enabled)\"\n\t@echo \"  dummy      to check syntax errors of document sources\"\n\n.PHONY: clean\nclean:\n\trm -rf $(BUILDDIR)/*\n\n.PHONY: html\nhtml:\n\t$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html\n\t@echo\n\t@echo \"Build finished. The HTML pages are in $(BUILDDIR)/html.\"\n\n.PHONY: dirhtml\ndirhtml:\n\t$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml\n\t@echo\n\t@echo \"Build finished. The HTML pages are in $(BUILDDIR)/dirhtml.\"\n\n.PHONY: singlehtml\nsinglehtml:\n\t$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml\n\t@echo\n\t@echo \"Build finished. The HTML page is in $(BUILDDIR)/singlehtml.\"\n\n.PHONY: pickle\npickle:\n\t$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle\n\t@echo\n\t@echo \"Build finished; now you can process the pickle files.\"\n\n.PHONY: json\njson:\n\t$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json\n\t@echo\n\t@echo \"Build finished; now you can process the JSON files.\"\n\n.PHONY: htmlhelp\nhtmlhelp:\n\t$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp\n\t@echo\n\t@echo \"Build finished; now you can run HTML Help Workshop with the\" \\\n\t      \".hhp project file in $(BUILDDIR)/htmlhelp.\"\n\n.PHONY: qthelp\nqthelp:\n\t$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp\n\t@echo\n\t@echo \"Build finished; now you can run \"qcollectiongenerator\" with the\" \\\n\t      \".qhcp project file in $(BUILDDIR)/qthelp, like this:\"\n\t@echo \"# qcollectiongenerator $(BUILDDIR)/qthelp/SymbiYosys.qhcp\"\n\t@echo \"To view the help file:\"\n\t@echo \"# assistant -collectionFile $(BUILDDIR)/qthelp/SymbiYosys.qhc\"\n\n.PHONY: applehelp\napplehelp:\n\t$(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp\n\t@echo\n\t@echo \"Build finished. The help book is in $(BUILDDIR)/applehelp.\"\n\t@echo \"N.B. You won't be able to view it unless you put it in\" \\\n\t      \"~/Library/Documentation/Help or install it in your application\" \\\n\t      \"bundle.\"\n\n.PHONY: devhelp\ndevhelp:\n\t$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp\n\t@echo\n\t@echo \"Build finished.\"\n\t@echo \"To view the help file:\"\n\t@echo \"# mkdir -p $$HOME/.local/share/devhelp/SymbiYosys\"\n\t@echo \"# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/SymbiYosys\"\n\t@echo \"# devhelp\"\n\n.PHONY: epub\nepub:\n\t$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub\n\t@echo\n\t@echo \"Build finished. The epub file is in $(BUILDDIR)/epub.\"\n\n.PHONY: epub3\nepub3:\n\t$(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3\n\t@echo\n\t@echo \"Build finished. The epub3 file is in $(BUILDDIR)/epub3.\"\n\n.PHONY: latex\nlatex:\n\t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex\n\t@echo\n\t@echo \"Build finished; the LaTeX files are in $(BUILDDIR)/latex.\"\n\t@echo \"Run \\`make' in that directory to run these through (pdf)latex\" \\\n\t      \"(use \\`make latexpdf' here to do that automatically).\"\n\n.PHONY: latexpdf\nlatexpdf:\n\t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex\n\t@echo \"Running LaTeX files through pdflatex...\"\n\t$(MAKE) -C $(BUILDDIR)/latex all-pdf\n\t@echo \"pdflatex finished; the PDF files are in $(BUILDDIR)/latex.\"\n\n.PHONY: latexpdfja\nlatexpdfja:\n\t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex\n\t@echo \"Running LaTeX files through platex and dvipdfmx...\"\n\t$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja\n\t@echo \"pdflatex finished; the PDF files are in $(BUILDDIR)/latex.\"\n\n.PHONY: text\ntext:\n\t$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text\n\t@echo\n\t@echo \"Build finished. The text files are in $(BUILDDIR)/text.\"\n\n.PHONY: man\nman:\n\t$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man\n\t@echo\n\t@echo \"Build finished. The manual pages are in $(BUILDDIR)/man.\"\n\n.PHONY: texinfo\ntexinfo:\n\t$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo\n\t@echo\n\t@echo \"Build finished. The Texinfo files are in $(BUILDDIR)/texinfo.\"\n\t@echo \"Run \\`make' in that directory to run these through makeinfo\" \\\n\t      \"(use \\`make info' here to do that automatically).\"\n\n.PHONY: info\ninfo:\n\t$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo\n\t@echo \"Running Texinfo files through makeinfo...\"\n\tmake -C $(BUILDDIR)/texinfo info\n\t@echo \"makeinfo finished; the Info files are in $(BUILDDIR)/texinfo.\"\n\n.PHONY: gettext\ngettext:\n\t$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale\n\t@echo\n\t@echo \"Build finished. The message catalogs are in $(BUILDDIR)/locale.\"\n\n.PHONY: changes\nchanges:\n\t$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes\n\t@echo\n\t@echo \"The overview file is in $(BUILDDIR)/changes.\"\n\n.PHONY: linkcheck\nlinkcheck:\n\t$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck\n\t@echo\n\t@echo \"Link check complete; look for any errors in the above output \" \\\n\t      \"or in $(BUILDDIR)/linkcheck/output.txt.\"\n\n.PHONY: doctest\ndoctest:\n\t$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest\n\t@echo \"Testing of doctests in the sources finished, look at the \" \\\n\t      \"results in $(BUILDDIR)/doctest/output.txt.\"\n\n.PHONY: coverage\ncoverage:\n\t$(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage\n\t@echo \"Testing of coverage in the sources finished, look at the \" \\\n\t      \"results in $(BUILDDIR)/coverage/python.txt.\"\n\n.PHONY: xml\nxml:\n\t$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml\n\t@echo\n\t@echo \"Build finished. The XML files are in $(BUILDDIR)/xml.\"\n\n.PHONY: pseudoxml\npseudoxml:\n\t$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml\n\t@echo\n\t@echo \"Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml.\"\n\n.PHONY: dummy\ndummy:\n\t$(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy\n\t@echo\n\t@echo \"Build finished. Dummy builder generates no files.\"\n"
  },
  {
    "path": "docs/examples/Makefile",
    "content": "SUBDIR=examples\nTESTDIR=../../tests\ninclude $(TESTDIR)/make/subdir.mk\n"
  },
  {
    "path": "docs/examples/abstract/.gitignore",
    "content": "/abstr/\n/props_cvr/\n/props_prv/\n"
  },
  {
    "path": "docs/examples/abstract/Makefile",
    "content": "SUBDIR=examples/abstract\nTESTDIR=../../../tests\ninclude $(TESTDIR)/make/subdir.mk\n"
  },
  {
    "path": "docs/examples/abstract/README.md",
    "content": "A simple example for using abstractions\n=======================================\n\nAbstractions are a way to deal with formal properties that are difficult to\nprove directly because they require the design to pass through many intermediate\nstages to observe the behavior in question. An abstraction can elide the\nirrelevant steps and boil the behavior down to the steps important to the proof.\n\nInstead of directly proving that the properties are satisfied by the design,\nabstractions can be used to prove in a two-step process\n 1. that the abstraction correctly models the design's behavior\n    (\"the design does X\")\n 2. that the properties are satisfied by the abstraction's behavior\n    (\"anything that does X will do Y\").\nFrom this, we can deduce by simple syllogism that the design must satisfy the\nproperties (\"the design does Y\").\n\nA simple example is the design in `demo.v`. It defines a module containing a\n20-bit counter, which has four outputs A, B, C, D that indicate when the counter\nvalue happens to be one of four specific numbers (123456, 234567, 345678, 456789).\n\n`props.sv` defines some properties about the outputs A, B, C, D and binds them\nto the module. These properties assert that the counter values are reached in\norder. For example, the first assertion describes that when output A is active\n(i.e. the counter value is 123456), the outputs B, C, and D are not (the counter\nvalue is not 234567, 345678, or 456789), and they will remain inactive for an\nunspecified number of cycles, followed by B becoming active (counter reaches\n234567). The cover property checks that it is possible for the module to pass\nthrough the four values A, B, C, D and then return to A at least once.\n\nHowever, it would be infeasible to prove these properties directly: because the\ncounter counts up one by one, it would take thousands of steps to pass from one\noutput activation to the next, and over a million steps to reach the sequence in\nthe cover property.\n\nIntroducing an abstraction is a way to get around this limitation. The\nabstraction in `abstr.sv` describes only the aspects of the counter's behavior\nthat are relevant to the problem at hand:\n - its value strictly increases except at reset and when overflowing from\n 2**20-1 to 0\n - a counter value strictly smaller than one of the four values of interest is\n always followed by either another value strictly smaller, or the value of\n interest itself.\n\nIn a first step, the test in `abstr.sby` proves (asserts) that the properties\nin the abstraction apply to the counter module. To do this, the macro\n`` `demo_counter_abstr_mode`` in `abstr.sv` is defined to mean `assert` when\nreading the file. Run it with the command `sby -f abstr.sby`; if it returns\n`PASS`, you have proved that the abstraction correctly models the counter.\n\nIn a second step, for the test in `props.sby` the actual counter implementation\nis removed from the design using the `cutpoint demo/counter` command. This\ncommand disconnects the net carrying the counter value from the logic driving\nit and instead replaces it with an `$anyseq` cell - meaning that the solver is\nfree to chose any new counter value that suits it every time step (cycle). The\ncounter value is restricted only by the properties in the abstraction - which\nthis time are assumed by defining `` `demo_counter_abstr_mode`` as `assume` when\nreading `abstr.sby`.\n\nConcretely, this means that instead of having to use 123456 time steps to\nconsider each individual value that the counter will hold between reset and A\nbecoming active, the solver can consider all 123456 values until the activation\nof A simultaneously, in a single time step.\n\nYou can see this in the cover trace produced for the cover property. After\nrunning `sby -f props.sby`, open the file `props_cvr/engine_0/trace0.vcd` in a\nwaveform viewer such as `gtkwave`. You will find that the counter value jumps\nimmediately from one value of interest to the next - the solver actually\nconsiders all the other possible values inbetween as well, but picks the one\nthat leads to the most useful behavior in the next time step, which is in this\ncase always the highest value permitted by the constraints of the abstraction.\n\nSuggested exercises:\n\n- Make modifications to `abstr.sv` and/or `demo.v`. Make a prediction if the\n  change will make `sby -f abstr.sby` or `sby -f props.sby prv` fail and test your\n  prediction.\n\n- What happens if we remove `abstr.sv` from `props.sby`, but leave the `cutpoint`\n  command in place?\n\n- What happens if we remove the `cutpoint` command from `props.sby`, but leave\n  `abstr.sv` in place?\n"
  },
  {
    "path": "docs/examples/abstract/abstr.sby",
    "content": "[options]\nmode prove\n\n[engines]\nsmtbmc\n\n[script]\nread -verific\nread -define demo_counter_abstr_mode=assert\nread -sv abstr.sv\nread -sv demo.v\nprep -top demo\n\n[files]\nabstr.sv\ndemo.v\n"
  },
  {
    "path": "docs/examples/abstract/abstr.sv",
    "content": "module demo_counter_abstr (\n\tinput clock, reset,\n\tinput [19:0] counter\n);\n\tdefault clocking @(posedge clock); endclocking\n\tdefault disable iff (reset);\n\n\t// make sure the counter doesn't jump over any of the \"magic values\"\n\t`demo_counter_abstr_mode property ((counter < 123456) |=> (counter <= 123456));\n\t`demo_counter_abstr_mode property ((counter < 234567) |=> (counter <= 234567));\n\t`demo_counter_abstr_mode property ((counter < 345678) |=> (counter <= 345678));\n\t`demo_counter_abstr_mode property ((counter < 456789) |=> (counter <= 456789));\n\n\t// strictly increasing, only allow overflow by visiting the max value\n\t`demo_counter_abstr_mode property (counter != 20'hfffff |=> $past(counter) < counter);\n\t`demo_counter_abstr_mode property (counter == 20'hfffff |=> counter == 20'h00000);\nendmodule\n\nbind demo demo_counter_abstr demo_counter_abstr_i (.*);\n"
  },
  {
    "path": "docs/examples/abstract/demo.v",
    "content": "module demo (\n\tinput clock,\n\tinput reset,\n\toutput A, B, C, D\n);\n\treg [19:0] counter = 0;\n\n\talways @(posedge clock) begin\n\t\tif (reset)\n\t\t\tcounter <= 0;\n\t\telse\n\t\t\tcounter <= counter + 20'd 1;\n\tend\n\n\tassign A = counter == 123456;\n\tassign B = counter == 234567;\n\tassign C = counter == 345678;\n\tassign D = counter == 456789;\nendmodule\n"
  },
  {
    "path": "docs/examples/abstract/props.sby",
    "content": "[tasks]\ncvr\nprv\n\n[options]\ncvr: mode cover\nprv: mode prove\n\n[engines]\ncvr: smtbmc\nprv: abc pdr\n\n[script]\nread -verific\nread -define demo_counter_abstr_mode=assume\nread -sv abstr.sv\nread -sv props.sv\nread -sv demo.v\nprep -top demo\ncutpoint demo/counter\n\n[files]\nabstr.sv\nprops.sv\ndemo.v\n"
  },
  {
    "path": "docs/examples/abstract/props.sv",
    "content": "module demo_props (\n\tinput clock, reset,\n\tinput A, B, C, D\n);\n\tdefault clocking @(posedge clock); endclocking\n\tdefault disable iff (reset);\n\n\tassert property (A |-> !{B,C,D} [*] ##1 B);\n\tassert property (B |-> !{A,C,D} [*] ##1 C);\n\tassert property (C |-> !{A,B,D} [*] ##1 D);\n\tassert property (D |-> !{A,B,C} [*] ##1 A);\n\n\tcover property (A ##[+] B ##[+] C ##[+] D ##[+] A);\nendmodule\n\nbind demo demo_props demo_props_i (.*);\n"
  },
  {
    "path": "docs/examples/autotune/README.md",
    "content": "# Autotune demo\n\nThis directory contains a simple sequential integer divider circuit. The\nverilog implementation in [divider.sv](divider.sv) comes with assertions that\nthis circuit will always produce the correct result and will always finish\nwithin a fixed number of cycles. The circuit has the divider bit-width as\nparameter.\n\nIncreasing the WIDTH parameter quickly turns proving those assertions into a\nvery difficult proof for fully autmated solvers. This makes it a good example\nfor the `--autotune` option which tries different backend engines to find the\nbest performing engine configuration for a given verification task.\n\nThe [divider.sby](divider.sby) file defines 3 tasks named `small`, `medium` and\n`large` which configure the divider with different bit-widths. To verify the\n`small` divider using the default engine run:\n\n    sby -f divider.sby small\n\nTo automatically try different backend engines using autotune, run\n\n    sby --autotune -f divider.sby small\n\nThe `small` task should finish quickly using both the default engine and using\nautotune. The `medium` and `large` tasks take significantly longer and show\ngreater differences between engine configurations. Note that the `large` tasks\ncan take many minutes to hours, depending on the machine you are using.\n\nYou can learn more about Sby's autotune feature from [Sby's\ndocumentation](https://symbiyosys.readthedocs.io/en/latest/autotune.html).\n"
  },
  {
    "path": "docs/examples/autotune/divider.sby",
    "content": "[tasks]\nsmall default\nmedium\nlarge\n\n[options]\nmode prove\nsmall: depth 11\nmedium: depth 15\nlarge: depth 19\n\n[engines]\nsmtbmc\n\n[script]\nsmall: read -define WIDTH=8\nmedium: read -define WIDTH=12\nlarge: read -define WIDTH=16\n\nread -formal divider.sv\nprep -top divider\n\n[files]\ndivider.sv\n"
  },
  {
    "path": "docs/examples/autotune/divider.sv",
    "content": "`ifndef WIDTH\n`define WIDTH 4\n`endif\n\nmodule divider #(\n    parameter WIDTH=`WIDTH\n) (\n    input wire clk,\n    input wire start,\n    input wire [WIDTH-1:0] dividend,\n    input wire [WIDTH-1:0] divisor,\n\n    output reg done,\n    output reg [WIDTH-1:0] quotient,\n    output wire [WIDTH-1:0] remainder\n);\n\n    reg [WIDTH-1:0] acc;\n\n    reg [WIDTH*2-1:0] sub;\n    reg [WIDTH-1:0] pos;\n\n    assign remainder = acc;\n\n    always @(posedge clk) begin\n        if (start) begin\n            acc <= dividend;\n            quotient <= 0;\n            sub <= divisor << (WIDTH - 1);\n            pos <= 1 << (WIDTH - 1);\n            done <= 0;\n        end else if (!done) begin\n            if (acc >= sub) begin\n                acc <= acc - sub[WIDTH-1:0];\n                quotient <= quotient + pos;\n            end\n\n            sub <= sub >> 1;\n            {pos, done} <= pos;\n        end\n    end\n\n\n`ifdef FORMAL\n    reg [WIDTH-1:0] start_dividend = 0;\n    reg [WIDTH-1:0] start_divisor = 0;\n\n    reg started = 0;\n    reg finished = 0;\n    reg [$clog2(WIDTH + 1):0] counter = 0;\n\n    always @(posedge clk) begin\n        // Bound the number of cycles until the result is ready\n        assert (counter <= WIDTH);\n\n        if (started) begin\n            if (finished || done) begin\n                finished <= 1;\n                // Make sure result stays until we start a new division\n                assert (done);\n\n                // Check the result\n                if (start_divisor == 0) begin\n                    assert (&quotient);\n                    assert (remainder == start_dividend);\n                end else begin\n                    assert (quotient == start_dividend / start_divisor);\n                    assert (remainder == start_dividend % start_divisor);\n                end\n            end else begin\n                counter <= counter + 1'b1;\n            end\n        end\n\n        // Track the requested inputs\n        if (start) begin\n            start_divisor <= divisor;\n            start_dividend <= dividend;\n            started <= 1;\n            counter <= 0;\n            finished <= 0;\n        end\n    end\n`endif\nendmodule\n"
  },
  {
    "path": "docs/examples/demos/.gitignore",
    "content": "/fib_cover\n/fib_prove\n/fib_live\n"
  },
  {
    "path": "docs/examples/demos/Makefile",
    "content": "SUBDIR=examples/demos\nTESTDIR=../../../tests\ninclude $(TESTDIR)/make/subdir.mk\n"
  },
  {
    "path": "docs/examples/demos/axicheck.v",
    "content": "module testbench (\n\tinput         clk,\n\toutput        trap,\n\n\toutput        mem_axi_awvalid,\n\tinput         mem_axi_awready,\n\toutput [31:0] mem_axi_awaddr,\n\toutput [ 2:0] mem_axi_awprot,\n\n\toutput        mem_axi_wvalid,\n\tinput         mem_axi_wready,\n\toutput [31:0] mem_axi_wdata,\n\toutput [ 3:0] mem_axi_wstrb,\n\n\tinput         mem_axi_bvalid,\n\toutput        mem_axi_bready,\n\n\toutput        mem_axi_arvalid,\n\tinput         mem_axi_arready,\n\toutput [31:0] mem_axi_araddr,\n\toutput [ 2:0] mem_axi_arprot,\n\n\tinput         mem_axi_rvalid,\n\toutput        mem_axi_rready,\n\tinput  [31:0] mem_axi_rdata\n);\n\treg resetn = 0;\n\n\talways @(posedge clk)\n\t\tresetn <= 1;\n\n\tpicorv32_axi #(\n\t\t.ENABLE_COUNTERS(1),\n\t\t.ENABLE_COUNTERS64(1),\n\t\t.ENABLE_REGS_16_31(1),\n\t\t.ENABLE_REGS_DUALPORT(1),\n\t\t.BARREL_SHIFTER(1),\n\t\t.TWO_CYCLE_COMPARE(0),\n\t\t.TWO_CYCLE_ALU(0),\n\t\t.COMPRESSED_ISA(0),\n\t\t.CATCH_MISALIGN(1),\n\t\t.CATCH_ILLINSN(1)\n\t) uut (\n\t\t.clk             (clk            ),\n\t\t.resetn          (resetn         ),\n\t\t.trap            (trap           ),\n\t\t.mem_axi_awvalid (mem_axi_awvalid),\n\t\t.mem_axi_awready (mem_axi_awready),\n\t\t.mem_axi_awaddr  (mem_axi_awaddr ),\n\t\t.mem_axi_awprot  (mem_axi_awprot ),\n\t\t.mem_axi_wvalid  (mem_axi_wvalid ),\n\t\t.mem_axi_wready  (mem_axi_wready ),\n\t\t.mem_axi_wdata   (mem_axi_wdata  ),\n\t\t.mem_axi_wstrb   (mem_axi_wstrb  ),\n\t\t.mem_axi_bvalid  (mem_axi_bvalid ),\n\t\t.mem_axi_bready  (mem_axi_bready ),\n\t\t.mem_axi_arvalid (mem_axi_arvalid),\n\t\t.mem_axi_arready (mem_axi_arready),\n\t\t.mem_axi_araddr  (mem_axi_araddr ),\n\t\t.mem_axi_arprot  (mem_axi_arprot ),\n\t\t.mem_axi_rvalid  (mem_axi_rvalid ),\n\t\t.mem_axi_rready  (mem_axi_rready ),\n\t\t.mem_axi_rdata   (mem_axi_rdata  )\n\t);\n\n\treg expect_bvalid_aw = 0;\n\treg expect_bvalid_w  = 0;\n\treg expect_rvalid    = 0;\n\n\treg [3:0] timeout_aw = 0;\n\treg [3:0] timeout_w  = 0;\n\treg [3:0] timeout_b  = 0;\n\treg [3:0] timeout_ar = 0;\n\treg [3:0] timeout_r  = 0;\n\treg [3:0] timeout_ex = 0;\n\n\talways @(posedge clk) begin\n\t\ttimeout_aw <= !mem_axi_awvalid || mem_axi_awready ? 0 : timeout_aw + 1;\n\t\ttimeout_w  <= !mem_axi_wvalid  || mem_axi_wready  ? 0 : timeout_w  + 1;\n\t\ttimeout_b  <= !mem_axi_bvalid  || mem_axi_bready  ? 0 : timeout_b  + 1;\n\t\ttimeout_ar <= !mem_axi_arvalid || mem_axi_arready ? 0 : timeout_ar + 1;\n\t\ttimeout_r  <= !mem_axi_rvalid  || mem_axi_rready  ? 0 : timeout_r  + 1;\n\t\ttimeout_ex <= !{expect_bvalid_aw, expect_bvalid_w, expect_rvalid} ? 0 : timeout_ex + 1;\n\t\trestrict(timeout_aw != 15);\n\t\trestrict(timeout_w  != 15);\n\t\trestrict(timeout_b  != 15);\n\t\trestrict(timeout_ar != 15);\n\t\trestrict(timeout_r  != 15);\n\t\trestrict(timeout_ex != 15);\n\t\trestrict(!trap);\n\n\tend\n\n\talways @(posedge clk) begin\n\t\tif (resetn) begin\n\t\t\tif (!$past(resetn)) begin\n\t\t\t\tassert(!mem_axi_awvalid);\n\t\t\t\tassert(!mem_axi_wvalid );\n\t\t\t\tassume(!mem_axi_bvalid );\n\t\t\t\tassert(!mem_axi_arvalid);\n\t\t\t\tassume(!mem_axi_rvalid );\n\t\t\tend else begin\n\t\t\t\t// Only one read/write transaction in flight at each point in time\n\n\t\t\t\tif (expect_bvalid_aw) begin\n\t\t\t\t\tassert(!mem_axi_awvalid);\n\t\t\t\tend\n\n\t\t\t\tif (expect_bvalid_w) begin\n\t\t\t\t\tassert(!mem_axi_wvalid);\n\t\t\t\tend\n\n\t\t\t\tif (expect_rvalid) begin\n\t\t\t\t\tassert(!mem_axi_arvalid);\n\t\t\t\tend\n\n\t\t\t\texpect_bvalid_aw = expect_bvalid_aw || (mem_axi_awvalid && mem_axi_awready);\n\t\t\t\texpect_bvalid_w  = expect_bvalid_w  || (mem_axi_wvalid  && mem_axi_wready );\n\t\t\t\texpect_rvalid    = expect_rvalid    || (mem_axi_arvalid && mem_axi_arready);\n\n\t\t\t\tif (expect_bvalid_aw || expect_bvalid_w) begin\n\t\t\t\t\tassert(!expect_rvalid);\n\t\t\t\tend\n\n\t\t\t\tif (expect_rvalid) begin\n\t\t\t\t\tassert(!expect_bvalid_aw);\n\t\t\t\t\tassert(!expect_bvalid_w);\n\t\t\t\tend\n\n\t\t\t\tif (!expect_bvalid_aw || !expect_bvalid_w) begin\n\t\t\t\t\tassume(!mem_axi_bvalid);\n\t\t\t\tend\n\n\t\t\t\tif (!expect_rvalid) begin\n\t\t\t\t\tassume(!mem_axi_rvalid);\n\t\t\t\tend\n\n\t\t\t\tif (mem_axi_bvalid && mem_axi_bready) begin\n\t\t\t\t\texpect_bvalid_aw = 0;\n\t\t\t\t\texpect_bvalid_w = 0;\n\t\t\t\tend\n\n\t\t\t\tif (mem_axi_rvalid && mem_axi_rready) begin\n\t\t\t\t\texpect_rvalid = 0;\n\t\t\t\tend\n\n\t\t\t\t// Check AXI Master Streams\n\n\t\t\t\tif ($past(mem_axi_awvalid && !mem_axi_awready)) begin\n\t\t\t\t\tassert(mem_axi_awvalid);\n\t\t\t\t\tassert($stable(mem_axi_awaddr));\n\t\t\t\t\tassert($stable(mem_axi_awprot));\n\t\t\t\tend\n\t\t\t\tif ($fell(mem_axi_awvalid)) begin\n\t\t\t\t\tassert($past(mem_axi_awready));\n\t\t\t\tend\n\t\t\t\tif ($fell(mem_axi_awready)) begin\n\t\t\t\t\tassume($past(mem_axi_awvalid));\n\t\t\t\tend\n\n\t\t\t\tif ($past(mem_axi_arvalid && !mem_axi_arready)) begin\n\t\t\t\t\tassert(mem_axi_arvalid);\n\t\t\t\t\tassert($stable(mem_axi_araddr));\n\t\t\t\t\tassert($stable(mem_axi_arprot));\n\t\t\t\tend\n\t\t\t\tif ($fell(mem_axi_arvalid)) begin\n\t\t\t\t\tassert($past(mem_axi_arready));\n\t\t\t\tend\n\t\t\t\tif ($fell(mem_axi_arready)) begin\n\t\t\t\t\tassume($past(mem_axi_arvalid));\n\t\t\t\tend\n\n\t\t\t\tif ($past(mem_axi_wvalid && !mem_axi_wready)) begin\n\t\t\t\t\tassert(mem_axi_wvalid);\n\t\t\t\t\tassert($stable(mem_axi_wdata));\n\t\t\t\t\tassert($stable(mem_axi_wstrb));\n\t\t\t\tend\n\t\t\t\tif ($fell(mem_axi_wvalid)) begin\n\t\t\t\t\tassert($past(mem_axi_wready));\n\t\t\t\tend\n\t\t\t\tif ($fell(mem_axi_wready)) begin\n\t\t\t\t\tassume($past(mem_axi_wvalid));\n\t\t\t\tend\n\n\t\t\t\t// Check AXI Slave Streams\n\n\t\t\t\tif ($past(mem_axi_bvalid && !mem_axi_bready)) begin\n\t\t\t\t\tassume(mem_axi_bvalid);\n\t\t\t\tend\n\t\t\t\tif ($fell(mem_axi_bvalid)) begin\n\t\t\t\t\tassume($past(mem_axi_bready));\n\t\t\t\tend\n\t\t\t\tif ($fell(mem_axi_bready)) begin\n\t\t\t\t\tassert($past(mem_axi_bvalid));\n\t\t\t\tend\n\n\t\t\t\tif ($past(mem_axi_rvalid && !mem_axi_rready)) begin\n\t\t\t\t\tassume(mem_axi_rvalid);\n\t\t\t\t\tassume($stable(mem_axi_rdata));\n\t\t\t\tend\n\t\t\t\tif ($fell(mem_axi_rvalid)) begin\n\t\t\t\t\tassume($past(mem_axi_rready));\n\t\t\t\tend\n\t\t\t\tif ($fell(mem_axi_rready)) begin\n\t\t\t\t\tassert($past(mem_axi_rvalid));\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nendmodule\n"
  },
  {
    "path": "docs/examples/demos/fib.sby",
    "content": "[tasks]\ncover\nprove\nlive\n\n[options]\ncover: mode cover\nprove: mode prove\nlive:  mode live\n\n[engines]\n\ncover:\nsmtbmc z3\n\nprove:\nabc pdr\n\nlive:\naiger suprove\n--\n\n[script]\nread -formal fib.sv\nprep -top fib\n\n[files]\nfib.sv\n"
  },
  {
    "path": "docs/examples/demos/fib.sv",
    "content": "module fib (\n\tinput clk, pause, start,\n\tinput [3:0] n,\n\toutput reg busy, done,\n\toutput reg [9:0] f\n);\n\treg [3:0] count;\n\treg [9:0] q;\n\n\tinitial begin\n\t\tdone = 0;\n\t\tbusy = 0;\n\tend\n\n\talways @(posedge clk) begin\n\t\tdone <= 0;\n\t\tif (!pause) begin\n\t\t\tif (!busy) begin\n\t\t\t\tif (start)\n\t\t\t\t\tbusy <= 1;\n\t\t\t\tcount <= 0;\n\t\t\t\tq <= 1;\n\t\t\t\tf <= 0;\n\t\t\tend else begin\n\t\t\t\tq <= f;\n\t\t\t\tf <= f + q;\n\t\t\t\tcount <= count + 1;\n\t\t\t\tif (count == n) begin\n\t\t\t\t\tbusy <= 0;\n\t\t\t\t\tdone <= 1;\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n`ifdef FORMAL\n\talways @(posedge clk) begin\n\t\tif (busy) begin\n\t\t\tassume (!start);\n\t\t\tassume ($stable(n));\n\t\tend\n\n\t\tif (done) begin\n\t\t\tcase ($past(n))\n\t\t\t\t0: assert (f == 1);\n\t\t\t\t1: assert (f == 1);\n\t\t\t\t2: assert (f == 2);\n\t\t\t\t3: assert (f == 3);\n\t\t\t\t4: assert (f == 5);\n\t\t\t\t5: assert (f == 8);\n\t\t\tendcase\n\t\t\tcover (f == 13);\n\t\t\tcover (f == 144);\n\t\t\tcover ($past(n) == 15);\n\t\tend\n\n\t\tassume property (s_eventually !pause);\n\n\t\tif (start && !pause)\n\t\t\tassert property (s_eventually done);\n\tend\n`endif\nendmodule\n"
  },
  {
    "path": "docs/examples/demos/memory.sby",
    "content": "[options]\ndepth 10\nmode bmc\n\n[engines]\nsmtbmc yices\n\n[script]\nread_verilog -formal demo.v\nprep -top top\n\n[file demo.v]\nmodule top (\n  input clk,\n  input [7:0] addr,\n  input [7:0] wdata,\n  output [7:0] rdata\n);\n  rand const reg [7:0] test_addr;\n  reg [7:0] test_data;\n  reg test_valid = 0;\n\n  always @(posedge clk) begin\n    if (addr == test_addr) begin\n      if (test_valid)\n        assert(test_data == rdata);\n      test_data <= wdata;\n      test_valid <= 1;\n    end\n  end\n\n  memory uut (\n    .clk  (clk  ),\n    .addr (addr ),\n    .wdata(wdata),\n    .rdata(rdata)\n  );\nendmodule\n\n\nmodule memory (\n  input clk,\n  input [7:0] addr,\n  input [7:0] wdata,\n  output [7:0] rdata\n);\n  reg [7:0] mem [0:255];\n\n  always @(posedge clk)\n    mem[addr] <= wdata;\n\n  assign rdata = mem[addr];\nendmodule\n"
  },
  {
    "path": "docs/examples/demos/picorv32.v",
    "content": "/*\n *  PicoRV32 -- A Small RISC-V (RV32I) Processor Core\n *\n *  Copyright (C) 2015  Claire Xenia Wolf <claire@yosyshq.com>\n *\n *  Permission to use, copy, modify, and/or distribute this software for any\n *  purpose with or without fee is hereby granted, provided that the above\n *  copyright notice and this permission notice appear in all copies.\n *\n *  THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n *\n */\n\n/* verilator lint_off WIDTH */\n/* verilator lint_off PINMISSING */\n/* verilator lint_off CASEOVERLAP */\n/* verilator lint_off CASEINCOMPLETE */\n\n`timescale 1 ns / 1 ps\n// `default_nettype none\n// `define DEBUGNETS\n// `define DEBUGREGS\n// `define DEBUGASM\n// `define DEBUG\n\n`ifdef DEBUG\n  `define debug(debug_command) debug_command\n`else\n  `define debug(debug_command)\n`endif\n\n`ifdef FORMAL\n  `define FORMAL_KEEP (* keep *)\n  `define assert(assert_expr) assert(assert_expr)\n`else\n  `ifdef DEBUGNETS\n    `define FORMAL_KEEP (* keep *)\n  `else\n    `define FORMAL_KEEP\n  `endif\n  `define assert(assert_expr) empty_statement\n`endif\n\n// uncomment this for register file in extra module\n// `define PICORV32_REGS picorv32_regs\n\n// this macro can be used to check if the verilog files in your\n// design are read in the correct order.\n`define PICORV32_V\n\n\n/***************************************************************\n * picorv32\n ***************************************************************/\n\nmodule picorv32 #(\n\tparameter [ 0:0] ENABLE_COUNTERS = 1,\n\tparameter [ 0:0] ENABLE_COUNTERS64 = 1,\n\tparameter [ 0:0] ENABLE_REGS_16_31 = 1,\n\tparameter [ 0:0] ENABLE_REGS_DUALPORT = 1,\n\tparameter [ 0:0] LATCHED_MEM_RDATA = 0,\n\tparameter [ 0:0] TWO_STAGE_SHIFT = 1,\n\tparameter [ 0:0] BARREL_SHIFTER = 0,\n\tparameter [ 0:0] TWO_CYCLE_COMPARE = 0,\n\tparameter [ 0:0] TWO_CYCLE_ALU = 0,\n\tparameter [ 0:0] COMPRESSED_ISA = 0,\n\tparameter [ 0:0] CATCH_MISALIGN = 1,\n\tparameter [ 0:0] CATCH_ILLINSN = 1,\n\tparameter [ 0:0] ENABLE_PCPI = 0,\n\tparameter [ 0:0] ENABLE_MUL = 0,\n\tparameter [ 0:0] ENABLE_FAST_MUL = 0,\n\tparameter [ 0:0] ENABLE_DIV = 0,\n\tparameter [ 0:0] ENABLE_IRQ = 0,\n\tparameter [ 0:0] ENABLE_IRQ_QREGS = 1,\n\tparameter [ 0:0] ENABLE_IRQ_TIMER = 1,\n\tparameter [ 0:0] ENABLE_TRACE = 0,\n\tparameter [ 0:0] REGS_INIT_ZERO = 0,\n\tparameter [31:0] MASKED_IRQ = 32'h 0000_0000,\n\tparameter [31:0] LATCHED_IRQ = 32'h ffff_ffff,\n\tparameter [31:0] PROGADDR_RESET = 32'h 0000_0000,\n\tparameter [31:0] PROGADDR_IRQ = 32'h 0000_0010,\n\tparameter [31:0] STACKADDR = 32'h ffff_ffff\n) (\n\tinput clk, resetn,\n\toutput reg trap,\n\n\toutput reg        mem_valid,\n\toutput reg        mem_instr,\n\tinput             mem_ready,\n\n\toutput reg [31:0] mem_addr,\n\toutput reg [31:0] mem_wdata,\n\toutput reg [ 3:0] mem_wstrb,\n\tinput      [31:0] mem_rdata,\n\n\t// Look-Ahead Interface\n\toutput            mem_la_read,\n\toutput            mem_la_write,\n\toutput     [31:0] mem_la_addr,\n\toutput reg [31:0] mem_la_wdata,\n\toutput reg [ 3:0] mem_la_wstrb,\n\n\t// Pico Co-Processor Interface (PCPI)\n\toutput reg        pcpi_valid,\n\toutput reg [31:0] pcpi_insn,\n\toutput     [31:0] pcpi_rs1,\n\toutput     [31:0] pcpi_rs2,\n\tinput             pcpi_wr,\n\tinput      [31:0] pcpi_rd,\n\tinput             pcpi_wait,\n\tinput             pcpi_ready,\n\n\t// IRQ Interface\n\tinput      [31:0] irq,\n\toutput reg [31:0] eoi,\n\n`ifdef RISCV_FORMAL\n\toutput reg        rvfi_valid,\n\toutput reg [63:0] rvfi_order,\n\toutput reg [31:0] rvfi_insn,\n\toutput reg        rvfi_trap,\n\toutput reg        rvfi_halt,\n\toutput reg        rvfi_intr,\n\toutput reg [ 1:0] rvfi_mode,\n\toutput reg [ 1:0] rvfi_ixl,\n\toutput reg [ 4:0] rvfi_rs1_addr,\n\toutput reg [ 4:0] rvfi_rs2_addr,\n\toutput reg [31:0] rvfi_rs1_rdata,\n\toutput reg [31:0] rvfi_rs2_rdata,\n\toutput reg [ 4:0] rvfi_rd_addr,\n\toutput reg [31:0] rvfi_rd_wdata,\n\toutput reg [31:0] rvfi_pc_rdata,\n\toutput reg [31:0] rvfi_pc_wdata,\n\toutput reg [31:0] rvfi_mem_addr,\n\toutput reg [ 3:0] rvfi_mem_rmask,\n\toutput reg [ 3:0] rvfi_mem_wmask,\n\toutput reg [31:0] rvfi_mem_rdata,\n\toutput reg [31:0] rvfi_mem_wdata,\n\n\toutput reg [63:0] rvfi_csr_mcycle_rmask,\n\toutput reg [63:0] rvfi_csr_mcycle_wmask,\n\toutput reg [63:0] rvfi_csr_mcycle_rdata,\n\toutput reg [63:0] rvfi_csr_mcycle_wdata,\n\n\toutput reg [63:0] rvfi_csr_minstret_rmask,\n\toutput reg [63:0] rvfi_csr_minstret_wmask,\n\toutput reg [63:0] rvfi_csr_minstret_rdata,\n\toutput reg [63:0] rvfi_csr_minstret_wdata,\n`endif\n\n\t// Trace Interface\n\toutput reg        trace_valid,\n\toutput reg [35:0] trace_data\n);\n\tlocalparam integer irq_timer = 0;\n\tlocalparam integer irq_ebreak = 1;\n\tlocalparam integer irq_buserror = 2;\n\n\tlocalparam integer irqregs_offset = ENABLE_REGS_16_31 ? 32 : 16;\n\tlocalparam integer regfile_size = (ENABLE_REGS_16_31 ? 32 : 16) + 4*ENABLE_IRQ*ENABLE_IRQ_QREGS;\n\tlocalparam integer regindex_bits = (ENABLE_REGS_16_31 ? 5 : 4) + ENABLE_IRQ*ENABLE_IRQ_QREGS;\n\n\tlocalparam WITH_PCPI = ENABLE_PCPI || ENABLE_MUL || ENABLE_FAST_MUL || ENABLE_DIV;\n\n\tlocalparam [35:0] TRACE_BRANCH = {4'b 0001, 32'b 0};\n\tlocalparam [35:0] TRACE_ADDR   = {4'b 0010, 32'b 0};\n\tlocalparam [35:0] TRACE_IRQ    = {4'b 1000, 32'b 0};\n\n\treg [63:0] count_cycle, count_instr;\n\treg [31:0] reg_pc, reg_next_pc, reg_op1, reg_op2, reg_out;\n\treg [4:0] reg_sh;\n\n\treg [31:0] next_insn_opcode;\n\treg [31:0] dbg_insn_opcode;\n\treg [31:0] dbg_insn_addr;\n\n\twire dbg_mem_valid = mem_valid;\n\twire dbg_mem_instr = mem_instr;\n\twire dbg_mem_ready = mem_ready;\n\twire [31:0] dbg_mem_addr  = mem_addr;\n\twire [31:0] dbg_mem_wdata = mem_wdata;\n\twire [ 3:0] dbg_mem_wstrb = mem_wstrb;\n\twire [31:0] dbg_mem_rdata = mem_rdata;\n\n\tassign pcpi_rs1 = reg_op1;\n\tassign pcpi_rs2 = reg_op2;\n\n\twire [31:0] next_pc;\n\n\treg irq_delay;\n\treg irq_active;\n\treg [31:0] irq_mask;\n\treg [31:0] irq_pending;\n\treg [31:0] timer;\n\n`ifndef PICORV32_REGS\n\treg [31:0] cpuregs [0:regfile_size-1];\n\n\tinteger i;\n\tinitial begin\n\t\tif (REGS_INIT_ZERO) begin\n\t\t\tfor (i = 0; i < regfile_size; i = i+1)\n\t\t\t\tcpuregs[i] = 0;\n\t\tend\n\tend\n`endif\n\n\ttask empty_statement;\n\t\t// This task is used by the `assert directive in non-formal mode to\n\t\t// avoid empty statement (which are unsupported by plain Verilog syntax).\n\t\tbegin end\n\tendtask\n\n`ifdef DEBUGREGS\n\twire [31:0] dbg_reg_x0  = 0;\n\twire [31:0] dbg_reg_x1  = cpuregs[1];\n\twire [31:0] dbg_reg_x2  = cpuregs[2];\n\twire [31:0] dbg_reg_x3  = cpuregs[3];\n\twire [31:0] dbg_reg_x4  = cpuregs[4];\n\twire [31:0] dbg_reg_x5  = cpuregs[5];\n\twire [31:0] dbg_reg_x6  = cpuregs[6];\n\twire [31:0] dbg_reg_x7  = cpuregs[7];\n\twire [31:0] dbg_reg_x8  = cpuregs[8];\n\twire [31:0] dbg_reg_x9  = cpuregs[9];\n\twire [31:0] dbg_reg_x10 = cpuregs[10];\n\twire [31:0] dbg_reg_x11 = cpuregs[11];\n\twire [31:0] dbg_reg_x12 = cpuregs[12];\n\twire [31:0] dbg_reg_x13 = cpuregs[13];\n\twire [31:0] dbg_reg_x14 = cpuregs[14];\n\twire [31:0] dbg_reg_x15 = cpuregs[15];\n\twire [31:0] dbg_reg_x16 = cpuregs[16];\n\twire [31:0] dbg_reg_x17 = cpuregs[17];\n\twire [31:0] dbg_reg_x18 = cpuregs[18];\n\twire [31:0] dbg_reg_x19 = cpuregs[19];\n\twire [31:0] dbg_reg_x20 = cpuregs[20];\n\twire [31:0] dbg_reg_x21 = cpuregs[21];\n\twire [31:0] dbg_reg_x22 = cpuregs[22];\n\twire [31:0] dbg_reg_x23 = cpuregs[23];\n\twire [31:0] dbg_reg_x24 = cpuregs[24];\n\twire [31:0] dbg_reg_x25 = cpuregs[25];\n\twire [31:0] dbg_reg_x26 = cpuregs[26];\n\twire [31:0] dbg_reg_x27 = cpuregs[27];\n\twire [31:0] dbg_reg_x28 = cpuregs[28];\n\twire [31:0] dbg_reg_x29 = cpuregs[29];\n\twire [31:0] dbg_reg_x30 = cpuregs[30];\n\twire [31:0] dbg_reg_x31 = cpuregs[31];\n`endif\n\n\t// Internal PCPI Cores\n\n\twire        pcpi_mul_wr;\n\twire [31:0] pcpi_mul_rd;\n\twire        pcpi_mul_wait;\n\twire        pcpi_mul_ready;\n\n\twire        pcpi_div_wr;\n\twire [31:0] pcpi_div_rd;\n\twire        pcpi_div_wait;\n\twire        pcpi_div_ready;\n\n\treg        pcpi_int_wr;\n\treg [31:0] pcpi_int_rd;\n\treg        pcpi_int_wait;\n\treg        pcpi_int_ready;\n\n\tgenerate if (ENABLE_FAST_MUL) begin\n\t\tpicorv32_pcpi_fast_mul pcpi_mul (\n\t\t\t.clk       (clk            ),\n\t\t\t.resetn    (resetn         ),\n\t\t\t.pcpi_valid(pcpi_valid     ),\n\t\t\t.pcpi_insn (pcpi_insn      ),\n\t\t\t.pcpi_rs1  (pcpi_rs1       ),\n\t\t\t.pcpi_rs2  (pcpi_rs2       ),\n\t\t\t.pcpi_wr   (pcpi_mul_wr    ),\n\t\t\t.pcpi_rd   (pcpi_mul_rd    ),\n\t\t\t.pcpi_wait (pcpi_mul_wait  ),\n\t\t\t.pcpi_ready(pcpi_mul_ready )\n\t\t);\n\tend else if (ENABLE_MUL) begin\n\t\tpicorv32_pcpi_mul pcpi_mul (\n\t\t\t.clk       (clk            ),\n\t\t\t.resetn    (resetn         ),\n\t\t\t.pcpi_valid(pcpi_valid     ),\n\t\t\t.pcpi_insn (pcpi_insn      ),\n\t\t\t.pcpi_rs1  (pcpi_rs1       ),\n\t\t\t.pcpi_rs2  (pcpi_rs2       ),\n\t\t\t.pcpi_wr   (pcpi_mul_wr    ),\n\t\t\t.pcpi_rd   (pcpi_mul_rd    ),\n\t\t\t.pcpi_wait (pcpi_mul_wait  ),\n\t\t\t.pcpi_ready(pcpi_mul_ready )\n\t\t);\n\tend else begin\n\t\tassign pcpi_mul_wr = 0;\n\t\tassign pcpi_mul_rd = 32'bx;\n\t\tassign pcpi_mul_wait = 0;\n\t\tassign pcpi_mul_ready = 0;\n\tend endgenerate\n\n\tgenerate if (ENABLE_DIV) begin\n\t\tpicorv32_pcpi_div pcpi_div (\n\t\t\t.clk       (clk            ),\n\t\t\t.resetn    (resetn         ),\n\t\t\t.pcpi_valid(pcpi_valid     ),\n\t\t\t.pcpi_insn (pcpi_insn      ),\n\t\t\t.pcpi_rs1  (pcpi_rs1       ),\n\t\t\t.pcpi_rs2  (pcpi_rs2       ),\n\t\t\t.pcpi_wr   (pcpi_div_wr    ),\n\t\t\t.pcpi_rd   (pcpi_div_rd    ),\n\t\t\t.pcpi_wait (pcpi_div_wait  ),\n\t\t\t.pcpi_ready(pcpi_div_ready )\n\t\t);\n\tend else begin\n\t\tassign pcpi_div_wr = 0;\n\t\tassign pcpi_div_rd = 32'bx;\n\t\tassign pcpi_div_wait = 0;\n\t\tassign pcpi_div_ready = 0;\n\tend endgenerate\n\n\talways @* begin\n\t\tpcpi_int_wr = 0;\n\t\tpcpi_int_rd = 32'bx;\n\t\tpcpi_int_wait  = |{ENABLE_PCPI && pcpi_wait,  (ENABLE_MUL || ENABLE_FAST_MUL) && pcpi_mul_wait,  ENABLE_DIV && pcpi_div_wait};\n\t\tpcpi_int_ready = |{ENABLE_PCPI && pcpi_ready, (ENABLE_MUL || ENABLE_FAST_MUL) && pcpi_mul_ready, ENABLE_DIV && pcpi_div_ready};\n\n\t\t(* parallel_case *)\n\t\tcase (1'b1)\n\t\t\tENABLE_PCPI && pcpi_ready: begin\n\t\t\t\tpcpi_int_wr = ENABLE_PCPI ? pcpi_wr : 0;\n\t\t\t\tpcpi_int_rd = ENABLE_PCPI ? pcpi_rd : 0;\n\t\t\tend\n\t\t\t(ENABLE_MUL || ENABLE_FAST_MUL) && pcpi_mul_ready: begin\n\t\t\t\tpcpi_int_wr = pcpi_mul_wr;\n\t\t\t\tpcpi_int_rd = pcpi_mul_rd;\n\t\t\tend\n\t\t\tENABLE_DIV && pcpi_div_ready: begin\n\t\t\t\tpcpi_int_wr = pcpi_div_wr;\n\t\t\t\tpcpi_int_rd = pcpi_div_rd;\n\t\t\tend\n\t\tendcase\n\tend\n\n\n\t// Memory Interface\n\n\treg [1:0] mem_state;\n\treg [1:0] mem_wordsize;\n\treg [31:0] mem_rdata_word;\n\treg [31:0] mem_rdata_q;\n\treg mem_do_prefetch;\n\treg mem_do_rinst;\n\treg mem_do_rdata;\n\treg mem_do_wdata;\n\n\twire mem_xfer;\n\treg mem_la_secondword, mem_la_firstword_reg, last_mem_valid;\n\twire mem_la_firstword = COMPRESSED_ISA && (mem_do_prefetch || mem_do_rinst) && next_pc[1] && !mem_la_secondword;\n\twire mem_la_firstword_xfer = COMPRESSED_ISA && mem_xfer && (!last_mem_valid ? mem_la_firstword : mem_la_firstword_reg);\n\n\treg prefetched_high_word;\n\treg clear_prefetched_high_word;\n\treg [15:0] mem_16bit_buffer;\n\n\twire [31:0] mem_rdata_latched_noshuffle;\n\twire [31:0] mem_rdata_latched;\n\n\twire mem_la_use_prefetched_high_word = COMPRESSED_ISA && mem_la_firstword && prefetched_high_word && !clear_prefetched_high_word;\n\tassign mem_xfer = (mem_valid && mem_ready) || (mem_la_use_prefetched_high_word && mem_do_rinst);\n\n\twire mem_busy = |{mem_do_prefetch, mem_do_rinst, mem_do_rdata, mem_do_wdata};\n\twire mem_done = resetn && ((mem_xfer && |mem_state && (mem_do_rinst || mem_do_rdata || mem_do_wdata)) || (&mem_state && mem_do_rinst)) &&\n\t\t\t(!mem_la_firstword || (~&mem_rdata_latched[1:0] && mem_xfer));\n\n\tassign mem_la_write = resetn && !mem_state && mem_do_wdata;\n\tassign mem_la_read = resetn && ((!mem_la_use_prefetched_high_word && !mem_state && (mem_do_rinst || mem_do_prefetch || mem_do_rdata)) ||\n\t\t\t(COMPRESSED_ISA && mem_xfer && (!last_mem_valid ? mem_la_firstword : mem_la_firstword_reg) && !mem_la_secondword && &mem_rdata_latched[1:0]));\n\tassign mem_la_addr = (mem_do_prefetch || mem_do_rinst) ? {next_pc[31:2] + mem_la_firstword_xfer, 2'b00} : {reg_op1[31:2], 2'b00};\n\n\tassign mem_rdata_latched_noshuffle = (mem_xfer || LATCHED_MEM_RDATA) ? mem_rdata : mem_rdata_q;\n\n\tassign mem_rdata_latched = COMPRESSED_ISA && mem_la_use_prefetched_high_word ? {16'bx, mem_16bit_buffer} :\n\t\t\tCOMPRESSED_ISA && mem_la_secondword ? {mem_rdata_latched_noshuffle[15:0], mem_16bit_buffer} :\n\t\t\tCOMPRESSED_ISA && mem_la_firstword ? {16'bx, mem_rdata_latched_noshuffle[31:16]} : mem_rdata_latched_noshuffle;\n\n\talways @(posedge clk) begin\n\t\tif (!resetn) begin\n\t\t\tmem_la_firstword_reg <= 0;\n\t\t\tlast_mem_valid <= 0;\n\t\tend else begin\n\t\t\tif (!last_mem_valid)\n\t\t\t\tmem_la_firstword_reg <= mem_la_firstword;\n\t\t\tlast_mem_valid <= mem_valid && !mem_ready;\n\t\tend\n\tend\n\n\talways @* begin\n\t\t(* full_case *)\n\t\tcase (mem_wordsize)\n\t\t\t0: begin\n\t\t\t\tmem_la_wdata = reg_op2;\n\t\t\t\tmem_la_wstrb = 4'b1111;\n\t\t\t\tmem_rdata_word = mem_rdata;\n\t\t\tend\n\t\t\t1: begin\n\t\t\t\tmem_la_wdata = {2{reg_op2[15:0]}};\n\t\t\t\tmem_la_wstrb = reg_op1[1] ? 4'b1100 : 4'b0011;\n\t\t\t\tcase (reg_op1[1])\n\t\t\t\t\t1'b0: mem_rdata_word = {16'b0, mem_rdata[15: 0]};\n\t\t\t\t\t1'b1: mem_rdata_word = {16'b0, mem_rdata[31:16]};\n\t\t\t\tendcase\n\t\t\tend\n\t\t\t2: begin\n\t\t\t\tmem_la_wdata = {4{reg_op2[7:0]}};\n\t\t\t\tmem_la_wstrb = 4'b0001 << reg_op1[1:0];\n\t\t\t\tcase (reg_op1[1:0])\n\t\t\t\t\t2'b00: mem_rdata_word = {24'b0, mem_rdata[ 7: 0]};\n\t\t\t\t\t2'b01: mem_rdata_word = {24'b0, mem_rdata[15: 8]};\n\t\t\t\t\t2'b10: mem_rdata_word = {24'b0, mem_rdata[23:16]};\n\t\t\t\t\t2'b11: mem_rdata_word = {24'b0, mem_rdata[31:24]};\n\t\t\t\tendcase\n\t\t\tend\n\t\tendcase\n\tend\n\n\talways @(posedge clk) begin\n\t\tif (mem_xfer) begin\n\t\t\tmem_rdata_q <= COMPRESSED_ISA ? mem_rdata_latched : mem_rdata;\n\t\t\tnext_insn_opcode <= COMPRESSED_ISA ? mem_rdata_latched : mem_rdata;\n\t\tend\n\n\t\tif (COMPRESSED_ISA && mem_done && (mem_do_prefetch || mem_do_rinst)) begin\n\t\t\tcase (mem_rdata_latched[1:0])\n\t\t\t\t2'b00: begin // Quadrant 0\n\t\t\t\t\tcase (mem_rdata_latched[15:13])\n\t\t\t\t\t\t3'b000: begin // C.ADDI4SPN\n\t\t\t\t\t\t\tmem_rdata_q[14:12] <= 3'b000;\n\t\t\t\t\t\t\tmem_rdata_q[31:20] <= {2'b0, mem_rdata_latched[10:7], mem_rdata_latched[12:11], mem_rdata_latched[5], mem_rdata_latched[6], 2'b00};\n\t\t\t\t\t\tend\n\t\t\t\t\t\t3'b010: begin // C.LW\n\t\t\t\t\t\t\tmem_rdata_q[31:20] <= {5'b0, mem_rdata_latched[5], mem_rdata_latched[12:10], mem_rdata_latched[6], 2'b00};\n\t\t\t\t\t\t\tmem_rdata_q[14:12] <= 3'b 010;\n\t\t\t\t\t\tend\n\t\t\t\t\t\t3'b 110: begin // C.SW\n\t\t\t\t\t\t\t{mem_rdata_q[31:25], mem_rdata_q[11:7]} <= {5'b0, mem_rdata_latched[5], mem_rdata_latched[12:10], mem_rdata_latched[6], 2'b00};\n\t\t\t\t\t\t\tmem_rdata_q[14:12] <= 3'b 010;\n\t\t\t\t\t\tend\n\t\t\t\t\tendcase\n\t\t\t\tend\n\t\t\t\t2'b01: begin // Quadrant 1\n\t\t\t\t\tcase (mem_rdata_latched[15:13])\n\t\t\t\t\t\t3'b 000: begin // C.ADDI\n\t\t\t\t\t\t\tmem_rdata_q[14:12] <= 3'b000;\n\t\t\t\t\t\t\tmem_rdata_q[31:20] <= $signed({mem_rdata_latched[12], mem_rdata_latched[6:2]});\n\t\t\t\t\t\tend\n\t\t\t\t\t\t3'b 010: begin // C.LI\n\t\t\t\t\t\t\tmem_rdata_q[14:12] <= 3'b000;\n\t\t\t\t\t\t\tmem_rdata_q[31:20] <= $signed({mem_rdata_latched[12], mem_rdata_latched[6:2]});\n\t\t\t\t\t\tend\n\t\t\t\t\t\t3'b 011: begin\n\t\t\t\t\t\t\tif (mem_rdata_latched[11:7] == 2) begin // C.ADDI16SP\n\t\t\t\t\t\t\t\tmem_rdata_q[14:12] <= 3'b000;\n\t\t\t\t\t\t\t\tmem_rdata_q[31:20] <= $signed({mem_rdata_latched[12], mem_rdata_latched[4:3],\n\t\t\t\t\t\t\t\t\t\tmem_rdata_latched[5], mem_rdata_latched[2], mem_rdata_latched[6], 4'b 0000});\n\t\t\t\t\t\t\tend else begin // C.LUI\n\t\t\t\t\t\t\t\tmem_rdata_q[31:12] <= $signed({mem_rdata_latched[12], mem_rdata_latched[6:2]});\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\t\t3'b100: begin\n\t\t\t\t\t\t\tif (mem_rdata_latched[11:10] == 2'b00) begin // C.SRLI\n\t\t\t\t\t\t\t\tmem_rdata_q[31:25] <= 7'b0000000;\n\t\t\t\t\t\t\t\tmem_rdata_q[14:12] <= 3'b 101;\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tif (mem_rdata_latched[11:10] == 2'b01) begin // C.SRAI\n\t\t\t\t\t\t\t\tmem_rdata_q[31:25] <= 7'b0100000;\n\t\t\t\t\t\t\t\tmem_rdata_q[14:12] <= 3'b 101;\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tif (mem_rdata_latched[11:10] == 2'b10) begin // C.ANDI\n\t\t\t\t\t\t\t\tmem_rdata_q[14:12] <= 3'b111;\n\t\t\t\t\t\t\t\tmem_rdata_q[31:20] <= $signed({mem_rdata_latched[12], mem_rdata_latched[6:2]});\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tif (mem_rdata_latched[12:10] == 3'b011) begin // C.SUB, C.XOR, C.OR, C.AND\n\t\t\t\t\t\t\t\tif (mem_rdata_latched[6:5] == 2'b00) mem_rdata_q[14:12] <= 3'b000;\n\t\t\t\t\t\t\t\tif (mem_rdata_latched[6:5] == 2'b01) mem_rdata_q[14:12] <= 3'b100;\n\t\t\t\t\t\t\t\tif (mem_rdata_latched[6:5] == 2'b10) mem_rdata_q[14:12] <= 3'b110;\n\t\t\t\t\t\t\t\tif (mem_rdata_latched[6:5] == 2'b11) mem_rdata_q[14:12] <= 3'b111;\n\t\t\t\t\t\t\t\tmem_rdata_q[31:25] <= mem_rdata_latched[6:5] == 2'b00 ? 7'b0100000 : 7'b0000000;\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\t\t3'b 110: begin // C.BEQZ\n\t\t\t\t\t\t\tmem_rdata_q[14:12] <= 3'b000;\n\t\t\t\t\t\t\t{ mem_rdata_q[31], mem_rdata_q[7], mem_rdata_q[30:25], mem_rdata_q[11:8] } <=\n\t\t\t\t\t\t\t\t\t$signed({mem_rdata_latched[12], mem_rdata_latched[6:5], mem_rdata_latched[2],\n\t\t\t\t\t\t\t\t\t\t\tmem_rdata_latched[11:10], mem_rdata_latched[4:3]});\n\t\t\t\t\t\tend\n\t\t\t\t\t\t3'b 111: begin // C.BNEZ\n\t\t\t\t\t\t\tmem_rdata_q[14:12] <= 3'b001;\n\t\t\t\t\t\t\t{ mem_rdata_q[31], mem_rdata_q[7], mem_rdata_q[30:25], mem_rdata_q[11:8] } <=\n\t\t\t\t\t\t\t\t\t$signed({mem_rdata_latched[12], mem_rdata_latched[6:5], mem_rdata_latched[2],\n\t\t\t\t\t\t\t\t\t\t\tmem_rdata_latched[11:10], mem_rdata_latched[4:3]});\n\t\t\t\t\t\tend\n\t\t\t\t\tendcase\n\t\t\t\tend\n\t\t\t\t2'b10: begin // Quadrant 2\n\t\t\t\t\tcase (mem_rdata_latched[15:13])\n\t\t\t\t\t\t3'b000: begin // C.SLLI\n\t\t\t\t\t\t\tmem_rdata_q[31:25] <= 7'b0000000;\n\t\t\t\t\t\t\tmem_rdata_q[14:12] <= 3'b 001;\n\t\t\t\t\t\tend\n\t\t\t\t\t\t3'b010: begin // C.LWSP\n\t\t\t\t\t\t\tmem_rdata_q[31:20] <= {4'b0, mem_rdata_latched[3:2], mem_rdata_latched[12], mem_rdata_latched[6:4], 2'b00};\n\t\t\t\t\t\t\tmem_rdata_q[14:12] <= 3'b 010;\n\t\t\t\t\t\tend\n\t\t\t\t\t\t3'b100: begin\n\t\t\t\t\t\t\tif (mem_rdata_latched[12] == 0 && mem_rdata_latched[6:2] == 0) begin // C.JR\n\t\t\t\t\t\t\t\tmem_rdata_q[14:12] <= 3'b000;\n\t\t\t\t\t\t\t\tmem_rdata_q[31:20] <= 12'b0;\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tif (mem_rdata_latched[12] == 0 && mem_rdata_latched[6:2] != 0) begin // C.MV\n\t\t\t\t\t\t\t\tmem_rdata_q[14:12] <= 3'b000;\n\t\t\t\t\t\t\t\tmem_rdata_q[31:25] <= 7'b0000000;\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tif (mem_rdata_latched[12] != 0 && mem_rdata_latched[11:7] != 0 && mem_rdata_latched[6:2] == 0) begin // C.JALR\n\t\t\t\t\t\t\t\tmem_rdata_q[14:12] <= 3'b000;\n\t\t\t\t\t\t\t\tmem_rdata_q[31:20] <= 12'b0;\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tif (mem_rdata_latched[12] != 0 && mem_rdata_latched[6:2] != 0) begin // C.ADD\n\t\t\t\t\t\t\t\tmem_rdata_q[14:12] <= 3'b000;\n\t\t\t\t\t\t\t\tmem_rdata_q[31:25] <= 7'b0000000;\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\t\t3'b110: begin // C.SWSP\n\t\t\t\t\t\t\t{mem_rdata_q[31:25], mem_rdata_q[11:7]} <= {4'b0, mem_rdata_latched[8:7], mem_rdata_latched[12:9], 2'b00};\n\t\t\t\t\t\t\tmem_rdata_q[14:12] <= 3'b 010;\n\t\t\t\t\t\tend\n\t\t\t\t\tendcase\n\t\t\t\tend\n\t\t\tendcase\n\t\tend\n\tend\n\n\talways @(posedge clk) begin\n\t\tif (resetn && !trap) begin\n\t\t\tif (mem_do_prefetch || mem_do_rinst || mem_do_rdata)\n\t\t\t\t`assert(!mem_do_wdata);\n\n\t\t\tif (mem_do_prefetch || mem_do_rinst)\n\t\t\t\t`assert(!mem_do_rdata);\n\n\t\t\tif (mem_do_rdata)\n\t\t\t\t`assert(!mem_do_prefetch && !mem_do_rinst);\n\n\t\t\tif (mem_do_wdata)\n\t\t\t\t`assert(!(mem_do_prefetch || mem_do_rinst || mem_do_rdata));\n\n\t\t\tif (mem_state == 2 || mem_state == 3)\n\t\t\t\t`assert(mem_valid || mem_do_prefetch);\n\t\tend\n\tend\n\n\talways @(posedge clk) begin\n\t\tif (!resetn || trap) begin\n\t\t\tif (!resetn)\n\t\t\t\tmem_state <= 0;\n\t\t\tif (!resetn || mem_ready)\n\t\t\t\tmem_valid <= 0;\n\t\t\tmem_la_secondword <= 0;\n\t\t\tprefetched_high_word <= 0;\n\t\tend else begin\n\t\t\tif (mem_la_read || mem_la_write) begin\n\t\t\t\tmem_addr <= mem_la_addr;\n\t\t\t\tmem_wstrb <= mem_la_wstrb & {4{mem_la_write}};\n\t\t\tend\n\t\t\tif (mem_la_write) begin\n\t\t\t\tmem_wdata <= mem_la_wdata;\n\t\t\tend\n\t\t\tcase (mem_state)\n\t\t\t\t0: begin\n\t\t\t\t\tif (mem_do_prefetch || mem_do_rinst || mem_do_rdata) begin\n\t\t\t\t\t\tmem_valid <= !mem_la_use_prefetched_high_word;\n\t\t\t\t\t\tmem_instr <= mem_do_prefetch || mem_do_rinst;\n\t\t\t\t\t\tmem_wstrb <= 0;\n\t\t\t\t\t\tmem_state <= 1;\n\t\t\t\t\tend\n\t\t\t\t\tif (mem_do_wdata) begin\n\t\t\t\t\t\tmem_valid <= 1;\n\t\t\t\t\t\tmem_instr <= 0;\n\t\t\t\t\t\tmem_state <= 2;\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\t1: begin\n\t\t\t\t\t`assert(mem_wstrb == 0);\n\t\t\t\t\t`assert(mem_do_prefetch || mem_do_rinst || mem_do_rdata);\n\t\t\t\t\t`assert(mem_valid == !mem_la_use_prefetched_high_word);\n\t\t\t\t\t`assert(mem_instr == (mem_do_prefetch || mem_do_rinst));\n\t\t\t\t\tif (mem_xfer) begin\n\t\t\t\t\t\tif (COMPRESSED_ISA && mem_la_read) begin\n\t\t\t\t\t\t\tmem_valid <= 1;\n\t\t\t\t\t\t\tmem_la_secondword <= 1;\n\t\t\t\t\t\t\tif (!mem_la_use_prefetched_high_word)\n\t\t\t\t\t\t\t\tmem_16bit_buffer <= mem_rdata[31:16];\n\t\t\t\t\t\tend else begin\n\t\t\t\t\t\t\tmem_valid <= 0;\n\t\t\t\t\t\t\tmem_la_secondword <= 0;\n\t\t\t\t\t\t\tif (COMPRESSED_ISA && !mem_do_rdata) begin\n\t\t\t\t\t\t\t\tif (~&mem_rdata[1:0] || mem_la_secondword) begin\n\t\t\t\t\t\t\t\t\tmem_16bit_buffer <= mem_rdata[31:16];\n\t\t\t\t\t\t\t\t\tprefetched_high_word <= 1;\n\t\t\t\t\t\t\t\tend else begin\n\t\t\t\t\t\t\t\t\tprefetched_high_word <= 0;\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tmem_state <= mem_do_rinst || mem_do_rdata ? 0 : 3;\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\t2: begin\n\t\t\t\t\t`assert(mem_wstrb != 0);\n\t\t\t\t\t`assert(mem_do_wdata);\n\t\t\t\t\tif (mem_xfer) begin\n\t\t\t\t\t\tmem_valid <= 0;\n\t\t\t\t\t\tmem_state <= 0;\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\t3: begin\n\t\t\t\t\t`assert(mem_wstrb == 0);\n\t\t\t\t\t`assert(mem_do_prefetch);\n\t\t\t\t\tif (mem_do_rinst) begin\n\t\t\t\t\t\tmem_state <= 0;\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tendcase\n\t\tend\n\n\t\tif (clear_prefetched_high_word)\n\t\t\tprefetched_high_word <= 0;\n\tend\n\n\n\t// Instruction Decoder\n\n\treg instr_lui, instr_auipc, instr_jal, instr_jalr;\n\treg instr_beq, instr_bne, instr_blt, instr_bge, instr_bltu, instr_bgeu;\n\treg instr_lb, instr_lh, instr_lw, instr_lbu, instr_lhu, instr_sb, instr_sh, instr_sw;\n\treg instr_addi, instr_slti, instr_sltiu, instr_xori, instr_ori, instr_andi, instr_slli, instr_srli, instr_srai;\n\treg instr_add, instr_sub, instr_sll, instr_slt, instr_sltu, instr_xor, instr_srl, instr_sra, instr_or, instr_and;\n\treg instr_rdcycle, instr_rdcycleh, instr_rdinstr, instr_rdinstrh, instr_ecall_ebreak;\n\treg instr_getq, instr_setq, instr_retirq, instr_maskirq, instr_waitirq, instr_timer;\n\twire instr_trap;\n\n\treg [regindex_bits-1:0] decoded_rd, decoded_rs1, decoded_rs2;\n\treg [31:0] decoded_imm, decoded_imm_j;\n\treg decoder_trigger;\n\treg decoder_trigger_q;\n\treg decoder_pseudo_trigger;\n\treg decoder_pseudo_trigger_q;\n\treg compressed_instr;\n\n\treg is_lui_auipc_jal;\n\treg is_lb_lh_lw_lbu_lhu;\n\treg is_slli_srli_srai;\n\treg is_jalr_addi_slti_sltiu_xori_ori_andi;\n\treg is_sb_sh_sw;\n\treg is_sll_srl_sra;\n\treg is_lui_auipc_jal_jalr_addi_add_sub;\n\treg is_slti_blt_slt;\n\treg is_sltiu_bltu_sltu;\n\treg is_beq_bne_blt_bge_bltu_bgeu;\n\treg is_lbu_lhu_lw;\n\treg is_alu_reg_imm;\n\treg is_alu_reg_reg;\n\treg is_compare;\n\n\tassign instr_trap = (CATCH_ILLINSN || WITH_PCPI) && !{instr_lui, instr_auipc, instr_jal, instr_jalr,\n\t\t\tinstr_beq, instr_bne, instr_blt, instr_bge, instr_bltu, instr_bgeu,\n\t\t\tinstr_lb, instr_lh, instr_lw, instr_lbu, instr_lhu, instr_sb, instr_sh, instr_sw,\n\t\t\tinstr_addi, instr_slti, instr_sltiu, instr_xori, instr_ori, instr_andi, instr_slli, instr_srli, instr_srai,\n\t\t\tinstr_add, instr_sub, instr_sll, instr_slt, instr_sltu, instr_xor, instr_srl, instr_sra, instr_or, instr_and,\n\t\t\tinstr_rdcycle, instr_rdcycleh, instr_rdinstr, instr_rdinstrh,\n\t\t\tinstr_getq, instr_setq, instr_retirq, instr_maskirq, instr_waitirq, instr_timer};\n\n\twire is_rdcycle_rdcycleh_rdinstr_rdinstrh;\n\tassign is_rdcycle_rdcycleh_rdinstr_rdinstrh = |{instr_rdcycle, instr_rdcycleh, instr_rdinstr, instr_rdinstrh};\n\n\treg [63:0] new_ascii_instr;\n\t`FORMAL_KEEP reg [63:0] dbg_ascii_instr;\n\t`FORMAL_KEEP reg [31:0] dbg_insn_imm;\n\t`FORMAL_KEEP reg [4:0] dbg_insn_rs1;\n\t`FORMAL_KEEP reg [4:0] dbg_insn_rs2;\n\t`FORMAL_KEEP reg [4:0] dbg_insn_rd;\n\t`FORMAL_KEEP reg [31:0] dbg_rs1val;\n\t`FORMAL_KEEP reg [31:0] dbg_rs2val;\n\t`FORMAL_KEEP reg dbg_rs1val_valid;\n\t`FORMAL_KEEP reg dbg_rs2val_valid;\n\n\talways @* begin\n\t\tnew_ascii_instr = \"\";\n\n\t\tif (instr_lui)      new_ascii_instr = \"lui\";\n\t\tif (instr_auipc)    new_ascii_instr = \"auipc\";\n\t\tif (instr_jal)      new_ascii_instr = \"jal\";\n\t\tif (instr_jalr)     new_ascii_instr = \"jalr\";\n\n\t\tif (instr_beq)      new_ascii_instr = \"beq\";\n\t\tif (instr_bne)      new_ascii_instr = \"bne\";\n\t\tif (instr_blt)      new_ascii_instr = \"blt\";\n\t\tif (instr_bge)      new_ascii_instr = \"bge\";\n\t\tif (instr_bltu)     new_ascii_instr = \"bltu\";\n\t\tif (instr_bgeu)     new_ascii_instr = \"bgeu\";\n\n\t\tif (instr_lb)       new_ascii_instr = \"lb\";\n\t\tif (instr_lh)       new_ascii_instr = \"lh\";\n\t\tif (instr_lw)       new_ascii_instr = \"lw\";\n\t\tif (instr_lbu)      new_ascii_instr = \"lbu\";\n\t\tif (instr_lhu)      new_ascii_instr = \"lhu\";\n\t\tif (instr_sb)       new_ascii_instr = \"sb\";\n\t\tif (instr_sh)       new_ascii_instr = \"sh\";\n\t\tif (instr_sw)       new_ascii_instr = \"sw\";\n\n\t\tif (instr_addi)     new_ascii_instr = \"addi\";\n\t\tif (instr_slti)     new_ascii_instr = \"slti\";\n\t\tif (instr_sltiu)    new_ascii_instr = \"sltiu\";\n\t\tif (instr_xori)     new_ascii_instr = \"xori\";\n\t\tif (instr_ori)      new_ascii_instr = \"ori\";\n\t\tif (instr_andi)     new_ascii_instr = \"andi\";\n\t\tif (instr_slli)     new_ascii_instr = \"slli\";\n\t\tif (instr_srli)     new_ascii_instr = \"srli\";\n\t\tif (instr_srai)     new_ascii_instr = \"srai\";\n\n\t\tif (instr_add)      new_ascii_instr = \"add\";\n\t\tif (instr_sub)      new_ascii_instr = \"sub\";\n\t\tif (instr_sll)      new_ascii_instr = \"sll\";\n\t\tif (instr_slt)      new_ascii_instr = \"slt\";\n\t\tif (instr_sltu)     new_ascii_instr = \"sltu\";\n\t\tif (instr_xor)      new_ascii_instr = \"xor\";\n\t\tif (instr_srl)      new_ascii_instr = \"srl\";\n\t\tif (instr_sra)      new_ascii_instr = \"sra\";\n\t\tif (instr_or)       new_ascii_instr = \"or\";\n\t\tif (instr_and)      new_ascii_instr = \"and\";\n\n\t\tif (instr_rdcycle)  new_ascii_instr = \"rdcycle\";\n\t\tif (instr_rdcycleh) new_ascii_instr = \"rdcycleh\";\n\t\tif (instr_rdinstr)  new_ascii_instr = \"rdinstr\";\n\t\tif (instr_rdinstrh) new_ascii_instr = \"rdinstrh\";\n\n\t\tif (instr_getq)     new_ascii_instr = \"getq\";\n\t\tif (instr_setq)     new_ascii_instr = \"setq\";\n\t\tif (instr_retirq)   new_ascii_instr = \"retirq\";\n\t\tif (instr_maskirq)  new_ascii_instr = \"maskirq\";\n\t\tif (instr_waitirq)  new_ascii_instr = \"waitirq\";\n\t\tif (instr_timer)    new_ascii_instr = \"timer\";\n\tend\n\n\treg [63:0] q_ascii_instr;\n\treg [31:0] q_insn_imm;\n\treg [31:0] q_insn_opcode;\n\treg [4:0] q_insn_rs1;\n\treg [4:0] q_insn_rs2;\n\treg [4:0] q_insn_rd;\n\treg dbg_next;\n\n\twire launch_next_insn;\n\treg dbg_valid_insn;\n\n\treg [63:0] cached_ascii_instr;\n\treg [31:0] cached_insn_imm;\n\treg [31:0] cached_insn_opcode;\n\treg [4:0] cached_insn_rs1;\n\treg [4:0] cached_insn_rs2;\n\treg [4:0] cached_insn_rd;\n\n\talways @(posedge clk) begin\n\t\tq_ascii_instr <= dbg_ascii_instr;\n\t\tq_insn_imm <= dbg_insn_imm;\n\t\tq_insn_opcode <= dbg_insn_opcode;\n\t\tq_insn_rs1 <= dbg_insn_rs1;\n\t\tq_insn_rs2 <= dbg_insn_rs2;\n\t\tq_insn_rd <= dbg_insn_rd;\n\t\tdbg_next <= launch_next_insn;\n\n\t\tif (!resetn || trap)\n\t\t\tdbg_valid_insn <= 0;\n\t\telse if (launch_next_insn)\n\t\t\tdbg_valid_insn <= 1;\n\n\t\tif (decoder_trigger_q) begin\n\t\t\tcached_ascii_instr <= new_ascii_instr;\n\t\t\tcached_insn_imm <= decoded_imm;\n\t\t\tif (&next_insn_opcode[1:0])\n\t\t\t\tcached_insn_opcode <= next_insn_opcode;\n\t\t\telse\n\t\t\t\tcached_insn_opcode <= {16'b0, next_insn_opcode[15:0]};\n\t\t\tcached_insn_rs1 <= decoded_rs1;\n\t\t\tcached_insn_rs2 <= decoded_rs2;\n\t\t\tcached_insn_rd <= decoded_rd;\n\t\tend\n\n\t\tif (launch_next_insn) begin\n\t\t\tdbg_insn_addr <= next_pc;\n\t\tend\n\tend\n\n\talways @* begin\n\t\tdbg_ascii_instr = q_ascii_instr;\n\t\tdbg_insn_imm = q_insn_imm;\n\t\tdbg_insn_opcode = q_insn_opcode;\n\t\tdbg_insn_rs1 = q_insn_rs1;\n\t\tdbg_insn_rs2 = q_insn_rs2;\n\t\tdbg_insn_rd = q_insn_rd;\n\n\t\tif (dbg_next) begin\n\t\t\tif (decoder_pseudo_trigger_q) begin\n\t\t\t\tdbg_ascii_instr = cached_ascii_instr;\n\t\t\t\tdbg_insn_imm = cached_insn_imm;\n\t\t\t\tdbg_insn_opcode = cached_insn_opcode;\n\t\t\t\tdbg_insn_rs1 = cached_insn_rs1;\n\t\t\t\tdbg_insn_rs2 = cached_insn_rs2;\n\t\t\t\tdbg_insn_rd = cached_insn_rd;\n\t\t\tend else begin\n\t\t\t\tdbg_ascii_instr = new_ascii_instr;\n\t\t\t\tif (&next_insn_opcode[1:0])\n\t\t\t\t\tdbg_insn_opcode = next_insn_opcode;\n\t\t\t\telse\n\t\t\t\t\tdbg_insn_opcode = {16'b0, next_insn_opcode[15:0]};\n\t\t\t\tdbg_insn_imm = decoded_imm;\n\t\t\t\tdbg_insn_rs1 = decoded_rs1;\n\t\t\t\tdbg_insn_rs2 = decoded_rs2;\n\t\t\t\tdbg_insn_rd = decoded_rd;\n\t\t\tend\n\t\tend\n\tend\n\n`ifdef DEBUGASM\n\talways @(posedge clk) begin\n\t\tif (dbg_next) begin\n\t\t\t$display(\"debugasm %x %x %s\", dbg_insn_addr, dbg_insn_opcode, dbg_ascii_instr ? dbg_ascii_instr : \"*\");\n\t\tend\n\tend\n`endif\n\n`ifdef DEBUG\n\talways @(posedge clk) begin\n\t\tif (dbg_next) begin\n\t\t\tif (&dbg_insn_opcode[1:0])\n\t\t\t\t$display(\"DECODE: 0x%08x 0x%08x %-0s\", dbg_insn_addr, dbg_insn_opcode, dbg_ascii_instr ? dbg_ascii_instr : \"UNKNOWN\");\n\t\t\telse\n\t\t\t\t$display(\"DECODE: 0x%08x     0x%04x %-0s\", dbg_insn_addr, dbg_insn_opcode[15:0], dbg_ascii_instr ? dbg_ascii_instr : \"UNKNOWN\");\n\t\tend\n\tend\n`endif\n\n\talways @(posedge clk) begin\n\t\tis_lui_auipc_jal <= |{instr_lui, instr_auipc, instr_jal};\n\t\tis_lui_auipc_jal_jalr_addi_add_sub <= |{instr_lui, instr_auipc, instr_jal, instr_jalr, instr_addi, instr_add, instr_sub};\n\t\tis_slti_blt_slt <= |{instr_slti, instr_blt, instr_slt};\n\t\tis_sltiu_bltu_sltu <= |{instr_sltiu, instr_bltu, instr_sltu};\n\t\tis_lbu_lhu_lw <= |{instr_lbu, instr_lhu, instr_lw};\n\t\tis_compare <= |{is_beq_bne_blt_bge_bltu_bgeu, instr_slti, instr_slt, instr_sltiu, instr_sltu};\n\n\t\tif (mem_do_rinst && mem_done) begin\n\t\t\tinstr_lui     <= mem_rdata_latched[6:0] == 7'b0110111;\n\t\t\tinstr_auipc   <= mem_rdata_latched[6:0] == 7'b0010111;\n\t\t\tinstr_jal     <= mem_rdata_latched[6:0] == 7'b1101111;\n\t\t\tinstr_jalr    <= mem_rdata_latched[6:0] == 7'b1100111 && mem_rdata_latched[14:12] == 3'b000;\n\t\t\tinstr_retirq  <= mem_rdata_latched[6:0] == 7'b0001011 && mem_rdata_latched[31:25] == 7'b0000010 && ENABLE_IRQ;\n\t\t\tinstr_waitirq <= mem_rdata_latched[6:0] == 7'b0001011 && mem_rdata_latched[31:25] == 7'b0000100 && ENABLE_IRQ;\n\n\t\t\tis_beq_bne_blt_bge_bltu_bgeu <= mem_rdata_latched[6:0] == 7'b1100011;\n\t\t\tis_lb_lh_lw_lbu_lhu          <= mem_rdata_latched[6:0] == 7'b0000011;\n\t\t\tis_sb_sh_sw                  <= mem_rdata_latched[6:0] == 7'b0100011;\n\t\t\tis_alu_reg_imm               <= mem_rdata_latched[6:0] == 7'b0010011;\n\t\t\tis_alu_reg_reg               <= mem_rdata_latched[6:0] == 7'b0110011;\n\n\t\t\t{ decoded_imm_j[31:20], decoded_imm_j[10:1], decoded_imm_j[11], decoded_imm_j[19:12], decoded_imm_j[0] } <= $signed({mem_rdata_latched[31:12], 1'b0});\n\n\t\t\tdecoded_rd <= mem_rdata_latched[11:7];\n\t\t\tdecoded_rs1 <= mem_rdata_latched[19:15];\n\t\t\tdecoded_rs2 <= mem_rdata_latched[24:20];\n\n\t\t\tif (mem_rdata_latched[6:0] == 7'b0001011 && mem_rdata_latched[31:25] == 7'b0000000 && ENABLE_IRQ && ENABLE_IRQ_QREGS)\n\t\t\t\tdecoded_rs1[regindex_bits-1] <= 1; // instr_getq\n\n\t\t\tif (mem_rdata_latched[6:0] == 7'b0001011 && mem_rdata_latched[31:25] == 7'b0000010 && ENABLE_IRQ)\n\t\t\t\tdecoded_rs1 <= ENABLE_IRQ_QREGS ? irqregs_offset : 3; // instr_retirq\n\n\t\t\tcompressed_instr <= 0;\n\t\t\tif (COMPRESSED_ISA && mem_rdata_latched[1:0] != 2'b11) begin\n\t\t\t\tcompressed_instr <= 1;\n\t\t\t\tdecoded_rd <= 0;\n\t\t\t\tdecoded_rs1 <= 0;\n\t\t\t\tdecoded_rs2 <= 0;\n\n\t\t\t\t{ decoded_imm_j[31:11], decoded_imm_j[4], decoded_imm_j[9:8], decoded_imm_j[10], decoded_imm_j[6],\n\t\t\t\t  decoded_imm_j[7], decoded_imm_j[3:1], decoded_imm_j[5], decoded_imm_j[0] } <= $signed({mem_rdata_latched[12:2], 1'b0});\n\n\t\t\t\tcase (mem_rdata_latched[1:0])\n\t\t\t\t\t2'b00: begin // Quadrant 0\n\t\t\t\t\t\tcase (mem_rdata_latched[15:13])\n\t\t\t\t\t\t\t3'b000: begin // C.ADDI4SPN\n\t\t\t\t\t\t\t\tis_alu_reg_imm <= |mem_rdata_latched[12:5];\n\t\t\t\t\t\t\t\tdecoded_rs1 <= 2;\n\t\t\t\t\t\t\t\tdecoded_rd <= 8 + mem_rdata_latched[4:2];\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t3'b010: begin // C.LW\n\t\t\t\t\t\t\t\tis_lb_lh_lw_lbu_lhu <= 1;\n\t\t\t\t\t\t\t\tdecoded_rs1 <= 8 + mem_rdata_latched[9:7];\n\t\t\t\t\t\t\t\tdecoded_rd <= 8 + mem_rdata_latched[4:2];\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t3'b110: begin // C.SW\n\t\t\t\t\t\t\t\tis_sb_sh_sw <= 1;\n\t\t\t\t\t\t\t\tdecoded_rs1 <= 8 + mem_rdata_latched[9:7];\n\t\t\t\t\t\t\t\tdecoded_rs2 <= 8 + mem_rdata_latched[4:2];\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tendcase\n\t\t\t\t\tend\n\t\t\t\t\t2'b01: begin // Quadrant 1\n\t\t\t\t\t\tcase (mem_rdata_latched[15:13])\n\t\t\t\t\t\t\t3'b000: begin // C.NOP / C.ADDI\n\t\t\t\t\t\t\t\tis_alu_reg_imm <= 1;\n\t\t\t\t\t\t\t\tdecoded_rd <= mem_rdata_latched[11:7];\n\t\t\t\t\t\t\t\tdecoded_rs1 <= mem_rdata_latched[11:7];\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t3'b001: begin // C.JAL\n\t\t\t\t\t\t\t\tinstr_jal <= 1;\n\t\t\t\t\t\t\t\tdecoded_rd <= 1;\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t3'b 010: begin // C.LI\n\t\t\t\t\t\t\t\tis_alu_reg_imm <= 1;\n\t\t\t\t\t\t\t\tdecoded_rd <= mem_rdata_latched[11:7];\n\t\t\t\t\t\t\t\tdecoded_rs1 <= 0;\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t3'b 011: begin\n\t\t\t\t\t\t\t\tif (mem_rdata_latched[12] || mem_rdata_latched[6:2]) begin\n\t\t\t\t\t\t\t\t\tif (mem_rdata_latched[11:7] == 2) begin // C.ADDI16SP\n\t\t\t\t\t\t\t\t\t\tis_alu_reg_imm <= 1;\n\t\t\t\t\t\t\t\t\t\tdecoded_rd <= mem_rdata_latched[11:7];\n\t\t\t\t\t\t\t\t\t\tdecoded_rs1 <= mem_rdata_latched[11:7];\n\t\t\t\t\t\t\t\t\tend else begin // C.LUI\n\t\t\t\t\t\t\t\t\t\tinstr_lui <= 1;\n\t\t\t\t\t\t\t\t\t\tdecoded_rd <= mem_rdata_latched[11:7];\n\t\t\t\t\t\t\t\t\t\tdecoded_rs1 <= 0;\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t3'b100: begin\n\t\t\t\t\t\t\t\tif (!mem_rdata_latched[11] && !mem_rdata_latched[12]) begin // C.SRLI, C.SRAI\n\t\t\t\t\t\t\t\t\tis_alu_reg_imm <= 1;\n\t\t\t\t\t\t\t\t\tdecoded_rd <= 8 + mem_rdata_latched[9:7];\n\t\t\t\t\t\t\t\t\tdecoded_rs1 <= 8 + mem_rdata_latched[9:7];\n\t\t\t\t\t\t\t\t\tdecoded_rs2 <= {mem_rdata_latched[12], mem_rdata_latched[6:2]};\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tif (mem_rdata_latched[11:10] == 2'b10) begin // C.ANDI\n\t\t\t\t\t\t\t\t\tis_alu_reg_imm <= 1;\n\t\t\t\t\t\t\t\t\tdecoded_rd <= 8 + mem_rdata_latched[9:7];\n\t\t\t\t\t\t\t\t\tdecoded_rs1 <= 8 + mem_rdata_latched[9:7];\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tif (mem_rdata_latched[12:10] == 3'b011) begin // C.SUB, C.XOR, C.OR, C.AND\n\t\t\t\t\t\t\t\t\tis_alu_reg_reg <= 1;\n\t\t\t\t\t\t\t\t\tdecoded_rd <= 8 + mem_rdata_latched[9:7];\n\t\t\t\t\t\t\t\t\tdecoded_rs1 <= 8 + mem_rdata_latched[9:7];\n\t\t\t\t\t\t\t\t\tdecoded_rs2 <= 8 + mem_rdata_latched[4:2];\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t3'b101: begin // C.J\n\t\t\t\t\t\t\t\tinstr_jal <= 1;\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t3'b110: begin // C.BEQZ\n\t\t\t\t\t\t\t\tis_beq_bne_blt_bge_bltu_bgeu <= 1;\n\t\t\t\t\t\t\t\tdecoded_rs1 <= 8 + mem_rdata_latched[9:7];\n\t\t\t\t\t\t\t\tdecoded_rs2 <= 0;\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t3'b111: begin // C.BNEZ\n\t\t\t\t\t\t\t\tis_beq_bne_blt_bge_bltu_bgeu <= 1;\n\t\t\t\t\t\t\t\tdecoded_rs1 <= 8 + mem_rdata_latched[9:7];\n\t\t\t\t\t\t\t\tdecoded_rs2 <= 0;\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tendcase\n\t\t\t\t\tend\n\t\t\t\t\t2'b10: begin // Quadrant 2\n\t\t\t\t\t\tcase (mem_rdata_latched[15:13])\n\t\t\t\t\t\t\t3'b000: begin // C.SLLI\n\t\t\t\t\t\t\t\tif (!mem_rdata_latched[12]) begin\n\t\t\t\t\t\t\t\t\tis_alu_reg_imm <= 1;\n\t\t\t\t\t\t\t\t\tdecoded_rd <= mem_rdata_latched[11:7];\n\t\t\t\t\t\t\t\t\tdecoded_rs1 <= mem_rdata_latched[11:7];\n\t\t\t\t\t\t\t\t\tdecoded_rs2 <= {mem_rdata_latched[12], mem_rdata_latched[6:2]};\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t3'b010: begin // C.LWSP\n\t\t\t\t\t\t\t\tif (mem_rdata_latched[11:7]) begin\n\t\t\t\t\t\t\t\t\tis_lb_lh_lw_lbu_lhu <= 1;\n\t\t\t\t\t\t\t\t\tdecoded_rd <= mem_rdata_latched[11:7];\n\t\t\t\t\t\t\t\t\tdecoded_rs1 <= 2;\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t3'b100: begin\n\t\t\t\t\t\t\t\tif (mem_rdata_latched[12] == 0 && mem_rdata_latched[11:7] != 0 && mem_rdata_latched[6:2] == 0) begin // C.JR\n\t\t\t\t\t\t\t\t\tinstr_jalr <= 1;\n\t\t\t\t\t\t\t\t\tdecoded_rd <= 0;\n\t\t\t\t\t\t\t\t\tdecoded_rs1 <= mem_rdata_latched[11:7];\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tif (mem_rdata_latched[12] == 0 && mem_rdata_latched[6:2] != 0) begin // C.MV\n\t\t\t\t\t\t\t\t\tis_alu_reg_reg <= 1;\n\t\t\t\t\t\t\t\t\tdecoded_rd <= mem_rdata_latched[11:7];\n\t\t\t\t\t\t\t\t\tdecoded_rs1 <= 0;\n\t\t\t\t\t\t\t\t\tdecoded_rs2 <= mem_rdata_latched[6:2];\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tif (mem_rdata_latched[12] != 0 && mem_rdata_latched[11:7] != 0 && mem_rdata_latched[6:2] == 0) begin // C.JALR\n\t\t\t\t\t\t\t\t\tinstr_jalr <= 1;\n\t\t\t\t\t\t\t\t\tdecoded_rd <= 1;\n\t\t\t\t\t\t\t\t\tdecoded_rs1 <= mem_rdata_latched[11:7];\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tif (mem_rdata_latched[12] != 0 && mem_rdata_latched[6:2] != 0) begin // C.ADD\n\t\t\t\t\t\t\t\t\tis_alu_reg_reg <= 1;\n\t\t\t\t\t\t\t\t\tdecoded_rd <= mem_rdata_latched[11:7];\n\t\t\t\t\t\t\t\t\tdecoded_rs1 <= mem_rdata_latched[11:7];\n\t\t\t\t\t\t\t\t\tdecoded_rs2 <= mem_rdata_latched[6:2];\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t3'b110: begin // C.SWSP\n\t\t\t\t\t\t\t\tis_sb_sh_sw <= 1;\n\t\t\t\t\t\t\t\tdecoded_rs1 <= 2;\n\t\t\t\t\t\t\t\tdecoded_rs2 <= mem_rdata_latched[6:2];\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tendcase\n\t\t\t\t\tend\n\t\t\t\tendcase\n\t\t\tend\n\t\tend\n\n\t\tif (decoder_trigger && !decoder_pseudo_trigger) begin\n\t\t\tpcpi_insn <= WITH_PCPI ? mem_rdata_q : 'bx;\n\n\t\t\tinstr_beq   <= is_beq_bne_blt_bge_bltu_bgeu && mem_rdata_q[14:12] == 3'b000;\n\t\t\tinstr_bne   <= is_beq_bne_blt_bge_bltu_bgeu && mem_rdata_q[14:12] == 3'b001;\n\t\t\tinstr_blt   <= is_beq_bne_blt_bge_bltu_bgeu && mem_rdata_q[14:12] == 3'b100;\n\t\t\tinstr_bge   <= is_beq_bne_blt_bge_bltu_bgeu && mem_rdata_q[14:12] == 3'b101;\n\t\t\tinstr_bltu  <= is_beq_bne_blt_bge_bltu_bgeu && mem_rdata_q[14:12] == 3'b110;\n\t\t\tinstr_bgeu  <= is_beq_bne_blt_bge_bltu_bgeu && mem_rdata_q[14:12] == 3'b111;\n\n\t\t\tinstr_lb    <= is_lb_lh_lw_lbu_lhu && mem_rdata_q[14:12] == 3'b000;\n\t\t\tinstr_lh    <= is_lb_lh_lw_lbu_lhu && mem_rdata_q[14:12] == 3'b001;\n\t\t\tinstr_lw    <= is_lb_lh_lw_lbu_lhu && mem_rdata_q[14:12] == 3'b010;\n\t\t\tinstr_lbu   <= is_lb_lh_lw_lbu_lhu && mem_rdata_q[14:12] == 3'b100;\n\t\t\tinstr_lhu   <= is_lb_lh_lw_lbu_lhu && mem_rdata_q[14:12] == 3'b101;\n\n\t\t\tinstr_sb    <= is_sb_sh_sw && mem_rdata_q[14:12] == 3'b000;\n\t\t\tinstr_sh    <= is_sb_sh_sw && mem_rdata_q[14:12] == 3'b001;\n\t\t\tinstr_sw    <= is_sb_sh_sw && mem_rdata_q[14:12] == 3'b010;\n\n\t\t\tinstr_addi  <= is_alu_reg_imm && mem_rdata_q[14:12] == 3'b000;\n\t\t\tinstr_slti  <= is_alu_reg_imm && mem_rdata_q[14:12] == 3'b010;\n\t\t\tinstr_sltiu <= is_alu_reg_imm && mem_rdata_q[14:12] == 3'b011;\n\t\t\tinstr_xori  <= is_alu_reg_imm && mem_rdata_q[14:12] == 3'b100;\n\t\t\tinstr_ori   <= is_alu_reg_imm && mem_rdata_q[14:12] == 3'b110;\n\t\t\tinstr_andi  <= is_alu_reg_imm && mem_rdata_q[14:12] == 3'b111;\n\n\t\t\tinstr_slli  <= is_alu_reg_imm && mem_rdata_q[14:12] == 3'b001 && mem_rdata_q[31:25] == 7'b0000000;\n\t\t\tinstr_srli  <= is_alu_reg_imm && mem_rdata_q[14:12] == 3'b101 && mem_rdata_q[31:25] == 7'b0000000;\n\t\t\tinstr_srai  <= is_alu_reg_imm && mem_rdata_q[14:12] == 3'b101 && mem_rdata_q[31:25] == 7'b0100000;\n\n\t\t\tinstr_add   <= is_alu_reg_reg && mem_rdata_q[14:12] == 3'b000 && mem_rdata_q[31:25] == 7'b0000000;\n\t\t\tinstr_sub   <= is_alu_reg_reg && mem_rdata_q[14:12] == 3'b000 && mem_rdata_q[31:25] == 7'b0100000;\n\t\t\tinstr_sll   <= is_alu_reg_reg && mem_rdata_q[14:12] == 3'b001 && mem_rdata_q[31:25] == 7'b0000000;\n\t\t\tinstr_slt   <= is_alu_reg_reg && mem_rdata_q[14:12] == 3'b010 && mem_rdata_q[31:25] == 7'b0000000;\n\t\t\tinstr_sltu  <= is_alu_reg_reg && mem_rdata_q[14:12] == 3'b011 && mem_rdata_q[31:25] == 7'b0000000;\n\t\t\tinstr_xor   <= is_alu_reg_reg && mem_rdata_q[14:12] == 3'b100 && mem_rdata_q[31:25] == 7'b0000000;\n\t\t\tinstr_srl   <= is_alu_reg_reg && mem_rdata_q[14:12] == 3'b101 && mem_rdata_q[31:25] == 7'b0000000;\n\t\t\tinstr_sra   <= is_alu_reg_reg && mem_rdata_q[14:12] == 3'b101 && mem_rdata_q[31:25] == 7'b0100000;\n\t\t\tinstr_or    <= is_alu_reg_reg && mem_rdata_q[14:12] == 3'b110 && mem_rdata_q[31:25] == 7'b0000000;\n\t\t\tinstr_and   <= is_alu_reg_reg && mem_rdata_q[14:12] == 3'b111 && mem_rdata_q[31:25] == 7'b0000000;\n\n\t\t\tinstr_rdcycle  <= ((mem_rdata_q[6:0] == 7'b1110011 && mem_rdata_q[31:12] == 'b11000000000000000010) ||\n\t\t\t                   (mem_rdata_q[6:0] == 7'b1110011 && mem_rdata_q[31:12] == 'b11000000000100000010)) && ENABLE_COUNTERS;\n\t\t\tinstr_rdcycleh <= ((mem_rdata_q[6:0] == 7'b1110011 && mem_rdata_q[31:12] == 'b11001000000000000010) ||\n\t\t\t                   (mem_rdata_q[6:0] == 7'b1110011 && mem_rdata_q[31:12] == 'b11001000000100000010)) && ENABLE_COUNTERS && ENABLE_COUNTERS64;\n\t\t\tinstr_rdinstr  <=  (mem_rdata_q[6:0] == 7'b1110011 && mem_rdata_q[31:12] == 'b11000000001000000010) && ENABLE_COUNTERS;\n\t\t\tinstr_rdinstrh <=  (mem_rdata_q[6:0] == 7'b1110011 && mem_rdata_q[31:12] == 'b11001000001000000010) && ENABLE_COUNTERS && ENABLE_COUNTERS64;\n\n\t\t\tinstr_ecall_ebreak <= ((mem_rdata_q[6:0] == 7'b1110011 && !mem_rdata_q[31:21] && !mem_rdata_q[19:7]) ||\n\t\t\t\t\t(COMPRESSED_ISA && mem_rdata_q[15:0] == 16'h9002));\n\n\t\t\tinstr_getq    <= mem_rdata_q[6:0] == 7'b0001011 && mem_rdata_q[31:25] == 7'b0000000 && ENABLE_IRQ && ENABLE_IRQ_QREGS;\n\t\t\tinstr_setq    <= mem_rdata_q[6:0] == 7'b0001011 && mem_rdata_q[31:25] == 7'b0000001 && ENABLE_IRQ && ENABLE_IRQ_QREGS;\n\t\t\tinstr_maskirq <= mem_rdata_q[6:0] == 7'b0001011 && mem_rdata_q[31:25] == 7'b0000011 && ENABLE_IRQ;\n\t\t\tinstr_timer   <= mem_rdata_q[6:0] == 7'b0001011 && mem_rdata_q[31:25] == 7'b0000101 && ENABLE_IRQ && ENABLE_IRQ_TIMER;\n\n\t\t\tis_slli_srli_srai <= is_alu_reg_imm && |{\n\t\t\t\tmem_rdata_q[14:12] == 3'b001 && mem_rdata_q[31:25] == 7'b0000000,\n\t\t\t\tmem_rdata_q[14:12] == 3'b101 && mem_rdata_q[31:25] == 7'b0000000,\n\t\t\t\tmem_rdata_q[14:12] == 3'b101 && mem_rdata_q[31:25] == 7'b0100000\n\t\t\t};\n\n\t\t\tis_jalr_addi_slti_sltiu_xori_ori_andi <= instr_jalr || is_alu_reg_imm && |{\n\t\t\t\tmem_rdata_q[14:12] == 3'b000,\n\t\t\t\tmem_rdata_q[14:12] == 3'b010,\n\t\t\t\tmem_rdata_q[14:12] == 3'b011,\n\t\t\t\tmem_rdata_q[14:12] == 3'b100,\n\t\t\t\tmem_rdata_q[14:12] == 3'b110,\n\t\t\t\tmem_rdata_q[14:12] == 3'b111\n\t\t\t};\n\n\t\t\tis_sll_srl_sra <= is_alu_reg_reg && |{\n\t\t\t\tmem_rdata_q[14:12] == 3'b001 && mem_rdata_q[31:25] == 7'b0000000,\n\t\t\t\tmem_rdata_q[14:12] == 3'b101 && mem_rdata_q[31:25] == 7'b0000000,\n\t\t\t\tmem_rdata_q[14:12] == 3'b101 && mem_rdata_q[31:25] == 7'b0100000\n\t\t\t};\n\n\t\t\tis_lui_auipc_jal_jalr_addi_add_sub <= 0;\n\t\t\tis_compare <= 0;\n\n\t\t\t(* parallel_case *)\n\t\t\tcase (1'b1)\n\t\t\t\tinstr_jal:\n\t\t\t\t\tdecoded_imm <= decoded_imm_j;\n\t\t\t\t|{instr_lui, instr_auipc}:\n\t\t\t\t\tdecoded_imm <= mem_rdata_q[31:12] << 12;\n\t\t\t\t|{instr_jalr, is_lb_lh_lw_lbu_lhu, is_alu_reg_imm}:\n\t\t\t\t\tdecoded_imm <= $signed(mem_rdata_q[31:20]);\n\t\t\t\tis_beq_bne_blt_bge_bltu_bgeu:\n\t\t\t\t\tdecoded_imm <= $signed({mem_rdata_q[31], mem_rdata_q[7], mem_rdata_q[30:25], mem_rdata_q[11:8], 1'b0});\n\t\t\t\tis_sb_sh_sw:\n\t\t\t\t\tdecoded_imm <= $signed({mem_rdata_q[31:25], mem_rdata_q[11:7]});\n\t\t\t\tdefault:\n\t\t\t\t\tdecoded_imm <= 1'bx;\n\t\t\tendcase\n\t\tend\n\n\t\tif (!resetn) begin\n\t\t\tis_beq_bne_blt_bge_bltu_bgeu <= 0;\n\t\t\tis_compare <= 0;\n\n\t\t\tinstr_beq   <= 0;\n\t\t\tinstr_bne   <= 0;\n\t\t\tinstr_blt   <= 0;\n\t\t\tinstr_bge   <= 0;\n\t\t\tinstr_bltu  <= 0;\n\t\t\tinstr_bgeu  <= 0;\n\n\t\t\tinstr_addi  <= 0;\n\t\t\tinstr_slti  <= 0;\n\t\t\tinstr_sltiu <= 0;\n\t\t\tinstr_xori  <= 0;\n\t\t\tinstr_ori   <= 0;\n\t\t\tinstr_andi  <= 0;\n\n\t\t\tinstr_add   <= 0;\n\t\t\tinstr_sub   <= 0;\n\t\t\tinstr_sll   <= 0;\n\t\t\tinstr_slt   <= 0;\n\t\t\tinstr_sltu  <= 0;\n\t\t\tinstr_xor   <= 0;\n\t\t\tinstr_srl   <= 0;\n\t\t\tinstr_sra   <= 0;\n\t\t\tinstr_or    <= 0;\n\t\t\tinstr_and   <= 0;\n\t\tend\n\tend\n\n\n\t// Main State Machine\n\n\tlocalparam cpu_state_trap   = 8'b10000000;\n\tlocalparam cpu_state_fetch  = 8'b01000000;\n\tlocalparam cpu_state_ld_rs1 = 8'b00100000;\n\tlocalparam cpu_state_ld_rs2 = 8'b00010000;\n\tlocalparam cpu_state_exec   = 8'b00001000;\n\tlocalparam cpu_state_shift  = 8'b00000100;\n\tlocalparam cpu_state_stmem  = 8'b00000010;\n\tlocalparam cpu_state_ldmem  = 8'b00000001;\n\n\treg [7:0] cpu_state;\n\treg [1:0] irq_state;\n\n\t`FORMAL_KEEP reg [127:0] dbg_ascii_state;\n\n\talways @* begin\n\t\tdbg_ascii_state = \"\";\n\t\tif (cpu_state == cpu_state_trap)   dbg_ascii_state = \"trap\";\n\t\tif (cpu_state == cpu_state_fetch)  dbg_ascii_state = \"fetch\";\n\t\tif (cpu_state == cpu_state_ld_rs1) dbg_ascii_state = \"ld_rs1\";\n\t\tif (cpu_state == cpu_state_ld_rs2) dbg_ascii_state = \"ld_rs2\";\n\t\tif (cpu_state == cpu_state_exec)   dbg_ascii_state = \"exec\";\n\t\tif (cpu_state == cpu_state_shift)  dbg_ascii_state = \"shift\";\n\t\tif (cpu_state == cpu_state_stmem)  dbg_ascii_state = \"stmem\";\n\t\tif (cpu_state == cpu_state_ldmem)  dbg_ascii_state = \"ldmem\";\n\tend\n\n\treg set_mem_do_rinst;\n\treg set_mem_do_rdata;\n\treg set_mem_do_wdata;\n\n\treg latched_store;\n\treg latched_stalu;\n\treg latched_branch;\n\treg latched_compr;\n\treg latched_trace;\n\treg latched_is_lu;\n\treg latched_is_lh;\n\treg latched_is_lb;\n\treg [regindex_bits-1:0] latched_rd;\n\n\treg [31:0] current_pc;\n\tassign next_pc = latched_store && latched_branch ? reg_out & ~1 : reg_next_pc;\n\n\treg [3:0] pcpi_timeout_counter;\n\treg pcpi_timeout;\n\n\treg [31:0] next_irq_pending;\n\treg do_waitirq;\n\n\treg [31:0] alu_out, alu_out_q;\n\treg alu_out_0, alu_out_0_q;\n\treg alu_wait, alu_wait_2;\n\n\treg [31:0] alu_add_sub;\n\treg [31:0] alu_shl, alu_shr;\n\treg alu_eq, alu_ltu, alu_lts;\n\n\tgenerate if (TWO_CYCLE_ALU) begin\n\t\talways @(posedge clk) begin\n\t\t\talu_add_sub <= instr_sub ? reg_op1 - reg_op2 : reg_op1 + reg_op2;\n\t\t\talu_eq <= reg_op1 == reg_op2;\n\t\t\talu_lts <= $signed(reg_op1) < $signed(reg_op2);\n\t\t\talu_ltu <= reg_op1 < reg_op2;\n\t\t\talu_shl <= reg_op1 << reg_op2[4:0];\n\t\t\talu_shr <= $signed({instr_sra || instr_srai ? reg_op1[31] : 1'b0, reg_op1}) >>> reg_op2[4:0];\n\t\tend\n\tend else begin\n\t\talways @* begin\n\t\t\talu_add_sub = instr_sub ? reg_op1 - reg_op2 : reg_op1 + reg_op2;\n\t\t\talu_eq = reg_op1 == reg_op2;\n\t\t\talu_lts = $signed(reg_op1) < $signed(reg_op2);\n\t\t\talu_ltu = reg_op1 < reg_op2;\n\t\t\talu_shl = reg_op1 << reg_op2[4:0];\n\t\t\talu_shr = $signed({instr_sra || instr_srai ? reg_op1[31] : 1'b0, reg_op1}) >>> reg_op2[4:0];\n\t\tend\n\tend endgenerate\n\n\talways @* begin\n\t\talu_out_0 = 'bx;\n\t\t(* parallel_case, full_case *)\n\t\tcase (1'b1)\n\t\t\tinstr_beq:\n\t\t\t\talu_out_0 = alu_eq;\n\t\t\tinstr_bne:\n\t\t\t\talu_out_0 = !alu_eq;\n\t\t\tinstr_bge:\n\t\t\t\talu_out_0 = !alu_lts;\n\t\t\tinstr_bgeu:\n\t\t\t\talu_out_0 = !alu_ltu;\n\t\t\tis_slti_blt_slt && (!TWO_CYCLE_COMPARE || !{instr_beq,instr_bne,instr_bge,instr_bgeu}):\n\t\t\t\talu_out_0 = alu_lts;\n\t\t\tis_sltiu_bltu_sltu && (!TWO_CYCLE_COMPARE || !{instr_beq,instr_bne,instr_bge,instr_bgeu}):\n\t\t\t\talu_out_0 = alu_ltu;\n\t\tendcase\n\n\t\talu_out = 'bx;\n\t\t(* parallel_case, full_case *)\n\t\tcase (1'b1)\n\t\t\tis_lui_auipc_jal_jalr_addi_add_sub:\n\t\t\t\talu_out = alu_add_sub;\n\t\t\tis_compare:\n\t\t\t\talu_out = alu_out_0;\n\t\t\tinstr_xori || instr_xor:\n\t\t\t\talu_out = reg_op1 ^ reg_op2;\n\t\t\tinstr_ori || instr_or:\n\t\t\t\talu_out = reg_op1 | reg_op2;\n\t\t\tinstr_andi || instr_and:\n\t\t\t\talu_out = reg_op1 & reg_op2;\n\t\t\tBARREL_SHIFTER && (instr_sll || instr_slli):\n\t\t\t\talu_out = alu_shl;\n\t\t\tBARREL_SHIFTER && (instr_srl || instr_srli || instr_sra || instr_srai):\n\t\t\t\talu_out = alu_shr;\n\t\tendcase\n\n`ifdef RISCV_FORMAL_BLACKBOX_ALU\n\t\talu_out_0 = $anyseq;\n\t\talu_out = $anyseq;\n`endif\n\tend\n\n\treg clear_prefetched_high_word_q;\n\talways @(posedge clk) clear_prefetched_high_word_q <= clear_prefetched_high_word;\n\n\talways @* begin\n\t\tclear_prefetched_high_word = clear_prefetched_high_word_q;\n\t\tif (!prefetched_high_word)\n\t\t\tclear_prefetched_high_word = 0;\n\t\tif (latched_branch || irq_state || !resetn)\n\t\t\tclear_prefetched_high_word = COMPRESSED_ISA;\n\tend\n\n\treg cpuregs_write;\n\treg [31:0] cpuregs_wrdata;\n\treg [31:0] cpuregs_rs1;\n\treg [31:0] cpuregs_rs2;\n\treg [regindex_bits-1:0] decoded_rs;\n\n\talways @* begin\n\t\tcpuregs_write = 0;\n\t\tcpuregs_wrdata = 'bx;\n\n\t\tif (cpu_state == cpu_state_fetch) begin\n\t\t\t(* parallel_case *)\n\t\t\tcase (1'b1)\n\t\t\t\tlatched_branch: begin\n\t\t\t\t\tcpuregs_wrdata = reg_pc + (latched_compr ? 2 : 4);\n\t\t\t\t\tcpuregs_write = 1;\n\t\t\t\tend\n\t\t\t\tlatched_store && !latched_branch: begin\n\t\t\t\t\tcpuregs_wrdata = latched_stalu ? alu_out_q : reg_out;\n\t\t\t\t\tcpuregs_write = 1;\n\t\t\t\tend\n\t\t\t\tENABLE_IRQ && irq_state[0]: begin\n\t\t\t\t\tcpuregs_wrdata = reg_next_pc | latched_compr;\n\t\t\t\t\tcpuregs_write = 1;\n\t\t\t\tend\n\t\t\t\tENABLE_IRQ && irq_state[1]: begin\n\t\t\t\t\tcpuregs_wrdata = irq_pending & ~irq_mask;\n\t\t\t\t\tcpuregs_write = 1;\n\t\t\t\tend\n\t\t\tendcase\n\t\tend\n\tend\n\n`ifndef PICORV32_REGS\n\talways @(posedge clk) begin\n\t\tif (resetn && cpuregs_write && latched_rd)\n`ifdef PICORV32_TESTBUG_001\n\t\t\tcpuregs[latched_rd ^ 1] <= cpuregs_wrdata;\n`elsif PICORV32_TESTBUG_002\n\t\t\tcpuregs[latched_rd] <= cpuregs_wrdata ^ 1;\n`else\n\t\t\tcpuregs[latched_rd] <= cpuregs_wrdata;\n`endif\n\tend\n\n\talways @* begin\n\t\tdecoded_rs = 'bx;\n\t\tif (ENABLE_REGS_DUALPORT) begin\n`ifndef RISCV_FORMAL_BLACKBOX_REGS\n\t\t\tcpuregs_rs1 = decoded_rs1 ? cpuregs[decoded_rs1] : 0;\n\t\t\tcpuregs_rs2 = decoded_rs2 ? cpuregs[decoded_rs2] : 0;\n`else\n\t\t\tcpuregs_rs1 = decoded_rs1 ? $anyseq : 0;\n\t\t\tcpuregs_rs2 = decoded_rs2 ? $anyseq : 0;\n`endif\n\t\tend else begin\n\t\t\tdecoded_rs = (cpu_state == cpu_state_ld_rs2) ? decoded_rs2 : decoded_rs1;\n`ifndef RISCV_FORMAL_BLACKBOX_REGS\n\t\t\tcpuregs_rs1 = decoded_rs ? cpuregs[decoded_rs] : 0;\n`else\n\t\t\tcpuregs_rs1 = decoded_rs ? $anyseq : 0;\n`endif\n\t\t\tcpuregs_rs2 = cpuregs_rs1;\n\t\tend\n\tend\n`else\n\twire[31:0] cpuregs_rdata1;\n\twire[31:0] cpuregs_rdata2;\n\n\twire [5:0] cpuregs_waddr = latched_rd;\n\twire [5:0] cpuregs_raddr1 = ENABLE_REGS_DUALPORT ? decoded_rs1 : decoded_rs;\n\twire [5:0] cpuregs_raddr2 = ENABLE_REGS_DUALPORT ? decoded_rs2 : 0;\n\n\t`PICORV32_REGS cpuregs (\n\t\t.clk(clk),\n\t\t.wen(resetn && cpuregs_write && latched_rd),\n\t\t.waddr(cpuregs_waddr),\n\t\t.raddr1(cpuregs_raddr1),\n\t\t.raddr2(cpuregs_raddr2),\n\t\t.wdata(cpuregs_wrdata),\n\t\t.rdata1(cpuregs_rdata1),\n\t\t.rdata2(cpuregs_rdata2)\n\t);\n\n\talways @* begin\n\t\tdecoded_rs = 'bx;\n\t\tif (ENABLE_REGS_DUALPORT) begin\n\t\t\tcpuregs_rs1 = decoded_rs1 ? cpuregs_rdata1 : 0;\n\t\t\tcpuregs_rs2 = decoded_rs2 ? cpuregs_rdata2 : 0;\n\t\tend else begin\n\t\t\tdecoded_rs = (cpu_state == cpu_state_ld_rs2) ? decoded_rs2 : decoded_rs1;\n\t\t\tcpuregs_rs1 = decoded_rs ? cpuregs_rdata1 : 0;\n\t\t\tcpuregs_rs2 = cpuregs_rs1;\n\t\tend\n\tend\n`endif\n\n\tassign launch_next_insn = cpu_state == cpu_state_fetch && decoder_trigger && (!ENABLE_IRQ || irq_delay || irq_active || !(irq_pending & ~irq_mask));\n\n\talways @(posedge clk) begin\n\t\ttrap <= 0;\n\t\treg_sh <= 'bx;\n\t\treg_out <= 'bx;\n\t\tset_mem_do_rinst = 0;\n\t\tset_mem_do_rdata = 0;\n\t\tset_mem_do_wdata = 0;\n\n\t\talu_out_0_q <= alu_out_0;\n\t\talu_out_q <= alu_out;\n\n\t\talu_wait <= 0;\n\t\talu_wait_2 <= 0;\n\n\t\tif (launch_next_insn) begin\n\t\t\tdbg_rs1val <= 'bx;\n\t\t\tdbg_rs2val <= 'bx;\n\t\t\tdbg_rs1val_valid <= 0;\n\t\t\tdbg_rs2val_valid <= 0;\n\t\tend\n\n\t\tif (WITH_PCPI && CATCH_ILLINSN) begin\n\t\t\tif (resetn && pcpi_valid && !pcpi_int_wait) begin\n\t\t\t\tif (pcpi_timeout_counter)\n\t\t\t\t\tpcpi_timeout_counter <= pcpi_timeout_counter - 1;\n\t\t\tend else\n\t\t\t\tpcpi_timeout_counter <= ~0;\n\t\t\tpcpi_timeout <= !pcpi_timeout_counter;\n\t\tend\n\n\t\tif (ENABLE_COUNTERS) begin\n\t\t\tcount_cycle <= resetn ? count_cycle + 1 : 0;\n\t\t\tif (!ENABLE_COUNTERS64) count_cycle[63:32] <= 0;\n\t\tend else begin\n\t\t\tcount_cycle <= 'bx;\n\t\t\tcount_instr <= 'bx;\n\t\tend\n\n\t\tnext_irq_pending = ENABLE_IRQ ? irq_pending & LATCHED_IRQ : 'bx;\n\n\t\tif (ENABLE_IRQ && ENABLE_IRQ_TIMER && timer) begin\n\t\t\ttimer <= timer - 1;\n\t\tend\n\n\t\tdecoder_trigger <= mem_do_rinst && mem_done;\n\t\tdecoder_trigger_q <= decoder_trigger;\n\t\tdecoder_pseudo_trigger <= 0;\n\t\tdecoder_pseudo_trigger_q <= decoder_pseudo_trigger;\n\t\tdo_waitirq <= 0;\n\n\t\ttrace_valid <= 0;\n\n\t\tif (!ENABLE_TRACE)\n\t\t\ttrace_data <= 'bx;\n\n\t\tif (!resetn) begin\n\t\t\treg_pc <= PROGADDR_RESET;\n\t\t\treg_next_pc <= PROGADDR_RESET;\n\t\t\tif (ENABLE_COUNTERS)\n\t\t\t\tcount_instr <= 0;\n\t\t\tlatched_store <= 0;\n\t\t\tlatched_stalu <= 0;\n\t\t\tlatched_branch <= 0;\n\t\t\tlatched_trace <= 0;\n\t\t\tlatched_is_lu <= 0;\n\t\t\tlatched_is_lh <= 0;\n\t\t\tlatched_is_lb <= 0;\n\t\t\tpcpi_valid <= 0;\n\t\t\tpcpi_timeout <= 0;\n\t\t\tirq_active <= 0;\n\t\t\tirq_delay <= 0;\n\t\t\tirq_mask <= ~0;\n\t\t\tnext_irq_pending = 0;\n\t\t\tirq_state <= 0;\n\t\t\teoi <= 0;\n\t\t\ttimer <= 0;\n\t\t\tif (~STACKADDR) begin\n\t\t\t\tlatched_store <= 1;\n\t\t\t\tlatched_rd <= 2;\n\t\t\t\treg_out <= STACKADDR;\n\t\t\tend\n\t\t\tcpu_state <= cpu_state_fetch;\n\t\tend else\n\t\t(* parallel_case, full_case *)\n\t\tcase (cpu_state)\n\t\t\tcpu_state_trap: begin\n\t\t\t\ttrap <= 1;\n\t\t\tend\n\n\t\t\tcpu_state_fetch: begin\n\t\t\t\tmem_do_rinst <= !decoder_trigger && !do_waitirq;\n\t\t\t\tmem_wordsize <= 0;\n\n\t\t\t\tcurrent_pc = reg_next_pc;\n\n\t\t\t\t(* parallel_case *)\n\t\t\t\tcase (1'b1)\n\t\t\t\t\tlatched_branch: begin\n\t\t\t\t\t\tcurrent_pc = latched_store ? (latched_stalu ? alu_out_q : reg_out) & ~1 : reg_next_pc;\n\t\t\t\t\t\t`debug($display(\"ST_RD:  %2d 0x%08x, BRANCH 0x%08x\", latched_rd, reg_pc + (latched_compr ? 2 : 4), current_pc);)\n\t\t\t\t\tend\n\t\t\t\t\tlatched_store && !latched_branch: begin\n\t\t\t\t\t\t`debug($display(\"ST_RD:  %2d 0x%08x\", latched_rd, latched_stalu ? alu_out_q : reg_out);)\n\t\t\t\t\tend\n\t\t\t\t\tENABLE_IRQ && irq_state[0]: begin\n\t\t\t\t\t\tcurrent_pc = PROGADDR_IRQ;\n\t\t\t\t\t\tirq_active <= 1;\n\t\t\t\t\t\tmem_do_rinst <= 1;\n\t\t\t\t\tend\n\t\t\t\t\tENABLE_IRQ && irq_state[1]: begin\n\t\t\t\t\t\teoi <= irq_pending & ~irq_mask;\n\t\t\t\t\t\tnext_irq_pending = next_irq_pending & irq_mask;\n\t\t\t\t\tend\n\t\t\t\tendcase\n\n\t\t\t\tif (ENABLE_TRACE && latched_trace) begin\n\t\t\t\t\tlatched_trace <= 0;\n\t\t\t\t\ttrace_valid <= 1;\n\t\t\t\t\tif (latched_branch)\n\t\t\t\t\t\ttrace_data <= (irq_active ? TRACE_IRQ : 0) | TRACE_BRANCH | (current_pc & 32'hfffffffe);\n\t\t\t\t\telse\n\t\t\t\t\t\ttrace_data <= (irq_active ? TRACE_IRQ : 0) | (latched_stalu ? alu_out_q : reg_out);\n\t\t\t\tend\n\n\t\t\t\treg_pc <= current_pc;\n\t\t\t\treg_next_pc <= current_pc;\n\n\t\t\t\tlatched_store <= 0;\n\t\t\t\tlatched_stalu <= 0;\n\t\t\t\tlatched_branch <= 0;\n\t\t\t\tlatched_is_lu <= 0;\n\t\t\t\tlatched_is_lh <= 0;\n\t\t\t\tlatched_is_lb <= 0;\n\t\t\t\tlatched_rd <= decoded_rd;\n\t\t\t\tlatched_compr <= compressed_instr;\n\n\t\t\t\tif (ENABLE_IRQ && ((decoder_trigger && !irq_active && !irq_delay && |(irq_pending & ~irq_mask)) || irq_state)) begin\n\t\t\t\t\tirq_state <=\n\t\t\t\t\t\tirq_state == 2'b00 ? 2'b01 :\n\t\t\t\t\t\tirq_state == 2'b01 ? 2'b10 : 2'b00;\n\t\t\t\t\tlatched_compr <= latched_compr;\n\t\t\t\t\tif (ENABLE_IRQ_QREGS)\n\t\t\t\t\t\tlatched_rd <= irqregs_offset | irq_state[0];\n\t\t\t\t\telse\n\t\t\t\t\t\tlatched_rd <= irq_state[0] ? 4 : 3;\n\t\t\t\tend else\n\t\t\t\tif (ENABLE_IRQ && (decoder_trigger || do_waitirq) && instr_waitirq) begin\n\t\t\t\t\tif (irq_pending) begin\n\t\t\t\t\t\tlatched_store <= 1;\n\t\t\t\t\t\treg_out <= irq_pending;\n\t\t\t\t\t\treg_next_pc <= current_pc + (compressed_instr ? 2 : 4);\n\t\t\t\t\t\tmem_do_rinst <= 1;\n\t\t\t\t\tend else\n\t\t\t\t\t\tdo_waitirq <= 1;\n\t\t\t\tend else\n\t\t\t\tif (decoder_trigger) begin\n\t\t\t\t\t`debug($display(\"-- %-0t\", $time);)\n\t\t\t\t\tirq_delay <= irq_active;\n\t\t\t\t\treg_next_pc <= current_pc + (compressed_instr ? 2 : 4);\n\t\t\t\t\tif (ENABLE_TRACE)\n\t\t\t\t\t\tlatched_trace <= 1;\n\t\t\t\t\tif (ENABLE_COUNTERS) begin\n\t\t\t\t\t\tcount_instr <= count_instr + 1;\n\t\t\t\t\t\tif (!ENABLE_COUNTERS64) count_instr[63:32] <= 0;\n\t\t\t\t\tend\n\t\t\t\t\tif (instr_jal) begin\n\t\t\t\t\t\tmem_do_rinst <= 1;\n\t\t\t\t\t\treg_next_pc <= current_pc + decoded_imm_j;\n\t\t\t\t\t\tlatched_branch <= 1;\n\t\t\t\t\tend else begin\n\t\t\t\t\t\tmem_do_rinst <= 0;\n\t\t\t\t\t\tmem_do_prefetch <= !instr_jalr && !instr_retirq;\n\t\t\t\t\t\tcpu_state <= cpu_state_ld_rs1;\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tcpu_state_ld_rs1: begin\n\t\t\t\treg_op1 <= 'bx;\n\t\t\t\treg_op2 <= 'bx;\n\n\t\t\t\t(* parallel_case *)\n\t\t\t\tcase (1'b1)\n\t\t\t\t\t(CATCH_ILLINSN || WITH_PCPI) && instr_trap: begin\n\t\t\t\t\t\tif (WITH_PCPI) begin\n\t\t\t\t\t\t\t`debug($display(\"LD_RS1: %2d 0x%08x\", decoded_rs1, cpuregs_rs1);)\n\t\t\t\t\t\t\treg_op1 <= cpuregs_rs1;\n\t\t\t\t\t\t\tdbg_rs1val <= cpuregs_rs1;\n\t\t\t\t\t\t\tdbg_rs1val_valid <= 1;\n\t\t\t\t\t\t\tif (ENABLE_REGS_DUALPORT) begin\n\t\t\t\t\t\t\t\tpcpi_valid <= 1;\n\t\t\t\t\t\t\t\t`debug($display(\"LD_RS2: %2d 0x%08x\", decoded_rs2, cpuregs_rs2);)\n\t\t\t\t\t\t\t\treg_sh <= cpuregs_rs2;\n\t\t\t\t\t\t\t\treg_op2 <= cpuregs_rs2;\n\t\t\t\t\t\t\t\tdbg_rs2val <= cpuregs_rs2;\n\t\t\t\t\t\t\t\tdbg_rs2val_valid <= 1;\n\t\t\t\t\t\t\t\tif (pcpi_int_ready) begin\n\t\t\t\t\t\t\t\t\tmem_do_rinst <= 1;\n\t\t\t\t\t\t\t\t\tpcpi_valid <= 0;\n\t\t\t\t\t\t\t\t\treg_out <= pcpi_int_rd;\n\t\t\t\t\t\t\t\t\tlatched_store <= pcpi_int_wr;\n\t\t\t\t\t\t\t\t\tcpu_state <= cpu_state_fetch;\n\t\t\t\t\t\t\t\tend else\n\t\t\t\t\t\t\t\tif (CATCH_ILLINSN && (pcpi_timeout || instr_ecall_ebreak)) begin\n\t\t\t\t\t\t\t\t\tpcpi_valid <= 0;\n\t\t\t\t\t\t\t\t\t`debug($display(\"EBREAK OR UNSUPPORTED INSN AT 0x%08x\", reg_pc);)\n\t\t\t\t\t\t\t\t\tif (ENABLE_IRQ && !irq_mask[irq_ebreak] && !irq_active) begin\n\t\t\t\t\t\t\t\t\t\tnext_irq_pending[irq_ebreak] = 1;\n\t\t\t\t\t\t\t\t\t\tcpu_state <= cpu_state_fetch;\n\t\t\t\t\t\t\t\t\tend else\n\t\t\t\t\t\t\t\t\t\tcpu_state <= cpu_state_trap;\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend else begin\n\t\t\t\t\t\t\t\tcpu_state <= cpu_state_ld_rs2;\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend else begin\n\t\t\t\t\t\t\t`debug($display(\"EBREAK OR UNSUPPORTED INSN AT 0x%08x\", reg_pc);)\n\t\t\t\t\t\t\tif (ENABLE_IRQ && !irq_mask[irq_ebreak] && !irq_active) begin\n\t\t\t\t\t\t\t\tnext_irq_pending[irq_ebreak] = 1;\n\t\t\t\t\t\t\t\tcpu_state <= cpu_state_fetch;\n\t\t\t\t\t\t\tend else\n\t\t\t\t\t\t\t\tcpu_state <= cpu_state_trap;\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\t\tENABLE_COUNTERS && is_rdcycle_rdcycleh_rdinstr_rdinstrh: begin\n\t\t\t\t\t\t(* parallel_case, full_case *)\n\t\t\t\t\t\tcase (1'b1)\n\t\t\t\t\t\t\tinstr_rdcycle:\n\t\t\t\t\t\t\t\treg_out <= count_cycle[31:0];\n\t\t\t\t\t\t\tinstr_rdcycleh && ENABLE_COUNTERS64:\n\t\t\t\t\t\t\t\treg_out <= count_cycle[63:32];\n\t\t\t\t\t\t\tinstr_rdinstr:\n\t\t\t\t\t\t\t\treg_out <= count_instr[31:0];\n\t\t\t\t\t\t\tinstr_rdinstrh && ENABLE_COUNTERS64:\n\t\t\t\t\t\t\t\treg_out <= count_instr[63:32];\n\t\t\t\t\t\tendcase\n\t\t\t\t\t\tlatched_store <= 1;\n\t\t\t\t\t\tcpu_state <= cpu_state_fetch;\n\t\t\t\t\tend\n\t\t\t\t\tis_lui_auipc_jal: begin\n\t\t\t\t\t\treg_op1 <= instr_lui ? 0 : reg_pc;\n\t\t\t\t\t\treg_op2 <= decoded_imm;\n\t\t\t\t\t\tif (TWO_CYCLE_ALU)\n\t\t\t\t\t\t\talu_wait <= 1;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tmem_do_rinst <= mem_do_prefetch;\n\t\t\t\t\t\tcpu_state <= cpu_state_exec;\n\t\t\t\t\tend\n\t\t\t\t\tENABLE_IRQ && ENABLE_IRQ_QREGS && instr_getq: begin\n\t\t\t\t\t\t`debug($display(\"LD_RS1: %2d 0x%08x\", decoded_rs1, cpuregs_rs1);)\n\t\t\t\t\t\treg_out <= cpuregs_rs1;\n\t\t\t\t\t\tdbg_rs1val <= cpuregs_rs1;\n\t\t\t\t\t\tdbg_rs1val_valid <= 1;\n\t\t\t\t\t\tlatched_store <= 1;\n\t\t\t\t\t\tcpu_state <= cpu_state_fetch;\n\t\t\t\t\tend\n\t\t\t\t\tENABLE_IRQ && ENABLE_IRQ_QREGS && instr_setq: begin\n\t\t\t\t\t\t`debug($display(\"LD_RS1: %2d 0x%08x\", decoded_rs1, cpuregs_rs1);)\n\t\t\t\t\t\treg_out <= cpuregs_rs1;\n\t\t\t\t\t\tdbg_rs1val <= cpuregs_rs1;\n\t\t\t\t\t\tdbg_rs1val_valid <= 1;\n\t\t\t\t\t\tlatched_rd <= latched_rd | irqregs_offset;\n\t\t\t\t\t\tlatched_store <= 1;\n\t\t\t\t\t\tcpu_state <= cpu_state_fetch;\n\t\t\t\t\tend\n\t\t\t\t\tENABLE_IRQ && instr_retirq: begin\n\t\t\t\t\t\teoi <= 0;\n\t\t\t\t\t\tirq_active <= 0;\n\t\t\t\t\t\tlatched_branch <= 1;\n\t\t\t\t\t\tlatched_store <= 1;\n\t\t\t\t\t\t`debug($display(\"LD_RS1: %2d 0x%08x\", decoded_rs1, cpuregs_rs1);)\n\t\t\t\t\t\treg_out <= CATCH_MISALIGN ? (cpuregs_rs1 & 32'h fffffffe) : cpuregs_rs1;\n\t\t\t\t\t\tdbg_rs1val <= cpuregs_rs1;\n\t\t\t\t\t\tdbg_rs1val_valid <= 1;\n\t\t\t\t\t\tcpu_state <= cpu_state_fetch;\n\t\t\t\t\tend\n\t\t\t\t\tENABLE_IRQ && instr_maskirq: begin\n\t\t\t\t\t\tlatched_store <= 1;\n\t\t\t\t\t\treg_out <= irq_mask;\n\t\t\t\t\t\t`debug($display(\"LD_RS1: %2d 0x%08x\", decoded_rs1, cpuregs_rs1);)\n\t\t\t\t\t\tirq_mask <= cpuregs_rs1 | MASKED_IRQ;\n\t\t\t\t\t\tdbg_rs1val <= cpuregs_rs1;\n\t\t\t\t\t\tdbg_rs1val_valid <= 1;\n\t\t\t\t\t\tcpu_state <= cpu_state_fetch;\n\t\t\t\t\tend\n\t\t\t\t\tENABLE_IRQ && ENABLE_IRQ_TIMER && instr_timer: begin\n\t\t\t\t\t\tlatched_store <= 1;\n\t\t\t\t\t\treg_out <= timer;\n\t\t\t\t\t\t`debug($display(\"LD_RS1: %2d 0x%08x\", decoded_rs1, cpuregs_rs1);)\n\t\t\t\t\t\ttimer <= cpuregs_rs1;\n\t\t\t\t\t\tdbg_rs1val <= cpuregs_rs1;\n\t\t\t\t\t\tdbg_rs1val_valid <= 1;\n\t\t\t\t\t\tcpu_state <= cpu_state_fetch;\n\t\t\t\t\tend\n\t\t\t\t\tis_lb_lh_lw_lbu_lhu && !instr_trap: begin\n\t\t\t\t\t\t`debug($display(\"LD_RS1: %2d 0x%08x\", decoded_rs1, cpuregs_rs1);)\n\t\t\t\t\t\treg_op1 <= cpuregs_rs1;\n\t\t\t\t\t\tdbg_rs1val <= cpuregs_rs1;\n\t\t\t\t\t\tdbg_rs1val_valid <= 1;\n\t\t\t\t\t\tcpu_state <= cpu_state_ldmem;\n\t\t\t\t\t\tmem_do_rinst <= 1;\n\t\t\t\t\tend\n\t\t\t\t\tis_slli_srli_srai && !BARREL_SHIFTER: begin\n\t\t\t\t\t\t`debug($display(\"LD_RS1: %2d 0x%08x\", decoded_rs1, cpuregs_rs1);)\n\t\t\t\t\t\treg_op1 <= cpuregs_rs1;\n\t\t\t\t\t\tdbg_rs1val <= cpuregs_rs1;\n\t\t\t\t\t\tdbg_rs1val_valid <= 1;\n\t\t\t\t\t\treg_sh <= decoded_rs2;\n\t\t\t\t\t\tcpu_state <= cpu_state_shift;\n\t\t\t\t\tend\n\t\t\t\t\tis_jalr_addi_slti_sltiu_xori_ori_andi, is_slli_srli_srai && BARREL_SHIFTER: begin\n\t\t\t\t\t\t`debug($display(\"LD_RS1: %2d 0x%08x\", decoded_rs1, cpuregs_rs1);)\n\t\t\t\t\t\treg_op1 <= cpuregs_rs1;\n\t\t\t\t\t\tdbg_rs1val <= cpuregs_rs1;\n\t\t\t\t\t\tdbg_rs1val_valid <= 1;\n\t\t\t\t\t\treg_op2 <= is_slli_srli_srai && BARREL_SHIFTER ? decoded_rs2 : decoded_imm;\n\t\t\t\t\t\tif (TWO_CYCLE_ALU)\n\t\t\t\t\t\t\talu_wait <= 1;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tmem_do_rinst <= mem_do_prefetch;\n\t\t\t\t\t\tcpu_state <= cpu_state_exec;\n\t\t\t\t\tend\n\t\t\t\t\tdefault: begin\n\t\t\t\t\t\t`debug($display(\"LD_RS1: %2d 0x%08x\", decoded_rs1, cpuregs_rs1);)\n\t\t\t\t\t\treg_op1 <= cpuregs_rs1;\n\t\t\t\t\t\tdbg_rs1val <= cpuregs_rs1;\n\t\t\t\t\t\tdbg_rs1val_valid <= 1;\n\t\t\t\t\t\tif (ENABLE_REGS_DUALPORT) begin\n\t\t\t\t\t\t\t`debug($display(\"LD_RS2: %2d 0x%08x\", decoded_rs2, cpuregs_rs2);)\n\t\t\t\t\t\t\treg_sh <= cpuregs_rs2;\n\t\t\t\t\t\t\treg_op2 <= cpuregs_rs2;\n\t\t\t\t\t\t\tdbg_rs2val <= cpuregs_rs2;\n\t\t\t\t\t\t\tdbg_rs2val_valid <= 1;\n\t\t\t\t\t\t\t(* parallel_case *)\n\t\t\t\t\t\t\tcase (1'b1)\n\t\t\t\t\t\t\t\tis_sb_sh_sw: begin\n\t\t\t\t\t\t\t\t\tcpu_state <= cpu_state_stmem;\n\t\t\t\t\t\t\t\t\tmem_do_rinst <= 1;\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tis_sll_srl_sra && !BARREL_SHIFTER: begin\n\t\t\t\t\t\t\t\t\tcpu_state <= cpu_state_shift;\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tdefault: begin\n\t\t\t\t\t\t\t\t\tif (TWO_CYCLE_ALU || (TWO_CYCLE_COMPARE && is_beq_bne_blt_bge_bltu_bgeu)) begin\n\t\t\t\t\t\t\t\t\t\talu_wait_2 <= TWO_CYCLE_ALU && (TWO_CYCLE_COMPARE && is_beq_bne_blt_bge_bltu_bgeu);\n\t\t\t\t\t\t\t\t\t\talu_wait <= 1;\n\t\t\t\t\t\t\t\t\tend else\n\t\t\t\t\t\t\t\t\t\tmem_do_rinst <= mem_do_prefetch;\n\t\t\t\t\t\t\t\t\tcpu_state <= cpu_state_exec;\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tendcase\n\t\t\t\t\t\tend else\n\t\t\t\t\t\t\tcpu_state <= cpu_state_ld_rs2;\n\t\t\t\t\tend\n\t\t\t\tendcase\n\t\t\tend\n\n\t\t\tcpu_state_ld_rs2: begin\n\t\t\t\t`debug($display(\"LD_RS2: %2d 0x%08x\", decoded_rs2, cpuregs_rs2);)\n\t\t\t\treg_sh <= cpuregs_rs2;\n\t\t\t\treg_op2 <= cpuregs_rs2;\n\t\t\t\tdbg_rs2val <= cpuregs_rs2;\n\t\t\t\tdbg_rs2val_valid <= 1;\n\n\t\t\t\t(* parallel_case *)\n\t\t\t\tcase (1'b1)\n\t\t\t\t\tWITH_PCPI && instr_trap: begin\n\t\t\t\t\t\tpcpi_valid <= 1;\n\t\t\t\t\t\tif (pcpi_int_ready) begin\n\t\t\t\t\t\t\tmem_do_rinst <= 1;\n\t\t\t\t\t\t\tpcpi_valid <= 0;\n\t\t\t\t\t\t\treg_out <= pcpi_int_rd;\n\t\t\t\t\t\t\tlatched_store <= pcpi_int_wr;\n\t\t\t\t\t\t\tcpu_state <= cpu_state_fetch;\n\t\t\t\t\t\tend else\n\t\t\t\t\t\tif (CATCH_ILLINSN && (pcpi_timeout || instr_ecall_ebreak)) begin\n\t\t\t\t\t\t\tpcpi_valid <= 0;\n\t\t\t\t\t\t\t`debug($display(\"EBREAK OR UNSUPPORTED INSN AT 0x%08x\", reg_pc);)\n\t\t\t\t\t\t\tif (ENABLE_IRQ && !irq_mask[irq_ebreak] && !irq_active) begin\n\t\t\t\t\t\t\t\tnext_irq_pending[irq_ebreak] = 1;\n\t\t\t\t\t\t\t\tcpu_state <= cpu_state_fetch;\n\t\t\t\t\t\t\tend else\n\t\t\t\t\t\t\t\tcpu_state <= cpu_state_trap;\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\t\tis_sb_sh_sw: begin\n\t\t\t\t\t\tcpu_state <= cpu_state_stmem;\n\t\t\t\t\t\tmem_do_rinst <= 1;\n\t\t\t\t\tend\n\t\t\t\t\tis_sll_srl_sra && !BARREL_SHIFTER: begin\n\t\t\t\t\t\tcpu_state <= cpu_state_shift;\n\t\t\t\t\tend\n\t\t\t\t\tdefault: begin\n\t\t\t\t\t\tif (TWO_CYCLE_ALU || (TWO_CYCLE_COMPARE && is_beq_bne_blt_bge_bltu_bgeu)) begin\n\t\t\t\t\t\t\talu_wait_2 <= TWO_CYCLE_ALU && (TWO_CYCLE_COMPARE && is_beq_bne_blt_bge_bltu_bgeu);\n\t\t\t\t\t\t\talu_wait <= 1;\n\t\t\t\t\t\tend else\n\t\t\t\t\t\t\tmem_do_rinst <= mem_do_prefetch;\n\t\t\t\t\t\tcpu_state <= cpu_state_exec;\n\t\t\t\t\tend\n\t\t\t\tendcase\n\t\t\tend\n\n\t\t\tcpu_state_exec: begin\n\t\t\t\treg_out <= reg_pc + decoded_imm;\n\t\t\t\tif ((TWO_CYCLE_ALU || TWO_CYCLE_COMPARE) && (alu_wait || alu_wait_2)) begin\n\t\t\t\t\tmem_do_rinst <= mem_do_prefetch && !alu_wait_2;\n\t\t\t\t\talu_wait <= alu_wait_2;\n\t\t\t\tend else\n\t\t\t\tif (is_beq_bne_blt_bge_bltu_bgeu) begin\n\t\t\t\t\tlatched_rd <= 0;\n\t\t\t\t\tlatched_store <= TWO_CYCLE_COMPARE ? alu_out_0_q : alu_out_0;\n\t\t\t\t\tlatched_branch <= TWO_CYCLE_COMPARE ? alu_out_0_q : alu_out_0;\n\t\t\t\t\tif (mem_done)\n\t\t\t\t\t\tcpu_state <= cpu_state_fetch;\n\t\t\t\t\tif (TWO_CYCLE_COMPARE ? alu_out_0_q : alu_out_0) begin\n\t\t\t\t\t\tdecoder_trigger <= 0;\n\t\t\t\t\t\tset_mem_do_rinst = 1;\n\t\t\t\t\tend\n\t\t\t\tend else begin\n\t\t\t\t\tlatched_branch <= instr_jalr;\n\t\t\t\t\tlatched_store <= 1;\n\t\t\t\t\tlatched_stalu <= 1;\n\t\t\t\t\tcpu_state <= cpu_state_fetch;\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tcpu_state_shift: begin\n\t\t\t\tlatched_store <= 1;\n\t\t\t\tif (reg_sh == 0) begin\n\t\t\t\t\treg_out <= reg_op1;\n\t\t\t\t\tmem_do_rinst <= mem_do_prefetch;\n\t\t\t\t\tcpu_state <= cpu_state_fetch;\n\t\t\t\tend else if (TWO_STAGE_SHIFT && reg_sh >= 4) begin\n\t\t\t\t\t(* parallel_case, full_case *)\n\t\t\t\t\tcase (1'b1)\n\t\t\t\t\t\tinstr_slli || instr_sll: reg_op1 <= reg_op1 << 4;\n\t\t\t\t\t\tinstr_srli || instr_srl: reg_op1 <= reg_op1 >> 4;\n\t\t\t\t\t\tinstr_srai || instr_sra: reg_op1 <= $signed(reg_op1) >>> 4;\n\t\t\t\t\tendcase\n\t\t\t\t\treg_sh <= reg_sh - 4;\n\t\t\t\tend else begin\n\t\t\t\t\t(* parallel_case, full_case *)\n\t\t\t\t\tcase (1'b1)\n\t\t\t\t\t\tinstr_slli || instr_sll: reg_op1 <= reg_op1 << 1;\n\t\t\t\t\t\tinstr_srli || instr_srl: reg_op1 <= reg_op1 >> 1;\n\t\t\t\t\t\tinstr_srai || instr_sra: reg_op1 <= $signed(reg_op1) >>> 1;\n\t\t\t\t\tendcase\n\t\t\t\t\treg_sh <= reg_sh - 1;\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tcpu_state_stmem: begin\n\t\t\t\tif (ENABLE_TRACE)\n\t\t\t\t\treg_out <= reg_op2;\n\t\t\t\tif (!mem_do_prefetch || mem_done) begin\n\t\t\t\t\tif (!mem_do_wdata) begin\n\t\t\t\t\t\t(* parallel_case, full_case *)\n\t\t\t\t\t\tcase (1'b1)\n\t\t\t\t\t\t\tinstr_sb: mem_wordsize <= 2;\n\t\t\t\t\t\t\tinstr_sh: mem_wordsize <= 1;\n\t\t\t\t\t\t\tinstr_sw: mem_wordsize <= 0;\n\t\t\t\t\t\tendcase\n\t\t\t\t\t\tif (ENABLE_TRACE) begin\n\t\t\t\t\t\t\ttrace_valid <= 1;\n\t\t\t\t\t\t\ttrace_data <= (irq_active ? TRACE_IRQ : 0) | TRACE_ADDR | ((reg_op1 + decoded_imm) & 32'hffffffff);\n\t\t\t\t\t\tend\n\t\t\t\t\t\treg_op1 <= reg_op1 + decoded_imm;\n\t\t\t\t\t\tset_mem_do_wdata = 1;\n\t\t\t\t\tend\n\t\t\t\t\tif (!mem_do_prefetch && mem_done) begin\n\t\t\t\t\t\tcpu_state <= cpu_state_fetch;\n\t\t\t\t\t\tdecoder_trigger <= 1;\n\t\t\t\t\t\tdecoder_pseudo_trigger <= 1;\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tcpu_state_ldmem: begin\n\t\t\t\tlatched_store <= 1;\n\t\t\t\tif (!mem_do_prefetch || mem_done) begin\n\t\t\t\t\tif (!mem_do_rdata) begin\n\t\t\t\t\t\t(* parallel_case, full_case *)\n\t\t\t\t\t\tcase (1'b1)\n\t\t\t\t\t\t\tinstr_lb || instr_lbu: mem_wordsize <= 2;\n\t\t\t\t\t\t\tinstr_lh || instr_lhu: mem_wordsize <= 1;\n\t\t\t\t\t\t\tinstr_lw: mem_wordsize <= 0;\n\t\t\t\t\t\tendcase\n\t\t\t\t\t\tlatched_is_lu <= is_lbu_lhu_lw;\n\t\t\t\t\t\tlatched_is_lh <= instr_lh;\n\t\t\t\t\t\tlatched_is_lb <= instr_lb;\n\t\t\t\t\t\tif (ENABLE_TRACE) begin\n\t\t\t\t\t\t\ttrace_valid <= 1;\n\t\t\t\t\t\t\ttrace_data <= (irq_active ? TRACE_IRQ : 0) | TRACE_ADDR | ((reg_op1 + decoded_imm) & 32'hffffffff);\n\t\t\t\t\t\tend\n\t\t\t\t\t\treg_op1 <= reg_op1 + decoded_imm;\n\t\t\t\t\t\tset_mem_do_rdata = 1;\n\t\t\t\t\tend\n\t\t\t\t\tif (!mem_do_prefetch && mem_done) begin\n\t\t\t\t\t\t(* parallel_case, full_case *)\n\t\t\t\t\t\tcase (1'b1)\n\t\t\t\t\t\t\tlatched_is_lu: reg_out <= mem_rdata_word;\n\t\t\t\t\t\t\tlatched_is_lh: reg_out <= $signed(mem_rdata_word[15:0]);\n\t\t\t\t\t\t\tlatched_is_lb: reg_out <= $signed(mem_rdata_word[7:0]);\n\t\t\t\t\t\tendcase\n\t\t\t\t\t\tdecoder_trigger <= 1;\n\t\t\t\t\t\tdecoder_pseudo_trigger <= 1;\n\t\t\t\t\t\tcpu_state <= cpu_state_fetch;\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tendcase\n\n\t\tif (ENABLE_IRQ) begin\n\t\t\tnext_irq_pending = next_irq_pending | irq;\n\t\t\tif(ENABLE_IRQ_TIMER && timer)\n\t\t\t\tif (timer - 1 == 0)\n\t\t\t\t\tnext_irq_pending[irq_timer] = 1;\n\t\tend\n\n\t\tif (CATCH_MISALIGN && resetn && (mem_do_rdata || mem_do_wdata)) begin\n\t\t\tif (mem_wordsize == 0 && reg_op1[1:0] != 0) begin\n\t\t\t\t`debug($display(\"MISALIGNED WORD: 0x%08x\", reg_op1);)\n\t\t\t\tif (ENABLE_IRQ && !irq_mask[irq_buserror] && !irq_active) begin\n\t\t\t\t\tnext_irq_pending[irq_buserror] = 1;\n\t\t\t\tend else\n\t\t\t\t\tcpu_state <= cpu_state_trap;\n\t\t\tend\n\t\t\tif (mem_wordsize == 1 && reg_op1[0] != 0) begin\n\t\t\t\t`debug($display(\"MISALIGNED HALFWORD: 0x%08x\", reg_op1);)\n\t\t\t\tif (ENABLE_IRQ && !irq_mask[irq_buserror] && !irq_active) begin\n\t\t\t\t\tnext_irq_pending[irq_buserror] = 1;\n\t\t\t\tend else\n\t\t\t\t\tcpu_state <= cpu_state_trap;\n\t\t\tend\n\t\tend\n\t\tif (CATCH_MISALIGN && resetn && mem_do_rinst && (COMPRESSED_ISA ? reg_pc[0] : |reg_pc[1:0])) begin\n\t\t\t`debug($display(\"MISALIGNED INSTRUCTION: 0x%08x\", reg_pc);)\n\t\t\tif (ENABLE_IRQ && !irq_mask[irq_buserror] && !irq_active) begin\n\t\t\t\tnext_irq_pending[irq_buserror] = 1;\n\t\t\tend else\n\t\t\t\tcpu_state <= cpu_state_trap;\n\t\tend\n\t\tif (!CATCH_ILLINSN && decoder_trigger_q && !decoder_pseudo_trigger_q && instr_ecall_ebreak) begin\n\t\t\tcpu_state <= cpu_state_trap;\n\t\tend\n\n\t\tif (!resetn || mem_done) begin\n\t\t\tmem_do_prefetch <= 0;\n\t\t\tmem_do_rinst <= 0;\n\t\t\tmem_do_rdata <= 0;\n\t\t\tmem_do_wdata <= 0;\n\t\tend\n\n\t\tif (set_mem_do_rinst)\n\t\t\tmem_do_rinst <= 1;\n\t\tif (set_mem_do_rdata)\n\t\t\tmem_do_rdata <= 1;\n\t\tif (set_mem_do_wdata)\n\t\t\tmem_do_wdata <= 1;\n\n\t\tirq_pending <= next_irq_pending & ~MASKED_IRQ;\n\n\t\tif (!CATCH_MISALIGN) begin\n\t\t\tif (COMPRESSED_ISA) begin\n\t\t\t\treg_pc[0] <= 0;\n\t\t\t\treg_next_pc[0] <= 0;\n\t\t\tend else begin\n\t\t\t\treg_pc[1:0] <= 0;\n\t\t\t\treg_next_pc[1:0] <= 0;\n\t\t\tend\n\t\tend\n\t\tcurrent_pc = 'bx;\n\tend\n\n`ifdef RISCV_FORMAL\n\treg dbg_irq_call;\n\treg dbg_irq_enter;\n\treg [31:0] dbg_irq_ret;\n\talways @(posedge clk) begin\n\t\trvfi_valid <= resetn && (launch_next_insn || trap) && dbg_valid_insn;\n\t\trvfi_order <= resetn ? rvfi_order + rvfi_valid : 0;\n\n\t\trvfi_insn <= dbg_insn_opcode;\n\t\trvfi_rs1_addr <= dbg_rs1val_valid ? dbg_insn_rs1 : 0;\n\t\trvfi_rs2_addr <= dbg_rs2val_valid ? dbg_insn_rs2 : 0;\n\t\trvfi_pc_rdata <= dbg_insn_addr;\n\t\trvfi_rs1_rdata <= dbg_rs1val_valid ? dbg_rs1val : 0;\n\t\trvfi_rs2_rdata <= dbg_rs2val_valid ? dbg_rs2val : 0;\n\t\trvfi_trap <= trap;\n\t\trvfi_halt <= trap;\n\t\trvfi_intr <= dbg_irq_enter;\n\t\trvfi_mode <= 3;\n\t\trvfi_ixl <= 1;\n\n\t\tif (!resetn) begin\n\t\t\tdbg_irq_call <= 0;\n\t\t\tdbg_irq_enter <= 0;\n\t\tend else\n\t\tif (rvfi_valid) begin\n\t\t\tdbg_irq_call <= 0;\n\t\t\tdbg_irq_enter <= dbg_irq_call;\n\t\tend else\n\t\tif (irq_state == 1) begin\n\t\t\tdbg_irq_call <= 1;\n\t\t\tdbg_irq_ret <= next_pc;\n\t\tend\n\n\t\tif (!resetn) begin\n\t\t\trvfi_rd_addr <= 0;\n\t\t\trvfi_rd_wdata <= 0;\n\t\tend else\n\t\tif (cpuregs_write && !irq_state) begin\n`ifdef PICORV32_TESTBUG_003\n\t\t\trvfi_rd_addr <= latched_rd ^ 1;\n`else\n\t\t\trvfi_rd_addr <= latched_rd;\n`endif\n`ifdef PICORV32_TESTBUG_004\n\t\t\trvfi_rd_wdata <= latched_rd ? cpuregs_wrdata ^ 1 : 0;\n`else\n\t\t\trvfi_rd_wdata <= latched_rd ? cpuregs_wrdata : 0;\n`endif\n\t\tend else\n\t\tif (rvfi_valid) begin\n\t\t\trvfi_rd_addr <= 0;\n\t\t\trvfi_rd_wdata <= 0;\n\t\tend\n\n\t\tcasez (dbg_insn_opcode)\n\t\t\t32'b 0000000_?????_000??_???_?????_0001011: begin // getq\n\t\t\t\trvfi_rs1_addr <= 0;\n\t\t\t\trvfi_rs1_rdata <= 0;\n\t\t\tend\n\t\t\t32'b 0000001_?????_?????_???_000??_0001011: begin // setq\n\t\t\t\trvfi_rd_addr <= 0;\n\t\t\t\trvfi_rd_wdata <= 0;\n\t\t\tend\n\t\t\t32'b 0000010_?????_00000_???_00000_0001011: begin // retirq\n\t\t\t\trvfi_rs1_addr <= 0;\n\t\t\t\trvfi_rs1_rdata <= 0;\n\t\t\tend\n\t\tendcase\n\n\t\tif (!dbg_irq_call) begin\n\t\t\tif (dbg_mem_instr) begin\n\t\t\t\trvfi_mem_addr <= 0;\n\t\t\t\trvfi_mem_rmask <= 0;\n\t\t\t\trvfi_mem_wmask <= 0;\n\t\t\t\trvfi_mem_rdata <= 0;\n\t\t\t\trvfi_mem_wdata <= 0;\n\t\t\tend else\n\t\t\tif (dbg_mem_valid && dbg_mem_ready) begin\n\t\t\t\trvfi_mem_addr <= dbg_mem_addr;\n\t\t\t\trvfi_mem_rmask <= dbg_mem_wstrb ? 0 : ~0;\n\t\t\t\trvfi_mem_wmask <= dbg_mem_wstrb;\n\t\t\t\trvfi_mem_rdata <= dbg_mem_rdata;\n\t\t\t\trvfi_mem_wdata <= dbg_mem_wdata;\n\t\t\tend\n\t\tend\n\tend\n\n\talways @* begin\n`ifdef PICORV32_TESTBUG_005\n\t\trvfi_pc_wdata = (dbg_irq_call ? dbg_irq_ret : dbg_insn_addr) ^ 4;\n`else\n\t\trvfi_pc_wdata = dbg_irq_call ? dbg_irq_ret : dbg_insn_addr;\n`endif\n\n\t\trvfi_csr_mcycle_rmask = 0;\n\t\trvfi_csr_mcycle_wmask = 0;\n\t\trvfi_csr_mcycle_rdata = 0;\n\t\trvfi_csr_mcycle_wdata = 0;\n\n\t\trvfi_csr_minstret_rmask = 0;\n\t\trvfi_csr_minstret_wmask = 0;\n\t\trvfi_csr_minstret_rdata = 0;\n\t\trvfi_csr_minstret_wdata = 0;\n\n\t\tif (rvfi_valid && rvfi_insn[6:0] == 7'b 1110011 && rvfi_insn[13:12] == 3'b010) begin\n\t\t\tif (rvfi_insn[31:20] == 12'h C00) begin\n\t\t\t\trvfi_csr_mcycle_rmask = 64'h 0000_0000_FFFF_FFFF;\n\t\t\t\trvfi_csr_mcycle_rdata = {32'h 0000_0000, rvfi_rd_wdata};\n\t\t\tend\n\t\t\tif (rvfi_insn[31:20] == 12'h C80) begin\n\t\t\t\trvfi_csr_mcycle_rmask = 64'h FFFF_FFFF_0000_0000;\n\t\t\t\trvfi_csr_mcycle_rdata = {rvfi_rd_wdata, 32'h 0000_0000};\n\t\t\tend\n\t\t\tif (rvfi_insn[31:20] == 12'h C02) begin\n\t\t\t\trvfi_csr_minstret_rmask = 64'h 0000_0000_FFFF_FFFF;\n\t\t\t\trvfi_csr_minstret_rdata = {32'h 0000_0000, rvfi_rd_wdata};\n\t\t\tend\n\t\t\tif (rvfi_insn[31:20] == 12'h C82) begin\n\t\t\t\trvfi_csr_minstret_rmask = 64'h FFFF_FFFF_0000_0000;\n\t\t\t\trvfi_csr_minstret_rdata = {rvfi_rd_wdata, 32'h 0000_0000};\n\t\t\tend\n\t\tend\n\tend\n`endif\n\n\t// Formal Verification\n`ifdef FORMAL\n\treg [3:0] last_mem_nowait;\n\talways @(posedge clk)\n\t\tlast_mem_nowait <= {last_mem_nowait, mem_ready || !mem_valid};\n\n\t// stall the memory interface for max 4 cycles\n\trestrict property (|last_mem_nowait || mem_ready || !mem_valid);\n\n\t// resetn low in first cycle, after that resetn high\n\trestrict property (resetn != $initstate);\n\n\t// this just makes it much easier to read traces. uncomment as needed.\n\t// assume property (mem_valid || !mem_ready);\n\n\treg ok;\n\talways @* begin\n\t\tif (resetn) begin\n\t\t\t// instruction fetches are read-only\n\t\t\tif (mem_valid && mem_instr)\n\t\t\t\tassert (mem_wstrb == 0);\n\n\t\t\t// cpu_state must be valid\n\t\t\tok = 0;\n\t\t\tif (cpu_state == cpu_state_trap)   ok = 1;\n\t\t\tif (cpu_state == cpu_state_fetch)  ok = 1;\n\t\t\tif (cpu_state == cpu_state_ld_rs1) ok = 1;\n\t\t\tif (cpu_state == cpu_state_ld_rs2) ok = !ENABLE_REGS_DUALPORT;\n\t\t\tif (cpu_state == cpu_state_exec)   ok = 1;\n\t\t\tif (cpu_state == cpu_state_shift)  ok = 1;\n\t\t\tif (cpu_state == cpu_state_stmem)  ok = 1;\n\t\t\tif (cpu_state == cpu_state_ldmem)  ok = 1;\n\t\t\tassert (ok);\n\t\tend\n\tend\n\n\treg last_mem_la_read = 0;\n\treg last_mem_la_write = 0;\n\treg [31:0] last_mem_la_addr;\n\treg [31:0] last_mem_la_wdata;\n\treg [3:0] last_mem_la_wstrb = 0;\n\n\talways @(posedge clk) begin\n\t\tlast_mem_la_read <= mem_la_read;\n\t\tlast_mem_la_write <= mem_la_write;\n\t\tlast_mem_la_addr <= mem_la_addr;\n\t\tlast_mem_la_wdata <= mem_la_wdata;\n\t\tlast_mem_la_wstrb <= mem_la_wstrb;\n\n\t\tif (last_mem_la_read) begin\n\t\t\tassert(mem_valid);\n\t\t\tassert(mem_addr == last_mem_la_addr);\n\t\t\tassert(mem_wstrb == 0);\n\t\tend\n\t\tif (last_mem_la_write) begin\n\t\t\tassert(mem_valid);\n\t\t\tassert(mem_addr == last_mem_la_addr);\n\t\t\tassert(mem_wdata == last_mem_la_wdata);\n\t\t\tassert(mem_wstrb == last_mem_la_wstrb);\n\t\tend\n\t\tif (mem_la_read || mem_la_write) begin\n\t\t\tassert(!mem_valid || mem_ready);\n\t\tend\n\tend\n`endif\nendmodule\n\n// This is a simple example implementation of PICORV32_REGS.\n// Use the PICORV32_REGS mechanism if you want to use custom\n// memory resources to implement the processor register file.\n// Note that your implementation must match the requirements of\n// the PicoRV32 configuration. (e.g. QREGS, etc)\nmodule picorv32_regs (\n\tinput clk, wen,\n\tinput [5:0] waddr,\n\tinput [5:0] raddr1,\n\tinput [5:0] raddr2,\n\tinput [31:0] wdata,\n\toutput [31:0] rdata1,\n\toutput [31:0] rdata2\n);\n\treg [31:0] regs [0:30];\n\n\talways @(posedge clk)\n\t\tif (wen) regs[~waddr[4:0]] <= wdata;\n\n\tassign rdata1 = regs[~raddr1[4:0]];\n\tassign rdata2 = regs[~raddr2[4:0]];\nendmodule\n\n\n/***************************************************************\n * picorv32_pcpi_mul\n ***************************************************************/\n\nmodule picorv32_pcpi_mul #(\n\tparameter STEPS_AT_ONCE = 1,\n\tparameter CARRY_CHAIN = 4\n) (\n\tinput clk, resetn,\n\n\tinput             pcpi_valid,\n\tinput      [31:0] pcpi_insn,\n\tinput      [31:0] pcpi_rs1,\n\tinput      [31:0] pcpi_rs2,\n\toutput reg        pcpi_wr,\n\toutput reg [31:0] pcpi_rd,\n\toutput reg        pcpi_wait,\n\toutput reg        pcpi_ready\n);\n\treg instr_mul, instr_mulh, instr_mulhsu, instr_mulhu;\n\twire instr_any_mul = |{instr_mul, instr_mulh, instr_mulhsu, instr_mulhu};\n\twire instr_any_mulh = |{instr_mulh, instr_mulhsu, instr_mulhu};\n\twire instr_rs1_signed = |{instr_mulh, instr_mulhsu};\n\twire instr_rs2_signed = |{instr_mulh};\n\n\treg pcpi_wait_q;\n\twire mul_start = pcpi_wait && !pcpi_wait_q;\n\n\talways @(posedge clk) begin\n\t\tinstr_mul <= 0;\n\t\tinstr_mulh <= 0;\n\t\tinstr_mulhsu <= 0;\n\t\tinstr_mulhu <= 0;\n\n\t\tif (resetn && pcpi_valid && pcpi_insn[6:0] == 7'b0110011 && pcpi_insn[31:25] == 7'b0000001) begin\n\t\t\tcase (pcpi_insn[14:12])\n\t\t\t\t3'b000: instr_mul <= 1;\n\t\t\t\t3'b001: instr_mulh <= 1;\n\t\t\t\t3'b010: instr_mulhsu <= 1;\n\t\t\t\t3'b011: instr_mulhu <= 1;\n\t\t\tendcase\n\t\tend\n\n\t\tpcpi_wait <= instr_any_mul;\n\t\tpcpi_wait_q <= pcpi_wait;\n\tend\n\n\treg [63:0] rs1, rs2, rd, rdx;\n\treg [63:0] next_rs1, next_rs2, this_rs2;\n\treg [63:0] next_rd, next_rdx, next_rdt;\n\treg [6:0] mul_counter;\n\treg mul_waiting;\n\treg mul_finish;\n\tinteger i, j;\n\n\t// carry save accumulator\n\talways @* begin\n\t\tnext_rd = rd;\n\t\tnext_rdx = rdx;\n\t\tnext_rs1 = rs1;\n\t\tnext_rs2 = rs2;\n\n\t\tfor (i = 0; i < STEPS_AT_ONCE; i=i+1) begin\n\t\t\tthis_rs2 = next_rs1[0] ? next_rs2 : 0;\n\t\t\tif (CARRY_CHAIN == 0) begin\n\t\t\t\tnext_rdt = next_rd ^ next_rdx ^ this_rs2;\n\t\t\t\tnext_rdx = ((next_rd & next_rdx) | (next_rd & this_rs2) | (next_rdx & this_rs2)) << 1;\n\t\t\t\tnext_rd = next_rdt;\n\t\t\tend else begin\n\t\t\t\tnext_rdt = 0;\n\t\t\t\tfor (j = 0; j < 64; j = j + CARRY_CHAIN)\n\t\t\t\t\t{next_rdt[j+CARRY_CHAIN-1], next_rd[j +: CARRY_CHAIN]} =\n\t\t\t\t\t\t\tnext_rd[j +: CARRY_CHAIN] + next_rdx[j +: CARRY_CHAIN] + this_rs2[j +: CARRY_CHAIN];\n\t\t\t\tnext_rdx = next_rdt << 1;\n\t\t\tend\n\t\t\tnext_rs1 = next_rs1 >> 1;\n\t\t\tnext_rs2 = next_rs2 << 1;\n\t\tend\n\tend\n\n\talways @(posedge clk) begin\n\t\tmul_finish <= 0;\n\t\tif (!resetn) begin\n\t\t\tmul_waiting <= 1;\n\t\tend else\n\t\tif (mul_waiting) begin\n\t\t\tif (instr_rs1_signed)\n\t\t\t\trs1 <= $signed(pcpi_rs1);\n\t\t\telse\n\t\t\t\trs1 <= $unsigned(pcpi_rs1);\n\n\t\t\tif (instr_rs2_signed)\n\t\t\t\trs2 <= $signed(pcpi_rs2);\n\t\t\telse\n\t\t\t\trs2 <= $unsigned(pcpi_rs2);\n\n\t\t\trd <= 0;\n\t\t\trdx <= 0;\n\t\t\tmul_counter <= (instr_any_mulh ? 63 - STEPS_AT_ONCE : 31 - STEPS_AT_ONCE);\n\t\t\tmul_waiting <= !mul_start;\n\t\tend else begin\n\t\t\trd <= next_rd;\n\t\t\trdx <= next_rdx;\n\t\t\trs1 <= next_rs1;\n\t\t\trs2 <= next_rs2;\n\n\t\t\tmul_counter <= mul_counter - STEPS_AT_ONCE;\n\t\t\tif (mul_counter[6]) begin\n\t\t\t\tmul_finish <= 1;\n\t\t\t\tmul_waiting <= 1;\n\t\t\tend\n\t\tend\n\tend\n\n\talways @(posedge clk) begin\n\t\tpcpi_wr <= 0;\n\t\tpcpi_ready <= 0;\n\t\tif (mul_finish && resetn) begin\n\t\t\tpcpi_wr <= 1;\n\t\t\tpcpi_ready <= 1;\n\t\t\tpcpi_rd <= instr_any_mulh ? rd >> 32 : rd;\n\t\tend\n\tend\nendmodule\n\nmodule picorv32_pcpi_fast_mul #(\n\tparameter EXTRA_MUL_FFS = 0,\n\tparameter EXTRA_INSN_FFS = 0,\n\tparameter MUL_CLKGATE = 0\n) (\n\tinput clk, resetn,\n\n\tinput             pcpi_valid,\n\tinput      [31:0] pcpi_insn,\n\tinput      [31:0] pcpi_rs1,\n\tinput      [31:0] pcpi_rs2,\n\toutput            pcpi_wr,\n\toutput     [31:0] pcpi_rd,\n\toutput            pcpi_wait,\n\toutput            pcpi_ready\n);\n\treg instr_mul, instr_mulh, instr_mulhsu, instr_mulhu;\n\twire instr_any_mul = |{instr_mul, instr_mulh, instr_mulhsu, instr_mulhu};\n\twire instr_any_mulh = |{instr_mulh, instr_mulhsu, instr_mulhu};\n\twire instr_rs1_signed = |{instr_mulh, instr_mulhsu};\n\twire instr_rs2_signed = |{instr_mulh};\n\n\treg shift_out;\n\treg [3:0] active;\n\treg [32:0] rs1, rs2, rs1_q, rs2_q;\n\treg [63:0] rd, rd_q;\n\n\twire pcpi_insn_valid = pcpi_valid && pcpi_insn[6:0] == 7'b0110011 && pcpi_insn[31:25] == 7'b0000001;\n\treg pcpi_insn_valid_q;\n\n\talways @* begin\n\t\tinstr_mul = 0;\n\t\tinstr_mulh = 0;\n\t\tinstr_mulhsu = 0;\n\t\tinstr_mulhu = 0;\n\n\t\tif (resetn && (EXTRA_INSN_FFS ? pcpi_insn_valid_q : pcpi_insn_valid)) begin\n\t\t\tcase (pcpi_insn[14:12])\n\t\t\t\t3'b000: instr_mul = 1;\n\t\t\t\t3'b001: instr_mulh = 1;\n\t\t\t\t3'b010: instr_mulhsu = 1;\n\t\t\t\t3'b011: instr_mulhu = 1;\n\t\t\tendcase\n\t\tend\n\tend\n\n\talways @(posedge clk) begin\n\t\tpcpi_insn_valid_q <= pcpi_insn_valid;\n\t\tif (!MUL_CLKGATE || active[0]) begin\n\t\t\trs1_q <= rs1;\n\t\t\trs2_q <= rs2;\n\t\tend\n\t\tif (!MUL_CLKGATE || active[1]) begin\n\t\t\trd <= $signed(EXTRA_MUL_FFS ? rs1_q : rs1) * $signed(EXTRA_MUL_FFS ? rs2_q : rs2);\n\t\tend\n\t\tif (!MUL_CLKGATE || active[2]) begin\n\t\t\trd_q <= rd;\n\t\tend\n\tend\n\n\talways @(posedge clk) begin\n\t\tif (instr_any_mul && !(EXTRA_MUL_FFS ? active[3:0] : active[1:0])) begin\n\t\t\tif (instr_rs1_signed)\n\t\t\t\trs1 <= $signed(pcpi_rs1);\n\t\t\telse\n\t\t\t\trs1 <= $unsigned(pcpi_rs1);\n\n\t\t\tif (instr_rs2_signed)\n\t\t\t\trs2 <= $signed(pcpi_rs2);\n\t\t\telse\n\t\t\t\trs2 <= $unsigned(pcpi_rs2);\n\t\t\tactive[0] <= 1;\n\t\tend else begin\n\t\t\tactive[0] <= 0;\n\t\tend\n\n\t\tactive[3:1] <= active;\n\t\tshift_out <= instr_any_mulh;\n\n\t\tif (!resetn)\n\t\t\tactive <= 0;\n\tend\n\n\tassign pcpi_wr = active[EXTRA_MUL_FFS ? 3 : 1];\n\tassign pcpi_wait = 0;\n\tassign pcpi_ready = active[EXTRA_MUL_FFS ? 3 : 1];\n`ifdef RISCV_FORMAL_ALTOPS\n\tassign pcpi_rd =\n\t\t\tinstr_mul    ? (pcpi_rs1 + pcpi_rs2) ^ 32'h5876063e :\n\t\t\tinstr_mulh   ? (pcpi_rs1 + pcpi_rs2) ^ 32'hf6583fb7 :\n\t\t\tinstr_mulhsu ? (pcpi_rs1 - pcpi_rs2) ^ 32'hecfbe137 :\n\t\t\tinstr_mulhu  ? (pcpi_rs1 + pcpi_rs2) ^ 32'h949ce5e8 : 1'bx;\n`else\n\tassign pcpi_rd = shift_out ? (EXTRA_MUL_FFS ? rd_q : rd) >> 32 : (EXTRA_MUL_FFS ? rd_q : rd);\n`endif\nendmodule\n\n\n/***************************************************************\n * picorv32_pcpi_div\n ***************************************************************/\n\nmodule picorv32_pcpi_div (\n\tinput clk, resetn,\n\n\tinput             pcpi_valid,\n\tinput      [31:0] pcpi_insn,\n\tinput      [31:0] pcpi_rs1,\n\tinput      [31:0] pcpi_rs2,\n\toutput reg        pcpi_wr,\n\toutput reg [31:0] pcpi_rd,\n\toutput reg        pcpi_wait,\n\toutput reg        pcpi_ready\n);\n\treg instr_div, instr_divu, instr_rem, instr_remu;\n\twire instr_any_div_rem = |{instr_div, instr_divu, instr_rem, instr_remu};\n\n\treg pcpi_wait_q;\n\twire start = pcpi_wait && !pcpi_wait_q;\n\n\talways @(posedge clk) begin\n\t\tinstr_div <= 0;\n\t\tinstr_divu <= 0;\n\t\tinstr_rem <= 0;\n\t\tinstr_remu <= 0;\n\n\t\tif (resetn && pcpi_valid && !pcpi_ready && pcpi_insn[6:0] == 7'b0110011 && pcpi_insn[31:25] == 7'b0000001) begin\n\t\t\tcase (pcpi_insn[14:12])\n\t\t\t\t3'b100: instr_div <= 1;\n\t\t\t\t3'b101: instr_divu <= 1;\n\t\t\t\t3'b110: instr_rem <= 1;\n\t\t\t\t3'b111: instr_remu <= 1;\n\t\t\tendcase\n\t\tend\n\n\t\tpcpi_wait <= instr_any_div_rem && resetn;\n\t\tpcpi_wait_q <= pcpi_wait && resetn;\n\tend\n\n\treg [31:0] dividend;\n\treg [62:0] divisor;\n\treg [31:0] quotient;\n\treg [31:0] quotient_msk;\n\treg running;\n\treg outsign;\n\n\talways @(posedge clk) begin\n\t\tpcpi_ready <= 0;\n\t\tpcpi_wr <= 0;\n\t\tpcpi_rd <= 'bx;\n\n\t\tif (!resetn) begin\n\t\t\trunning <= 0;\n\t\tend else\n\t\tif (start) begin\n\t\t\trunning <= 1;\n\t\t\tdividend <= (instr_div || instr_rem) && pcpi_rs1[31] ? -pcpi_rs1 : pcpi_rs1;\n\t\t\tdivisor <= ((instr_div || instr_rem) && pcpi_rs2[31] ? -pcpi_rs2 : pcpi_rs2) << 31;\n\t\t\toutsign <= (instr_div && (pcpi_rs1[31] != pcpi_rs2[31]) && |pcpi_rs2) || (instr_rem && pcpi_rs1[31]);\n\t\t\tquotient <= 0;\n\t\t\tquotient_msk <= 1 << 31;\n\t\tend else\n\t\tif (!quotient_msk && running) begin\n\t\t\trunning <= 0;\n\t\t\tpcpi_ready <= 1;\n\t\t\tpcpi_wr <= 1;\n`ifdef RISCV_FORMAL_ALTOPS\n\t\t\tcase (1)\n\t\t\t\tinstr_div:  pcpi_rd <= (pcpi_rs1 - pcpi_rs2) ^ 32'h7f8529ec;\n\t\t\t\tinstr_divu: pcpi_rd <= (pcpi_rs1 - pcpi_rs2) ^ 32'h10e8fd70;\n\t\t\t\tinstr_rem:  pcpi_rd <= (pcpi_rs1 - pcpi_rs2) ^ 32'h8da68fa5;\n\t\t\t\tinstr_remu: pcpi_rd <= (pcpi_rs1 - pcpi_rs2) ^ 32'h3138d0e1;\n\t\t\tendcase\n`else\n\t\t\tif (instr_div || instr_divu)\n\t\t\t\tpcpi_rd <= outsign ? -quotient : quotient;\n\t\t\telse\n\t\t\t\tpcpi_rd <= outsign ? -dividend : dividend;\n`endif\n\t\tend else begin\n\t\t\tif (divisor <= dividend) begin\n\t\t\t\tdividend <= dividend - divisor;\n\t\t\t\tquotient <= quotient | quotient_msk;\n\t\t\tend\n\t\t\tdivisor <= divisor >> 1;\n`ifdef RISCV_FORMAL_ALTOPS\n\t\t\tquotient_msk <= quotient_msk >> 5;\n`else\n\t\t\tquotient_msk <= quotient_msk >> 1;\n`endif\n\t\tend\n\tend\nendmodule\n\n\n/***************************************************************\n * picorv32_axi\n ***************************************************************/\n\nmodule picorv32_axi #(\n\tparameter [ 0:0] ENABLE_COUNTERS = 1,\n\tparameter [ 0:0] ENABLE_COUNTERS64 = 1,\n\tparameter [ 0:0] ENABLE_REGS_16_31 = 1,\n\tparameter [ 0:0] ENABLE_REGS_DUALPORT = 1,\n\tparameter [ 0:0] TWO_STAGE_SHIFT = 1,\n\tparameter [ 0:0] BARREL_SHIFTER = 0,\n\tparameter [ 0:0] TWO_CYCLE_COMPARE = 0,\n\tparameter [ 0:0] TWO_CYCLE_ALU = 0,\n\tparameter [ 0:0] COMPRESSED_ISA = 0,\n\tparameter [ 0:0] CATCH_MISALIGN = 1,\n\tparameter [ 0:0] CATCH_ILLINSN = 1,\n\tparameter [ 0:0] ENABLE_PCPI = 0,\n\tparameter [ 0:0] ENABLE_MUL = 0,\n\tparameter [ 0:0] ENABLE_FAST_MUL = 0,\n\tparameter [ 0:0] ENABLE_DIV = 0,\n\tparameter [ 0:0] ENABLE_IRQ = 0,\n\tparameter [ 0:0] ENABLE_IRQ_QREGS = 1,\n\tparameter [ 0:0] ENABLE_IRQ_TIMER = 1,\n\tparameter [ 0:0] ENABLE_TRACE = 0,\n\tparameter [ 0:0] REGS_INIT_ZERO = 0,\n\tparameter [31:0] MASKED_IRQ = 32'h 0000_0000,\n\tparameter [31:0] LATCHED_IRQ = 32'h ffff_ffff,\n\tparameter [31:0] PROGADDR_RESET = 32'h 0000_0000,\n\tparameter [31:0] PROGADDR_IRQ = 32'h 0000_0010,\n\tparameter [31:0] STACKADDR = 32'h ffff_ffff\n) (\n\tinput clk, resetn,\n\toutput trap,\n\n\t// AXI4-lite master memory interface\n\n\toutput        mem_axi_awvalid,\n\tinput         mem_axi_awready,\n\toutput [31:0] mem_axi_awaddr,\n\toutput [ 2:0] mem_axi_awprot,\n\n\toutput        mem_axi_wvalid,\n\tinput         mem_axi_wready,\n\toutput [31:0] mem_axi_wdata,\n\toutput [ 3:0] mem_axi_wstrb,\n\n\tinput         mem_axi_bvalid,\n\toutput        mem_axi_bready,\n\n\toutput        mem_axi_arvalid,\n\tinput         mem_axi_arready,\n\toutput [31:0] mem_axi_araddr,\n\toutput [ 2:0] mem_axi_arprot,\n\n\tinput         mem_axi_rvalid,\n\toutput        mem_axi_rready,\n\tinput  [31:0] mem_axi_rdata,\n\n\t// Pico Co-Processor Interface (PCPI)\n\toutput        pcpi_valid,\n\toutput [31:0] pcpi_insn,\n\toutput [31:0] pcpi_rs1,\n\toutput [31:0] pcpi_rs2,\n\tinput         pcpi_wr,\n\tinput  [31:0] pcpi_rd,\n\tinput         pcpi_wait,\n\tinput         pcpi_ready,\n\n\t// IRQ interface\n\tinput  [31:0] irq,\n\toutput [31:0] eoi,\n\n`ifdef RISCV_FORMAL\n\toutput        rvfi_valid,\n\toutput [63:0] rvfi_order,\n\toutput [31:0] rvfi_insn,\n\toutput        rvfi_trap,\n\toutput        rvfi_halt,\n\toutput        rvfi_intr,\n\toutput [ 4:0] rvfi_rs1_addr,\n\toutput [ 4:0] rvfi_rs2_addr,\n\toutput [31:0] rvfi_rs1_rdata,\n\toutput [31:0] rvfi_rs2_rdata,\n\toutput [ 4:0] rvfi_rd_addr,\n\toutput [31:0] rvfi_rd_wdata,\n\toutput [31:0] rvfi_pc_rdata,\n\toutput [31:0] rvfi_pc_wdata,\n\toutput [31:0] rvfi_mem_addr,\n\toutput [ 3:0] rvfi_mem_rmask,\n\toutput [ 3:0] rvfi_mem_wmask,\n\toutput [31:0] rvfi_mem_rdata,\n\toutput [31:0] rvfi_mem_wdata,\n`endif\n\n\t// Trace Interface\n\toutput        trace_valid,\n\toutput [35:0] trace_data\n);\n\twire        mem_valid;\n\twire [31:0] mem_addr;\n\twire [31:0] mem_wdata;\n\twire [ 3:0] mem_wstrb;\n\twire        mem_instr;\n\twire        mem_ready;\n\twire [31:0] mem_rdata;\n\n\tpicorv32_axi_adapter axi_adapter (\n\t\t.clk            (clk            ),\n\t\t.resetn         (resetn         ),\n\t\t.mem_axi_awvalid(mem_axi_awvalid),\n\t\t.mem_axi_awready(mem_axi_awready),\n\t\t.mem_axi_awaddr (mem_axi_awaddr ),\n\t\t.mem_axi_awprot (mem_axi_awprot ),\n\t\t.mem_axi_wvalid (mem_axi_wvalid ),\n\t\t.mem_axi_wready (mem_axi_wready ),\n\t\t.mem_axi_wdata  (mem_axi_wdata  ),\n\t\t.mem_axi_wstrb  (mem_axi_wstrb  ),\n\t\t.mem_axi_bvalid (mem_axi_bvalid ),\n\t\t.mem_axi_bready (mem_axi_bready ),\n\t\t.mem_axi_arvalid(mem_axi_arvalid),\n\t\t.mem_axi_arready(mem_axi_arready),\n\t\t.mem_axi_araddr (mem_axi_araddr ),\n\t\t.mem_axi_arprot (mem_axi_arprot ),\n\t\t.mem_axi_rvalid (mem_axi_rvalid ),\n\t\t.mem_axi_rready (mem_axi_rready ),\n\t\t.mem_axi_rdata  (mem_axi_rdata  ),\n\t\t.mem_valid      (mem_valid      ),\n\t\t.mem_instr      (mem_instr      ),\n\t\t.mem_ready      (mem_ready      ),\n\t\t.mem_addr       (mem_addr       ),\n\t\t.mem_wdata      (mem_wdata      ),\n\t\t.mem_wstrb      (mem_wstrb      ),\n\t\t.mem_rdata      (mem_rdata      )\n\t);\n\n\tpicorv32 #(\n\t\t.ENABLE_COUNTERS     (ENABLE_COUNTERS     ),\n\t\t.ENABLE_COUNTERS64   (ENABLE_COUNTERS64   ),\n\t\t.ENABLE_REGS_16_31   (ENABLE_REGS_16_31   ),\n\t\t.ENABLE_REGS_DUALPORT(ENABLE_REGS_DUALPORT),\n\t\t.TWO_STAGE_SHIFT     (TWO_STAGE_SHIFT     ),\n\t\t.BARREL_SHIFTER      (BARREL_SHIFTER      ),\n\t\t.TWO_CYCLE_COMPARE   (TWO_CYCLE_COMPARE   ),\n\t\t.TWO_CYCLE_ALU       (TWO_CYCLE_ALU       ),\n\t\t.COMPRESSED_ISA      (COMPRESSED_ISA      ),\n\t\t.CATCH_MISALIGN      (CATCH_MISALIGN      ),\n\t\t.CATCH_ILLINSN       (CATCH_ILLINSN       ),\n\t\t.ENABLE_PCPI         (ENABLE_PCPI         ),\n\t\t.ENABLE_MUL          (ENABLE_MUL          ),\n\t\t.ENABLE_FAST_MUL     (ENABLE_FAST_MUL     ),\n\t\t.ENABLE_DIV          (ENABLE_DIV          ),\n\t\t.ENABLE_IRQ          (ENABLE_IRQ          ),\n\t\t.ENABLE_IRQ_QREGS    (ENABLE_IRQ_QREGS    ),\n\t\t.ENABLE_IRQ_TIMER    (ENABLE_IRQ_TIMER    ),\n\t\t.ENABLE_TRACE        (ENABLE_TRACE        ),\n\t\t.REGS_INIT_ZERO      (REGS_INIT_ZERO      ),\n\t\t.MASKED_IRQ          (MASKED_IRQ          ),\n\t\t.LATCHED_IRQ         (LATCHED_IRQ         ),\n\t\t.PROGADDR_RESET      (PROGADDR_RESET      ),\n\t\t.PROGADDR_IRQ        (PROGADDR_IRQ        ),\n\t\t.STACKADDR           (STACKADDR           )\n\t) picorv32_core (\n\t\t.clk      (clk   ),\n\t\t.resetn   (resetn),\n\t\t.trap     (trap  ),\n\n\t\t.mem_valid(mem_valid),\n\t\t.mem_addr (mem_addr ),\n\t\t.mem_wdata(mem_wdata),\n\t\t.mem_wstrb(mem_wstrb),\n\t\t.mem_instr(mem_instr),\n\t\t.mem_ready(mem_ready),\n\t\t.mem_rdata(mem_rdata),\n\n\t\t.pcpi_valid(pcpi_valid),\n\t\t.pcpi_insn (pcpi_insn ),\n\t\t.pcpi_rs1  (pcpi_rs1  ),\n\t\t.pcpi_rs2  (pcpi_rs2  ),\n\t\t.pcpi_wr   (pcpi_wr   ),\n\t\t.pcpi_rd   (pcpi_rd   ),\n\t\t.pcpi_wait (pcpi_wait ),\n\t\t.pcpi_ready(pcpi_ready),\n\n\t\t.irq(irq),\n\t\t.eoi(eoi),\n\n`ifdef RISCV_FORMAL\n\t\t.rvfi_valid    (rvfi_valid    ),\n\t\t.rvfi_order    (rvfi_order    ),\n\t\t.rvfi_insn     (rvfi_insn     ),\n\t\t.rvfi_trap     (rvfi_trap     ),\n\t\t.rvfi_halt     (rvfi_halt     ),\n\t\t.rvfi_intr     (rvfi_intr     ),\n\t\t.rvfi_rs1_addr (rvfi_rs1_addr ),\n\t\t.rvfi_rs2_addr (rvfi_rs2_addr ),\n\t\t.rvfi_rs1_rdata(rvfi_rs1_rdata),\n\t\t.rvfi_rs2_rdata(rvfi_rs2_rdata),\n\t\t.rvfi_rd_addr  (rvfi_rd_addr  ),\n\t\t.rvfi_rd_wdata (rvfi_rd_wdata ),\n\t\t.rvfi_pc_rdata (rvfi_pc_rdata ),\n\t\t.rvfi_pc_wdata (rvfi_pc_wdata ),\n\t\t.rvfi_mem_addr (rvfi_mem_addr ),\n\t\t.rvfi_mem_rmask(rvfi_mem_rmask),\n\t\t.rvfi_mem_wmask(rvfi_mem_wmask),\n\t\t.rvfi_mem_rdata(rvfi_mem_rdata),\n\t\t.rvfi_mem_wdata(rvfi_mem_wdata),\n`endif\n\n\t\t.trace_valid(trace_valid),\n\t\t.trace_data (trace_data)\n\t);\nendmodule\n\n\n/***************************************************************\n * picorv32_axi_adapter\n ***************************************************************/\n\nmodule picorv32_axi_adapter (\n\tinput clk, resetn,\n\n\t// AXI4-lite master memory interface\n\n\toutput        mem_axi_awvalid,\n\tinput         mem_axi_awready,\n\toutput [31:0] mem_axi_awaddr,\n\toutput [ 2:0] mem_axi_awprot,\n\n\toutput        mem_axi_wvalid,\n\tinput         mem_axi_wready,\n\toutput [31:0] mem_axi_wdata,\n\toutput [ 3:0] mem_axi_wstrb,\n\n\tinput         mem_axi_bvalid,\n\toutput        mem_axi_bready,\n\n\toutput        mem_axi_arvalid,\n\tinput         mem_axi_arready,\n\toutput [31:0] mem_axi_araddr,\n\toutput [ 2:0] mem_axi_arprot,\n\n\tinput         mem_axi_rvalid,\n\toutput        mem_axi_rready,\n\tinput  [31:0] mem_axi_rdata,\n\n\t// Native PicoRV32 memory interface\n\n\tinput         mem_valid,\n\tinput         mem_instr,\n\toutput        mem_ready,\n\tinput  [31:0] mem_addr,\n\tinput  [31:0] mem_wdata,\n\tinput  [ 3:0] mem_wstrb,\n\toutput [31:0] mem_rdata\n);\n\treg ack_awvalid;\n\treg ack_arvalid;\n\treg ack_wvalid;\n\treg xfer_done;\n\n\tassign mem_axi_awvalid = mem_valid && |mem_wstrb && !ack_awvalid;\n\tassign mem_axi_awaddr = mem_addr;\n\tassign mem_axi_awprot = 0;\n\n\tassign mem_axi_arvalid = mem_valid && !mem_wstrb && !ack_arvalid;\n\tassign mem_axi_araddr = mem_addr;\n\tassign mem_axi_arprot = mem_instr ? 3'b100 : 3'b000;\n\n\tassign mem_axi_wvalid = mem_valid && |mem_wstrb && !ack_wvalid;\n\tassign mem_axi_wdata = mem_wdata;\n\tassign mem_axi_wstrb = mem_wstrb;\n\n\tassign mem_ready = mem_axi_bvalid || mem_axi_rvalid;\n\tassign mem_axi_bready = mem_valid && |mem_wstrb;\n\tassign mem_axi_rready = mem_valid && !mem_wstrb;\n\tassign mem_rdata = mem_axi_rdata;\n\n\talways @(posedge clk) begin\n\t\tif (!resetn) begin\n\t\t\tack_awvalid <= 0;\n\t\tend else begin\n\t\t\txfer_done <= mem_valid && mem_ready;\n\t\t\tif (mem_axi_awready && mem_axi_awvalid)\n\t\t\t\tack_awvalid <= 1;\n\t\t\tif (mem_axi_arready && mem_axi_arvalid)\n\t\t\t\tack_arvalid <= 1;\n\t\t\tif (mem_axi_wready && mem_axi_wvalid)\n\t\t\t\tack_wvalid <= 1;\n\t\t\tif (xfer_done || !mem_valid) begin\n\t\t\t\tack_awvalid <= 0;\n\t\t\t\tack_arvalid <= 0;\n\t\t\t\tack_wvalid <= 0;\n\t\t\tend\n\t\tend\n\tend\nendmodule\n\n\n/***************************************************************\n * picorv32_wb\n ***************************************************************/\n\nmodule picorv32_wb #(\n\tparameter [ 0:0] ENABLE_COUNTERS = 1,\n\tparameter [ 0:0] ENABLE_COUNTERS64 = 1,\n\tparameter [ 0:0] ENABLE_REGS_16_31 = 1,\n\tparameter [ 0:0] ENABLE_REGS_DUALPORT = 1,\n\tparameter [ 0:0] TWO_STAGE_SHIFT = 1,\n\tparameter [ 0:0] BARREL_SHIFTER = 0,\n\tparameter [ 0:0] TWO_CYCLE_COMPARE = 0,\n\tparameter [ 0:0] TWO_CYCLE_ALU = 0,\n\tparameter [ 0:0] COMPRESSED_ISA = 0,\n\tparameter [ 0:0] CATCH_MISALIGN = 1,\n\tparameter [ 0:0] CATCH_ILLINSN = 1,\n\tparameter [ 0:0] ENABLE_PCPI = 0,\n\tparameter [ 0:0] ENABLE_MUL = 0,\n\tparameter [ 0:0] ENABLE_FAST_MUL = 0,\n\tparameter [ 0:0] ENABLE_DIV = 0,\n\tparameter [ 0:0] ENABLE_IRQ = 0,\n\tparameter [ 0:0] ENABLE_IRQ_QREGS = 1,\n\tparameter [ 0:0] ENABLE_IRQ_TIMER = 1,\n\tparameter [ 0:0] ENABLE_TRACE = 0,\n\tparameter [ 0:0] REGS_INIT_ZERO = 0,\n\tparameter [31:0] MASKED_IRQ = 32'h 0000_0000,\n\tparameter [31:0] LATCHED_IRQ = 32'h ffff_ffff,\n\tparameter [31:0] PROGADDR_RESET = 32'h 0000_0000,\n\tparameter [31:0] PROGADDR_IRQ = 32'h 0000_0010,\n\tparameter [31:0] STACKADDR = 32'h ffff_ffff\n) (\n\toutput trap,\n\n\t// Wishbone interfaces\n\tinput wb_rst_i,\n\tinput wb_clk_i,\n\n\toutput reg [31:0] wbm_adr_o,\n\toutput reg [31:0] wbm_dat_o,\n\tinput [31:0] wbm_dat_i,\n\toutput reg wbm_we_o,\n\toutput reg [3:0] wbm_sel_o,\n\toutput reg wbm_stb_o,\n\tinput wbm_ack_i,\n\toutput reg wbm_cyc_o,\n\n\t// Pico Co-Processor Interface (PCPI)\n\toutput        pcpi_valid,\n\toutput [31:0] pcpi_insn,\n\toutput [31:0] pcpi_rs1,\n\toutput [31:0] pcpi_rs2,\n\tinput         pcpi_wr,\n\tinput  [31:0] pcpi_rd,\n\tinput         pcpi_wait,\n\tinput         pcpi_ready,\n\n\t// IRQ interface\n\tinput  [31:0] irq,\n\toutput [31:0] eoi,\n\n`ifdef RISCV_FORMAL\n\toutput        rvfi_valid,\n\toutput [63:0] rvfi_order,\n\toutput [31:0] rvfi_insn,\n\toutput        rvfi_trap,\n\toutput        rvfi_halt,\n\toutput        rvfi_intr,\n\toutput [ 4:0] rvfi_rs1_addr,\n\toutput [ 4:0] rvfi_rs2_addr,\n\toutput [31:0] rvfi_rs1_rdata,\n\toutput [31:0] rvfi_rs2_rdata,\n\toutput [ 4:0] rvfi_rd_addr,\n\toutput [31:0] rvfi_rd_wdata,\n\toutput [31:0] rvfi_pc_rdata,\n\toutput [31:0] rvfi_pc_wdata,\n\toutput [31:0] rvfi_mem_addr,\n\toutput [ 3:0] rvfi_mem_rmask,\n\toutput [ 3:0] rvfi_mem_wmask,\n\toutput [31:0] rvfi_mem_rdata,\n\toutput [31:0] rvfi_mem_wdata,\n`endif\n\n\t// Trace Interface\n\toutput        trace_valid,\n\toutput [35:0] trace_data,\n\n\toutput mem_instr\n);\n\twire        mem_valid;\n\twire [31:0] mem_addr;\n\twire [31:0] mem_wdata;\n\twire [ 3:0] mem_wstrb;\n\treg         mem_ready;\n\treg [31:0] mem_rdata;\n\n\twire clk;\n\twire resetn;\n\n\tassign clk = wb_clk_i;\n\tassign resetn = ~wb_rst_i;\n\n\tpicorv32 #(\n\t\t.ENABLE_COUNTERS     (ENABLE_COUNTERS     ),\n\t\t.ENABLE_COUNTERS64   (ENABLE_COUNTERS64   ),\n\t\t.ENABLE_REGS_16_31   (ENABLE_REGS_16_31   ),\n\t\t.ENABLE_REGS_DUALPORT(ENABLE_REGS_DUALPORT),\n\t\t.TWO_STAGE_SHIFT     (TWO_STAGE_SHIFT     ),\n\t\t.BARREL_SHIFTER      (BARREL_SHIFTER      ),\n\t\t.TWO_CYCLE_COMPARE   (TWO_CYCLE_COMPARE   ),\n\t\t.TWO_CYCLE_ALU       (TWO_CYCLE_ALU       ),\n\t\t.COMPRESSED_ISA      (COMPRESSED_ISA      ),\n\t\t.CATCH_MISALIGN      (CATCH_MISALIGN      ),\n\t\t.CATCH_ILLINSN       (CATCH_ILLINSN       ),\n\t\t.ENABLE_PCPI         (ENABLE_PCPI         ),\n\t\t.ENABLE_MUL          (ENABLE_MUL          ),\n\t\t.ENABLE_FAST_MUL     (ENABLE_FAST_MUL     ),\n\t\t.ENABLE_DIV          (ENABLE_DIV          ),\n\t\t.ENABLE_IRQ          (ENABLE_IRQ          ),\n\t\t.ENABLE_IRQ_QREGS    (ENABLE_IRQ_QREGS    ),\n\t\t.ENABLE_IRQ_TIMER    (ENABLE_IRQ_TIMER    ),\n\t\t.ENABLE_TRACE        (ENABLE_TRACE        ),\n\t\t.REGS_INIT_ZERO      (REGS_INIT_ZERO      ),\n\t\t.MASKED_IRQ          (MASKED_IRQ          ),\n\t\t.LATCHED_IRQ         (LATCHED_IRQ         ),\n\t\t.PROGADDR_RESET      (PROGADDR_RESET      ),\n\t\t.PROGADDR_IRQ        (PROGADDR_IRQ        ),\n\t\t.STACKADDR           (STACKADDR           )\n\t) picorv32_core (\n\t\t.clk      (clk   ),\n\t\t.resetn   (resetn),\n\t\t.trap     (trap  ),\n\n\t\t.mem_valid(mem_valid),\n\t\t.mem_addr (mem_addr ),\n\t\t.mem_wdata(mem_wdata),\n\t\t.mem_wstrb(mem_wstrb),\n\t\t.mem_instr(mem_instr),\n\t\t.mem_ready(mem_ready),\n\t\t.mem_rdata(mem_rdata),\n\n\t\t.pcpi_valid(pcpi_valid),\n\t\t.pcpi_insn (pcpi_insn ),\n\t\t.pcpi_rs1  (pcpi_rs1  ),\n\t\t.pcpi_rs2  (pcpi_rs2  ),\n\t\t.pcpi_wr   (pcpi_wr   ),\n\t\t.pcpi_rd   (pcpi_rd   ),\n\t\t.pcpi_wait (pcpi_wait ),\n\t\t.pcpi_ready(pcpi_ready),\n\n\t\t.irq(irq),\n\t\t.eoi(eoi),\n\n`ifdef RISCV_FORMAL\n\t\t.rvfi_valid    (rvfi_valid    ),\n\t\t.rvfi_order    (rvfi_order    ),\n\t\t.rvfi_insn     (rvfi_insn     ),\n\t\t.rvfi_trap     (rvfi_trap     ),\n\t\t.rvfi_halt     (rvfi_halt     ),\n\t\t.rvfi_intr     (rvfi_intr     ),\n\t\t.rvfi_rs1_addr (rvfi_rs1_addr ),\n\t\t.rvfi_rs2_addr (rvfi_rs2_addr ),\n\t\t.rvfi_rs1_rdata(rvfi_rs1_rdata),\n\t\t.rvfi_rs2_rdata(rvfi_rs2_rdata),\n\t\t.rvfi_rd_addr  (rvfi_rd_addr  ),\n\t\t.rvfi_rd_wdata (rvfi_rd_wdata ),\n\t\t.rvfi_pc_rdata (rvfi_pc_rdata ),\n\t\t.rvfi_pc_wdata (rvfi_pc_wdata ),\n\t\t.rvfi_mem_addr (rvfi_mem_addr ),\n\t\t.rvfi_mem_rmask(rvfi_mem_rmask),\n\t\t.rvfi_mem_wmask(rvfi_mem_wmask),\n\t\t.rvfi_mem_rdata(rvfi_mem_rdata),\n\t\t.rvfi_mem_wdata(rvfi_mem_wdata),\n`endif\n\n\t\t.trace_valid(trace_valid),\n\t\t.trace_data (trace_data)\n\t);\n\n\tlocalparam IDLE = 2'b00;\n\tlocalparam WBSTART = 2'b01;\n\tlocalparam WBEND = 2'b10;\n\n\treg [1:0] state;\n\n\twire we;\n\tassign we = (mem_wstrb[0] | mem_wstrb[1] | mem_wstrb[2] | mem_wstrb[3]);\n\n\talways @(posedge wb_clk_i) begin\n\t\tif (wb_rst_i) begin\n\t\t\twbm_adr_o <= 0;\n\t\t\twbm_dat_o <= 0;\n\t\t\twbm_we_o <= 0;\n\t\t\twbm_sel_o <= 0;\n\t\t\twbm_stb_o <= 0;\n\t\t\twbm_cyc_o <= 0;\n\t\t\tstate <= IDLE;\n\t\tend else begin\n\t\t\tcase (state)\n\t\t\t\tIDLE: begin\n\t\t\t\t\tif (mem_valid) begin\n\t\t\t\t\t\twbm_adr_o <= mem_addr;\n\t\t\t\t\t\twbm_dat_o <= mem_wdata;\n\t\t\t\t\t\twbm_we_o <= we;\n\t\t\t\t\t\twbm_sel_o <= mem_wstrb;\n\n\t\t\t\t\t\twbm_stb_o <= 1'b1;\n\t\t\t\t\t\twbm_cyc_o <= 1'b1;\n\t\t\t\t\t\tstate <= WBSTART;\n\t\t\t\t\tend else begin\n\t\t\t\t\t\tmem_ready <= 1'b0;\n\n\t\t\t\t\t\twbm_stb_o <= 1'b0;\n\t\t\t\t\t\twbm_cyc_o <= 1'b0;\n\t\t\t\t\t\twbm_we_o <= 1'b0;\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\tWBSTART:begin\n\t\t\t\t\tif (wbm_ack_i) begin\n\t\t\t\t\t\tmem_rdata <= wbm_dat_i;\n\t\t\t\t\t\tmem_ready <= 1'b1;\n\n\t\t\t\t\t\tstate <= WBEND;\n\n\t\t\t\t\t\twbm_stb_o <= 1'b0;\n\t\t\t\t\t\twbm_cyc_o <= 1'b0;\n\t\t\t\t\t\twbm_we_o <= 1'b0;\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\tWBEND: begin\n\t\t\t\t\tmem_ready <= 1'b0;\n\n\t\t\t\t\tstate <= IDLE;\n\t\t\t\tend\n\t\t\t\tdefault:\n\t\t\t\t\tstate <= IDLE;\n\t\t\tendcase\n\t\tend\n\tend\nendmodule\n"
  },
  {
    "path": "docs/examples/demos/picorv32_axicheck.sby",
    "content": "[tasks]\nyices\nboolector\nz3\nabc\n\n[options]\nmode bmc\ndepth 10\n\n[engines]\nyices: smtbmc yices\nboolector: smtbmc boolector -ack\nz3: smtbmc --nomem z3\nabc: abc bmc3\n\n[script]\nread_verilog -formal -norestrict -assume-asserts picorv32.v\nread_verilog -formal axicheck.v\nprep -top testbench\n\n[files]\npicorv32.v\naxicheck.v\n\n"
  },
  {
    "path": "docs/examples/demos/up_down_counter.sby",
    "content": "[tasks]\nsuprove\navy\n\n[options]\nmode prove\n\n[engines]\nsuprove: aiger suprove\navy: aiger avy\n\n[script]\nread_verilog -formal demo.v\nprep -top top\n\n[file demo.v]\nmodule top(input clk, input up, down);\n  reg [4:0] counter = 0;\n  always @(posedge clk) begin\n    if (up && counter != 10) counter <= counter + 1;\n    if (down && counter != 0) counter <= counter - 1;\n  end\n  assert property (counter != 15);\nendmodule\n"
  },
  {
    "path": "docs/examples/dft/data_diode.sby",
    "content": "[tasks]\ndiode\ndirect\n\n[options]\nmode prove\n\ndiode: expect pass\ndirect: expect fail\n\nfst on\n\n[engines]\nsmtbmc\n\n[script]\ndiode: read -define USE_DIODE\n\nverific -sv data_diode.sv\n\nhierarchy -top top\n\n# $overwrite_tag currently requires these two passes directly after importing\n# the design. Otherwise the target signals of $overwrite_tag cannot be properly\n# resolved nor can `dft_tag -overwrite-only` itself detect this situation to\n# report it as an error.\nflatten\ndft_tag -overwrite-only\n\n# Then the design can be prepared as usual\nprep\n\n# And finally the tagging logic can be resolved, which requires converting all\n# FFs into simple D-FFs. Here, if this isn't done `dft_tag` will produce\n# warnings and tell you to run the required passes.\nasync2sync\ndffunmap\ndft_tag -tag-public\n\n# The `Unhandled cell` warnings produced by `dft_tag` mean that there is no\n# bit-precise tag propagation model for the listed cell. The cell in question\n# will still propagate tags, but `dft_tag` will use a generic model that\n# assumes all inputs can propagate to all outputs independent of the value of\n# other inputs on the same cell. For built-in logic cells this is a sound\n# over-approximation, but may produce more false-positives than a bit-precise\n# approximation would.\n\n[files]\ndata_diode.sv\n"
  },
  {
    "path": "docs/examples/dft/data_diode.sv",
    "content": "// Simple sync FIFO implementation using an extra bit in the read and write\n// pointers to distinguish the completely full and completely empty case.\nmodule fifo #(\n    DEPTH_BITS = 4,\n    WIDTH = 8\n) (\n    input wire clk,\n    input wire rst,\n\n    input wire             in_valid,\n    input wire [WIDTH-1:0] in_data,\n    output reg             in_ready,\n\n    output reg             out_valid,\n    output reg [WIDTH-1:0] out_data,\n    input wire             out_ready\n);\n\n    reg [WIDTH-1:0] buffer [1<<DEPTH_BITS];\n\n    reg [DEPTH_BITS:0] write_addr;\n    reg [DEPTH_BITS:0] read_addr;\n\n    wire in_transfer = in_valid && in_ready;\n    wire out_transfer = out_valid && out_ready;\n\n    wire [DEPTH_BITS:0] write_limit = read_addr ^ (1 << DEPTH_BITS);\n\n    assign in_ready = write_addr != write_limit;\n    assign out_valid = read_addr != write_addr;\n\n    assign out_data = buffer[read_addr[DEPTH_BITS-1:0]];\n\n    always @(posedge clk) begin\n        if (rst) begin\n            read_addr <= 0;\n            write_addr <= 0;\n        end else begin\n            if (in_transfer) begin\n                buffer[write_addr[DEPTH_BITS-1:0]] <= in_data;\n                write_addr <= write_addr + 1'b1;\n            end\n\n            if (out_transfer) begin\n                read_addr <= read_addr + 1'b1;\n            end\n        end\n    end\n\nendmodule\n\n// This module sends some data and should not be able to receive any\n// information back, even under the assumption that it can trick the other side\n// into performing unintended operations that would attempt to establish a\n// covert channel.\nmodule untrusted_module (\n    input wire clk,\n    input wire rst,\n\n    input wire en,\n\n    output reg        out_valid,\n    output reg [31:0] out_data,\n    input wire        out_ready\n);\n\n    reg [31:0] lfsr;\n\n    always @(posedge clk) begin\n        if (rst) begin\n            lfsr <= '1;\n        end else if (en && out_valid && out_ready) begin\n            lfsr <= (lfsr << 1) ^ (lfsr[31] ? 32'h04C11DB7 : 0);\n        end\n    end\n\n    assign out_valid = out_ready && en;\n    assign out_data = lfsr;\n\nendmodule\n\n// This module receives data and contains secret data. It should guard this\n// secret data even if there are bugs in this module's implementations that the\n// other side could exploit in an attempt to establish a covert channel to\n// exfiltrate the secrets.\nmodule secret_storing_module (\n    input wire clk,\n    input wire rst,\n\n    input wire en,\n    output reg [31:0] counter,\n\n    input wire        in_valid,\n    input wire [31:0] in_data,\n    output reg        in_ready\n);\n\n    reg [3:0] busy;\n\n    always @(posedge clk) begin\n        if (rst) begin\n            counter <= '0;\n            busy <= '0;\n        end else if (en) begin\n            if (busy) begin\n                busy <= busy - 4'b1;\n            end else if (in_valid && in_ready) begin\n                busy <= counter[3:0];\n                counter <= counter + in_data;\n            end\n        end\n    end\n\n    assign in_ready = en && !busy;\n\nendmodule\n\n\n// This data diode wraps a FIFO, but ensures that there is no backwards data\n// flow via the handshaking signals. Instead it limits the sender to send with\n// a fixed rate and only notifies the receiver\nmodule data_diode #(\n    DEPTH_BITS = 4,\n    WIDTH = 8,\n    RATE = 2\n) (\n    input wire clk,\n    input wire rst,\n\n    input wire             in_valid,\n    input wire [WIDTH-1:0] in_data,\n    output reg             in_ready,\n\n    output reg             out_valid,\n    output reg [WIDTH-1:0] out_data,\n    input wire             out_ready,\n\n    output reg overflow\n);\n    reg [$clog2(RATE):0] delay;\n    wire fifo_ready;\n\n    always @(posedge clk) begin\n        if (rst) begin\n            delay <= 0;\n            overflow <= 0;\n        end else begin\n            if (in_valid && in_ready) begin\n                if (!fifo_ready) begin\n                    overflow <= 1;\n                end\n                delay <= RATE - 1;\n            end else if (delay) begin\n                delay <= delay - 1'b1;\n            end\n        end\n    end\n\n    assign in_ready = !delay;\n\n    fifo #(.DEPTH_BITS(DEPTH_BITS), .WIDTH(WIDTH)) fifo (\n        .clk(clk),\n        .rst(rst),\n\n        .in_valid(in_valid),\n        .in_data(in_data),\n        .in_ready(fifo_ready),\n\n        .out_valid(out_valid),\n        .out_data(out_data),\n        .out_ready(out_ready)\n    );\n\nendmodule\n\n// The top module either connects the untrusted and secret storing modules\n// directly or via the data diode.\nmodule top(\n    input wire clk,\n    input wire rst,\n\n    input wire source_en,\n    input wire sink_en,\n\n`ifdef USE_DIODE\n    output reg overflow,\n`endif\n    output reg [31:0] counter\n\n);\n\n    wire        source_valid;\n    wire [31:0] source_data;\n    wire        source_ready;\n\n    wire        sink_valid;\n    wire [31:0] sink_data;\n    wire        sink_ready;\n\n    untrusted_module source (\n        .clk(clk),\n        .rst(rst),\n        .en(source_en),\n\n        .out_valid(source_valid),\n        .out_data(source_data),\n        .out_ready(source_ready)\n    );\n\n`ifdef USE_DIODE\n    data_diode #(.WIDTH(32)) data_diode (\n        .clk(clk),\n        .rst(rst),\n\n        .in_valid(source_valid),\n        .in_data(source_data),\n        .in_ready(source_ready),\n\n        .out_valid(sink_valid),\n        .out_data(sink_data),\n        .out_ready(sink_ready),\n\n        .overflow(overflow)\n    );\n`else\n    fifo #(.WIDTH(32)) fifo (\n        .clk(clk),\n        .rst(rst),\n\n        .in_valid(source_valid),\n        .in_data(source_data),\n        .in_ready(source_ready),\n\n        .out_valid(sink_valid),\n        .out_data(sink_data),\n        .out_ready(sink_ready)\n    );\n`endif\n\n    secret_storing_module sink (\n        .clk(clk),\n        .rst(rst),\n        .en(sink_en),\n\n        .in_valid(sink_valid),\n        .in_data(sink_data),\n        .in_ready(sink_ready),\n\n        .counter(counter)\n    );\nendmodule\n\n// We can inject a tag into signals of a module by binding a checker that\n// contains an `$overwrite_tag` item.\nchecker mark_secret(counter);\n    $overwrite_tag(\"secret\", counter);\nendchecker\n\nbind secret_storing_module mark_secret counter_is_secret(counter);\n\n// We cen inject a check for a tag by binding a checker that has an assertion\n// that uses `$get_tag`.\n//\n// When using a direct connection, this assertion will fail as there is a\n// covert channel via the FIFO's handshaking signals. When the data-diode is\n// used, we are able to prove the absance of such a covert channel.\nchecker leak_check(clk, signal);\n   secret_leaked: assert property (@(posedge clk) !$get_tag(\"secret\", signal));\nendchecker\n\nbind untrusted_module leak_check leak_check(clk, out_ready);\n\n\nchecker initial_reset(clk, rst);\n    assume property (@(posedge clk) $initstate |-> rst);\nendchecker\n\nbind top initial_reset initial_reset(clk, rst);\n"
  },
  {
    "path": "docs/examples/fifo/.gitignore",
    "content": "fifo_*/"
  },
  {
    "path": "docs/examples/fifo/Makefile",
    "content": "SUBDIR=examples/fifo\nTESTDIR=../../../tests\ninclude $(TESTDIR)/make/subdir.mk\n"
  },
  {
    "path": "docs/examples/fifo/fifo.sby",
    "content": "[tasks]\nbasic bmc\nnofullskip prove\ncover\nnoverific cover\nbasic cover : default\n\n[options]\ncover:\nmode cover\n--\nprove:\nmode prove\n--\nbmc: \nmode bmc\n--\n\n[engines]\nsmtbmc boolector\n\n[script]\nnofullskip: read -define NO_FULL_SKIP=1\nnoverific: read -noverific\nread -formal fifo.sv\nprep -top fifo\n\n[files]\nfifo.sv\n"
  },
  {
    "path": "docs/examples/fifo/fifo.sv",
    "content": "// address generator/counter\nmodule addr_gen \n#(  parameter MAX_DATA=16\n) ( input en, clk, rst,\n    output reg [3:0] addr\n);\n    initial addr <= 0;\n\n    // async reset\n    // increment address when enabled\n    always @(posedge clk or posedge rst)\n        if (rst)\n            addr <= 0;\n        else if (en) begin\n            if (addr == MAX_DATA-1)\n                addr <= 0;\n            else\n                addr <= addr + 1;\n        end\nendmodule\n\n// Define our top level fifo entity\nmodule fifo \n#(  parameter MAX_DATA=16\n) ( input wen, ren, clk, rst,\n    input [7:0] wdata,\n    output [7:0] rdata,\n    output [4:0] count,\n    output full, empty\n);\n    wire wskip, rskip;\n    reg [4:0] data_count;\n\n    // fifo storage\n    // async read, sync write\n    wire [3:0] waddr, raddr;\n    reg [7:0] data [MAX_DATA-1:0];\n    always @(posedge clk)\n        if (wen) \n            data[waddr] <= wdata;\n    assign rdata = data[raddr];\n    // end storage\n\n    // addr_gen for both write and read addresses\n    addr_gen #(.MAX_DATA(MAX_DATA))\n    fifo_writer (\n        .en     (wen || wskip),\n        .clk    (clk  ),\n        .rst    (rst),\n        .addr   (waddr)\n    );\n\n    addr_gen #(.MAX_DATA(MAX_DATA))\n    fifo_reader (\n        .en     (ren || rskip),\n        .clk    (clk  ),\n        .rst    (rst),\n        .addr   (raddr)\n    );\n\n    // status signals\n    initial data_count <= 0;\n\n    always @(posedge clk or posedge rst) begin\n        if (rst)\n            data_count <= 0;\n        else if (wen && !ren && data_count < MAX_DATA)\n            data_count <= data_count + 1;\n        else if (ren && !wen && data_count > 0)\n            data_count <= data_count - 1;\n    end\n\n    assign full  = data_count == MAX_DATA;\n    assign empty = (data_count == 0) && ~rst;\n    assign count = data_count;\n\n    // overflow protection\n`ifndef NO_FULL_SKIP\n    // write while full => overwrite oldest data, move read pointer\n    assign rskip = wen && !ren && data_count >= MAX_DATA;\n    // read while empty => read invalid data, keep write pointer in sync\n    assign wskip = ren && !wen && data_count == 0;\n`else\n    assign rskip = 0;\n    assign wskip = 0;\n`endif // NO_FULL_SKIP\n\n`ifdef FORMAL\n    // observers\n    wire [4:0] addr_diff;\n    assign addr_diff = waddr >= raddr \n                     ? waddr - raddr \n                     : waddr + MAX_DATA - raddr;\n\n    // tests\n    always @(posedge clk) begin\n        if (~rst) begin\n            // waddr and raddr can only be non zero if reset is low\n            w_nreset: cover (waddr || raddr);\n\n            // count never more than max\n            a_oflow:  assert (count <= MAX_DATA);\n            a_oflow2: assert (waddr < MAX_DATA);\n\n            // count should be equal to the difference between writer and reader address\n            a_count_diff: assert (count == addr_diff \n                               || count == MAX_DATA && addr_diff == 0);\n\n            // count should only be able to increase or decrease by 1\n            a_counts: assert (count == 0\n                           || count == $past(count)\n                           || count == $past(count) + 1\n                           || count == $past(count) - 1);\n\n            // read/write addresses can only increase (or stay the same)\n            a_raddr: assert (raddr == 0\n                          || raddr == $past(raddr)\n                          || raddr == $past(raddr + 1));\n            a_waddr: assert (waddr == 0\n                          || waddr == $past(waddr)\n                          || waddr == $past(waddr + 1));\n\n            // full and empty work as expected\n            a_full:  assert (!full || count == MAX_DATA);\n            w_full:  cover  (wen && !ren && count == MAX_DATA-1);\n            a_empty: assert (!empty || count == 0);\n            w_empty: cover  (ren && !wen && count == 1);\n\n            // reading/writing non zero values\n            w_nzero_write: cover (wen && wdata);\n            w_nzero_read:  cover (ren && rdata);\n        end else begin\n            // waddr and raddr are zero while reset is high\n            a_reset: assert (!waddr && !raddr);\n            w_reset: cover  (rst);\n\n            // outputs are zero while reset is high\n            a_zero_out: assert (!empty && !full && !count);\n        end\n    end\n\n`ifdef VERIFIC\n    // if we have verific we can also do the following additional tests\n    // read/write enables enable\n    ap_raddr2: assert property (@(posedge clk) disable iff (rst) ren |=> $changed(raddr));\n    ap_waddr2: assert property (@(posedge clk) disable iff (rst) wen |=> $changed(waddr));\n\n    // read/write needs enable UNLESS full/empty\n    ap_raddr3: assert property (@(posedge clk) disable iff (rst) !ren && !full  |=> $stable(raddr));\n    ap_waddr3: assert property (@(posedge clk) disable iff (rst) !wen && !empty |=> $stable(waddr));\n\n    // use block formatting for w_underfill so it's easier to describe in docs\n    // and is more readily comparable with the non SVA implementation\n    property write_skip;\n        @(posedge clk) disable iff (rst)\n        !wen |=> $changed(waddr);\n    endproperty\n    w_underfill: cover property (write_skip);\n\n    // look for an overfill where the value in memory changes\n    // the change in data makes certain that the value is overriden\n    let d_change = (wdata != rdata);\n    property read_skip;\n        @(posedge clk) disable iff (rst) \n        !ren && d_change |=> $changed(raddr);\n    endproperty\n    w_overfill:  cover property (read_skip);\n`else // !VERIFIC\n    // implementing w_underfill without properties\n    // can't use !$past(wen) since it will always trigger in the first cycle\n    reg past_nwen;\n    initial past_nwen <= 0;\n    always @(posedge clk) begin\n        if (rst) past_nwen <= 0;\n        if (!rst) begin\n            w_underfill: cover (past_nwen && $changed(waddr));\n            past_nwen <= !wen;\n        end\n    end\n    // end w_underfill\n\n    // w_overfill does the same, but has been separated so that w_underfill\n    // can be included in the docs more cleanly\n    reg past_nren;\n    initial past_nren <= 0;\n    always @(posedge clk) begin\n        if (rst) past_nren <= 0;\n        if (!rst) begin\n            w_overfill: cover (past_nren && $changed(raddr));\n            past_nren <= !ren;\n        end\n    end\n`endif // VERIFIC\n\n`endif // FORMAL\n\nendmodule\n"
  },
  {
    "path": "docs/examples/fifo/fifo_extra_tests.sby",
    "content": "--pycode-begin--\n# This is for our test infrastructure and not part of the example\n\n# Read fifo.sby and patch it on the fly:\nfor line in open(\"fifo.sby\"):\n    line = line.rstrip()\n\n    # change the tasks to run as tests\n    if line.endswith(\": default\"):\n        line = \"nofullskip noverific : default\"\n\n    output(line)\n\n    # add expect fail to the failing tasks\n    if line == \"[options]\":\n        output(\"nofullskip: expect fail\")\n\n--pycode-end--\n"
  },
  {
    "path": "docs/examples/fifo/golden/fifo.sby",
    "content": "[tasks]\nbasic bmc\nnofullskip prove\ncover\nnoverific cover\nbigtest cover\n\n[options]\ncover:\nmode cover\n--\nprove:\nmode prove\n--\nbmc: \nmode bmc\n--\nbigtest: depth 120\n~bigtest: depth 10\nnofullskip: expect fail\n\n[engines]\nsmtbmc boolector\n\n[script]\nnofullskip: read -define NO_FULL_SKIP=1\nnoverific: read -noverific\nread -formal fifo.sv\nbigtest: hierarchy -check -top fifo -chparam MAX_DATA 100 -chparam ADDR_BITS 7\n~bigtest: hierarchy -check -top fifo -chparam MAX_DATA 5 -chparam ADDR_BITS 3\nprep -top fifo\n\n[files]\nfifo.sv\n"
  },
  {
    "path": "docs/examples/fifo/golden/fifo.sv",
    "content": "// address generator/counter\nmodule addr_gen \n#(  parameter MAX_DATA=16,\n    parameter ADDR_BITS=5\n) ( input en, clk, rst,\n    output reg [ADDR_BITS-1:0] addr\n);\n    initial addr <= 0;\n\n    // async reset\n    // increment address when enabled\n    always @(posedge clk or posedge rst)\n        if (rst)\n            addr <= 0;\n        else if (en) begin\n            if (addr == MAX_DATA-1)\n                addr <= 0;\n            else\n                addr <= addr + 1;\n        end\nendmodule\n\n// Define our top level fifo entity\nmodule fifo \n#(  parameter MAX_DATA=16,\n    parameter ADDR_BITS=5\n) ( input wen, ren, clk, rst,\n    input [7:0] wdata,\n    output [7:0] rdata,\n    output [ADDR_BITS:0] count,\n    output full, empty\n);\n    wire wskip, rskip;\n    reg [ADDR_BITS:0] data_count;\n\n    // fifo storage\n    // async read, sync write\n    wire [ADDR_BITS-1:0] waddr, raddr;\n    reg [7:0] data [MAX_DATA-1:0];\n    always @(posedge clk)\n        if (wen) \n            data[waddr] <= wdata;\n    assign rdata = data[raddr];\n    // end storage\n\n    // addr_gen for both write and read addresses\n    addr_gen #(.MAX_DATA(MAX_DATA), .ADDR_BITS(ADDR_BITS)) \n    fifo_writer (\n        .en     (wen || wskip),\n        .clk    (clk  ),\n        .rst    (rst),\n        .addr   (waddr)\n    );\n\n    addr_gen #(.MAX_DATA(MAX_DATA), .ADDR_BITS(ADDR_BITS)) \n    fifo_reader (\n        .en     (ren || rskip),\n        .clk    (clk  ),\n        .rst    (rst),\n        .addr   (raddr)\n    );\n\n    // status signals\n    initial data_count <= 0;\n\n    always @(posedge clk or posedge rst) begin\n        if (rst)\n            data_count <= 0;\n        else if (wen && !ren && data_count < MAX_DATA)\n            data_count <= data_count + 1;\n        else if (ren && !wen && data_count > 0)\n            data_count <= data_count - 1;\n    end\n\n    assign full  = data_count == MAX_DATA;\n    assign empty = (data_count == 0) && ~rst;\n    assign count = data_count;\n\n    // overflow protection\n`ifndef NO_FULL_SKIP\n    // write while full => overwrite oldest data, move read pointer\n    assign rskip = wen && !ren && data_count >= MAX_DATA;\n    // read while empty => read invalid data, keep write pointer in sync\n    assign wskip = ren && !wen && data_count == 0;\n`else\n    assign rskip = 0;\n    assign wskip = 0;\n`endif // NO_FULL_SKIP\n\n`ifdef FORMAL\n    // observers\n    wire [ADDR_BITS:0] addr_diff;\n    assign addr_diff = waddr >= raddr \n                  ? waddr - raddr \n                  : waddr + MAX_DATA - raddr;\n\n    // tests\n    always @(posedge clk) begin\n        if (~rst) begin\n            // waddr and raddr can only be non zero if reset is low\n            w_nreset: cover (waddr || raddr);\n\n            // count never more than max\n            a_oflow:  assert (count <= MAX_DATA);\n            a_oflow2: assert (waddr < MAX_DATA);\n\n            // count should be equal to the difference between writer and reader address\n            a_count_diff: assert (count == addr_diff \n                               || count == MAX_DATA && addr_diff == 0);\n\n            // count should only be able to increase or decrease by 1\n            a_counts: assert (count == 0\n                           || count == $past(count)\n                           || count == $past(count) + 1\n                           || count == $past(count) - 1);\n\n            // read/write addresses can only increase (or stay the same)\n            a_raddr: assert (raddr == 0\n                          || raddr == $past(raddr)\n                          || raddr == $past(raddr + 1));\n            a_waddr: assert (waddr == 0\n                          || waddr == $past(waddr)\n                          || waddr == $past(waddr + 1));\n\n            // full and empty work as expected\n            a_full:  assert (!full || count == MAX_DATA);\n            w_full:  cover  (wen && !ren && count == MAX_DATA-1);\n            a_empty: assert (!empty || count == 0);\n            w_empty: cover  (ren && !wen && count == 1);\n\n            // reading/writing non zero values\n            w_nzero_write: cover (wen && wdata);\n            w_nzero_read:  cover (ren && rdata);\n        end else begin\n            // waddr and raddr are zero while reset is high\n            a_reset: assert (!waddr && !raddr);\n            w_reset: cover  (rst);\n\n            // outputs are zero while reset is high\n            a_zero_out: assert (!empty && !full && !count);\n        end\n    end\n\n`ifdef VERIFIC\n    // if we have verific we can also do the following additional tests\n    // read/write enables enable\n    ap_raddr2: assert property (@(posedge clk) disable iff (rst) ren |=> $changed(raddr));\n    ap_waddr2: assert property (@(posedge clk) disable iff (rst) wen |=> $changed(waddr));\n\n    // read/write needs enable UNLESS full/empty\n    ap_raddr3: assert property (@(posedge clk) disable iff (rst) !ren && !full  |=> $stable(raddr));\n    ap_waddr3: assert property (@(posedge clk) disable iff (rst) !wen && !empty |=> $stable(waddr));\n\n    // use block formatting for w_underfill so it's easier to describe in docs\n    // and is more readily comparable with the non SVA implementation\n    property write_skip;\n        @(posedge clk) disable iff (rst)\n        !wen |=> $changed(waddr);\n    endproperty\n    w_underfill: cover property (write_skip);\n\n    // look for an overfill where the value in memory changes\n    // the change in data makes certain that the value is overriden\n    let d_change = (wdata != rdata);\n    property read_skip;\n        @(posedge clk) disable iff (rst) \n        !ren && d_change |=> $changed(raddr);\n    endproperty\n    w_overfill:  cover property (read_skip);\n`else // !VERIFIC\n    // implementing w_underfill without properties\n    // can't use !$past(wen) since it will always trigger in the first cycle\n    reg past_nwen;\n    initial past_nwen <= 0;\n    always @(posedge clk) begin\n        if (rst) past_nwen <= 0;\n        if (!rst) begin\n            w_underfill: cover (past_nwen && $changed(waddr));\n            past_nwen <= !wen;\n        end\n    end\n    // end w_underfill\n\n    // w_overfill does the same, but has been separated so that w_underfill\n    // can be included in the docs more cleanly\n    reg past_nren;\n    initial past_nren <= 0;\n    always @(posedge clk) begin\n        if (rst) past_nren <= 0;\n        if (!rst) begin\n            w_overfill: cover (past_nren && $changed(raddr));\n            past_nren <= !ren;\n        end\n    end\n`endif // VERIFIC\n\n`endif // FORMAL\n\nendmodule\n"
  },
  {
    "path": "docs/examples/fifo/noskip.gtkw",
    "content": "[*]\n[*] GTKWave Analyzer v3.4.0 (w)1999-2022 BSI\n[*] Thu Jun 09 02:02:01 2022\n[*]\n[timestart] 0\n[size] 1000 320\n[pos] -1 -1\n*-3.253757 18 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1\n[sst_width] 246\n[signals_width] 200\n[sst_expanded] 1\n[sst_vpaned_height] 58\n@28\nfifo.clk\n@22\nfifo.data_count[4:0]\nfifo.addr_diff[4:0]\n@28\nfifo.ren\nfifo.wen\n@22\nfifo.raddr[3:0]\nfifo.waddr[3:0]\n@28\nfifo.rskip\nfifo.wskip\n[pattern_trace] 1\n[pattern_trace] 0\n"
  },
  {
    "path": "docs/examples/indinv/.gitignore",
    "content": "/prove_p0_k/\n/prove_p0_p[0123]/\n/prove_p23_p[0123]/\n"
  },
  {
    "path": "docs/examples/indinv/Makefile",
    "content": "SUBDIR=examples/indinv\nTESTDIR=../../../tests\ninclude $(TESTDIR)/make/subdir.mk\n"
  },
  {
    "path": "docs/examples/indinv/README.md",
    "content": "Proving and Using Inductive Invariants for Interval Property Checking\n=====================================================================\n\nInductive invariants are boolean functions over the design state, that\n 1. return true for every reachable state (=invariants), and\n 2. if they return true for a state then they will also return true\n    for every state reachable from the given state (=inductive)\n    \nFormally, inductive invariants are sets of states that are closed under\nthe state transition function (=inductive), and contain the entire set\nof reachable states (=invariants).\n\nA user-friendly set of features to support Inductive Invariants (and Interval\nProperty Checking) is in development. Until this is completed we recommend\nthe following technique for proving and using inductive invariants.\n\nConsider the following circuit (stripped-down [example.sv](example.sv)):\n\n```SystemVerilog\nmodule example(clk, state); \n\tinput logic clk;\n\toutput logic [4:0] state = 27;\n\n\talways_ff @(posedge clk) state <= (5'd 2 * state - 5'd 1) ^ (state & 5'd 7);\n\n\talways_comb assert (state != 0);\nendmodule\n```\n\nFor better understanding of this circuit we provide the complete state graph\nfor that example design (as generated by [example.py](example.py)):\n\n```\nThe 5-bit function f(x) produces 2 loops:\n   f = lambda x: (2*x-1) ^ (x&7)\n\n4-Element Loop:\n   31 ->- 26 ->- 17 ->-  0 ->- 31\n8 Lead-Ins:\n    0 -<-  1 -<-  2\n             `<- 18\n   17 -<- 10 -<-  7\n             `<- 23\n   26 -<- 15 -<-  8\n             `<- 24\n   31 -<- 16 -<-  9\n             `<- 25\n\n4-Element Loop:\n   28 ->- 19 ->-  6 ->- 13 ->- 28\n8 Lead-Ins:\n    6 -<-  3 -<-  4\n             `<- 20\n   13 -<- 22 -<- 11\n             `<- 27\n   19 -<- 12 -<-  5\n             `<- 21\n   28 -<- 29 -<- 14\n             `<- 30\n\nLoop Membership:\n   (31, 26, 17, 0) |***....****....****....****....*|\n   (28, 19, 6, 13) |...****....****....****....****.|\n```\n\nWe can see that there are two distinct sets of states. The assertion `state != 0` holds\nbecause the init state 27 is in the second set of states, and the zero-state is in the\nfirst set of states. Let's call the `state != 0` property `p0`:\n\n```SystemVerilog\nlet p0 = (state != 0);\n```\n\nSo `p0` is a true invariant for our circuit, but it is not an inductive invariant,\nbecause we can go from a state for which `p0` is true to a state for which it is\nfalse. Specifically there are two such states for this circuit: 1 and 17\n\n(The property `p0` is k-inductive for k=6, but for this example we are\nonly interested in proofs via 1-induction.)\n\nThe following property however would be inductive, as can be easily confirmed\nby looking up the 4 states in the state chart above.\n\n```SystemVerilog\nlet p1 = (state == 28 || state == 19 || state == 6 || state == 13);\n```\n\nOr, using more fancy SystemVerilog syntax:\n\n```SystemVerilog\nlet p1 = (state inside {28, 19, 6, 13});\n```\n\nBut `p1` is not an invariant of our circuit, as can be easily seen: The initial\nstate 27 is not one of the 4 states included in our property.\n\nWe can of course add additional states to our property until it covers the entire\npath from the initial state to state 13:\n\n```SystemVerilog\nlet p2 = (state inside {28, 19, 6, 13, 22, 27});\n```\n\nThe property `p2` is an inductive invariant. Actually, it is an exact\ndescription of the reachable state space. (As such it is by definition an\ninvariant of the circuit, and inductive.)\n\nHowever, in real-world verification problems we can't usually enumerate states\nlike this. Instead, we need to find more generic functions that are inductive\ninvariants of the circuit. In almost all cases those will be functions that\nover-estimate the set of reachable states, instead of describing it exact.\n\nOne such function for the above design would be the following property.\n\n```SystemVerilog\nlet p3 = (state[0] & state[1]) ^ state[2];\n```\n\nThe SBY file [prove_p23.sby](prove_p23.sby) demonstrates how to prove that `p2`\nand `p3` are inductive invariants. (Trying to prove `p0` or `p1` in that manner\nfails, as they are not inductive invariants.)\n\nAnd finally [prove_p0.sby](prove_p0.sby) demonstrates how to prove the original\nproperty `p0`, using the inductive invariants we found to strengthen the proof.\n"
  },
  {
    "path": "docs/examples/indinv/example.py",
    "content": "from collections import defaultdict\nimport inspect\n\nN = 32\nf = lambda x: (2*x-1) ^ (x&7)\n\ntable = [f(i) & (N-1) for i in range(N)]\nrtable = [table.count(i) for i in range(N)]\n\ndef getPath(v):\n    if table[v] is None:\n        return [v]\n    bak = table[v]\n    table[v] = None\n    r = [v] + getPath(bak)\n    table[v] = bak\n    return r\n\ndef getPaths():\n    visited = set()\n    paths = list()\n    for i in range(N):\n        if rtable[i] == 0:\n            paths.append(getPath(i))\n    for path in paths:\n        for i in path:\n            visited.add(i)\n    for i in range(N):\n        if i not in visited:\n            paths.append(getPath(i))\n            for j in paths[-1]:\n                visited.add(j)\n    return paths\n\npathsByLidx = defaultdict(set)\nloopsByIdx = dict()\nloopsByLidx = dict()\n\nfor path in getPaths():\n    i = path.index(path[-1])+1\n    head, loop, lidx = tuple(path[:i]), tuple(path[i:]), max(path[i:])\n    pathsByLidx[lidx].add((head, loop))\n\nprint()\nprint(\"The %d-bit function f(x) produces %d loops:\" % (N.bit_length()-1, len(pathsByLidx)))\nprint(\"  \", inspect.getsource(f).strip())\n\nfor lidx, paths in pathsByLidx.items():\n    loop = None\n    for path in paths:\n        for i in path[0] + path[1]:\n            loopsByIdx[i] = lidx\n        if loop is None or path[1][0] > loop[0]:\n            loop = path[1]\n\n    loopsByLidx[lidx] = loop\n    \n    print()\n    print(\"%d-Element Loop:\" % len(loop))\n    print(\"  \", \" ->- \".join([\"%2d\" % i for i in loop + (loop[0],)]))\n\n    lines = []\n    lastPath = []\n    for path in sorted([tuple(reversed(p[0])) for p in paths]):\n        line = \"\"\n        for i in range(len(path)):\n            if i < len(lastPath) and lastPath[i] == path[i]:\n                line += \" %s   \" % (\" \" if i == 0 else \"|  \")\n            else:\n                line += \" %s %2d\" % (\" \" if i == 0 else \"`<-\" if len(lastPath) else \"-<-\", path[i])\n                lastPath = []\n        lastPath = path\n        lines.append(line)\n\n    for i in range(len(lines)-1, -1, -1):\n        line, nextline = list(lines[i]), \"\" if i == len(lines)-1 else lines[i+1]\n        if len(nextline) < len(line): nextline = nextline.ljust(len(line))\n\n        for k in range(len(line)):\n            if line[k] == \"|\" and nextline[k] in \" -\":\n                line[k] = \" \"\n\n        lines[i] = \"\".join(line)\n\n    print(\"%d Lead-Ins:\" % len(lines))\n    for line in lines:\n        print(line)\n\nprint()\nprint(\"Loop Membership:\")\nfor lidx in pathsByLidx:\n    print(\"%18s |\" % (loopsByLidx[lidx],), end=\"\")\n    for i in range(N):\n        print(\"*\" if loopsByIdx[i] == lidx else \".\", end=\"\")\n    print(\"|\")\nprint()\n"
  },
  {
    "path": "docs/examples/indinv/example.sv",
    "content": "module example(clk, state); \n\tinput logic clk;\n\toutput logic [4:0] state = 27;\n\n\talways_ff @(posedge clk) state <= (5'd 2 * state - 5'd 1) ^ (state & 5'd 7);\n\n\tlet p0 = (state != 0);\n\tlet p1 = (state inside {28, 19, 6, 13});\n\tlet p2 = (state inside {28, 19, 6, 13, 22, 27});\n\tlet p3 = (state[0] & state[1]) ^ state[2];\n\n`ifdef ASSERT_PX\n\talways_comb assert (`ASSERT_PX);\n`endif\n`ifdef ASSUME_PX\n\talways_comb assume (`ASSUME_PX);\n`endif\nendmodule\n"
  },
  {
    "path": "docs/examples/indinv/prove_p0.sby",
    "content": "[tasks]\np0 p1 p2 p3 k :\np2 p3 k : default\n\n[options]\nmode prove\nk:  depth 6\n~k: depth 1\n\n[engines]\nsmtbmc\n\n[script]\nread -verific\nread -define ASSERT_PX=p0\np0: read -define ASSUME_PX=p0\np1: read -define ASSUME_PX=p1\np2: read -define ASSUME_PX=p2\np3: read -define ASSUME_PX=p3\nread -sv example.sv\nprep -top example\n\n[files]\nexample.sv\n"
  },
  {
    "path": "docs/examples/indinv/prove_p23.sby",
    "content": "[tasks]\np0 unknown\np1 failing\np2 p3 : default\n\n[options]\nmode prove\ndepth 1\nunknown: expect unknown\nfailing: expect fail\n\n[engines]\nsmtbmc\n\n[script]\nread -verific\np0: read -define ASSERT_PX=p0\np1: read -define ASSERT_PX=p1\np2: read -define ASSERT_PX=p2\np3: read -define ASSERT_PX=p3\nread -sv example.sv\nprep -top example\n\n[files]\nexample.sv\n"
  },
  {
    "path": "docs/examples/multiclk/.gitignore",
    "content": "/dpmem/\n"
  },
  {
    "path": "docs/examples/multiclk/Makefile",
    "content": "SUBDIR=examples/multiclk\nTESTDIR=../../../tests\ninclude $(TESTDIR)/make/subdir.mk\n"
  },
  {
    "path": "docs/examples/multiclk/dpmem.sby",
    "content": "[options]\nmode bmc\ndepth 15\nmulticlock on\n\n[engines]\nsmtbmc\n\n[script]\nread -formal dpmem.sv\nprep -top top\nchformal -early -assume\n\n[files]\ndpmem.sv\n"
  },
  {
    "path": "docs/examples/multiclk/dpmem.sv",
    "content": "module dpmem (\n\tinput            rc,\n\tinput      [3:0] ra,\n\toutput reg [3:0] rd,\n\n\tinput            wc,\n\tinput            we,\n\tinput      [3:0] wa,\n\tinput      [3:0] wd\n);\n\treg [3:0] mem [0:15];\n\n\talways @(posedge rc) begin\n\t\trd <= mem[ra];\n\tend\n\n\talways @(posedge wc) begin\n\t\tif (we) mem[wa] <= wd;\n\tend\nendmodule\n\nmodule top (\n\tinput            rc,\n\tinput      [3:0] ra,\n\toutput     [3:0] rd,\n\n\tinput            wc,\n\tinput            we,\n\tinput      [3:0] wa,\n\tinput      [3:0] wd\n);\n\tdpmem uut (\n\t\t.rc(rc),\n\t\t.ra(ra),\n\t\t.rd(rd),\n\t\t.wc(wc),\n\t\t.we(we),\n\t\t.wa(wa),\n\t\t.wd(wd)\n\t);\n\n\treg shadow_valid = 0;\n\treg [3:0] shadow_data;\n\t(* anyconst *) reg [3:0] shadow_addr;\n\n\treg init = 1;\n\t(* gclk *) reg gclk;\n\n\talways @(posedge gclk) begin\n\t\tif (!init) begin\n\t\t\tassume ($stable(rc) || $stable(wc));\n\n\t\t\tif ($rose(rc) && shadow_valid && shadow_addr == $past(ra)) begin\n\t\t\t\tassert (shadow_data == rd);\n\t\t\tend\n\n\t\t\tif ($rose(wc) && $past(we) && shadow_addr == $past(wa)) begin\n\t\t\t\tshadow_data <= $past(wd);\n\t\t\t\tshadow_valid <= 1;\n\t\t\tend\n\t\tend\n\n\t\tinit <= 0;\n\tend\nendmodule\n"
  },
  {
    "path": "docs/examples/puzzles/.gitignore",
    "content": "/pour_853_to_4\n/wolf_goat_cabbage\n/primegen_primegen\n/primegen_primes_pass\n/primegen_primes_fail\n/djb2hash\n"
  },
  {
    "path": "docs/examples/puzzles/Makefile",
    "content": "SUBDIR=examples/puzzles\nTESTDIR=../../../tests\ninclude $(TESTDIR)/make/subdir.mk\n"
  },
  {
    "path": "docs/examples/puzzles/djb2hash.sby",
    "content": "[options]\nmode bmc\nexpect fail\n\n[engines]\nsmtbmc yices\n\n[script]\nread -noverific\nread -sv djb2hash.sv\nprep -top djb2hash\n\n[files]\ndjb2hash.sv\n"
  },
  {
    "path": "docs/examples/puzzles/djb2hash.sv",
    "content": "// find a hash collision for DJB2 hash where it visits the same state twice\nmodule djb2hash (input clock);\n\t(* keep *) rand const reg [31:0] magic;\n\t(* keep *) rand reg [7:0] inputval;\n\t(* keep *) reg [31:0] state = 5381;\n\t(* keep *) integer cnt = 0;\n\n\talways @(posedge clock) begin\n\t\tstate <= ((state << 5) + state) ^ inputval;\n\t\tif (state == magic) cnt <= cnt + 1;\n\t\tassert (cnt < 2);\n\tend\nendmodule\n"
  },
  {
    "path": "docs/examples/puzzles/pour_853_to_4.sby",
    "content": "[options]\nmode cover\ndepth 100\n\n[engines]\nsmtbmc\n\n[script]\nread -formal pour_853_to_4.sv\nprep -top pour_853_to_4\n\n[files]\npour_853_to_4.sv\n"
  },
  {
    "path": "docs/examples/puzzles/pour_853_to_4.sv",
    "content": "// You are given an 8 litre bucket of water, and two empty buckets which can\n// contain 5 and 3 litre respectively. You are required to divide the water into\n// two by pouring water between buckets (that is, to end up with 4 litre in the 8\n// litre bucket, and 4 litre in the 5 litre bucket).\n//\n// Inspired by:\n// https://github.com/DennisYurichev/random_notes/blob/master/Z3/pour.py\n\nmodule pour_853_to_4 (\n\tinput clk,\n\tinput [1:0] src, dst\n);\n\treg [3:0] state [0:2];\n\treg [3:0] max_amount [0:2];\n\treg [3:0] xfer_amount;\n\n\tinitial begin\n\t\tstate[0] = 8;\n\t\tstate[1] = 0;\n\t\tstate[2] = 0;\n\n\t\tmax_amount[0] = 8;\n\t\tmax_amount[1] = 5;\n\t\tmax_amount[2] = 3;\n\tend\n\n\talways @* begin\n\t\tassume (src < 3);\n\t\tassume (dst < 3);\n\t\tassume (src != dst);\n\n\t\tif (state[src] > (max_amount[dst] - state[dst]))\n\t\t\txfer_amount = max_amount[dst] - state[dst];\n\t\telse\n\t\t\txfer_amount = state[src];\n\n\t\tcover (state[0] == 4 && state[1] == 4);\n\tend\n\n\talways @(posedge clk) begin\n\t\tstate[src] <= state[src] - xfer_amount;\n\t\tstate[dst] <= state[dst] + xfer_amount;\n\tend\nendmodule\n"
  },
  {
    "path": "docs/examples/puzzles/primegen.sby",
    "content": "[tasks]\nprimegen\nprimes_fail\nprimes_pass\n\n[options]\nmode cover\ndepth 1\nprimes_fail: expect fail,error\n\n[engines]\nsmtbmc --dumpsmt2 --progress --stbv z3\n\n[script]\nread -noverific\nread -formal primegen.sv\nprimes_fail: chparam -set offset 7 primes\nprimegen: prep -top primegen\n~primegen: prep -top primes\n\n[files]\nprimegen.sv\n"
  },
  {
    "path": "docs/examples/puzzles/primegen.sv",
    "content": "module primegen;\n\t(* anyconst *) reg [31:0] prime;\n\t(* allconst *) reg [15:0] factor;\n\n\talways @* begin\n\t\tif (1 < factor && factor < prime)\n\t\t\tassume ((prime % factor) != 0);\n\t\tassume (prime > 1000000000);\n\t\tcover (1);\n\tend\nendmodule\n\nmodule primes;\n\tparameter [8:0] offset = 500;\n\t(* anyconst *) reg [8:0] prime1;\n\twire [9:0] prime2 = prime1 + offset;\n\t(* allconst *) reg [4:0] factor;\n\n\talways @* begin\n\t\tif (1 < factor && factor < prime1)\n\t\t\tassume ((prime1 % factor) != 0);\n\t\tif (1 < factor && factor < prime2)\n\t\t\tassume ((prime2 % factor) != 0);\n\t\tassume (1 < prime1);\n\t\tcover (1);\n\tend\nendmodule\n"
  },
  {
    "path": "docs/examples/puzzles/wolf_goat_cabbage.sby",
    "content": "[options]\nmode cover\ndepth 100\n\n[engines]\nsmtbmc\n\n[script]\nread -formal wolf_goat_cabbage.sv\nprep -top wolf_goat_cabbage\n\n[files]\nwolf_goat_cabbage.sv\n"
  },
  {
    "path": "docs/examples/puzzles/wolf_goat_cabbage.sv",
    "content": "// A person needs to cross a river with a wolf, a goat and a cabbage. Their boat is only large\n// enough to carry themselves and one of their three possessions, so they must transport these\n// items one at a time. However, if they leave the wolf and the goat together unattended,\n// then the wolf will eat the goat; similarly, if they leave the goat and the cabbage together\n// unattended, then the goat will eat the cabbage. How can the person get across safely with\n// their three items?\n\nmodule wolf_goat_cabbage (input clk, input w, g, c);\n\t// everyone starts at the 1st river bank\n\treg bank_w = 0; // wolf\n\treg bank_g = 0; // goat\n\treg bank_c = 0; // cabbage\n\treg bank_p = 0; // person\n\n\talways @(posedge clk) begin\n\t\t// person travels and takes the selected item with them\n\t\tbank_p <= !bank_p;\n\t\tif (w && (bank_w == bank_p)) bank_w <= !bank_p;\n\t\tif (g && (bank_g == bank_p)) bank_g <= !bank_p;\n\t\tif (c && (bank_c == bank_p)) bank_c <= !bank_p;\n\n\t\t// maximum one of the control signals must be high\n\t\tassume (w+g+c <= 1);\n\n\t\t// we want wolf, goat, and cabbage on the 2nd river bank\n\t\tcover(bank_w == 1 && bank_g == 1 && bank_c == 1);\n\n\t\t// don't leave wolf and goat unattended\n\t\tif (bank_w != bank_p) begin\n\t\t\tassume (bank_w != bank_g);\n\t\tend\n\n\t\t// don't leave goat and cabbage unattended\n\t\tif (bank_g != bank_p) begin\n\t\t\tassume (bank_g != bank_c);\n\t\tend\n\tend\nendmodule\n"
  },
  {
    "path": "docs/examples/quickstart/.gitignore",
    "content": "demo\nmemory\nprove\ncover\n"
  },
  {
    "path": "docs/examples/quickstart/Makefile",
    "content": "SUBDIR=examples/quickstart\nTESTDIR=../../../tests\ninclude $(TESTDIR)/make/subdir.mk\n"
  },
  {
    "path": "docs/examples/quickstart/cover.sby",
    "content": "[options]\nmode cover\n\n[engines]\nsmtbmc\n\n[script]\nread -formal cover.sv\nprep -top top\n\n[files]\ncover.sv\n"
  },
  {
    "path": "docs/examples/quickstart/cover.sv",
    "content": "module top (\n\tinput clk,\n\tinput [7:0] din\n);\n\treg [31:0] state = 0;\n\n\talways @(posedge clk) begin\n\t\tstate <= ((state << 5) + state) ^ din;\n\tend\n\n`ifdef FORMAL\n\talways @(posedge clk) begin\n\t\tcover (state == 'd 12345678);\n\t\tcover (state == 'h 12345678);\n\tend\n`endif\nendmodule\n"
  },
  {
    "path": "docs/examples/quickstart/demo.sby",
    "content": "[options]\nmode bmc\ndepth 100\n\n[engines]\nsmtbmc\n\n[script]\nread -formal demo.sv\nprep -top demo\n\n[files]\ndemo.sv\n"
  },
  {
    "path": "docs/examples/quickstart/demo.sv",
    "content": "module demo (\n  input clk,\n  output reg [5:0] counter\n);\n  initial counter = 0;\n\n  always @(posedge clk) begin\n    if (counter == 15)\n      counter <= 0;\n    else\n      counter <= counter + 1;\n  end\n\n`ifdef FORMAL\n  always @(posedge clk) begin\n    assert (counter < 32);\n  end\n`endif\nendmodule\n"
  },
  {
    "path": "docs/examples/quickstart/memory.sby",
    "content": "[options]\nmode bmc\ndepth 10\nexpect fail\n\n[engines]\nsmtbmc boolector\n\n[script]\nread -formal memory.sv\nprep -top testbench\n\n[files]\nmemory.sv\n"
  },
  {
    "path": "docs/examples/quickstart/memory.sv",
    "content": "module testbench (\n  input clk, wen,\n  input [9:0] addr,\n  input [7:0] wdata,\n  output [7:0] rdata\n);\n  memory uut (\n    .clk  (clk  ),\n    .wen  (wen  ),\n    .addr (addr ),\n    .wdata(wdata),\n    .rdata(rdata)\n  );\n\n  (* anyconst *) reg [9:0] test_addr;\n  reg test_data_valid = 0;\n  reg [7:0] test_data;\n\n  always @(posedge clk) begin\n    if (addr == test_addr) begin\n      if (wen) begin\n        test_data <= wdata;\n\ttest_data_valid <= 1;\n      end\n      if (test_data_valid) begin\n        assert(test_data == rdata);\n      end\n    end\n  end\nendmodule\n\nmodule memory (\n  input clk, wen,\n  input [9:0] addr,\n  input [7:0] wdata,\n  output [7:0] rdata\n);\n  reg [7:0] bank0 [0:255];\n  reg [7:0] bank1 [0:255];\n  reg [7:0] bank2 [0:255];\n  reg [7:0] bank3 [0:255];\n\n  wire [1:0] mem_sel = addr[9:8];\n  wire [7:0] mem_addr = addr[7:0];\n\n  always @(posedge clk) begin\n    case (mem_sel)\n      0: if (wen) bank0[mem_addr] <= wdata;\n      1: if (wen) bank1[mem_addr] <= wdata;\n      2: if (wen) bank1[mem_addr] <= wdata;  // BUG: Should assign to bank2\n      3: if (wen) bank3[mem_addr] <= wdata;\n    endcase\n  end\n\n  assign rdata =\n    mem_sel == 0 ? bank0[mem_addr] :\n    mem_sel == 1 ? bank1[mem_addr] :\n    mem_sel == 2 ? bank2[mem_addr] :\n    mem_sel == 3 ? bank3[mem_addr] : 'bx;\nendmodule\n"
  },
  {
    "path": "docs/examples/quickstart/prove.sby",
    "content": "[options]\nmode prove\n\n[engines]\nsmtbmc\n\n[script]\nread -formal prove.sv\nprep -top testbench\n\n[files]\nprove.sv\n"
  },
  {
    "path": "docs/examples/quickstart/prove.sv",
    "content": "module testbench (\n  input clk,\n  input reset,\n  input [7:0] din,\n  output reg [7:0] dout\n);\n  demo uut (\n    .clk  (clk  ),\n    .reset(reset),\n    .din  (din  ),\n    .dout (dout )\n  );\n\n  reg init = 1;\n  always @(posedge clk) begin\n    if (init) assume (reset);\n    if (!reset) assert (!dout[1:0]);\n    init <= 0;\n  end\nendmodule\n\nmodule demo (\n  input clk,\n  input reset,\n  input [7:0] din,\n  output reg [7:0] dout\n);\n  reg [7:0] buffer;\n  reg [1:0] state;\n\n  always @(posedge clk) begin\n    if (reset) begin\n      dout <= 0;\n      state <= 0;\n    end else\n    case (state)\n      0: begin\n        buffer <= din;\n\tstate <= 1;\n      end\n      1: begin\n        if (buffer[1:0])\n\t  buffer <= buffer + 1;\n\telse\n\t  state <= 2;\n      end\n      2: begin\n        dout <= dout + buffer;\n\tstate <= 0;\n      end\n    endcase\n  end\nendmodule\n"
  },
  {
    "path": "docs/examples/tristate/Makefile",
    "content": "SUBDIR=examples/tristate\nTESTDIR=../../../tests\ninclude $(TESTDIR)/make/subdir.mk\n"
  },
  {
    "path": "docs/examples/tristate/README.md",
    "content": "# Tristate demo\n\nRun \n\n    sby -f tristate.sby pass\n\nto run the pass task. This uses the top module that exclusively enables each of the submodules.\n\nRun \n\n    sby -f tristate.sby fail\n\nto run the fail task. This uses the top module that allows submodule to independently enable its tristate outputs.\n"
  },
  {
    "path": "docs/examples/tristate/tristate.sby",
    "content": "[tasks]\npass\nfail\n\n[options]\nfail: expect fail\nmode prove\ndepth 5\n\n[engines]\nsmtbmc\n\n[script]\nread -sv tristates.v\npass: prep -top top_pass\nfail: prep -top top_fail\nflatten; tribuf -formal\n\n[files]\ntristates.v\n"
  },
  {
    "path": "docs/examples/tristate/tristates.v",
    "content": "`default_nettype none\nmodule module1 (input wire active, output wire tri_out);\n    assign tri_out = active ? 1'b0 : 1'bz;\nendmodule\n\nmodule module2 (input wire active, output wire tri_out);\n    assign tri_out = active ? 1'b0 : 1'bz;\nendmodule\n\nmodule top_pass (input wire clk, input wire active1, output wire out);\n    module1 module1 (.active(active1), .tri_out(out));\n    module2 module2 (.active(!active1), .tri_out(out));\nendmodule\n\nmodule top_fail (input wire clk, input wire active1, input wire active2, output wire out);\n    module1 module1 (.active(active1), .tri_out(out));\n    module2 module2 (.active(active2), .tri_out(out));\nendmodule\n"
  },
  {
    "path": "docs/examples/vhd/.gitignore",
    "content": "formal_bind*/"
  },
  {
    "path": "docs/examples/vhd/formal_bind.sby",
    "content": "[tasks]\nbmc\ncover\n\n[options]\nbmc: mode bmc\ncover: mode cover\ndepth 16\n\n[engines]\nsmtbmc bitwuzla\n\n[script]\nverific -vhdl updowncount.vhd\nverific -sv formal_bind.sv\nprep -top updowncount\n\n[files]\nupdowncount.vhd\nformal_bind.sv\n"
  },
  {
    "path": "docs/examples/vhd/formal_bind.sv",
    "content": "module formal_bind(input clk, rst, up, down, [3:0] count);\n\n    initial assume(rst);\n\n    assert property(@(posedge clk) count != 4'd15);\n    cover property(@(posedge clk) count == 4'd10);\n\nendmodule\n\nbind updowncount formal_bind fb_inst(.*);\n"
  },
  {
    "path": "docs/examples/vhd/updowncount.vhd",
    "content": "library IEEE;\nuse IEEE.STD_LOGIC_1164.ALL;\nuse IEEE.STD_LOGIC_ARITH.all;\nuse IEEE.STD_LOGIC_UNSIGNED.all;\n\nentity updowncount is\n    port (\n        rst, clk : in std_logic ;\n        up, down : in std_logic ;\n        o: out std_logic_vector(0 to 3)\n    );\nend updowncount;\n\narchitecture rtl of updowncount is\n    signal count : std_logic_vector(0 to 3) := \"0000\";\n    begin\n        process(clk)\n        begin\n            if (rising_edge(clk)) then\n                if (rst = '1') then\n                    count <= \"0000\";\n                else\n                    if (up = '1' and count <= \"1010\") then\n                        count <= count + \"0001\";\n                    end if;\n                    if (down = '1' and count > \"0000\") then\n                        count <= count - \"0001\";\n                    end if;\n                end if;\n            end if;\n        end process;\n    o <= count;\nend rtl;\n"
  },
  {
    "path": "docs/source/autotune.rst",
    "content": "Autotune: Automatic Engine Selection\n====================================\n\nSelecting the best performing engine for a given verification task often\nrequires some amount of trial and error. To reduce the manual work required for\nthis, sby offers the ``--autotune`` option. This takes an ``.sby`` file and\nruns it using engines and engine configurations. At the end it produces a\nreport listing the fastest engines among these candidates.\n\nUsing Autotune\n--------------\n\nTo run autotune, you can add the ``--autotune`` option to your usual sby\ninvocation. For example, if you usually run ``sby demo.sby`` you would run\n``sby --autotune demo.sby`` instead. When the ``.sby`` file contains multiple\ntasks, autotune is run for each task independently. As without ``--autotune``,\nit is possible to specify which tasks to run on the command line.\n\nAutotune runs without requiring further interaction, and will eventually print a\nlist of engine configurations and their respective solving times. To\npermanently use an engine configuration you can copy it from the\n``sby --autotune`` output into the ``[engines]`` section of your ``.sby`` file.\n\nExample\n^^^^^^^\n\nThe Sby repository contains a `small example`_ in the ``docs/examples/autotune``\ndirectory.\n\nThe ``divider.sby`` file contains the following ``[engines]`` section:\n\n.. code-block:: sby\n\n   [engines]\n   smtbmc\n\nWe notice that running ``sby -f divider.sby medium`` takes a long time and want\nto see if another engine would speed things up, so we run\n``sby --autotune -f divider.sby medium``. After a few minutes this prints:\n\n.. code-block:: text\n\n   SBY [divider_medium] finished engines:\n   SBY [divider_medium]   #4: engine_7: smtbmc --nopresat bitwuzla -- --noincr (32 seconds, status=PASS)\n   SBY [divider_medium]   #3: engine_2: smtbmc boolector -- --noincr (32 seconds, status=PASS)\n   SBY [divider_medium]   #2: engine_3: smtbmc --nopresat boolector -- --noincr (32 seconds, status=PASS)\n   SBY [divider_medium]   #1: engine_6: smtbmc bitwuzla -- --noincr (31 seconds, status=PASS)\n   SBY [divider_medium] DONE (AUTOTUNED, rc=0)\n\nThis tells us that for the ``medium`` task, the best engine choice (#1) is\n``smtbmc bitwuzla -- --noincr``. To use this engine by default we can change\nthe ``[engines]`` section of ``divider.sby`` to:\n\n.. code-block:: sby\n\n   [engines]\n   smtbmc bitwuzla -- --noincr\n\n.. _`small example`: https://github.com/YosysHQ/sby/tree/master/docs/examples/autotune\n\nAutotune Log Output\n-------------------\n\nThe log output in ``--autotune`` mode differs from the usual sby log output.\n\nIt also starts with preparing the design (this includes running the user\nprovided ``[script]``) so it can be passed to the solvers. This is only done\nonce and will be reused to run every candidate engine.\n\n.. code-block:: text\n\n   SBY [demo] model 'base': preparing now...\n   SBY [demo] base: starting process \"cd demo/src; yosys -ql ../model/design.log ../model/design.ys\"\n   SBY [demo] base: finished (returncode=0)\n   SBY [demo] prepared model 'base'\n\nThis is followed by selecting the engine candidates to run. The design\nand sby configuration are analyzed to skip engines that are not compatible or\nunlikely to work well. When an engine is skipped due to a recommendation, a\ncorresponding log message is displayed as well as the total number of\ncandidates to try:\n\n.. code-block:: text\n\n   SBY [demo] checking more than 20 timesteps (100), disabling nonincremental smtbmc\n   SBY [demo] testing 16 engine configurations...\n\nAfter this, the candidate engine configurations are started. Depending on the\nconfiguration, engines can run in parallel. The engine output itself is not\nlogged to stdout when running autotune, so you will only see messages about\nstarting an engine:\n\n.. code-block:: text\n\n   SBY [demo] engine_1 (smtbmc --nopresat boolector): starting... (14 configurations pending)\n   SBY [demo] engine_2 (smtbmc bitwuzla): starting... (13 configurations pending)\n   SBY [demo] engine_3 (smtbmc --nopresat bitwuzla): starting... (12 configurations pending)\n   ...\n\nThe engine log that would normally be printed is instead redirected to files\nnamed ``engine_*_autotune.txt`` within sby's working directory.\n\nTo run an engine, further preparation steps may be necessary. These are cached\nand will be reused for every engine requiring them, while properly accounting\nfor the required prepration time. Below is an example of the log output\nproduced by such a preparation step. Note that this runs in parallel, so it may\nbe interspersed with other log output.\n\n.. code-block:: text\n\n   SBY [demo] model 'smt2': preparing now...\n   SBY [demo] smt2: starting process \"cd demo/model; yosys -ql design_smt2.log design_smt2.ys\"\n   ...\n   SBY [demo] smt2: finished (returncode=0)\n   SBY [demo] prepared model 'smt2'\n\nWhenever an engine finishes, a log message is printed:\n\n.. code-block:: text\n\n   SBY [demo] engine_4 (smtbmc --unroll yices): succeeded (status=PASS)\n   SBY [demo] engine_4 (smtbmc --unroll yices): took 30 seconds (first engine to finish)\n\nWhen an engine takes longer than the current hard timeout, it is stopped:\n\n.. code-block:: text\n\n   SBY [demo] engine_2 (smtbmc bitwuzla): timeout (150 seconds)\n\nDepending on the configuration, autotune will also stop an engine earlier when\nreaching a soft timeout. If no other engine finishes in less\ntime, the engine will be retried later with a longer soft timeout:\n\n.. code-block:: text\n\n   SBY [demo] engine_0 (smtbmc boolector): timeout (60 seconds, will be retried if necessary)\n\n\nFinally, a summary of all finished engines is printed, sorted by\ntheir solving time:\n\n.. code-block:: text\n\n   SBY [demo] finished engines:\n   SBY [demo]   #3: engine_1: smtbmc --nopresat boolector (52 seconds, status=PASS)\n   SBY [demo]   #2: engine_5: smtbmc --nopresat --unroll yices (41 seconds, status=PASS)\n   SBY [demo]   #1: engine_4: smtbmc --unroll yices (30 seconds, status=PASS)\n   SBY [demo] DONE (AUTOTUNED, rc=0)\n\nIf any tried engine encounters an error or produces an unexpected result,\nautotune will also output a list of failed engines. Note that when the sby file\ndoes not contain the ``expect`` option, autotune defaults to\n``expect pass,fail`` to simplify running autotune on a verification task with a\ncurrently unknown outcome.\n\nConfiguring Autotune\n--------------------\n\nAutotune can be configured by adding an ``[autotune]`` section to the ``.sby``\nfile. Each line in that section has the form ``option_name value``, the\npossible options and their supported values are described below. In addition,\nthe ``--autotune-config`` command line option can be used to specify a file\ncontaining further autotune options, using the same syntax. When both are used,\nthe command line option takes precedence. This makes it easy to run autotune\nwith existing ``.sby`` files without having to modify them.\n\nAutotune Options\n----------------\n\n+--------------------+------------------------------------------------------+\n| Autotune Option    | Description                                          |\n+====================+======================================================+\n| ``timeout``        | Set a different timeout value (in seconds) used only |\n|                    | for autotune. The value ``none`` can be used to      |\n|                    | disable the timeout. Default: uses the non-autotune  |\n|                    | timeout option.                                      |\n+--------------------+------------------------------------------------------+\n| ``soft_timeout``   | Initial timeout value (in seconds) used to interrupt |\n|                    | a candidate engine when other candidates are         |\n|                    | pending. Increased every time a candidate is retried |\n|                    | to ensure progress. Default: ``60``                  |\n+--------------------+------------------------------------------------------+\n| ``wait``           | Additional time to wait past the time taken by the   |\n|                    | fastest finished engine candidate so far. Can be an  |\n|                    | absolute time in seconds, a percentage of the        |\n|                    | fastest candidate or a sum of both.                  |\n|                    | Default: ``50%+10``                                  |\n+--------------------+------------------------------------------------------+\n| ``parallel``       | Maximal number of engine candidates to try in        |\n|                    | parallel. When set to ``auto``, the number of        |\n|                    | available CPUs is used. Default: ``auto``            |\n+--------------------+------------------------------------------------------+\n| ``presat``         | Filter candidates by whether they perform a presat   |\n|                    | check. Values ``on``, ``off``, ``any``.              |\n|                    | Default: ``any``                                     |\n+--------------------+------------------------------------------------------+\n| ``incr``           | Filter candidates by whether they use incremental    |\n|                    | solving (when applicable). Values ``on``, ``off``,   |\n|                    | ``any``, ``auto`` (see next option).                 |\n|                    | Default: ``auto``                                    |\n+--------------------+------------------------------------------------------+\n| ``incr_threshold`` | Number of timesteps required to only consider        |\n|                    | incremental solving when ``incr`` is set to          |\n|                    | ``auto``. Default: ``20``                            |\n+--------------------+------------------------------------------------------+\n| ``mem``            | Filter candidates by whether they have native        |\n|                    | support for memory. Values ``on``, ``any``, ``auto`` |\n|                    | (see next option). Default: ``auto``                 |\n+--------------------+------------------------------------------------------+\n| ``mem_threshold``  | Number of memory bits required to only consider      |\n|                    | candidates with native memory support when ``mem``   |\n|                    | is set to ``auto``. Default: ``10240``               |\n+--------------------+------------------------------------------------------+\n| ``forall``         | Filter candidates by whether they support            |\n|                    | ``$allconst``/``$allseq``. Values ``on``, ``any``,   |\n|                    | ``auto`` (``on`` when ``$allconst``/``allseq`` are   |\n|                    | found in the design). Default: ``auto``              |\n+--------------------+------------------------------------------------------+\n"
  },
  {
    "path": "docs/source/conf.diff",
    "content": "--- a/docs/source/conf.py\n+++ b/docs/source/conf.py\n@@ -1,5 +1,5 @@\n #!/usr/bin/env python3\n-project = 'YosysHQ Docs'\n+project = 'YosysHQ SBY'\n author = 'YosysHQ GmbH'\n copyright ='2021 YosysHQ GmbH'\n \n"
  },
  {
    "path": "docs/source/conf.py",
    "content": "#!/usr/bin/env python3\nimport sys\nimport os\n\nfrom sphinx.application import Sphinx\n\nsys.path.append(os.path.abspath(f\"{__file__}/../../../sbysrc\"))\n\nproject = 'YosysHQ SBY'\nauthor = 'YosysHQ GmbH'\ncopyright = '2023 YosysHQ GmbH'\n\n# select HTML theme\n\nhtml_theme = \"furo-ys\"\nhtml_css_files = ['custom.css']\n\n# These folders are copied to the documentation's HTML output\nhtml_static_path = ['../static']\n\nextensions = ['sphinx.ext.autosectionlabel']\nextensions += ['sphinxarg.ext']\n\ndef setup(app: Sphinx) -> None:\n    from furo_ys.lexers.SBYLexer import SBYLexer\n    app.add_lexer(\"sby\", SBYLexer)\n\n    from furo_ys.lexers.YoscryptLexer import YoscryptLexer\n    app.add_lexer(\"yoscrypt\", YoscryptLexer)\n"
  },
  {
    "path": "docs/source/index.rst",
    "content": "\nSymbiYosys (sby) Documentation\n==============================\n\nSymbiYosys (sby) is a front-end driver program for Yosys-based formal\nhardware verification flows. SymbiYosys provides flows for the following\nformal tasks:\n\n   * Bounded verification of safety properties (assertions)\n   * Unbounded verification of safety properties\n   * Generation of test benches from cover statements\n   * Verification of liveness properties\n\n.. toctree::\n   :maxdepth: 3\n\n   install.rst\n   quickstart.rst\n   usage.rst\n   reference.rst\n   autotune.rst\n   verilog.rst\n   verific.rst\n   license.rst\n\n"
  },
  {
    "path": "docs/source/install.rst",
    "content": ".. _install-doc:\n\nInstallation guide\n==================\n\nThis document will guide you through the process of installing sby.\n\nCAD suite(s)\n************\n\nSby (SymbiYosys) is part of the `Tabby CAD Suite\n<https://www.yosyshq.com/tabby-cad-datasheet>`_ and the `OSS CAD Suite\n<https://github.com/YosysHQ/oss-cad-suite-build>`_! The easiest way to use sby\nis to install the binary software suite, which contains all required\ndependencies, including all supported solvers.\n\n* `Contact YosysHQ <https://www.yosyshq.com/contact>`_ for a `Tabby CAD Suite\n  <https://www.yosyshq.com/tabby-cad-datasheet>`_ Evaluation License and\n  download link\n* OR go to https://github.com/YosysHQ/oss-cad-suite-build/releases to download\n  the free OSS CAD Suite\n* Follow the `Install Instructions on GitHub\n  <https://github.com/YosysHQ/oss-cad-suite-build#installation>`_\n\nMake sure to get a Tabby CAD Suite Evaluation License for extensive\nSystemVerilog Assertion (SVA) support, as well as industry-grade SystemVerilog\nand VHDL parsers!\n\nFor more information about the difference between Tabby CAD Suite and the OSS\nCAD Suite, please visit https://www.yosyshq.com/tabby-cad-datasheet.\n\nInstalling from source\n**********************\n\nFollow the instructions below to install sby and its dependencies. Yosys and sby\nare non-optional.  Boolector is recommended to install but not required.  The\nother packages are only required for some engine configurations.\n\nPrerequisites\n-------------\n\nInstalling prerequisites (this command is for Ubuntu 20.04):\n\n.. code-block:: text\n\n   sudo apt-get install build-essential clang bison flex \\\n                        libreadline-dev gawk tcl-dev libffi-dev git \\\n                        graphviz xdot pkg-config python3 zlib1g-dev\n   \n   python3 -m pip install click\n\nRequired components\n-------------------\n\nYosys, Yosys-SMTBMC and ABC\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nhttps://yosyshq.net/yosys/\n\nhttps://people.eecs.berkeley.edu/~alanmi/abc/\n\nNote that this will install Yosys, Yosys-SMTBMC and ABC (as ``yosys-abc``):\n\n.. code-block:: text\n\n   git clone https://github.com/YosysHQ/yosys --recurse-submodules\n   cd yosys\n   make -j$(nproc)\n   sudo make install\n\nsby\n^^^\n\nhttps://github.com/YosysHQ/sby\n\n.. code-block:: text\n\n   git clone https://github.com/YosysHQ/sby\n   cd sby\n   sudo make install\n\nRecommended components\n----------------------\n\nBoolector\n^^^^^^^^^\n\nhttps://boolector.github.io\n\n.. code-block:: text\n    \n    git clone https://github.com/boolector/boolector\n    cd boolector\n    ./contrib/setup-btor2tools.sh\n    ./contrib/setup-lingeling.sh\n    ./configure.sh\n    make -C build -j$(nproc)\n    sudo cp build/bin/{boolector,btor*} /usr/local/bin/\n    sudo cp deps/btor2tools/build/bin/btorsim /usr/local/bin/\n\nTo use the ``btor`` engine you will need to install btor2tools from \n`commit c35cf1c <https://github.com/Boolector/btor2tools/commit/c35cf1c>`_ or\nnewer. \n\nYices 2\n-------\n\nhttp://yices.csl.sri.com/\n\n.. code-block:: text\n\n   git clone https://github.com/SRI-CSL/yices2.git yices2\n   cd yices2\n   autoconf\n   ./configure\n   make -j$(nproc)\n   sudo make install\n\nOptional components\n-------------------\nAdditional solver engines can be installed as per their instructions, links are\nprovided below.\n\nZ3\n^^^\n\n  https://github.com/Z3Prover/z3\n\nsuper_prove\n^^^^^^^^^^^\n  https://github.com/sterin/super-prove-build\n\nAvy\n^^^\n  https://arieg.bitbucket.io/avy/\n\nrIC3\n^^^^\n  https://github.com/gipsyh/rIC3/\n\nThe minimum required version is 1.3.5\n"
  },
  {
    "path": "docs/source/license.rst",
    "content": "\nSymbiYosys license\n==================\n\nSymbiYosys (sby) itself is licensed under the ISC license:\n\n.. code-block:: text\n\n   SymbiYosys (sby) -- Front-end for Yosys-based formal verification flows\n   \n   Copyright (C) 2016  Claire Xenia Wolf <claire@yosyshq.com>\n   \n   Permission to use, copy, modify, and/or distribute this software for any\n   purpose with or without fee is hereby granted, provided that the above\n   copyright notice and this permission notice appear in all copies.\n   \n   THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\nNote that the solvers and other components used by SymbiYosys come with their\nown license terms.\n\n"
  },
  {
    "path": "docs/source/quickstart.rst",
    "content": "\nGetting started\n===============\n\n.. note:: \n\n    This tutorial assumes sby and boolector installation as per the \n    :ref:`install-doc`.  For this tutorial, it is also recommended to install \n    `GTKWave <http://gtkwave.sourceforge.net/>`_, an open source VCD viewer.\n    `Source files used in this tutorial\n    <https://github.com/YosysHQ/sby/tree/master/docs/examples/fifo>`_ can be \n    found on the sby git, under ``docs/examples/fifo``.\n\nFirst In, First Out (FIFO) buffer\n*********************************\n\nFrom `Wikipedia <https://en.wikipedia.org/wiki/FIFO_(computing_and_electronics)>`_,\na FIFO is \n    \n    a method for organizing the manipulation of a data structure (often,\n    specifically a data buffer) where the oldest (first) entry, or \"head\" of the\n    queue, is processed first.\n\n    Such processing is analogous to servicing people in a queue area on a\n    first-come, first-served (FCFS) basis, i.e. in the same sequence in which\n    they arrive at the queue's tail. \n\nIn hardware we can create such a construct by providing two addresses into a\nregister file.  This tutorial will use an example implementation provided in\n`fifo.sv`. \n\nFirst, the address generator module:  \n\n.. literalinclude:: ../examples/fifo/fifo.sv\n   :language: systemverilog\n   :start-at: address generator\n   :end-at: endmodule\n\nThis module is instantiated twice; once for the write address and once for the\nread address.  In both cases, the address will start at and reset to 0, and will\nincrement by 1 when an enable signal is received.  When the address pointers\nincrement from the maximum storage value they reset back to 0, providing a\ncircular queue.\n\nNext, the register file:\n\n.. literalinclude:: ../examples/fifo/fifo.sv\n   :language: systemverilog\n   :start-at: fifo storage\n   :end-before: end storage\n   :dedent:\n\nNotice that this register design includes a synchronous write and asynchronous\nread.  Each word is 8 bits, and up to 16 words can be stored in the buffer.\n\nVerification properties\n***********************\n\nIn order to verify our design we must first define properties that it must\nsatisfy.  For example, there must never be more than there is memory available.\nBy assigning a signal to count the number of values in the buffer, we can make\nthe following assertion in the code:\n\n.. literalinclude:: ../examples/fifo/fifo.sv\n   :language: systemverilog\n   :start-at: a_oflow\n   :end-at: ;\n   :dedent:\n\nIt is also possible to use the prior value of a signal for comparison.  This can\nbe used, for example, to ensure that the count is only able to increase or\ndecrease by 1.  A case must be added to handle resetting the count directly to\n0, as well as if the count does not change.  This can be seen in the following\ncode; at least one of these conditions must be true at all times if our design\nis to be correct.\n\n.. literalinclude:: ../examples/fifo/fifo.sv\n   :language: systemverilog\n   :start-at: a_counts\n   :end-at: ;\n   :dedent:\n\nAs our count signal is used independently of the read and write pointers, we\nmust verify that the count is always correct.  While the write pointer will\nalways be at the same point or *after* the read pointer, the circular buffer\nmeans that the write *address* could wrap around and appear *less than* the read\naddress.  So we must first perform some simple arithmetic to find the absolute\ndifference in addresses, and then compare with the count signal.\n\n.. literalinclude:: ../examples/fifo/fifo.sv\n   :language: systemverilog\n   :start-at: assign addr_diff\n   :end-at: ;\n   :dedent:\n\n.. literalinclude:: ../examples/fifo/fifo.sv\n   :language: systemverilog\n   :start-at: a_count_diff\n   :end-at: ;\n   :dedent:\n\nSymbiYosys\n**********\n\nSymbiYosys (sby) uses a .sby file to define a set of tasks used for\nverification.  \n\n**basic**\n    Bounded model check of design.\n\n**nofullskip**\n    Demonstration of failing model using an unbounded model check.\n\n**cover**\n    Cover mode (testing cover statements).\n\n**noverific**\n    Test fallback to default Verilog frontend.\n\nThe use of the ``:default`` tag indicates that by default, basic and cover\nshould be run if no tasks are specified, such as when running the command below.\n\n    sby fifo.sby\n\n.. note:: \n\n    The default set of tests should all pass.  If this is not the case there may\n    be a problem with the installation of sby or one of its solvers. \n\nTo see what happens when a test fails, the below command can be used.  Note the\nuse of the ``-f`` flag to automatically overwrite existing task output.  While\nthis may not be necessary on the first run, it is quite useful when making\nadjustments to code and rerunning tests to validate.\n    \n    sby -f fifo.sby nofullskip\n\nThe nofullskip task disables the code shown below.  Because the count signal has\nbeen written such that it cannot exceed MAX_DATA, removing this code will lead\nto the ``a_count_diff`` assertion failing.  Without this assertion, there is no\nguarantee that data will be read in the same order it was written should an\noverflow occur and the oldest data be written.\n\n.. literalinclude:: ../examples/fifo/fifo.sv\n   :language: systemverilog\n   :start-at: NO_FULL_SKIP\n   :end-at: endif\n   :lines: 1-5,9\n\nThe last few lines of output for the nofullskip task should be similar to the\nfollowing:\n\n.. code-block:: text\n\n    SBY [fifo_nofullskip] engine_0.basecase: ##  Assert failed in fifo: a_count_diff\n    SBY [fifo_nofullskip] engine_0.basecase: ##  Writing trace to VCD file: engine_0/trace.vcd\n    SBY [fifo_nofullskip] engine_0.basecase: ##  Writing trace to Verilog testbench: engine_0/trace_tb.v\n    SBY [fifo_nofullskip] engine_0.basecase: ##  Writing trace to constraints file: engine_0/trace.smtc\n    SBY [fifo_nofullskip] engine_0.basecase: ##  Status: failed\n    SBY [fifo_nofullskip] engine_0.basecase: finished (returncode=1)\n    SBY [fifo_nofullskip] engine_0: Status returned by engine for basecase: FAIL\n    SBY [fifo_nofullskip] engine_0.induction: terminating process\n    SBY [fifo_nofullskip] summary: Elapsed clock time [H:MM:SS (secs)]: 0:00:02 (2)\n    SBY [fifo_nofullskip] summary: Elapsed process time unvailable on Windows\n    SBY [fifo_nofullskip] summary: engine_0 (smtbmc boolector) returned FAIL for basecase\n    SBY [fifo_nofullskip] summary: counterexample trace: fifo_nofullskip/engine_0/trace.vcd\n    SBY [fifo_nofullskip] DONE (FAIL, rc=2)\n    SBY The following tasks failed: ['nofullskip']\n\nUsing the ``noskip.gtkw`` file provided, use the below command to examine the\nerror trace.\n\n    gtkwave fifo_nofullskip/engine_0/trace.vcd noskip.gtkw\n\nThis should result in something similar to the below image.  We can immediately\nsee that ``data_count`` and ``addr_diff`` are different.  Looking a bit deeper\nwe can see that in order to reach this state the read enable signal was high in\nthe first clock cycle while write enable is low.  This leads to an underfill\nwhere a value is read while the buffer is empty and the read address increments\nto a higher value than the write address.\n\n.. image:: media/gtkwave_noskip.png\n\nDuring correct operation, the ``w_underfill`` statement will cover the underflow\ncase.  Examining ``fifo_cover/logfile.txt`` will reveal which trace file\nincludes the cover statment we are looking for.  If this file doesn't exist, run\nthe code below.\n\n    sby fifo.sby cover\n\nSearching the file for ``w_underfill`` will reveal the below.\n\n.. code-block:: text\n\n    $ grep \"w_underfill\" fifo_cover/logfile.txt -A 1\n    SBY [fifo_cover] engine_0: ##  Reached cover statement at w_underfill in step 2.\n    SBY [fifo_cover] engine_0: ##  Writing trace to VCD file: engine_0/trace4.vcd\n\nWe can then run gtkwave with the trace file indicated to see the correct\noperation as in the image below.  When the buffer is empty, a read with no write\nwill result in the ``wksip`` signal going high, incrementing *both* read and\nwrite addresses and avoiding underflow.\n    \n    gtkwave fifo_cover/engine_0/trace4.vcd noskip.gtkw\n\n.. image:: media/gtkwave_coverskip.png\n\n.. note::\n\n    Implementation of the ``w_underfill`` cover statement depends on whether\n    Verific is used or not.  See the `Concurrent assertions`_ section for more\n    detail.\n\nExercise\n********\n\nAdjust the ``[script]`` section of ``fifo.sby`` so that it looks like the below.\n\n.. code-block:: sby\n\n    [script]\n    nofullskip: read -define NO_FULL_SKIP=1\n    noverific: read -noverific\n    read -formal fifo.sv\n    hierarchy -check -top fifo -chparam MAX_DATA 17\n    prep -top fifo\n\nThe ``hierarchy`` command we added changes the ``MAX_DATA`` parameter of the top\nmodule to be 17.  Now run the ``basic`` task and see what happens.  It should\nfail and give an error like ``Assert failed in fifo: a_count_diff``. Can you\nmodify the verilog code so that it works with larger values of ``MAX_DATA``\nwhile still passing all of the tests?\n\n.. note::\n\n    If you need a **hint**, try increasing the width of the address wires.  4 bits\n    supports up to 2\\ :sup:`4`\\ =16 addresses.  Are there other signals that \n    need to be wider?  Can you make the width parameterisable to support \n    arbitrarily large buffers?  \n\nOnce the tests are passing with ``MAX_DATA=17``, try something bigger, like 64,\nor 100.  Does the ``basic`` task still pass?  What about ``cover``?  By default,\n``bmc`` & ``cover`` modes will run to a depth of 20 cycles.  If a maximum of one\nvalue can be loaded in each cycle, how many cycles will it take to load 100\nvalues?  Using the :ref:`.sby reference page <Reference for .sby file format>`,\ntry to increase the cover mode depth to be at least a few cycles larger than the\n``MAX_DATA``.\n\n.. note::\n    \n    Reference files are provided in the ``fifo/golden`` directory, showing how\n    the verilog could have been modified and how a ``bigtest`` task could be\n    added.\n\nConcurrent assertions\n*********************\n\nUntil this point, all of the properties described have been *immediate*\nassertions.  As the name suggests, immediate assertions are evaluated\nimmediately whereas concurrent assertions allow for the capture of sequences of\nevents which occur across time.  The use of concurrent assertions requires a\nmore advanced series of checks.  \n\nCompare the difference in implementation of ``w_underfill`` depending on the\npresence of Verific.  ``w_underfill`` looks for a sequence of events where the\nwrite enable is low but the write address changes in the following cycle.  This\nis the expected behaviour for reading while empty and implies that the\n``w_skip`` signal went high.  Verific enables elaboration of SystemVerilog\nAssertions (SVA) properties.  Here we use such a property, ``write_skip``.  \n\n.. literalinclude:: ../examples/fifo/fifo.sv\n   :language: systemverilog\n   :start-at: property write_skip\n   :end-at: w_underfill\n   :dedent:\n\nThis property describes a *sequence* of events which occurs on the ``clk``\nsignal and are disabled/restarted when the ``rst`` signal is high.  The property\nfirst waits for a low ``wen`` signal, and then a change in ``waddr`` in the\nfollowing cycle.  ``w_underfill`` is then a cover of this property to verify\nthat it is possible.  Now look at the implementation without Verific.\n\n.. literalinclude:: ../examples/fifo/fifo.sv\n   :language: systemverilog\n   :start-at: reg past_nwen;\n   :end-before: end w_underfill\n   :dedent:\n\nIn this case we do not have access to SVA properties and are more limited in the\ntools available to us.  Ideally we would use ``$past`` to read the value of\n``wen`` in the previous cycle and then check for a change in ``waddr``. However,\nin the first cycle of simulation, reading ``$past`` will return a value of\n``X``.  This results in false triggers of the property so we instead implement\nthe ``past_nwen`` register which we can initialise to ``0`` and ensure it does\nnot trigger in the first cycle.\n\nAs verification properties become more complex and check longer sequences, the\nadditional effort of hand-coding without SVA properties becomes much more\ndifficult.  Using a parser such as Verific supports these checks *without*\nhaving to write out potentially complicated state machines. Verific is included\nfor use in the *Tabby CAD Suite*.\n\n.. note::\n\n   The Verific frontend for Yosys requires the commercial `Tabby CAD Suite`_.\n   This is not the same as simply having a Verific license when using Yosys.\n\n.. _Tabby CAD Suite: https://www.yosyshq.com/tabby-cad-datasheet\n\nFurther information\n*******************\nFor more information on the uses of assertions and the difference between\nimmediate and concurrent assertions, refer to appnote 109: `Property Checking\nwith SystemVerilog Assertions \n<https://yosyshq.readthedocs.io/projects/ap109/en/latest/>`_.\n"
  },
  {
    "path": "docs/source/reference.rst",
    "content": ".. role:: sby(code)\n   :language: sby\n\nReference for .sby file format\n==============================\n\nA ``.sby`` file consists of sections. Each section start with a single-line\nsection header in square brackets. The order of sections in a ``.sby`` file is\nfor the most part irrelevant, but by convention the usual order is\n:sby:`[tasks]`, :sby:`[options]`, :sby:`[engines]`, :sby:`[script]`,  and\n:sby:`[files]`.\n\nTasks section\n-------------\n\nThe optional :sby:`[tasks]` section can be used to configure multiple\nverification tasks in a single ``.sby`` file. Each line in the :sby:`[tasks]`\nsection configures one task. For example:\n\n.. code-block:: sby\n\n   [tasks]\n   task1 task_1_or_2 task_1_or_3\n   task2 task_1_or_2\n   task3 task_1_or_3\n\nEach task can be assigned additional group aliases, such as ``task_1_or_2``\nand ``task_1_or_3`` in the above example.\n\nOne or more tasks can be specified as additional command line arguments when\ncalling ``sby`` on a ``.sby`` file:\n\n.. code-block:: text\n\n   sby example.sby task2\n\nIf no task is specified then all tasks in the :sby:`[tasks]` section are run.\n\nAfter the :sby:`[tasks]` section individual lines can be specified for specific\ntasks or task groups:\n\n.. code-block:: sby\n\n   [options]\n   task_1_or_2: mode bmc\n   task_1_or_2: depth 100\n   task3: mode prove\n\nIf the tag ``<taskname>:`` is used on a line by itself then the conditional string\nextends until the next conditional block or ``--`` on a line by itself.\n\n.. code-block:: sby\n\n   [options]\n   task_1_or_2:\n   mode bmc\n   depth 100\n\n   task3:\n   mode prove\n   --\n\nThe tag ``~<taskname>:`` can be used for a line or block that should not be used when\nthe given task is active:\n\n.. code-block:: sby\n\n   [options]\n   ~task3:\n   mode bmc\n   depth 100\n\n   task3:\n   mode prove\n   --\n\nThe following example demonstrates how to configure safety and liveness checks for all\ncombinations of some host implementations A and B and device implementations X and Y:\n\n.. code-block:: sby\n\n   [tasks]\n   prove_hAdX prove hostA deviceX\n   prove_hBdX prove hostB deviceX\n   prove_hAdY prove hostA deviceY\n   prove_hBdY prove hostB deviceY\n   live_hAdX live hostA deviceX\n   live_hBdX live hostB deviceX\n   live_hAdY live hostA deviceY\n   live_hBdY live hostB deviceY\n\n\n   [options]\n   prove: mode prove\n   live: mode live\n\n   [engines]\n   prove: abc pdr\n   live: aiger suprove\n\n   [script]\n   hostA: read -sv hostA.v\n   hostB: read -sv hostB.v\n   deviceX: read -sv deviceX.v\n   deviceY: read -sv deviceY.v\n   ...\n\nThe :sby:`[tasks]` section must appear in the ``.sby`` file before the first\n``<taskname>:`` or ``~<taskname>:`` tag.\n\nThe command ``sby --dumptasks <sby_file>`` prints the list of all tasks defined in\na given ``.sby`` file.\n\nOptions section\n---------------\n\nThe :sby:`[options]` section contains lines with key-value pairs. The ``mode``\noption is mandatory. The possible values for the ``mode`` option are:\n\n========= ===========\nMode      Description\n========= ===========\n``bmc``   Bounded model check to verify safety properties (``assert(...)`` statements)\n``prove`` Unbounded model check to verify safety properties (``assert(...)`` statements)\n``live``  Unbounded model check to verify liveness properties (``assert(s_eventually ...)`` statements)\n``cover`` Generate set of shortest traces required to reach all cover() statements\n========= ===========\n\n..\n   ``equiv`` Formal equivalence checking (usually to verify pre- and post-synthesis equivalence)\n   ``synth`` Reactive Synthesis (synthesis of circuit from safety properties)\n\nAll other options have default values and thus are optional. The available\noptions are:\n\n+-------------------+------------+---------------------------------------------------------+\n|   Option          |   Modes    | Description                                             |\n+===================+============+=========================================================+\n| ``expect``        |   All      | Expected result as comma-separated list of the tokens   |\n|                   |            | ``pass``, ``fail``, ``unknown``, ``error``, and         |\n|                   |            | ``timeout``. Unexpected results yield a nonzero return  |\n|                   |            | code . Default: ``pass``                                |\n+-------------------+------------+---------------------------------------------------------+\n| ``timeout``       |   All      | Timeout in seconds. Default: ``none`` (i.e. no timeout) |\n+-------------------+------------+---------------------------------------------------------+\n| ``multiclock``    |   All      | Create a model with multiple clocks and/or asynchronous |\n|                   |            | logic. Values: ``on``, ``off``. Default: ``off``        |\n+-------------------+------------+---------------------------------------------------------+\n| ``wait``          |   All      | Instead of terminating when the first engine returns,   |\n|                   |            | wait for all engines to return and check for            |\n|                   |            | consistency. Values: ``on``, ``off``. Default: ``off``  |\n+-------------------+------------+---------------------------------------------------------+\n| ``vcd``           |   All      | Write VCD traces for counter-example or cover traces.   |\n|                   |            | Values: ``on``, ``off``. Default: ``on``                |\n+-------------------+------------+---------------------------------------------------------+\n| ``vcd_sim``       |   All      | When generating VCD traces, use Yosys's ``sim``         |\n|                   |            | command. Replaces the engine native VCD output.         |\n|                   |            | Values: ``on``, ``off``. Default: ``off``               |\n+-------------------+------------+---------------------------------------------------------+\n| ``fst``           |   All      | Generate FST traces using Yosys's sim command.          |\n|                   |            | Values: ``on``, ``off``. Default: ``off``               |\n+-------------------+------------+---------------------------------------------------------+\n| ``cycle_width``   |   All      | Cycle width used by Yosys's sim command.                |\n|                   |            | Values: even numbers >= 2. Default: ``10``              |\n+-------------------+------------+---------------------------------------------------------+\n| ``aigsmt``        |   All      | Which SMT2 solver to use for converting AIGER witnesses |\n|                   |            | to counter example traces. Use ``none`` to disable      |\n|                   |            | conversion of AIGER witnesses. Default: ``yices``       |\n+-------------------+------------+---------------------------------------------------------+\n| ``tbtop``         |   All      | The top module for generated Verilog test benches, as   |\n|                   |            | hierarchical path relative to the design top module.    |\n+-------------------+------------+---------------------------------------------------------+\n| ``make_model``    |   All      | Force generation of the named formal models. Takes a    |\n|                   |            | comma-separated list of model names. For a model        |\n|                   |            | ``<name>`` this will generate the                       |\n|                   |            | ``model/design_<name>.*`` files within the working      |\n|                   |            | directory, even when not required to run the task.      |\n+-------------------+------------+---------------------------------------------------------+\n| ``smtc``          | ``bmc``,   | Pass this ``.smtc`` file to the smtbmc engine. All      |\n|                   | ``prove``, | other engines are disabled when this option is used.    |\n|                   | ``cover``  | Default: None                                           |\n+-------------------+------------+---------------------------------------------------------+\n| ``depth``         | ``bmc``,   | Depth of the bounded model check. Only the specified    |\n|                   | ``cover``  | number of cycles are considered. Default: ``20``        |\n|                   +------------+---------------------------------------------------------+\n|                   | ``prove``  | Depth for the k-induction performed by the ``smtbmc``   |\n|                   |            | engine. Other engines ignore this option in ``prove``   |\n|                   |            | mode. Default: ``20``                                   |\n+-------------------+------------+---------------------------------------------------------+\n| ``skip``          | ``bmc``,   | Skip the specified number of time steps. Only valid     |\n|                   | ``cover``  | with smtbmc engine. All other engines are disabled when |\n|                   |            | this option is used. Default: None                      |\n+-------------------+------------+---------------------------------------------------------+\n| ``append``        | ``bmc``,   | When generating a counter-example trace, add the        |\n|                   | ``prove``, | specified number of cycles at the end of the trace.     |\n|                   | ``cover``  | Default: ``0``                                          |\n+-------------------+------------+---------------------------------------------------------+\n| ``append_assume`` | ``bmc``,   | Uphold assumptions when appending cycles at the end of  |\n|                   | ``prove``, | the trace. Depending on the engine and options used     |\n|                   | ``cover``  | this may be implicitly on or not supported (as          |\n|                   |            | indicated in SBY's log output).                         |\n|                   |            | Values: ``on``, ``off``. Default: ``on``                |\n+-------------------+------------+---------------------------------------------------------+\n| ``cover_assert``  | ``cover``  | Check for assertion properties during ``cover`` mode.   |\n|                   |            | Values: ``on``, ``off``. Default: ``off``               |\n+-------------------+------------+---------------------------------------------------------+\n\nCancelledby section\n-------------------\n\nAt times it may be desirable for one task to be able to stop another task early,\nsuch as when a ``prove`` task and a ``bmc`` task are testing the same properties\nand the ``prove`` task finishes first, making the ``bmc`` task redundant.  This\nis where the optional ``[cancelledby]`` section comes in.  Each line corresponds\nto the name of a task which, if finished, will cancel the current task.  No\ndistinction is made for whether the task finished successfully, only that\nit is finished.  Remember that tags can be used to control which tasks contain a\ngiven configuration line.\n\nExample:\n\n.. code-block:: sby\n\n   [cancelledby]\n   task1:\n   task2\n   task3\n\n   task2:\n   task3\n   task4\n\nIn this example, ``task1`` can be cancelled by ``task2`` or ``task3``, while\n``task2`` can be cancelled by ``task3`` or ``task4``.  If ``task4`` is the first\nto finish, then ``task2`` will be cancelled, which in turn leads to ``task1``\nbeing cancelled.  A task can only be cancelled if it has not yet finished, and\nwill check the finished tasks before starting and periodically while the\nattached engines are running.\n\nNormally, only the current taskloop is checked for finished tasks.  This means\nthat intertask cancellations are not possible across separate SBY invocations or\nwhen using the ``--sequential`` flag.  By providing SBY with the\n``--statuscancels`` flag however the status database will be used, allowing\ntasks to be cancelled independently of taskloop.  Note that this also means that\na task may be cancelled by a previous invocation unless ``--statusreset`` is\ncalled first.\n\nExample:\n\n.. code-block:: shell\n\n   sby $sbyfile --statusreset\n   sby $sbyfile a b &\n   sby $sbyfile c d --statuscancels\n\nIn this example, the ``a`` and ``b`` tasks can only be cancelled by each other,\nwhile the ``c`` and ``d`` tasks can be cancelled by any task.\n\nEngines section\n---------------\n\nThe ``[engines]`` section configures which engines should be used to solve the\ngiven problem. Each line in the ``[engines]`` section specifies one engine. When\nmore than one engine is specified then the result returned by the first engine\nto finish is used.\n\nEach engine configuration consists of an engine name followed by engine options,\nusually followed by a solver name and solver options.\n\nExample:\n\n.. code-block:: sby\n\n   [engines]\n   smtbmc --syn --nopresat z3 rewriter.cache_all=true opt.enable_sat=true\n   abc sim3 -W 15\n\nIn the first line ``smtbmc`` is the engine, ``--syn --nopresat`` are engine options,\n``z3`` is the solver, and ``rewriter.cache_all=true opt.enable_sat=true`` are\nsolver options.\n\nIn the 2nd line ``abc`` is the engine, there are no engine options, ``sim3`` is the\nsolver, and ``-W 15`` are solver options.\n\nThe following mode/engine/solver combinations are currently supported:\n\n+-----------+--------------------------+\n| Mode      | Engine                   |\n+===========+==========================+\n| ``bmc``   | ``smtbmc [all solvers]`` |\n|           |                          |\n|           | ``btor btormc``          |\n|           |                          |\n|           | ``btor pono``            |\n|           |                          |\n|           | ``abc bmc3``             |\n|           |                          |\n|           | ``abc sim3``             |\n|           |                          |\n|           | ``aiger aigbmc``         |\n+-----------+--------------------------+\n| ``prove`` | ``smtbmc [all solvers]`` |\n|           |                          |\n|           | ``abc pdr``              |\n|           |                          |\n|           | ``aiger avy``            |\n|           |                          |\n|           | ``aiger rIC3``           |\n|           |                          |\n|           | ``aiger suprove``        |\n+-----------+--------------------------+\n| ``cover`` | ``smtbmc [all solvers]`` |\n|           |                          |\n|           | ``btor btormc``          |\n+-----------+--------------------------+\n| ``live``  | ``aiger suprove``        |\n+-----------+--------------------------+\n\n``smtbmc`` engine\n~~~~~~~~~~~~~~~~~\n\nThe ``smtbmc`` engine supports the ``bmc``, ``prove``, and ``cover`` modes and supports\nthe following options:\n\n+------------------+---------------------------------------------------------+\n|   Option         | Description                                             |\n+==================+=========================================================+\n| ``--nomem``      | Don't use the SMT theory of arrays to model memories.   |\n|                  | Instead synthesize memories to registers and address    |\n|                  | logic.                                                  |\n+------------------+---------------------------------------------------------+\n| ``--syn``        | Synthesize the circuit to a gate-level representation   |\n|                  | instead of using word-level SMT operators. This also    |\n|                  | runs some low-level logic optimization on the circuit.  |\n+------------------+---------------------------------------------------------+\n| ``--stbv``       | Use large bit vectors (instead of uninterpreted         |\n|                  | functions) to represent the circuit state.              |\n+------------------+---------------------------------------------------------+\n| ``--stdt``       | Use SMT-LIB 2.6 datatypes to represent states.          |\n+------------------+---------------------------------------------------------+\n| ``--nopresat``   | Do not run \"presat\" SMT queries that make sure that     |\n|                  | assumptions are non-conflicting (and potentially        |\n|                  | warmup the SMT solver).                                 |\n+------------------+---------------------------------------------------------+\n| ``--keep-going`` | In BMC mode, continue after the first failed assertion  |\n|                  | and report further failed assertions. In prove mode,    |\n|                  | ``--keep-going`` is currently only supported with       |\n|                  | ``abc pdr`` (see the ``abc`` engine section below).     |\n+------------------+---------------------------------------------------------+\n| ``--unroll``,    | Disable/enable unrolling of the SMT problem. The        |\n| ``--nounroll``   | default value depends on the solver being used.         |\n+------------------+---------------------------------------------------------+\n| ``--dumpsmt2``   | Write the SMT2 trace to an additional output file.      |\n|                  | (Useful for benchmarking and troubleshooting.)          |\n+------------------+---------------------------------------------------------+\n| ``--progress``   | Enable Yosys-SMTBMC timer display.                      |\n+------------------+---------------------------------------------------------+\n\nAny SMT2 solver that is compatible with ``yosys-smtbmc`` can be passed as\nargument to the ``smtbmc`` engine. The solver options are passed to the solver\nas additional command line options.\n\nThe following solvers are currently supported by ``yosys-smtbmc``:\n\n* yices\n* boolector\n* bitwuzla\n* z3\n* mathsat\n* cvc4\n* cvc5\n\nAny additional options after ``--`` are passed to ``yosys-smtbmc`` as-is.\n\n``btor`` engine\n~~~~~~~~~~~~~~~\n\nThe ``btor`` engine supports hardware modelcheckers that accept btor2 files.\nThe engine supports no engine options and supports the following solvers:\n\n+-------------------------------+---------------------------------+\n|   Solver                      |   Modes                         |\n+===============================+=================================+\n| ``btormc``                    |   ``bmc``, ``cover``            |\n+-------------------------------+---------------------------------+\n| ``pono``                      |   ``bmc``                       |\n+-------------------------------+---------------------------------+\n\nSolver options are passed to the solver as additional command line options.\n\n``aiger`` engine\n~~~~~~~~~~~~~~~~\n\nThe ``aiger`` engine is a generic front-end for hardware modelcheckers that are capable\nof processing AIGER files. The engine supports no engine options and supports the following\nsolvers:\n\n+-------------------------------+---------------------------------+\n|   Solver                      |   Modes                         |\n+===============================+=================================+\n| ``suprove``                   |   ``prove``, ``live``           |\n+-------------------------------+---------------------------------+\n| ``avy``                       |   ``prove``                     |\n+-------------------------------+---------------------------------+\n| ``rIC3``                      |   ``prove``                     |\n+-------------------------------+---------------------------------+\n| ``aigbmc``                    |   ``bmc``                       |\n+-------------------------------+---------------------------------+\n\nSolver options are passed to the solver as additional command line options.\n\n``abc`` engine\n~~~~~~~~~~~~~~\n\nThe ``abc`` engine is a front-end for the functionality in Berkeley ABC. It\nsupports the following solvers:\n\n+------------+-----------------+---------------------------------+\n|   Solver   |   Modes         |   ABC Command                   |\n+============+=================+=================================+\n| ``bmc3``   |  ``bmc``        |  ``bmc3 -F <depth> -v``         |\n+------------+-----------------+---------------------------------+\n| ``sim3``   |  ``bmc``        |  ``sim3 -F <depth> -v``         |\n+------------+-----------------+---------------------------------+\n| ``pdr``    |  ``prove``      |  ``pdr``                        |\n+------------+-----------------+---------------------------------+\n\nSolver options are passed as additional arguments to the ABC command\nimplementing the solver.\n\nWhen using the ``pdr`` solver, the ``abc`` engine supports the following\nengine option (currently the only engine option available for ``abc``):\n\n+------------------+---------------------------------------------------------+\n|   Option         | Description                                             |\n+==================+=========================================================+\n| ``--keep-going`` | Continue after the first proven or failed assertion and |\n|                  | report individual per-property results, rather than a   |\n|                  | single global result.                                   |\n+------------------+---------------------------------------------------------+\n\nExample:\n\n.. code-block:: sby\n\n   [engines]\n   abc --keep-going pdr\n\n``itp`` engine\n~~~~~~~~~~~~~~\n\nThe ``itp`` engine implements interpolation-based model checking using Craig\ninterpolants extracted from SAT resolution proofs. It uses the external\n`itp-bmc <https://github.com/inquisitour/itp-bmc>`_ tool.\n\nSupported modes: ``prove``, ``bmc``\n\n**Installation:**\n\nInstall the ``itp-bmc`` binary to PATH:\n\n.. code-block:: bash\n\n   git clone https://github.com/inquisitour/itp-bmc.git\n   cd itp-bmc\n   make\n   sudo cp bmc /usr/local/bin/itp-bmc\n\nOr set the ``ITP_BMC`` environment variable, or use the ``--itp-bmc`` command-line flag to specify the path directly.\n\n**Engine arguments:**\n\n.. code-block:: sby\n\n   [engines]\n   itp <bound> <skip>\n\n+------------+--------------------------------------------------------------+\n|  Argument  | Description                                                  |\n+============+==============================================================+\n| ``bound``  | Maximum unrolling depth. Default: value of ``depth`` option  |\n+------------+--------------------------------------------------------------+\n| ``skip``   | Number of initial timeframes to skip before checking bad     |\n|            | states. Useful for designs requiring reset cycles.           |\n|            | Default: value of ``skip`` option or 0                       |\n+------------+--------------------------------------------------------------+\n\n**Example:**\n\n.. code-block:: sby\n\n   [options]\n   mode prove\n\n   [engines]\n   itp 20 0\n\nFor designs requiring reset cycles (e.g. riscv-formal):\n\n.. code-block:: sby\n\n   [engines]\n   itp 15 10\n\n.. note::\n\n   The ``itp`` engine does not currently produce counterexample witness\n   traces. When a property violation is found, only FAIL status is reported.\n\n``none`` engine\n~~~~~~~~~~~~~~~\n\nThe ``none`` engine does not run any solver. It can be used together with the\n``make_model`` option to manually generate any model supported by one of the\nother engines. This makes it easier to use the same models outside of sby.\n\n\nScript section\n--------------\n\nThe :sby:`[script]` section contains the Yosys script that reads and elaborates\nthe design under test. For example, for a simple project contained in a single\ndesign file ``mytest.sv`` with the top-module ``mytest``:\n\n.. code-block:: sby\n\n   [script]\n   read -sv mytest.sv\n   prep -top mytest\n\nOr explicitly using the Verific SystemVerilog parser (default for ``read -sv``\nwhen Yosys is built with Verific support):\n\n.. code-block:: sby\n\n   [script]\n   verific -sv mytest.sv\n   verific -import mytest\n   prep -top mytest\n\nOr explicitly using the native Yosys Verilog parser (default for ``read -sv``\nwhen Yosys is not built with Verific support):\n\n.. code-block:: sby\n\n   [script]\n   read_verilog -sv mytest.sv\n   prep -top mytest\n\nRun ``yosys`` in a terminal window and enter ``help`` on the Yosys prompt\nfor a command list. Run ``help <command>`` for a detailed description of the\ncommand, for example ``help prep``.\n\n\nFiles section\n-------------\n\nThe files section lists the source files for the proof, meaning all the\nfiles Yosys will need to access when reading the design, including for\nexample data files for ``$readmemh`` and ``$readmemb``.\n\n``sby`` copies these files to ``<outdir>/src/`` before running the Yosys\nscript. When the Yosys script is executed, it will use the copies in\n``<outdir>/src/``. (Alternatively absolute filenames can be used in the\nYosys script for files not listed in the files section.)\n\nFor example:\n\n.. code-block:: sby\n\n   [files]\n   top.sv\n   ../common/defines.vh\n   /data/prj42/modules/foobar.sv\n\nWill copy these files as ``top.v``, ``defines.vh``, and ``foobar.sv``\nto ``<outdir>/src/``.\n\nIf the name of the file in ``<outdir>/src/`` should be different from the\nbasename of the specified file, then the new file name can be specified before\nthe source file name. For example:\n\n.. code-block:: sby\n\n   [files]\n   top.sv\n   defines.vh ../common/defines_footest.vh\n   foo/bar.sv /data/prj42/modules/foobar.sv\n\nFile sections\n-------------\n\nFile sections can be used to create additional files in ``<outdir>/src/`` from\nthe literal content of the :sby:`[file <filename>]` section (\"here document\").\nFor example:\n\n.. code-block:: sby\n\n   [file params.vh]\n   `define RESET_LEN 42\n   `define FAULT_CYCLE 57\n\nPycode blocks\n-------------\n\nBlocks enclosed in ``--pycode-begin--`` and ``--pycode-end--`` lines are interpreted\nas Python code. The function ``output(line)`` can be used to add configuration\nfile lines from the python code. The variable ``task`` contains the current task name,\nif any, and ``None`` otherwise. The variable ``tags`` contains a set of all tags\nassociated with the current task.\n\n.. code-block:: sby\n\n   [tasks]\n   --pycode-begin--\n   for uut in \"rotate reflect\".split():\n     for op in \"SRL SRA SLL SRO SLO ROR ROL FSR FSL\".split():\n       output(\"%s_%s %s %s\" % (uut, op, uut, op))\n   --pycode-end--\n\n   ...\n\n   [script]\n   --pycode-begin--\n   for op in \"SRL SRA SLL SRO SLO ROR ROL FSR FSL\".split():\n     if op in tags:\n       output(\"read -define %s\" % op)\n   --pycode-end--\n   rotate: read -define UUT=shifter_rotate\n   reflect: read -define UUT=shifter_reflect\n   read -sv test.v\n   read -sv shifter_reflect.v\n   read -sv shifter_rotate.v\n   prep -top test\n\n   ...\n\nThe command ``sby --dumpcfg <sby_file>`` can be used to print the configuration without\nspecialization for any particular task, and ``sby --dumpcfg <sby_file> <task_name>`` can\nbe used to print the configuration with specialization for a particular task.\n"
  },
  {
    "path": "docs/source/requirements.txt",
    "content": "furo-ys @ git+https://github.com/YosysHQ/furo-ys\nsphinx-argparse\n"
  },
  {
    "path": "docs/source/usage.rst",
    "content": "Using `sby`\n===========\n\nOnce SBY is installed and available on the command line as `sby`, either built from source or using\none of the available CAD suites, it can be called as follows.  Note that this information is also\navailable via `sby --help`.  For more information on installation, see :ref:`install-doc`.\n\n.. argparse::\n    :module: sby_cmdline\n    :func: parser_func\n    :prog: sby\n"
  },
  {
    "path": "docs/source/verific.rst",
    "content": "\nSystemVerilog, VHDL, SVA\n========================\n\n.. note::\n\n   This document describes features only available with the commercial `Tabby\n   CAD Suite`_ and the included Verific frontend. This is not the same as simply\n   having a Verific license when using Yosys.\n\n.. _Tabby CAD Suite: https://www.yosyshq.com/tabby-cad-datasheet\n\nRun ``verific -sv <files>`` in the ``[script]`` section of you ``.sby`` file\nto read a SystemVerilog source file, and ``verific -vhdl <files>`` to read a\nVHDL source file.\n\nAfter all source files have been read, run ``verific -import <topmodule>``\nto import the design elaborated at the specified top module. This step is\noptional (will be performed automatically) if the top-level module of\nyour design has been read using Verific.\n\nUse ``read -sv`` to automatically use Verific to read a source file if Yosys\nhas been built with Verific.\n\nRun ``yosys -h verific`` in a terminal window and enter for more information\non the ``verific`` script command.\n\n.. _sva:\n\nSupported SVA Property Syntax\n-----------------------------\n\nSVA support in Yosys' Verific bindings is currently in development. At the time\nof writing, the following subset of SVA property syntax is supported in\nconcurrent assertions, assumptions, and cover statements when using the\n``verific`` command in Yosys to read the design.\n\nHigh-Level Convenience Features\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nMost of the high-level convenience features of the SVA language are supported,\nsuch as\n\n  * ``default clocking`` ... ``endclocking``\n  * ``default disable iff`` ... ``;``\n  * ``property`` ... ``endproperty``\n  * ``sequence`` ... ``endsequence``\n  * ``checker`` ... ``endchecker``\n  * Arguments to sequences, properties, and checkers\n  * Storing sequences, properties, and checkers in packages\n\nIn addition the SVA-specific features, the SystemVerilog ``bind`` statement and\ndeep hierarchical references are supported, simplifying the integration of\nformal properties with the design under test.\n\nThe ``verific`` command also allows parsing of VHDL designs and supports binding\nSystemVerilog modules to VHDL entities and deep hierarchical references from a\nSystemVerilog formal test-bench into a VHDL design under test.\n\nExpressions in Sequences\n~~~~~~~~~~~~~~~~~~~~~~~~\n\nAny standard Verilog boolean expression is supported, as well as the\nSystemVerilog functions ``$past``, ``$stable``, ``$changed``, ``$rose``, and\n``$fell``. This functions can also be used outside of SVA sequences.\n\nAdditionally the ``<sequence>.triggered`` syntax for checking if the end of\nany given sequence matches the current cycle is supported in expressions.\n\nFinally the usual SystemVerilog functions such as ``$countones``, ``$onehot``,\nand ``$onehot0`` are also supported.\n\nSequences\n~~~~~~~~~\n\nMost importantly, expressions and variable-length concatenation are supported:\n\n  * *expression*\n  * *sequence* ``##N`` *sequence*\n  * *sequence* ``##[*]`` *sequence*\n  * *sequence* ``##[+]`` *sequence*\n  * *sequence* ``##[N:M]`` *sequence*\n  * *sequence* ``##[N:$]`` *sequence*\n\nAlso variable-length repetition:\n\n  * *sequence* ``[*]``\n  * *sequence* ``[+]``\n  * *sequence* ``[*N]``\n  * *sequence* ``[*N:M]``\n  * *sequence* ``[*N:$]``\n\nAnd the following more complex operators:\n\n  * *sequence* ``or`` *sequence*\n  * *sequence* ``and`` *sequence*\n  * *expression* ``throughout`` *sequence*\n  * *sequence* ``intersect`` *sequence*\n  * *sequence* ``within`` *sequence*\n  * ``first_match(`` *sequence* ``)``\n  * *expression* ``[=N]``\n  * *expression* ``[=N:M]``\n  * *expression* ``[=N:$]``\n  * *expression* ``[->N]``\n  * *expression* ``[->N:M]``\n  * *expression* ``[->N:$]``\n\nProperties\n~~~~~~~~~~\n\nCurrently only a certain set of patterns are supported for SVA properties:\n\n  * [*antecedent_condition*] *sequence*\n  * [*antecedent_condition*] ``not`` *sequence*\n  * *antecedent_condition* *sequence* *until_condition*\n  * *antecedent_condition* ``not`` *sequence* *until_condition*\n\nWhere *antecedent_condition* is one of:\n\n  * sequence ``|->``\n  * sequence ``|=>``\n\nAnd *until_condition* is one of:\n\n  * ``until`` *expression*\n  * ``s_until`` *expression*\n  * ``until_with`` *expression*\n  * ``s_until_with`` *expression*\n\nClocking and Reset\n~~~~~~~~~~~~~~~~~~\n\nThe following constructs are supported for clocking and reset in most of the\nplaces the SystemVerilog standard permits them. However, properties spanning\nmultiple different clock domains are currently unsupported.\n\n  * ``@(posedge`` *clock* ``)``\n  * ``@(negedge`` *clock* ``)``\n  * ``@(posedge`` *clock* ``iff`` *enable* ``)``\n  * ``@(negedge`` *clock* ``iff`` *enable* ``)``\n  * ``disable iff (`` *expression* ``)``\n\nSVA properties in a VHDL design\n-------------------------------\n\nThe below code snippet, taken from an example SBY configuration included in\n|vhd_example|_, shows a VHDL design ``updowncount.vhd`` being loaded, followed\nby a SystemVerilog file ``formal_bind.sv``.\n\n.. |vhd_example| replace:: ``docs/examples/vhd``\n.. _vhd_example: https://github.com/YosysHQ/sby/tree/master/docs/examples/vhd\n\n.. literalinclude:: ../examples/vhd/formal_bind.sby\n  :language: yoscrypt\n  :start-after: [script]\n  :end-before: [files]\n  :caption: ``formal_bind.sby`` script section\n\n.. literalinclude:: ../examples/vhd/formal_bind.sv\n  :language: SystemVerilog\n  :caption: ``formal_bind.sv``\n\nAs you can see, the ``formal_bind.sv`` file includes a ``formal_bind`` module\nand makes use of the ``bind`` keyword in SystemVerilog to create an instance of\nthis module connecting the inputs to the signals of the same name in the VHDL\ndesign.  SVA properties can then be applied to those signals as if the whole\ndesign was in SystemVerilog.\n"
  },
  {
    "path": "docs/source/verilog.rst",
    "content": "\nFormal extensions to Verilog\n============================\n\nAny Verilog file may be read using ``read -formal <file>`` within the\nSymbiYosys ``script`` section.  Multiple files may be given on the sames\nline, or various files may be read in subsequent lines.\n\n``read -formal`` will also define the ``FORMAL`` macro, which can be used\nto separate a section having formal properties from the rest of the logic\nwithin the core.\n\n.. code-block:: systemverilog\n\n   module somemodule(port1, port2, ...);\n       // User logic here\n       //\n   `ifdef FORMAL\n       // Formal properties here\n   `endif\n   endmodule\n\nThe ``bind()`` operator can also be used when using the Verific front end. This\nwill provide an option to attach formal properties to a given piece of logic,\nwithout actually modifying the module in question to do so as we did in the\nexample above. Refer to :doc:`verific` for more on the Verific front end.\n\nSystemVerilog Immediate Assertions\n----------------------------------\n\nSymbiYosys supports three basic immediate assertion types.\n\n1. ``assume(<expr>);``\n\n   An assumption restricts the possibilities the formal tool examines, making\n   the search space smaller.  In any solver generated trace, all of the\n   assumptions will always be true.\n\n2. ``assert(<expr>);``\n\n   An assertion is something the solver will try to make false.  Any time\n   SymbiYosys is run with ``mode bmc``, the proof will fail if some set\n   of inputs can cause the ``<expr>`` within the assertion to be zero (false).\n   When SymbiYosys is run with ``mode prove``, the proof may also yield an\n   ``UNKNOWN`` result if an assertion can be made to fail during the induction\n   step.\n\n3. ``cover(<expr>);``\n\n   A cover statement only applies when SymbiYosys is ran with option\n   ``mode cover``.  In this case, the formal solver will start at the\n   beginning of time (i.e. when all initial statements are true), and it will\n   try to find some clock when ``<expr>`` can be made to be true.  Such a\n   cover run will \"PASS\" once all internal ``cover()`` statements have been\n   fulfilled.  It will \"FAIL\" if any ``cover()`` statement exists that cannot\n   be reached in the first ``N`` states, where ``N`` is set by the\n   ``depth`` option.  A cover pass will also fail if an assertion needs to\n   be broken in order to reach the covered state.\n\nTo be used, each of these statements needs to be placed into an *immediate*\ncontext.  That is, it needs to be placed within an ``always`` block of some\ntype.  Two types of ``always`` block contexts are permitted:\n\n- ``always @(*)``\n\n  Formal properties within an ``always @(*)`` block will be checked on every\n  time step.  For synchronous proofs, the property will be checked every\n  clock period.  For asynchronous proofs, i.e. those with ``multiclock on``,\n  the property will still be checked on every time step but, depending upon\n  how you set up your time steps, it may also be checked multiple times\n  per clock interval.\n\n  As an example, consider the following assertion that the ``error_flag``\n  signal must remain low.\n\n.. code-block:: systemverilog\n\n   always @(*)\n       assert(!error_flag);\n\nWhile it is not recommended that formal properties be mixed with logic in\nthe same ``always @(*)`` block, the language supports it.  In such cases,\nthe formal property will be evaluated as though it took place in the middle\nof the logic block.\n\n- ``always @(posedge <clock>)``\n\n  The second form of immediate assertion is one within a clocked always block.\n  This form of assertion is required when attempting to use the ``$past``,\n  ``$stable``, ``$changed``, ``$rose``, or ``$fell`` SystemVerilog functions\n  discussed in the next section.\n\n  Unlike the ``@(*)`` assertion, this one will only be checked on the clock\n  edge.  Depending upon how the clock is set up, that may mean that there are\n  several formal time steps between when this assertion is checked.\n\n  The two types of immediate assertions, both with and without a clock\n  reference, are very similar.  There is one critical difference between\n  them, however.  The clocked assertion will not be checked until the\n  positive edge of the clock following the time period in question.  Within\n  a synchronous design, this means that the fault will not lie on the last\n  time step, but rather the time step prior.  New users often find this\n  non-intuitive.\n\nOne subtlety to be aware of is that any ``always @(*)`` assertion that\ndepends upon an ``always @(posedge <clock>)`` assumption might fail before\nthe assumption is applied.  One solution is to use all clocked or all\ncombinatorial blocks.  Another solution is to move the assertion into an\n``always @(posedge <clock>)`` block.\n\nSystemVerilog Functions\n-----------------------\n\nYosys supports five formal related functions: ``$past``, ``$stable``,\n``$changed``, ``$rose``, and ``$fell``.  Internally, these are all implemented\nin terms of the implementation of the ``$past`` operator.\n\nThe ``$past(<expr>)`` function returns the value of ``<expr>`` from one clock\nago.  It can only be used within a clocked always block, since the clock is\nused to define \"one clock ago.\"  It is roughly equivalent to,\n\n.. code-block:: systemverilog\n\n   reg past_value;\n   always @(posedge clock)\n       past_value <= expression;\n\nThere are two keys to the use of ``$past``.  The first is that\n``$past(<expr>)`` can only be used within a clocked always block.  The second\nis that there is no initial value given to any ``$past(<expr>)``.  That means\nthat on the first clock period of any design, ``$past(<expr>)`` will be\nundefined.\n\nYosys supports both one and two arguments to ``$past``.  In the two argument\nform, ``$past(<expr>,N)``, the expression returns the value of ``<expr>``\nfrom ``N`` clocks ago.  ``N`` must be a synthesis time constant.\n\n``$stable(<expr>)`` is short hand for ``<expr> == $past(<expr>)``.\n\n``$changed(<expr>)`` is short hand for ``<expr> != $past(<expr>)``.\n\nWhile the next two functions, ``$rose`` and ``$fell``, can be applied to\nmulti-bit expressions, only the least significant bits will be examined.\nIf we allow that ``<expr>`` has only a single bit within it, perhaps selected\nfrom the least significant bit of a larger expression, then we can\nexpress the following equivalencies.\n\n``$rose(<expr>)`` is short hand for ``<expr> && !$past(<expr)``.\n\n``$fell(<expr>)`` is short hand for ``!<expr> && $past(<expr)``.\n\nLiveness and Fairness\n---------------------\n\nTBD\n\n``assert property (eventually <expr>);``\n\n``assume property (eventually <expr>);``\n\nUnconstrained Variables\n-----------------------\n\nYosys supports four attributes which can be used to create unconstrained\nvariables.  These attributes can be applied to the variable at declaration\ntime, as in\n\n.. code-block:: systemverilog\n\n   (* anyconst *) reg some_value;\n\nThe ``(* anyconst *)`` attribute will create a solver chosen constant.\nIt is often used when verifying memories: the proof allows the solver to\npick a constant address, and then proves that the value at that address\nmatches however the designer desires.\n\n``(* anyseq *)`` differs from ``(* anyconst *)`` in that the solver chosen\nvalue can change from one time step to the next.  In many ways, it is\nsimilar to how the solver will treat an input to the design, with the\ndifference that an ``(* anyseq *)`` variable can originate internal\nto the design.\n\nBoth ``(* anyseq *)`` and ``(* anyconst *)`` marked values can be constrained\nwith assumptions.\n\nYosys supports two other attributes useful to formal processing,\n``(* allconst *)`` and ``(* allseq *)``.  These are very similar in their\nfunctionality to the ``(* anyseq *)`` and ``(* anyconst *)`` attributes we\njust discussed for creating unconstrained values.  Indeed, for both assertions\nand cover statements, the two sets are identical.  Where they differ is\nwith respect to assumptions.  Assumed properties of an ``(* allseq *)``\nor ``(* allconst *)`` value will be applied to all possible values of that\nvariable may take on.  This gets around the annoying reality associated with\ndefining a property using ``(* anyconst *)`` or ``(* anyseq *)`` only to\nhave the solver pick a value which wasn't the one that was constrained.\n\nGlobal Clock\n------------\n\nAccessing the formal timestep becomes important when verifying code in any\nasynchronous context.  In such asynchronous contexts, there may be multiple\nindependent clocks within the design.  Each of the clocks may be defined by\nan assumption allowing the designer to carefully select the relationships\nbetween them.\n\nAll of this requires the ``multiclock on`` line in the SBY options section.\n\nIt also requires the ``(* gclk *)`` attribute.\n\nTo use ``(* gclk *)``, define a register with that attribute, as in:\n\n.. code-block:: systemverilog\n\n    (* gclk *) reg formal_timestep;\n\nYou can then reference this ``formal_timestep`` in the clocking section\nof an always block, as in,\n\n.. code-block:: systemverilog\n\n    always @(posedge formal_timestep)\n        assume(incoming_clock == !$past(incoming_clock));\n\nSystemVerilog Concurrent Assertions\n-----------------------------------\n\nTBD, see :ref:`sva`.\n\n"
  },
  {
    "path": "docs/static/custom.css",
    "content": "/* empty */\n"
  },
  {
    "path": "extern/launcher.c",
    "content": "/* This file comes from the PyPA Setuptools repository, commit 16e452a:\nhttps://github.com/pypa/setuptools\nModifications include this comment and inline inclusion of the LICENSE text. */\n\n/* Copyright (C) 2016 Jason R Coombs <jaraco@jaraco.com>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE. */\n\n/*  Setuptools Script Launcher for Windows\n\n    This is a stub executable for Windows that functions somewhat like\n    Effbot's \"exemaker\", in that it runs a script with the same name but\n    a .py extension, using information from a #! line.  It differs in that\n    it spawns the actual Python executable, rather than attempting to\n    hook into the Python DLL.  This means that the script will run with\n    sys.executable set to the Python executable, where exemaker ends up with\n    sys.executable pointing to itself.  (Which means it won't work if you try\n    to run another Python process using sys.executable.)\n\n    To build/rebuild with mingw32, do this in the setuptools project directory:\n\n       gcc -DGUI=0           -mno-cygwin -O -s -o setuptools/cli.exe launcher.c\n       gcc -DGUI=1 -mwindows -mno-cygwin -O -s -o setuptools/gui.exe launcher.c\n\n    To build for Windows RT, install both Visual Studio Express for Windows 8\n    and for Windows Desktop (both freeware), create \"win32\" application using\n    \"Windows Desktop\" version, create new \"ARM\" target via\n    \"Configuration Manager\" menu and modify \".vcxproj\" file by adding\n    \"<WindowsSDKDesktopARMSupport>true</WindowsSDKDesktopARMSupport>\" tag\n    as child of \"PropertyGroup\" tags that has \"Debug|ARM\" and \"Release|ARM\"\n    properties.\n\n    It links to msvcrt.dll, but this shouldn't be a problem since it doesn't\n    actually run Python in the same process.  Note that using 'exec' instead\n    of 'spawn' doesn't work, because on Windows this leads to the Python\n    executable running in the *background*, attached to the same console\n    window, meaning you get a command prompt back *before* Python even finishes\n    starting.  So, we have to use spawnv() and wait for Python to exit before\n    continuing.  :(\n*/\n\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <windows.h>\n#include <tchar.h>\n#include <fcntl.h>\n#include <unistd.h>\n\nint child_pid=0;\n\nint fail(const char *format, const char *data) {\n    /* Print error message to stderr and return 2 */\n    fprintf(stderr, format, data);\n    return 2;\n}\n\nchar *quoted(char *data) {\n    int i, ln = strlen(data), nb;\n\n    /* We allocate twice as much space as needed to deal with worse-case\n       of having to escape everything. */\n    char *result = (char *)calloc(ln*2+3, sizeof(char));\n    char *presult = result;\n\n    *presult++ = '\"';\n    for (nb=0, i=0; i < ln; i++)\n      {\n        if (data[i] == '\\\\')\n          nb += 1;\n        else if (data[i] == '\"')\n          {\n            for (; nb > 0; nb--)\n              *presult++ = '\\\\';\n            *presult++ = '\\\\';\n          }\n        else\n          nb = 0;\n        *presult++ = data[i];\n      }\n\n    for (; nb > 0; nb--)        /* Deal w trailing slashes */\n      *presult++ = '\\\\';\n\n    *presult++ = '\"';\n    *presult++ = 0;\n    return result;\n}\n\n\n\n\n\n\n\n\n\n\nchar *loadable_exe(char *exename) {\n    /* HINSTANCE hPython;  DLL handle for python executable */\n    char *result;\n\n    /* hPython = LoadLibraryEx(exename, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);\n    if (!hPython) return NULL; */\n\n    /* Return the absolute filename for spawnv */\n    result = (char *)calloc(MAX_PATH, sizeof(char));\n    strncpy(result, exename, MAX_PATH);\n    /*if (result) GetModuleFileNameA(hPython, result, MAX_PATH);\n\n    FreeLibrary(hPython); */\n    return result;\n}\n\n\nchar *find_exe(char *exename, char *script) {\n    char drive[_MAX_DRIVE], dir[_MAX_DIR], fname[_MAX_FNAME], ext[_MAX_EXT];\n    char path[_MAX_PATH], c, *result;\n\n    /* convert slashes to backslashes for uniform search below */\n    result = exename;\n    while (c = *result++) if (c=='/') result[-1] = '\\\\';\n\n    _splitpath(exename, drive, dir, fname, ext);\n    if (drive[0] || dir[0]=='\\\\') {\n        return loadable_exe(exename);   /* absolute path, use directly */\n    }\n    /* Use the script's parent directory, which should be the Python home\n       (This should only be used for bdist_wininst-installed scripts, because\n        easy_install-ed scripts use the absolute path to python[w].exe\n    */\n    _splitpath(script, drive, dir, fname, ext);\n    result = dir + strlen(dir) -1;\n    if (*result == '\\\\') result--;\n    while (*result != '\\\\' && result>=dir) *result-- = 0;\n    _makepath(path, drive, dir, exename, NULL);\n    return loadable_exe(path);\n}\n\n\nchar **parse_argv(char *cmdline, int *argc)\n{\n    /* Parse a command line in-place using MS C rules */\n\n    char **result = (char **)calloc(strlen(cmdline), sizeof(char *));\n    char *output = cmdline;\n    char c;\n    int nb = 0;\n    int iq = 0;\n    *argc = 0;\n\n    result[0] = output;\n    while (isspace(*cmdline)) cmdline++;   /* skip leading spaces */\n\n    do {\n        c = *cmdline++;\n        if (!c || (isspace(c) && !iq)) {\n            while (nb) {*output++ = '\\\\'; nb--; }\n            *output++ = 0;\n            result[++*argc] = output;\n            if (!c) return result;\n            while (isspace(*cmdline)) cmdline++;  /* skip leading spaces */\n            if (!*cmdline) return result;  /* avoid empty arg if trailing ws */\n            continue;\n        }\n        if (c == '\\\\')\n            ++nb;   /* count \\'s */\n        else {\n            if (c == '\"') {\n                if (!(nb & 1)) { iq = !iq; c = 0; }  /* skip \" unless odd # of \\ */\n                nb = nb >> 1;   /* cut \\'s in half */\n            }\n            while (nb) {*output++ = '\\\\'; nb--; }\n            if (c) *output++ = c;\n        }\n    } while (1);\n}\n\nvoid pass_control_to_child(DWORD control_type) {\n    /*\n     * distribute-issue207\n     * passes the control event to child process (Python)\n     */\n    if (!child_pid) {\n        return;\n    }\n    GenerateConsoleCtrlEvent(child_pid,0);\n}\n\nBOOL control_handler(DWORD control_type) {\n    /*\n     * distribute-issue207\n     * control event handler callback function\n     */\n    switch (control_type) {\n        case CTRL_C_EVENT:\n            pass_control_to_child(0);\n            break;\n    }\n    return TRUE;\n}\n\nint create_and_wait_for_subprocess(char* command) {\n    /*\n     * distribute-issue207\n     * launches child process (Python)\n     */\n    DWORD return_value = 0;\n    LPSTR commandline = command;\n    STARTUPINFOA s_info;\n    PROCESS_INFORMATION p_info;\n    ZeroMemory(&p_info, sizeof(p_info));\n    ZeroMemory(&s_info, sizeof(s_info));\n    s_info.cb = sizeof(STARTUPINFO);\n    // set-up control handler callback funciotn\n    SetConsoleCtrlHandler((PHANDLER_ROUTINE) control_handler, TRUE);\n    if (!CreateProcessA(NULL, commandline, NULL, NULL, TRUE, 0, NULL, NULL, &s_info, &p_info)) {\n        fprintf(stderr, \"failed to create process.\\n\");\n        return 0;\n    }\n    child_pid = p_info.dwProcessId;\n    // wait for Python to exit\n    WaitForSingleObject(p_info.hProcess, INFINITE);\n    if (!GetExitCodeProcess(p_info.hProcess, &return_value)) {\n        fprintf(stderr, \"failed to get exit code from process.\\n\");\n        return 0;\n    }\n    return return_value;\n}\n\nchar* join_executable_and_args(char *executable, char **args, int argc)\n{\n    /*\n     * distribute-issue207\n     * CreateProcess needs a long string of the executable and command-line arguments,\n     * so we need to convert it from the args that was built\n     */\n    int len,counter;\n    char* cmdline;\n\n    len=strlen(executable)+2;\n    for (counter=1; counter<argc; counter++) {\n        len+=strlen(args[counter])+1;\n    }\n\n    cmdline = (char*)calloc(len, sizeof(char));\n    sprintf(cmdline, \"%s\", executable);\n    len=strlen(executable);\n    for (counter=1; counter<argc; counter++) {\n        sprintf(cmdline+len, \" %s\", args[counter]);\n        len+=strlen(args[counter])+1;\n    }\n    return cmdline;\n}\n\nint run(int argc, char **argv, int is_gui) {\n\n    char python[256];   /* python executable's filename*/\n    char *pyopt;        /* Python option */\n    char script[256];   /* the script's filename */\n\n    int scriptf;        /* file descriptor for script file */\n\n    char **newargs, **newargsp, **parsedargs; /* argument array for exec */\n    char *ptr, *end;    /* working pointers for string manipulation */\n    char *cmdline;\n    int i, parsedargc;              /* loop counter */\n\n    /* compute script name from our .exe name*/\n    GetModuleFileNameA(NULL, script, sizeof(script));\n    end = script + strlen(script);\n    while( end>script && *end != '.')\n        *end-- = '\\0';\n    *end-- = '\\0';\n    strcat(script, (GUI ? \"-script.pyw\" : \"-script.py\"));\n\n    /* figure out the target python executable */\n\n    scriptf = open(script, O_RDONLY);\n    if (scriptf == -1) {\n        return fail(\"Cannot open %s\\n\", script);\n    }\n    end = python + read(scriptf, python, sizeof(python));\n    close(scriptf);\n\n    ptr = python-1;\n    while(++ptr < end && *ptr && *ptr!='\\n' && *ptr!='\\r') {;}\n\n    *ptr-- = '\\0';\n\n    if (strncmp(python, \"#!\", 2)) {\n        /* default to python.exe if no #! header */\n        strcpy(python, \"#!python.exe\");\n    }\n\n    parsedargs = parse_argv(python+2, &parsedargc);\n\n    /* Using spawnv() can fail strangely if you e.g. find the Cygwin\n       Python, so we'll make sure Windows can find and load it */\n\n    ptr = find_exe(parsedargs[0], script);\n    if (!ptr) {\n        return fail(\"Cannot find Python executable %s\\n\", parsedargs[0]);\n    }\n\n    /* printf(\"Python executable: %s\\n\", ptr); */\n\n    /* Argument array needs to be\n       parsedargc + argc, plus 1 for null sentinel */\n\n    newargs = (char **)calloc(parsedargc + argc + 1, sizeof(char *));\n    newargsp = newargs;\n\n    *newargsp++ = quoted(ptr);\n    for (i = 1; i<parsedargc; i++) *newargsp++ = quoted(parsedargs[i]);\n\n    *newargsp++ = quoted(script);\n    for (i = 1; i < argc; i++)     *newargsp++ = quoted(argv[i]);\n\n    *newargsp++ = NULL;\n\n    /* printf(\"args 0: %s\\nargs 1: %s\\n\", newargs[0], newargs[1]); */\n\n    if (is_gui) {\n        /* Use exec, we don't need to wait for the GUI to finish */\n        execv(ptr, (char * const *)(newargs));\n        return fail(\"Could not exec %s\", ptr);   /* shouldn't get here! */\n    }\n\n    /*\n     * distribute-issue207: using CreateProcessA instead of spawnv\n     */\n    cmdline = join_executable_and_args(ptr, newargs, parsedargc + argc);\n    return create_and_wait_for_subprocess(cmdline);\n}\n\nint WINAPI WinMain(HINSTANCE hI, HINSTANCE hP, LPSTR lpCmd, int nShow) {\n    return run(__argc, __argv, GUI);\n}\n\nint main(int argc, char** argv) {\n    return run(argc, argv, GUI);\n}\n"
  },
  {
    "path": "sbysrc/sby.py",
    "content": "#!/usr/bin/env python3\n#\n# SymbiYosys (sby) -- Front-end for Yosys-based formal verification flows\n#\n# Copyright (C) 2016  Claire Xenia Wolf <claire@yosyshq.com>\n#\n# Permission to use, copy, modify, and/or distribute this software for any\n# purpose with or without fee is hereby granted, provided that the above\n# copyright notice and this permission notice appear in all copies.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n#\n\nimport json, os, sys, shutil, tempfile, re\n##yosys-sys-path##\nfrom sby_cmdline import parser_func\nfrom sby_core import SbyConfig, SbyTask, SbyAbort, SbyTaskloop, process_filename, dress_message\nfrom sby_jobserver import SbyJobClient, process_jobserver_environment\nfrom sby_status import SbyStatusDb, remove_db, FileInUseError\nimport time, platform, click\n\nrelease_version = 'unknown SBY version'\n##yosys-release-version##\n\nprocess_jobserver_environment()  # needs to be called early\n\nparser = parser_func(release_version)\n\nargs = parser.parse_args()\n\nsbyfile = args.sbyfile\nworkdir = args.workdir\nworkdir_prefix = args.workdir_prefix\nif workdir is not None and workdir_prefix is not None:\n    print(\"ERROR: -d and --prefix are mutually exclusive.\", file=sys.stderr)\n    sys.exit(1)\ntasknames = args.arg_tasknames + args.tasknames\nopt_force = args.force\nopt_backup = args.backup\nopt_tmpdir = args.tmpdir\nexe_paths = args.exe_paths\nthrow_err = args.throw_err\ndump_cfg = args.dump_cfg\ndump_tags = args.dump_tags\ndump_tasks = args.dump_tasks\ndump_defaults = args.dump_defaults\ndump_taskinfo = args.dump_taskinfo\ndump_files = args.dump_files\nreusedir = False\nsetupmode = args.setupmode\nlinkmode = args.linkmode\nautotune = args.autotune\nautotune_config = args.autotune_config\nsequential = args.sequential\njobcount = args.jobcount\ninit_config_file = args.init_config_file\nstatus_show = args.status\nstatus_reset = args.status_reset\nstatus_cancels = args.status_cancels\ntask_status = args.task_status\nstatus_live_formats = args.live_formats\nstatus_format = args.status_format\nstatus_latest = args.status_latest\n\nif autotune and linkmode:\n    print(\"ERROR: --link flag currently not available with --autotune\")\n    sys.exit(1)\n\nif status_show or status_reset or task_status or status_format:\n    target = workdir_prefix or workdir or sbyfile\n    if target is None:\n        print(\"ERROR: Specify a .sby config file or working directory to use --status.\")\n        sys.exit(1)\n    if not os.path.isdir(target) and target.endswith('.sby'):\n        target = target[:-4]\n    if not os.path.isdir(target):\n        print(f\"ERROR: No directory found at {target!r}.\", file=sys.stderr)\n        sys.exit(1)\n\n    try:\n        with open(f\"{target}/status.path\", \"r\") as status_path_file:\n            status_path = f\"{target}/{status_path_file.read().rstrip()}\"\n    except FileNotFoundError:\n        status_path = f\"{target}/status.sqlite\"\n\n    if not os.path.exists(status_path):\n        print(f\"ERROR: No status database found at {status_path!r}.\", file=sys.stderr)\n        sys.exit(1)\n\n    status_db = SbyStatusDb(status_path, task=None)\n\n    if status_reset:\n        status_db.reset()\n    elif status_db.test_schema():\n        print(f\"ERROR: Status database does not match expected format.  Use --statusreset to reset.\")\n        sys.exit(1)\n\n    if status_show:\n        status_db.print_status_summary(status_latest)\n\n    if status_format:\n        status_db.print_status_summary_fmt(tasknames, status_format, status_latest)\n        \n    if task_status:\n        status_db.print_task_summary()\n\n    status_db.db.close()\n\n    if status_live_formats:\n        print(f\"WARNING: --live option found but not used.\")\n\n    sys.exit(0)\nelif status_latest:\n    print(f\"WARNING: --latest flag found but not used.\")\n\n\nif sbyfile is not None:\n    if os.path.isdir(sbyfile):\n        if workdir is not None:\n            print(\"ERROR: Can't use -d when running in existing directory.\", file=sys.stderr)\n            sys.exit(1)\n        workdir = sbyfile\n        sbyfile += \"/config.sby\"\n        reusedir = True\n        if not opt_force and os.path.exists(workdir + \"/model\"):\n            print(\"ERROR: Use -f to re-run in existing directory.\", file=sys.stderr)\n            sys.exit(1)\n        if tasknames:\n            print(\"ERROR: Can't use tasks when running in existing directory.\", file=sys.stderr)\n            sys.exit(1)\n        if setupmode:\n            print(\"ERROR: Can't use --setup with existing directory.\", file=sys.stderr)\n            sys.exit(1)\n        if opt_force:\n            for f in \"PASS FAIL UNKNOWN ERROR TIMEOUT\".split():\n                if os.path.exists(workdir + \"/\" + f):\n                    os.remove(workdir + \"/\" + f)\n    elif not sbyfile.endswith(\".sby\"):\n        print(\"ERROR: Sby file does not have .sby file extension.\", file=sys.stderr)\n        sys.exit(1)\n\nelif init_config_file is not None:\n    sv_file = init_config_file + \".sv\"\n    sby_file = init_config_file + \".sby\"\n    with open(sby_file, 'w') as config:\n        config.write(f\"\"\"[options]\nmode bmc\n\n[engines]\nsmtbmc\n\n[script]\nread -formal {sv_file}\nprep -top top\n\n[files]\n{sv_file}\n\"\"\")\n\n    print(f\"sby config written to {sby_file}\", file=sys.stderr)\n    sys.exit(0)\n\nearly_logmsgs = list()\n\ndef early_log(workdir, msg):\n    early_logmsgs.append(dress_message(workdir, msg))\n    click.echo(early_logmsgs[-1])\n\ndef read_sbyconfig(sbydata, taskname):\n    cfgdata = list()\n    tasklist = list()\n    defaultlist = None\n    task_matched = False\n\n    pycode = None\n    tasks_section = False\n    task_tags_active = set()\n    task_tags_all = set()\n    task_skip_block = False\n    task_skiping_blocks = False\n\n    def handle_line(line):\n        nonlocal pycode, tasks_section, task_tags_active, task_tags_all\n        nonlocal task_skip_block, task_skiping_blocks, task_matched\n        nonlocal defaultlist\n\n        line = line.rstrip(\"\\n\")\n        line = line.rstrip(\"\\r\")\n\n        if pycode is not None:\n            if line == \"--pycode-end--\":\n                gdict = globals().copy()\n                gdict[\"task\"] = taskname\n                gdict[\"tags\"] = set(task_tags_active)\n                gdict[\"output_lines\"] = list()\n                exec(\"def output(line):\\n  output_lines.append(line)\\n\" + pycode, gdict)\n                pycode = None\n                for line in gdict[\"output_lines\"]:\n                    handle_line(line)\n                return\n            pycode += line + \"\\n\"\n            return\n\n        if line == \"--pycode-begin--\":\n            pycode = \"\"\n            return\n\n        if tasks_section and line.startswith(\"[\"):\n            tasks_section = False\n\n        if task_skiping_blocks:\n            if line.strip() == \"--\":\n                task_skip_block = False\n                task_skiping_blocks = False\n                return\n\n        if not tasks_section:\n            found_task_tag = False\n            task_skip_line = False\n\n            for t in task_tags_all:\n                if line.startswith(t+\":\"):\n                    line = line[len(t)+1:].lstrip()\n                    match = t in task_tags_active\n                elif line.startswith(\"~\"+t+\":\"):\n                    line = line[len(t)+2:].lstrip()\n                    match = t not in task_tags_active\n                else:\n                    continue\n\n                if line == \"\":\n                    task_skiping_blocks = True\n                    task_skip_block = not match\n                    task_skip_line = True\n                else:\n                    task_skip_line = not match\n\n                found_task_tag = True\n                break\n\n            if len(task_tags_all) and not found_task_tag:\n                tokens = line.split()\n                if len(tokens) > 0 and tokens[0][0] == line[0] and tokens[0].endswith(\":\"):\n                    print(f\"ERROR: Invalid task specifier \\\"{tokens[0]}\\\".\", file=sys.stderr)\n                    sys.exit(1)\n\n            if task_skip_line or task_skip_block:\n                return\n\n        if tasks_section:\n            if taskname is None:\n                cfgdata.append(line)\n            if line.startswith(\"#\"):\n                return\n\n            line = line.split(\":\")\n            if len(line) == 1:\n                line = line[0].split()\n                if len(line) > 0:\n                    lhs, rhs = line[:1], line[1:]\n                else:\n                    return\n            elif len(line) == 2:\n                lhs, rhs = line[0].split(), line[1].split()\n            else:\n                print(\"ERROR: Syntax error in tasks block.\", file=sys.stderr)\n                sys.exit(1)\n\n            for tagname in rhs:\n                if tagname == \"default\":\n                    continue\n                if all(map(lambda c: c not in \"(?*.[]|)\", tagname)):\n                    task_tags_all.add(tagname)\n\n            for tname in lhs:\n                if all(map(lambda c: c not in \"(?*.[]|)\", tname)):\n                    if tname not in tasklist:\n                        tasklist.append(tname)\n                    if \"default\" in rhs:\n                        if defaultlist is None:\n                            defaultlist = list()\n                        if tname not in defaultlist:\n                            defaultlist.append(tname)\n                    task_tags_all.add(tname)\n\n                if taskname is not None and re.fullmatch(tname, taskname):\n                    task_matched = True\n                    task_tags_active.add(tname)\n                    for tagname in rhs:\n                        if tagname == \"default\":\n                            continue\n                        if all(map(lambda c: c not in \"(?*.[]|)\", tagname)):\n                            task_tags_active.add(tagname)\n                        else:\n                            for t in task_tags_all:\n                                if re.fullmatch(tagname, t):\n                                    task_tags_active.add(t)\n\n        elif line == \"[tasks]\":\n            if taskname is None:\n                cfgdata.append(line)\n            tasks_section = True\n\n        else:\n            cfgdata.append(line)\n\n    for line in sbydata:\n        handle_line(line)\n\n    if taskname is not None and not task_matched:\n        print(f\"ERROR: Task name '{taskname}' didn't match any lines in [tasks].\", file=sys.stderr)\n        sys.exit(1)\n\n    if defaultlist is None:\n        defaultlist = tasklist\n\n    return cfgdata, tasklist, defaultlist, sorted(list(task_tags_all))\n\n\nsbydata = list()\nif sbyfile is None:\n    print(\"Reading .sby configuration from stdin:\")\nwith (open(sbyfile, \"r\") if sbyfile is not None else sys.stdin) as f:\n    for line in f:\n        sbydata.append(line)\n\nif dump_cfg:\n    assert len(tasknames) < 2\n    sbyconfig, _, _, _ = read_sbyconfig(sbydata, tasknames[0] if len(tasknames) else None)\n    print(\"\\n\".join(sbyconfig))\n    sys.exit(0)\n\nif dump_files:\n    file_set = set()\n\n    def find_files(taskname):\n        sbyconfig, _, _, _ = read_sbyconfig(sbydata, taskname)\n\n        start_index = -1\n        for i in range(len(sbyconfig)):\n            if sbyconfig[i].strip() == \"[files]\":\n                start_index = i\n                break\n\n        if start_index == -1:\n            return\n\n        for line in sbyconfig[start_index+1:]:\n            line = line.strip()\n            if line.startswith(\"[\"):\n                break\n            if line == \"\" or line.startswith(\"#\"):\n                continue\n            filename = line.split()[-1]\n            file_set.add(process_filename(filename))\n\n    if len(tasknames):\n        for taskname in tasknames:\n            find_files(taskname)\n    else:\n        find_files(None)\n    print(\"\\n\".join(str(f) for f in file_set))\n    sys.exit(0)\n\nif dump_tags:\n    _, _, _, tagnames = read_sbyconfig(sbydata, None)\n    for tag in tagnames:\n        print(tag)\n    sys.exit(0)\n\nif dump_tasks or dump_defaults or dump_tags:\n    _, tasks, dtasks, tags = read_sbyconfig(sbydata, None)\n    for name in tasks if dump_tasks else dtasks if dump_defaults else tags:\n        if name is not None:\n            print(name)\n    sys.exit(0)\n\nif dump_taskinfo:\n    _, _, tasknames, _ = read_sbyconfig(sbydata, None)\n    taskinfo = {}\n    for taskname in tasknames or [None]:\n        task_sbyconfig, _, _, _ = read_sbyconfig(sbydata, taskname)\n        taskinfo[taskname or \"\"] = info = {}\n        cfg = SbyConfig()\n        cfg.parse_config(task_sbyconfig)\n        taskinfo[taskname or \"\"] = {\n            \"mode\": cfg.options.get(\"mode\"),\n            \"engines\": cfg.engines,\n            \"script\": cfg.script,\n            \"cancelledby\": cfg.cancelledby,\n        }\n    print(json.dumps(taskinfo, indent=2))\n    sys.exit(0)\n\nif len(tasknames) == 0:\n    _, _, tasknames, _ = read_sbyconfig(sbydata, None)\n    if len(tasknames) == 0:\n        tasknames = [None]\n\nif (workdir is not None) and (len(tasknames) != 1):\n    print(\"ERROR: Exactly one task is required when workdir is specified. Specify the task or use --prefix instead of -d.\", file=sys.stderr)\n    sys.exit(1)\n\n# Check there are no files in this dir or any of its subdirs\ndef check_dirtree_empty_of_files(dir):\n    list = os.listdir(dir)\n    if list:\n        for fn in list:\n            child_dir = os.path.join(dir, fn)\n            if os.path.isdir(child_dir) and check_dirtree_empty_of_files(child_dir):\n                continue\n            return False\n    return True\n\ndef start_task(taskloop, taskname):\n    sbyconfig, _, _, _ = read_sbyconfig(sbydata, taskname)\n\n    my_opt_tmpdir = opt_tmpdir\n    my_workdir = None\n    my_status_db = None\n\n    if workdir is not None:\n        my_workdir = workdir\n    elif workdir_prefix is not None:\n        if taskname is None:\n            my_workdir = workdir_prefix\n        else:\n            my_workdir = workdir_prefix + \"_\" + taskname\n            my_status_db = f\"../{os.path.basename(workdir_prefix)}/status.sqlite\"\n\n    if my_workdir is None and sbyfile is not None and not my_opt_tmpdir:\n        my_workdir = sbyfile[:-4]\n        if taskname is not None:\n            my_status_db = f\"../{os.path.basename(my_workdir)}/status.sqlite\"\n            my_workdir += \"_\" + taskname\n\n    if my_workdir is not None:\n        if opt_backup:\n            backup_idx = 0\n            while os.path.exists(\"{}.bak{:03d}\".format(my_workdir, backup_idx)):\n                backup_idx += 1\n            early_log(my_workdir, \"Moving directory '{}' to '{}'.\".format(my_workdir, \"{}.bak{:03d}\".format(my_workdir, backup_idx)))\n            shutil.move(my_workdir, \"{}.bak{:03d}\".format(my_workdir, backup_idx))\n\n        if opt_force and not reusedir:\n            early_log(my_workdir, f\"Removing directory '{os.path.abspath(my_workdir)}'.\")\n            shutil.rmtree(my_workdir, ignore_errors=True)\n\n        if reusedir:\n            pass\n        elif os.path.isdir(my_workdir) and not check_dirtree_empty_of_files(my_workdir):\n            print(f\"ERROR: Directory '{my_workdir}' already exists, use -f to overwrite the existing directory.\")\n            sys.exit(1)\n        elif not os.path.isdir(my_workdir):\n            os.makedirs(my_workdir)\n\n    else:\n        my_opt_tmpdir = True\n        my_workdir = tempfile.mkdtemp()\n\n    if os.getenv(\"SBY_WORKDIR_GITIGNORE\"):\n        with open(f\"{my_workdir}/.gitignore\", \"w\") as gitignore:\n            print(\"*\", file=gitignore)\n\n    if my_status_db is not None:\n        os.makedirs(f\"{my_workdir}/{os.path.dirname(my_status_db)}\", exist_ok=True)\n        if os.getenv(\"SBY_WORKDIR_GITIGNORE\"):\n            with open(f\"{my_workdir}/{os.path.dirname(my_status_db)}/.gitignore\", \"w\") as gitignore:\n                print(\"*\", file=gitignore)\n        with open(f\"{my_workdir}/status.path\", \"w\") as status_path:\n            print(my_status_db, file=status_path)\n        if os.path.exists(f\"{my_workdir}/{my_status_db}\") and opt_force:\n            try:\n                remove_db(f\"{my_workdir}/{my_status_db}\")\n            except FileInUseError:\n                # don't delete an open database\n                pass\n\n    junit_ts_name = os.path.basename(sbyfile[:-4]) if sbyfile is not None else workdir if workdir is not None else \"stdin\"\n    junit_tc_name = taskname if taskname is not None else \"default\"\n\n    if reusedir:\n        junit_filename = os.path.basename(my_workdir)\n    elif sbyfile is not None:\n        junit_filename = os.path.basename(sbyfile[:-4])\n        if taskname is not None:\n            junit_filename += \"_\" + taskname\n    elif taskname is not None:\n        junit_filename = taskname\n    else:\n        junit_filename = \"junit\"\n\n    task = SbyTask(sbyconfig, my_workdir, early_logmsgs, reusedir, status_cancels, taskloop, name=taskname, live_formats=status_live_formats)\n\n    for k, v in exe_paths.items():\n        task.exe_paths[k] = v\n\n    def exit_callback():\n        if not autotune and not setupmode:\n            task.summarize()\n            task.write_summary_file()\n\n        if my_opt_tmpdir:\n            task.log(f\"Removing directory '{my_workdir}'.\")\n            shutil.rmtree(my_workdir, ignore_errors=True)\n\n        if setupmode:\n            task.log(f\"SETUP COMPLETE (rc={task.retcode})\")\n        else:\n            task.log(f\"DONE ({task.status}, rc={task.retcode})\")\n        task.logfile.close()\n\n        if not my_opt_tmpdir and not setupmode and not autotune:\n            with open(\"{}/{}.xml\".format(task.workdir, junit_filename), \"w\") as f:\n                task.print_junit_result(f, junit_ts_name, junit_tc_name, junit_format_strict=False)\n\n            with open(f\"{task.workdir}/status\", \"w\") as f:\n                print(f\"{task.status} {task.retcode} {task.total_time}\", file=f)\n\n    task.exit_callback = exit_callback\n\n    if not autotune:\n        task.setup_procs(setupmode, linkmode)\n        task.task_local_abort = not throw_err\n\n    return task\n\nfailed = []\nretcode = 0\n\nif jobcount is not None and jobcount < 1:\n    print(\"ERROR: The -j option requires a positive number as argument\")\n    sys.exit(1)\n\n# Autotune is already parallel, parallelizing it across tasks needs some more work\nif autotune:\n    sequential = True\n\nif sequential:\n    if autotune:\n        jobclient = None  # TODO make autotune use a jobclient\n    else:\n        jobclient = SbyJobClient(jobcount)\n\n    for taskname in tasknames:\n        taskloop = SbyTaskloop(jobclient)\n        try:\n            task = start_task(taskloop, taskname)\n        except SbyAbort:\n            if throw_err:\n                raise\n            sys.exit(1)\n\n        if autotune:\n            from sby_autotune import SbyAutotune\n            SbyAutotune(task, autotune_config).run()\n        elif setupmode:\n            task.exit_callback()\n        else:\n            taskloop.run()\n        retcode |= task.retcode\n        if task.retcode:\n            failed.append(taskname)\nelse:\n    jobclient = SbyJobClient(jobcount)\n    taskloop = SbyTaskloop(jobclient)\n\n    tasks = {}\n    for taskname in tasknames:\n        try:\n            tasks[taskname] = start_task(taskloop, taskname)\n        except SbyAbort:\n            if throw_err:\n                raise\n            sys.exit(1)\n\n    taskloop.run()\n\n    for taskname, task in tasks.items():\n        retcode |= task.retcode\n        if task.retcode:\n            failed.append(taskname)\n\nif failed and (len(tasknames) > 1 or tasknames[0] is not None):\n    click.echo(dress_message(None, click.style(f\"The following tasks failed: {failed}\", fg=\"red\", bold=True)))\n\nsys.exit(retcode)\n"
  },
  {
    "path": "sbysrc/sby_autotune.py",
    "content": "#\n# SymbiYosys (sby) -- Front-end for Yosys-based formal verification flows\n#\n# Copyright (C) 2022  Jannis Harder <jix@yosyshq.com> <me@jix.one>\n#\n# Permission to use, copy, modify, and/or distribute this software for any\n# purpose with or without fee is hereby granted, provided that the above\n# copyright notice and this permission notice appear in all copies.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n#\n\nimport os\nimport re\nimport subprocess\nfrom shutil import rmtree, which\nfrom time import monotonic\nfrom sby_core import SbyAbort, SbyTask\n\n\nclass SbyAutotuneConfig:\n    \"\"\"Autotune configuration parsed from the sby file or an external autotune config\n    file.\n    \"\"\"\n    def __init__(self):\n        self.timeout = None\n        self.soft_timeout = 60\n        self.wait_percentage = 50\n        self.wait_seconds = 10\n        self.parallel = \"auto\"\n\n        self.presat = None\n        self.incr = \"auto\"\n        self.incr_threshold = 20\n        self.mem = \"auto\"\n        self.mem_threshold = 10240\n        self.forall = \"auto\"\n\n    def config_line(self, log, line, file_kind=\"sby file\"):\n        option, *arg = line.split(None, 1)\n        if not arg:\n            log.error(f\"{file_kind} syntax error: {line}\")\n        arg = arg[0].strip()\n\n        BOOL_OR_ANY = {\"on\": True, \"off\": False, \"any\": None}\n        BOOL_ANY_OR_AUTO = {\"on\": True, \"off\": False, \"any\": None, \"auto\": \"auto\"}\n        ON_ANY_OR_AUTO = {\"on\": True, \"any\": None, \"auto\": \"auto\"}\n\n        def enum_option(values):\n            if arg not in values:\n                values_str = ', '.join(repr(value) for value in sorted(values))\n                log.error(f\"{file_kind}: invalid value '{arg}' for autotune option {option}, valid values are: {values_str}\")\n            return values[arg]\n\n        def int_option():\n            try:\n                return int(arg)\n            except ValueError:\n                log.error(f\"{file_kind}: invalid value '{arg}' for autotune option {option}, expected an integer value\")\n\n        if option == \"timeout\":\n            self.timeout = \"none\" if arg == \"none\" else int_option()\n        elif option == \"soft_timeout\":\n            self.soft_timeout = int_option()\n        elif option == \"wait\":\n            self.wait_percentage = 0\n            self.wait_seconds = 0\n            for part in arg.split(\"+\"):\n                part = part.strip()\n                if part.endswith(\"%\"):\n                    self.wait_percentage += int(part[:-1].strip())\n                else:\n                    self.wait_seconds += int(part)\n        elif option == \"parallel\":\n            self.parallel = \"auto\" if arg == \"auto\" else int_option()\n        elif option == \"presat\":\n            self.presat = enum_option(BOOL_OR_ANY)\n        elif option == \"incr\":\n            self.incr = enum_option(BOOL_ANY_OR_AUTO)\n        elif option == \"incr_threshold\":\n            self.incr_threshold = int_option()\n        elif option == \"mem\":\n            self.mem = enum_option(ON_ANY_OR_AUTO)\n        elif option == \"mem_threshold\":\n            self.mem_threshold = int_option()\n        elif option == \"forall\":\n            self.forall = enum_option(ON_ANY_OR_AUTO)\n        else:\n            log.error(f\"{file_kind} syntax error: {line}\")\n\n    def parse_file(self, log, file):\n        for line in file:\n            line = re.sub(r\"\\s*(\\s#.*)?$\", \"\", line)\n            if line == \"\" or line[0] == \"#\":\n                continue\n            self.config_line(log, line.rstrip(), \"autotune configuration file\")\n\nclass SbyAutotuneCandidate:\n    \"\"\"An engine configuration to try and its current state during autotuning.\n    \"\"\"\n    def __init__(self, autotune, engine):\n        self.autotune = autotune\n        self.engine = engine\n\n        self.state = \"pending\"\n        self.engine_idx = None\n        self.info = f\"{' '.join(self.engine)}:\"\n        self.suspended = 0\n        self.suspend = 1\n\n        self.engine_retcode = None\n        self.engine_status = None\n        self.total_adjusted_time = None\n\n        self.soft_timeout = self.autotune.config.soft_timeout\n\n        if tuple(self.engine) not in self.autotune.candidate_engines:\n            self.autotune.active_candidates.append(self)\n            self.autotune.candidate_engines.add(tuple(self.engine))\n\n    def set_engine_idx(self, idx):\n        self.engine_idx = idx\n        self.info = f\"engine_{idx} ({' '.join(self.engine)}):\"\n\n    def set_running(self):\n        assert not self.suspended\n        assert self.state == \"pending\"\n        assert self in self.autotune.active_candidates\n        self.state = \"running\"\n\n    def retry_later(self):\n        assert self.state == \"running\"\n        assert self in self.autotune.active_candidates\n        self.state = \"pending\"\n        self.soft_timeout *= 2\n        self.suspended = self.suspend\n\n    def timed_out(self):\n        assert self.state == \"running\"\n        self.autotune.active_candidates.remove(self)\n        self.state = \"timeout\"\n\n    def failed(self):\n        assert self.state == \"running\"\n        self.autotune.active_candidates.remove(self)\n        self.autotune.failed_candidates.append(self)\n        self.state = \"failed\"\n\n    def finished(self):\n        assert self.state == \"running\"\n        self.autotune.active_candidates.remove(self)\n        self.autotune.finished_candidates.append(self)\n        self.state = \"finished\"\n\n    def threads(self):\n        if self.autotune.config.mode == \"prove\" and self.engine[0] == \"smtbmc\":\n            return 2\n        return 1\n\n\nclass SbyAutotune:\n    \"\"\"Performs automatic engine selection for a given task.\n    \"\"\"\n    def __init__(self, task, config_file=None):\n        self.task_exit_callback = task.exit_callback\n        task.exit_callback = lambda: None\n        task.check_timeout = lambda: None\n        task.status = \"TIMEOUT\"\n        task.retcode = 8\n\n        task.proc_failed = self.proc_failed\n\n        self.config = None\n\n        if config_file:\n            with open(config_file) as config:\n                self.config.parse_file(task, config)\n\n        self.task = task\n\n        self.done = False\n        self.threads_running = 0\n\n        self.next_engine_idx = 0\n\n        self.model_requests = {}\n\n        self.timeout = None\n        self.best_time = None\n        self.have_pending_candidates = False\n\n        self.active_candidates = []\n        self.finished_candidates = []\n        self.failed_candidates = []\n\n        self.candidate_engines = set()\n\n    def available(self, tool):\n        if not which(tool):\n            return False\n\n        if tool == \"btorsim\":\n            error_msg = subprocess.run(\n                [\"btorsim\", \"--vcd\"],\n                capture_output=True,\n                text=True,\n            ).stderr\n            if \"invalid command line option\" in error_msg:\n                self.log('found version of \"btorsim\" is too old and does not support the --vcd option')\n                return False\n\n        return True\n\n    def candidate(self, *engine):\n        flat_engine = []\n        def flatten(part):\n            if part is None:\n                return\n            elif isinstance(part, (tuple, list)):\n                for subpart in part:\n                    flatten(subpart)\n            else:\n                flat_engine.append(part)\n\n        flatten(engine)\n\n        SbyAutotuneCandidate(self, flat_engine)\n\n    def configure(self):\n        self.config.mode = self.task.opt_mode\n        self.config.skip = self.task.opt_skip\n\n        if self.config.incr == \"auto\":\n            self.config.incr = None\n            if self.config.mode != \"live\":\n                steps = self.task.opt_depth - (self.config.skip or 0)\n                if steps > self.config.incr_threshold:\n                    self.log(f\"checking more than {self.config.incr_threshold} timesteps ({steps}), disabling nonincremental smtbmc\")\n                    self.config.incr = True\n\n        if self.config.mem == \"auto\":\n            self.config.mem = None\n            if self.task.design is None:\n                self.log(\"warning: unknown amount of memory bits in design\")\n            elif self.task.design.memory_bits > self.config.mem_threshold:\n                self.log(\n                    f\"more than {self.config.mem_threshold} bits of memory in design ({self.task.design.memory_bits} bits), \"\n                    \"disabling engines without native memory support\"\n                )\n                self.config.mem = True\n\n        if self.config.forall == \"auto\":\n            self.config.forall = None\n            if self.task.design.forall:\n                self.log(\"design uses $allconst/$allseq, disabling engines without forall support\")\n                self.config.forall = True\n\n        if self.config.mode not in [\"bmc\", \"prove\"]:\n            self.config.presat = None\n\n        if self.config.parallel == \"auto\":\n            try:\n                self.config.parallel = len(os.sched_getaffinity(0))\n            except AttributeError:\n                self.config.parallel = os.cpu_count()  # TODO is this correct?\n\n        if self.config.timeout is None:\n            self.config.timeout = self.task.opt_timeout\n        elif self.config.timeout == \"none\":\n            self.config.timeout = None\n\n    def build_candidates(self):\n        if self.config.mode == \"live\":\n            # Not much point in autotuning here...\n            self.candidate(\"aiger\", \"suprove\")\n            return\n\n        if self.config.presat is None:\n            presat_flags = [None, \"--nopresat\"]\n        elif self.config.presat:\n            presat_flags = [None]\n        else:\n            presat_flags = [\"--nopresat\"]\n\n        if self.config.incr is None:\n            noincr_flags = [None, [\"--\", \"--noincr\"]]\n        elif self.config.incr:\n            noincr_flags = [None]\n        else:\n            noincr_flags = [[\"--\", \"--noincr\"]]\n\n        if self.config.forall:\n            self.log('disabling engines \"smtbmc boolector\" and \"smtbmc bitwuzla\" as they do not support forall')\n        else:\n            for solver in [\"boolector\", \"bitwuzla\"]:\n                if not self.available(solver):\n                    self.log(f'disabling engine \"smtbmc {solver}\" as the solver \"{solver}\" was not found')\n                    continue\n                for noincr in noincr_flags:\n                    for presat in presat_flags:\n                        self.candidate(\"smtbmc\", presat, solver, noincr)\n\n        if not self.available(\"btorsim\"):\n            self.log('disabling engine \"btor\" as the \"btorsim\" tool was not found')\n        elif self.config.forall:\n            self.log('disabling engine \"btor\" as it does not support forall')\n        else:\n            if self.config.mode in [\"bmc\", \"cover\"]:\n                if not self.available(\"btormc\"):\n                    self.log('disabling engine \"btor btormc\" as the \"btormc\" tool was not found')\n                elif self.config.presat:\n                    self.log('disabling engine \"btor btormc\" as it does not support presat checking')\n                else:\n                    self.candidate(\"btor\", \"btormc\")\n\n            if self.config.mode == \"bmc\":\n                if not self.available(\"pono\"):\n                    self.log('disabling engine \"btor btormc\" as the \"btormc\" tool was not found')\n                elif self.config.presat:\n                    self.log('disabling engine \"btor pono\" as it does not support presat checking')\n                elif self.config.skip:\n                    self.log('disabling engine \"btor pono\" as it does not support the \"skip\" option')\n                else:\n                    self.candidate(\"btor\", \"pono\")\n\n        for solver in [\"yices\", \"z3\"]:\n            if not self.available(solver):\n                self.log(f'disabling engine \"smtbmc {solver}\" as the solver \"{solver}\" was not found')\n                continue\n            for unroll in [\"--unroll\", \"--nounroll\"]:\n                if solver == \"yices\" and self.config.forall:\n                    self.log('disabling engine \"smtbmc yices\" due to limited forall support')\n                    # TODO yices implicitly uses --noincr for forall problems and\n                    # requires --stbv which does not play well with memory, still test it?\n                    continue\n\n                stmode = \"--stdt\" if self.config.forall else None\n\n                for noincr in noincr_flags:\n                    for presat in presat_flags:\n                        self.candidate(\"smtbmc\", presat, stmode, unroll, solver, noincr)\n\n        if self.config.mode not in [\"bmc\", \"prove\"]:\n            pass\n        elif self.config.presat:\n            self.log('disabling engines \"abc\" and \"aiger\" as they do not support presat checking')\n        elif self.config.forall:\n            self.log('disabling engines \"abc\" and \"aiger\" as they do not support forall')\n        elif self.config.mem:\n            self.log('disabling engines \"abc\" and \"aiger\" as they do not support memory')\n        elif self.config.skip:\n            self.log('disabling engines \"abc\" and \"aiger\" as they do not support the \"skip\" option')\n        elif self.config.mode == \"bmc\":\n            self.candidate(\"abc\", \"bmc3\")\n\n            if not self.available(\"aigbmc\"):\n                self.log('disabling engine \"aiger aigbmc\" as the \"aigbmc\" tool was not found')\n            else:\n                self.candidate(\"aiger\", \"aigbmc\")\n            # abc sim3 will never finish\n        elif self.config.mode == \"prove\":\n            self.candidate(\"abc\", \"pdr\")\n\n            if not self.available(\"suprove\"):\n                self.log('disabling engine \"aiger suprove\" as the \"suprove\" tool was not found')\n            else:\n                self.candidate(\"aiger\", \"suprove\")\n            # avy seems to crash in the presence of assumptions\n\n    def log(self, message):\n        self.task.log(message)\n\n    def run(self):\n        self.task.handle_non_engine_options()\n        self.task.setup_status_db(':memory:')\n        self.config = self.task.autotune_config or SbyAutotuneConfig()\n\n        if \"expect\" not in self.task.options:\n            self.task.expect = [\"PASS\", \"FAIL\"]\n            # TODO check that solvers produce consistent results?\n\n        if \"TIMEOUT\" in self.task.expect:\n            self.task.error(\"cannot autotune a task with option 'expect timeout'\")\n\n        if self.task.reusedir:\n            rmtree(f\"{self.task.workdir}/model\", ignore_errors=True)\n        else:\n            self.task.copy_src()\n\n        self.model(None, \"prep\")\n        self.task.taskloop.run()\n\n        if self.task.status == \"ERROR\":\n            return\n\n        self.configure()\n\n        self.build_candidates()\n        if not self.active_candidates:\n            self.task.error(\"no supported engines found for the current configuration and design\")\n        self.log(f\"testing {len(self.active_candidates)} engine configurations...\")\n\n        self.start_engines()\n        self.task.taskloop.run()\n\n        self.finished_candidates.sort(key=lambda candidate: candidate.total_adjusted_time)\n\n        if self.failed_candidates:\n            self.log(\"failed engines:\")\n            for candidate in self.failed_candidates:\n                self.log(\n                    f\"  engine_{candidate.engine_idx}: {' '.join(candidate.engine)}\"\n                    f\" (returncode={candidate.engine_retcode} status={candidate.engine_status})\"\n                )\n\n        if self.finished_candidates:\n            self.log(\"finished engines:\")\n            for place, candidate in list(enumerate(self.finished_candidates, 1))[::-1]:\n                self.log(\n                    f\"  #{place}: engine_{candidate.engine_idx}: {' '.join(candidate.engine)}\"\n                    f\" ({candidate.total_adjusted_time} seconds, status={candidate.engine_status})\"\n                )\n\n        if self.finished_candidates:\n            self.task.status = \"AUTOTUNED\"\n            self.task.retcode = 0\n        elif self.failed_candidates:\n            self.task.status = \"FAIL\"\n            self.task.retcode = 2\n\n        self.task_exit_callback()\n\n    def next_candidate(self, peek=False):\n        # peek=True is used to check whether we need to timeout running candidates to\n        # give other candidates a chance.\n        can_retry = None\n\n        for candidate in self.active_candidates:\n            if candidate.state == \"pending\":\n                if not candidate.suspended:\n                    return candidate\n                if can_retry is None or can_retry.suspended > candidate.suspended:\n                    can_retry = candidate\n\n        if can_retry and not peek:\n            shift = can_retry.suspended\n            for candidate in self.active_candidates:\n                if candidate.state == \"pending\":\n                    candidate.suspended -= shift\n\n        return can_retry\n\n    def start_engines(self):\n        self.task.taskloop.poll_now = True\n\n        while self.threads_running < self.config.parallel:\n            candidate = self.next_candidate()\n            if candidate is None:\n                self.have_pending_candidates = False\n                return\n\n            candidate_threads = candidate.threads()\n            if self.threads_running:\n                if self.threads_running + candidate_threads > self.config.parallel:\n                    break\n\n            candidate.set_running()\n            candidate.set_engine_idx(self.next_engine_idx)\n            self.next_engine_idx += 1\n\n            try:\n                engine_task = SbyAutotuneTask(self, candidate)\n                pending = sum(c.state == \"pending\" for c in  self.active_candidates)\n                self.log(f\"{candidate.info} starting... ({pending} configurations pending)\")\n                self.threads_running += candidate_threads\n                engine_task.setup_procs(False)\n            except SbyAbort:\n                pass\n\n        self.have_pending_candidates = bool(self.next_candidate(peek=True))\n\n    def engine_finished(self, engine_task):\n        self.threads_running -= engine_task.candidate.threads()\n\n        candidate = engine_task.candidate\n\n        time = candidate.total_adjusted_time\n\n        if engine_task.status == \"TIMEOUT\":\n            if self.timeout is None or time < self.timeout:\n                candidate.retry_later()\n                self.log(f\"{candidate.info} timeout ({time} seconds, will be retried if necessary)\")\n            else:\n                candidate.timed_out()\n                self.log(f\"{candidate.info} timeout ({time} seconds)\")\n        elif engine_task.retcode:\n            candidate.failed()\n            self.log(f\"{candidate.info} failed (returncode={candidate.engine_retcode} status={candidate.engine_status})\")\n        else:\n            candidate.finished()\n\n            self.log(f\"{candidate.info} succeeded (status={candidate.engine_status})\")\n\n            if self.best_time is None:\n                self.log(f\"{candidate.info} took {time} seconds (first engine to finish)\")\n                self.best_time = time\n            elif time < self.best_time:\n                self.log(f\"{candidate.info} took {time} seconds (best candidate, previous best: {self.best_time} seconds)\")\n                self.best_time = time\n            else:\n                self.log(f\"{candidate.info} took {time} seconds\")\n\n            new_timeout = int(time + self.config.wait_seconds + time * self.config.wait_percentage // 100)\n\n            if self.timeout is None or new_timeout < self.timeout:\n                self.timeout = new_timeout\n\n        self.start_engines()\n\n    def model(self, engine_task, name):\n        if self.task not in self.task.taskloop.tasks:\n            self.task.taskloop.tasks.append(self.task)\n        if name in self.model_requests:\n            request = self.model_requests[name]\n        else:\n            self.model_requests[name] = request = SbyModelRequest(self, name)\n\n        request.attach_engine_task(engine_task)\n\n        return request.procs\n\n    def proc_failed(self, proc):\n        for name, request in self.model_requests.items():\n            if proc in request.procs:\n                for task in request.engine_tasks:\n                    task = task or self.task\n                    task.status = \"ERROR\"\n                    task.log(f\"could not prepare model '{name}', see toplevel logfile\")\n                    task.terminate()\n        pass\n\n\nclass SbyModelRequest:\n    \"\"\"Handles sharing and canceling of model generation from several SbyAutotuneTask\n    instances.\n    \"\"\"\n    def __init__(self, autotune, name):\n        self.autotune = autotune\n        self.name = name\n        self.engine_tasks = []\n\n        autotune.log(f\"model '{name}': preparing now...\")\n\n        self.make_model()\n\n    def make_model(self):\n        self.start_time = monotonic()\n        self.total_time = None\n        self.min_time = 0\n\n        self.procs = self.autotune.task.model(self.name)\n        for proc in self.procs:\n            proc.register_dep(self)\n\n    def attach_engine_task(self, engine_task):\n        if self.total_time is None:\n            if engine_task:\n                if self.start_time is None:\n                    model_time = 0\n                    extra_time = self.min_time\n                else:\n                    model_time = monotonic() - self.start_time\n                    extra_time = max(0, self.min_time - model_time)\n\n                engine_task.model_time += model_time\n\n                engine_task.check_timeout(extra_time)\n\n            if self.start_time is None:\n                self.make_model()\n\n            self.engine_tasks.append(engine_task)\n            if engine_task:\n                engine_task.model_requests.append(self)\n\n        else:\n            if engine_task:\n                engine_task.model_time += self.total_time\n\n    def detach_engine_task(self, engine_task):\n        self.engine_tasks.remove(engine_task)\n        if not self.engine_tasks and self.total_time is None:\n            self.autotune.log(f\"cancelled model '{self.name}'\")\n            del self.autotune.task.models[self.name]\n            for proc in self.procs:\n                proc.terminate(True)\n\n            self.min_time = max(self.min_time, monotonic() - self.start_time)\n            self.start_time = None\n\n            self.procs = []\n\n    def poll(self):\n        if self.total_time is None and all(proc.finished for proc in self.procs):\n            self.autotune.log(f\"prepared model '{self.name}'\")\n\n            self.total_time = self.min_time = monotonic() - self.start_time\n\n\nclass SbyAutotuneTask(SbyTask):\n    \"\"\"Task that shares the workdir with a parent task, runs in parallel to other\n    autotune tasks and can be cancelled independent from other autotune tasks while\n    sharing model generation with other tasks.\n    \"\"\"\n    def __init__(self, autotune, candidate):\n        task = autotune.task\n        self.autotune = autotune\n        self.candidate = candidate\n        super().__init__(\n            sbyconfig=None,\n            workdir=task.workdir,\n            early_logs=[],\n            reusedir=True,\n            taskloop=task.taskloop,\n            logfile=open(f\"{task.workdir}/engine_{candidate.engine_idx}_autotune.txt\", \"a\"),\n        )\n        self.task_local_abort = True\n        self.log_targets = [self.logfile]\n        self.exe_paths = autotune.task.exe_paths\n        self.reusedir = False\n        self.design = autotune.task.design\n\n        self.model_time = 0\n        self.model_requests = []\n\n        self.exit_callback = self.autotune_exit_callback\n\n\n    def parse_config(self, f):\n        super().parse_config(f)\n        self.engines = []\n\n    def engine_list(self):\n        return [(self.candidate.engine_idx, self.candidate.engine)]\n\n    def copy_src(self, _):\n        pass\n\n    def model(self, model_name):\n        self.log(f\"using model '{model_name}'\")\n        return self.autotune.model(self, model_name)\n\n    def autotune_exit_callback(self):\n        self.summarize()\n\n        self.candidate.total_adjusted_time = int(monotonic() - self.start_clock_time + self.model_time)\n        self.candidate.engine_retcode = self.retcode\n        self.candidate.engine_status = self.status\n\n        self.autotune.engine_finished(self)\n        for request in self.model_requests:\n            request.detach_engine_task(self)\n\n    def check_timeout(self, extra_time=0):\n        model_time = self.model_time + extra_time\n        total_adjusted_time = int(monotonic() - self.start_clock_time + model_time)\n\n        if self.autotune.timeout is not None:\n            timeout = self.autotune.timeout\n        else:\n            if not self.autotune.have_pending_candidates:\n                return\n            timeout = self.candidate.soft_timeout\n\n        if not self.timeout_reached and total_adjusted_time >= timeout:\n            self.log(f\"Reached autotune TIMEOUT ({timeout} seconds). Terminating all subprocesses.\")\n            self.status = \"TIMEOUT\"\n            self.total_adjusted_time = total_adjusted_time\n            self.terminate(timeout=True)\n"
  },
  {
    "path": "sbysrc/sby_cmdline.py",
    "content": "import argparse\n\nclass DictAction(argparse.Action):\n    def __call__(self, parser, namespace, values, option_string=None):\n        assert isinstance(getattr(namespace, self.dest), dict), f\"Use ArgumentParser.set_defaults() to initialize {self.dest} to dict()\"\n        name = option_string.lstrip(parser.prefix_chars).replace(\"-\", \"_\")\n        getattr(namespace, self.dest)[name] = values\n\ndef parser_func(release_version='unknown SBY version'):\n    parser = argparse.ArgumentParser(prog=\"sby\",\n            usage=\"%(prog)s [options] [<jobname>.sby [tasknames] | <dirname>]\")\n    parser.set_defaults(exe_paths=dict())\n\n    parser.add_argument(\"-d\", metavar=\"<dirname>\", dest=\"workdir\",\n            help=\"set workdir name. default: <jobname> or <jobname>_<taskname>. When there is more than one task, use --prefix instead\")\n    parser.add_argument(\"--prefix\", metavar=\"<dirname>\", dest=\"workdir_prefix\",\n            help=\"set the workdir name prefix. `_<taskname>` will be appended to the path for each task\")\n    parser.add_argument(\"-f\", action=\"store_true\", dest=\"force\",\n            help=\"remove workdir if it already exists\")\n    parser.add_argument(\"-b\", action=\"store_true\", dest=\"backup\",\n            help=\"backup workdir if it already exists\")\n    parser.add_argument(\"-t\", action=\"store_true\", dest=\"tmpdir\",\n            help=\"run in a temporary workdir (remove when finished)\")\n    parser.add_argument(\"-T\", metavar=\"<taskname>\", action=\"append\", dest=\"tasknames\", default=list(),\n            help=\"add taskname (useful when sby file is read from stdin)\")\n    parser.add_argument(\"-E\", action=\"store_true\", dest=\"throw_err\",\n            help=\"throw an exception (incl stack trace) for most errors\")\n    parser.add_argument(\"-j\", metavar=\"<N>\", type=int, dest=\"jobcount\",\n            help=\"maximum number of processes to run in parallel\")\n    parser.add_argument(\"--sequential\", action=\"store_true\", dest=\"sequential\",\n            help=\"run tasks in sequence, not in parallel\")\n    parser.add_argument(\"--live\", action=\"append\", choices=[\"csv\", \"jsonl\"], dest=\"live_formats\",\n            help=\"print live updates of property statuses during task execution, may be specified multiple times\")\n\n    parser.add_argument(\"--autotune\", action=\"store_true\", dest=\"autotune\",\n            help=\"automatically find a well performing engine and engine configuration for each task\")\n    parser.add_argument(\"--autotune-config\", dest=\"autotune_config\",\n            help=\"read an autotune configuration file (overrides the sby file's autotune options)\")\n\n    parser.add_argument(\"--yosys\", metavar=\"<path_to_executable>\",\n            action=DictAction, dest=\"exe_paths\")\n    parser.add_argument(\"--abc\", metavar=\"<path_to_executable>\",\n            action=DictAction, dest=\"exe_paths\")\n    parser.add_argument(\"--smtbmc\", metavar=\"<path_to_executable>\",\n            action=DictAction, dest=\"exe_paths\")\n    parser.add_argument(\"--witness\", metavar=\"<path_to_executable>\",\n            action=DictAction, dest=\"exe_paths\")\n    parser.add_argument(\"--suprove\", metavar=\"<path_to_executable>\",\n            action=DictAction, dest=\"exe_paths\")\n    parser.add_argument(\"--aigbmc\", metavar=\"<path_to_executable>\",\n            action=DictAction, dest=\"exe_paths\")\n    parser.add_argument(\"--avy\", metavar=\"<path_to_executable>\",\n            action=DictAction, dest=\"exe_paths\")\n    parser.add_argument(\"--rIC3\", metavar=\"<path_to_executable>\",\n            action=DictAction, dest=\"exe_paths\")\n    parser.add_argument(\"--btormc\", metavar=\"<path_to_executable>\",\n            action=DictAction, dest=\"exe_paths\")\n    parser.add_argument(\"--itp-bmc\", metavar=\"<path_to_executable>\",\n            action=DictAction, dest=\"exe_paths\")\n    parser.add_argument(\"--pono\", metavar=\"<path_to_executable>\",\n            action=DictAction, dest=\"exe_paths\",\n            help=\"configure which executable to use for the respective tool\")\n    parser.add_argument(\"--dumpcfg\", action=\"store_true\", dest=\"dump_cfg\",\n            help=\"print the pre-processed configuration file\")\n    parser.add_argument(\"--dumptags\", action=\"store_true\", dest=\"dump_tags\",\n            help=\"print the list of task tags\")\n    parser.add_argument(\"--dumptasks\", action=\"store_true\", dest=\"dump_tasks\",\n            help=\"print the list of tasks\")\n    parser.add_argument(\"--dumpdefaults\", action=\"store_true\", dest=\"dump_defaults\",\n            help=\"print the list of default tasks\")\n    parser.add_argument(\"--dumptaskinfo\", action=\"store_true\", dest=\"dump_taskinfo\",\n            help=\"output a summary of tasks as JSON\")\n    parser.add_argument(\"--dumpfiles\", action=\"store_true\", dest=\"dump_files\",\n            help=\"print the list of source files\")\n    parser.add_argument(\"--setup\", action=\"store_true\", dest=\"setupmode\",\n            help=\"set up the working directory and exit\")\n    parser.add_argument(\"--link\", action=\"store_true\", dest=\"linkmode\",\n            help=\"make symbolic links to source files instead of copying them\")\n\n    parser.add_argument(\"--status\", action=\"store_true\", dest=\"status\",\n            help=\"summarize the contents of the status database\")\n    parser.add_argument(\"--statusfmt\", action=\"store\", default=\"\", choices=[\"csv\", \"jsonl\"], dest=\"status_format\",\n            help=\"print the most recent status for each property in specified format\")\n    parser.add_argument(\"--latest\", action=\"store_true\", dest=\"status_latest\",\n            help=\"only check statuses from the most recent run of a task\")\n    parser.add_argument(\"--statusreset\", action=\"store_true\", dest=\"status_reset\",\n            help=\"reset the contents of the status database\")\n    parser.add_argument(\"--statuscancels\", action=\"store_true\", dest=\"status_cancels\",\n            help=\"intertask cancellations can be triggered by the status database\")\n\n    parser.add_argument(\"--taskstatus\", action=\"store_true\", dest=\"task_status\",\n            help=\"display the status of tasks in the status database\")\n\n    parser.add_argument(\"--init-config-file\", dest=\"init_config_file\",\n            help=\"create a default .sby config file\")\n    parser.add_argument(\"sbyfile\", metavar=\"<jobname>.sby | <dirname>\", nargs=\"?\",\n            help=\".sby file OR directory containing config.sby file\")\n    parser.add_argument(\"arg_tasknames\", metavar=\"tasknames\", nargs=\"*\",\n            help=\"tasks to run (only valid when <jobname>.sby is used)\")\n\n    parser.add_argument('--version', action='version', version=release_version)\n\n    return parser\n"
  },
  {
    "path": "sbysrc/sby_core.py",
    "content": "#\n# SymbiYosys (sby) -- Front-end for Yosys-based formal verification flows\n#\n# Copyright (C) 2016  Claire Xenia Wolf <claire@yosyshq.com>\n#\n# Permission to use, copy, modify, and/or distribute this software for any\n# purpose with or without fee is hereby granted, provided that the above\n# copyright notice and this permission notice appear in all copies.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n#\n\nimport os, re, sys, signal, platform, click\nimport time\nif os.name == \"posix\":\n    import resource, fcntl\nimport subprocess\nfrom pathlib import Path\nfrom dataclasses import dataclass, field\nfrom collections import defaultdict\nfrom typing import Optional\nfrom shutil import copyfile, copytree, rmtree\nfrom select import select\nfrom time import monotonic, localtime, sleep, strftime\nfrom sby_design import SbyProperty, SbyModule, design_hierarchy\nfrom sby_status import SbyStatusDb\n\nall_procs_running = []\n\ndef force_shutdown(signum, frame):\n    click.echo(\"SBY ---- Keyboard interrupt or external termination signal ----\")\n    for proc in list(all_procs_running):\n        proc.terminate()\n    sys.exit(1)\n\nif os.name == \"posix\":\n    signal.signal(signal.SIGHUP, force_shutdown)\nsignal.signal(signal.SIGINT, force_shutdown)\nsignal.signal(signal.SIGTERM, force_shutdown)\n\ndef process_filename(filename):\n    filename = os.path.expandvars(filename)\n    return Path(filename).expanduser()\n\ndef dress_message(workdir, logmessage):\n    tm = localtime()\n    if workdir is not None:\n        logmessage = \"[\" + click.style(workdir, fg=\"blue\") + \"] \" + logmessage\n    return \" \".join([\n        click.style(\"SBY\", fg=\"blue\"),\n        click.style(\"{:2d}:{:02d}:{:02d}\".format(tm.tm_hour, tm.tm_min, tm.tm_sec), fg=\"green\"),\n        logmessage\n    ])\n\nclass SbyProc:\n    def __init__(self, task, info, deps, cmdline, logfile=None, logstderr=True, silent=False):\n        self.running = False\n        self.finished = False\n        self.terminated = False\n        self.exited = False\n        self.checkretcode = False\n        self.retcodes = [0]\n        self.task = task\n        self.info = info\n        self.deps = deps\n        if os.name == \"posix\":\n            self.cmdline = cmdline\n        else:\n            # Windows command interpreter equivalents for sequential\n            # commands (; => &) command grouping ({} => ()).\n            replacements = {\n                \";\" : \"&\",\n                \"{\" : \"(\",\n                \"}\" : \")\",\n            }\n            parts = cmdline.split(\"'\")\n            for i in range(len(parts)):\n                if i % 2 == 0:\n                    cmdline_copy = parts[i]\n                    for u, w in replacements.items():\n                        cmdline_copy = cmdline_copy.replace(u, w)\n                    parts[i] = cmdline_copy\n            self.cmdline = '\"'.join(parts)\n        self.logfile = logfile\n        self.noprintregex = None\n        self.notify = []\n        self.linebuffer = \"\"\n        self.logstderr = logstderr\n        self.silent = silent\n        self.wait = False\n        self.job_lease = None\n        self.next_db = 0.0\n\n        self.task.update_proc_pending(self)\n\n        for dep in self.deps:\n            dep.register_dep(self)\n\n        self.output_callback = None\n        self.exit_callbacks = []\n        self.error_callback = None\n\n        if self.task.timeout_reached:\n            self.terminate(True)\n\n    def register_dep(self, next_proc):\n        if self.finished:\n            next_proc.poll()\n        else:\n            self.notify.append(next_proc)\n\n    def register_exit_callback(self, callback):\n        self.exit_callbacks.append(callback)\n\n    def log(self, line):\n        if line is not None and (self.noprintregex is None or not self.noprintregex.match(line)):\n            if self.logfile is not None:\n                click.echo(line, file=self.logfile)\n            self.task.log(f\"{click.style(self.info, fg='magenta')}: {line}\")\n\n    def handle_output(self, line):\n        if self.terminated or len(line) == 0:\n            return\n        if self.output_callback is not None:\n            line = self.output_callback(line)\n        self.log(line)\n\n    def handle_exit(self, retcode):\n        if self.terminated:\n            return\n        if self.logfile is not None:\n            self.logfile.close()\n        for callback in self.exit_callbacks:\n            callback(retcode)\n\n    def handle_error(self, retcode):\n        if self.terminated:\n            return\n        if self.logfile is not None:\n            self.logfile.close()\n        if self.error_callback is not None:\n            self.error_callback(retcode)\n\n    def terminate(self, force=False):\n        if (self.task.opt_wait or self.wait) and not force:\n            return\n        if self.running:\n            if not self.silent:\n                self.task.log(f\"{click.style(self.info, fg='magenta')}: terminating process\")\n            if os.name == \"posix\":\n                try:\n                    os.killpg(self.p.pid, signal.SIGTERM)\n                except PermissionError:\n                    pass\n            self.p.terminate()\n            self.task.update_proc_stopped(self)\n        elif not self.finished and not self.terminated and not self.exited:\n            self.task.update_proc_canceled(self)\n        self.terminated = True\n\n    def poll(self, force_unchecked=False):\n        if self.task.task_local_abort and not force_unchecked:\n            try:\n                self.poll(True)\n            except SbyAbort:\n                self.task.terminate(True)\n            return\n        if self.finished or self.terminated or self.exited:\n            return\n\n        for task in self.task.taskloop.tasks_done:\n            if task.name in self.task.cancelledby:\n                if not self.silent:\n                    self.task.log(f\"Cancelled by {task.name!r} task\")\n                self.task.cancel()\n                return\n\n        if self.task.status_cancels and time.time() >= self.next_db:\n            tasks_status = self.task.status_db.all_tasks_status()\n            for task_status in tasks_status.values():\n                if (task_status[\"status\"] in [\"PASS\", \"FAIL\", \"CANCELLED\"] and\n                    task_status[\"name\"] in self.task.cancelledby):\n                    if not self.silent:\n                        status_time = time.localtime(task_status[\"status_created\"])\n                        if status_time.tm_yday == time.localtime().tm_yday:\n                            # same day, format time only\n                            time_format = r\"%H:%M:%S\"\n                        else:\n                            time_format = r\"%x %H:%M:%S\"\n                        self.task.log(\n                            f'Cancelled by {task_status[\"name\"]!r} task '\n                            f'with status {task_status[\"status\"]!r} '\n                            f'at {time.strftime(time_format, status_time)} '\n                            '(consider calling sby with --statusreset if this seems wrong)'\n                        )\n                    self.task.cancel()\n                    return\n            # don't hit the database every poll\n            self.next_db = time.time() + 10\n\n        if not self.running:\n            for dep in self.deps:\n                if not dep.finished:\n                    return\n\n            if self.task.taskloop.jobclient:\n                if self.job_lease is None:\n                    self.job_lease = self.task.taskloop.jobclient.request_lease()\n\n                if not self.job_lease.is_ready:\n                    return\n\n            if not self.silent:\n                self.task.log(f\"{click.style(self.info, fg='magenta')}: starting process \\\"{self.cmdline}\\\"\")\n\n            if os.name == \"posix\":\n                def preexec_fn():\n                    signal.signal(signal.SIGINT, signal.SIG_IGN)\n                    os.setpgrp()\n\n                self.p = subprocess.Popen([\"/usr/bin/env\", \"bash\", \"-c\", self.cmdline], stdin=subprocess.DEVNULL, stdout=subprocess.PIPE,\n                        stderr=(subprocess.STDOUT if self.logstderr else None), preexec_fn=preexec_fn)\n\n                fl = fcntl.fcntl(self.p.stdout, fcntl.F_GETFL)\n                fcntl.fcntl(self.p.stdout, fcntl.F_SETFL, fl | os.O_NONBLOCK)\n\n            else:\n                self.p = subprocess.Popen(self.cmdline + \" & exit !errorlevel!\", shell=True, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE,\n                        stderr=(subprocess.STDOUT if self.logstderr else None))\n\n            self.task.update_proc_running(self)\n            self.running = True\n            return\n\n        self.read_output()\n\n        if self.p.poll() is not None:\n            # The process might have written something since the last time we checked\n            self.read_output()\n\n            if self.job_lease:\n                self.job_lease.done()\n\n            if self.terminated:\n                # task already terminated, do not finish\n                return\n\n            if not self.silent:\n                self.task.log(f\"{click.style(self.info, fg='magenta')}: finished (returncode={self.p.returncode})\")\n\n            self.task.update_proc_stopped(self)\n            self.running = False\n            self.exited = True\n\n            if os.name == \"nt\":\n                if self.p.returncode == 9009:\n                    returncode = 127\n                else:\n                    returncode = self.p.returncode & 0xff\n            else:\n                returncode = self.p.returncode\n\n            if returncode == 127:\n                if not self.silent:\n                    self.task.log(f\"{click.style(self.info, fg='magenta')}: COMMAND NOT FOUND. ERROR.\")\n                self.handle_error(returncode)\n                self.terminated = True\n                self.task.proc_failed(self)\n                return\n\n            if self.checkretcode and returncode not in self.retcodes:\n                if not self.silent:\n                    self.task.log(f\"{click.style(self.info, fg='magenta')}: task failed. ERROR.\")\n                self.handle_error(returncode)\n                self.terminated = True\n                self.task.proc_failed(self)\n                return\n\n            self.handle_exit(returncode)\n\n            self.finished = True\n            for next_proc in self.notify:\n                next_proc.poll()\n            return\n\n    def read_output(self):\n        while True:\n            outs = self.p.stdout.readline().decode(\"utf-8\")\n            if len(outs) == 0: break\n            if outs[-1] != '\\n':\n                self.linebuffer += outs\n                break\n            outs = (self.linebuffer + outs).strip()\n            self.linebuffer = \"\"\n            self.handle_output(outs)\n\n\nclass SbyAbort(BaseException):\n    pass\n\n\nclass SbyConfig:\n    def __init__(self):\n        self.options = dict()\n        self.engines = dict()\n        self.setup = dict()\n        self.stage = dict()\n        self.script = list()\n        self.autotune_config = None\n        self.files = dict()\n        self.verbatim_files = dict()\n        self.cancelledby = list()\n        pass\n\n    def parse_config(self, f):\n        mode = None\n        engine_mode = None\n        stage_name = None\n\n        for line in f:\n            raw_line = line\n            if mode in [\"options\", \"engines\", \"files\", \"autotune\", \"setup\", \"stage\"]:\n                line = re.sub(r\"\\s*(\\s#.*)?$\", \"\", line)\n                if line == \"\" or line[0] == \"#\":\n                    continue\n            else:\n                line = line.rstrip()\n            # print(line)\n            if mode is None and (len(line) == 0 or line[0] == \"#\"):\n                continue\n            match = re.match(r\"^\\s*\\[(.*)\\]\\s*$\", line)\n            if match:\n                entries = match.group(1).strip().split(maxsplit = 1)\n                if len(entries) == 0:\n                    self.error(f\"sby file syntax error: Expected section header, got '{line}'\")\n                elif len(entries) == 1:\n                    section, args = (*entries, None)\n                else:\n                    section, args = entries\n\n                if section == \"options\":\n                    mode = \"options\"\n                    if len(self.options) != 0:\n                        self.error(f\"sby file syntax error: '[options]' section already defined\")\n\n                    if args is not None:\n                        self.error(f\"sby file syntax error: '[options]' section does not accept any arguments. got {args}\")\n                    continue\n\n                if section == \"engines\":\n                    mode = \"engines\"\n\n                    if args is None:\n                        engine_mode = None\n                    else:\n                        section_args = args.split()\n\n                        if len(section_args) > 1:\n                            self.error(f\"sby file syntax error: '[engines]' section expects at most 1 argument, got '{' '.join(section_args)}'\")\n\n                        if section_args[0] not in (\"bmc\", \"prove\", \"cover\", \"live\"):\n                            self.error(f\"sby file syntax error: Expected one of 'bmc', 'prove', 'cover', 'live' as '[engines]' argument, got '{section_args[0]}'\")\n\n                        engine_mode = section_args[0]\n\n                    if engine_mode in self.engines:\n                        if engine_mode is None:\n                            self.error(f\"Already defined engine block\")\n                        else:\n                            self.error(f\"Already defined engine block for mode '{engine_mode}'\")\n                    else:\n                        self.engines[engine_mode] = list()\n\n                    continue\n\n                if section == \"setup\":\n                    self.error(f\"sby file syntax error: the '[setup]' section is not yet supported\")\n\n                    mode = \"setup\"\n                    if len(self.setup) != 0:\n                        self.error(f\"sby file syntax error: '[setup]' section already defined\")\n\n                    if args is not None:\n                        self.error(f\"sby file syntax error: '[setup]' section does not accept any arguments, got '{args}'\")\n\n                    continue\n\n                # [stage <NAME> (PARENTS,...)]\n                if section == \"stage\":\n                    self.error(f\"sby file syntax error: the '[stage]' section is not yet supported\")\n\n                    mode = \"stage\"\n\n                    if args is None:\n                        self.error(f\"sby file syntax error: '[stage]' section expects arguments, got none\")\n\n                    section_args = args.strip().split(maxsplit = 1)\n\n\n                    if len(section_args) == 1:\n                        parents = None\n                    else:\n                        parents = list(map(lambda a: a.strip(), section_args[1].split(',')))\n\n                    stage_name = section_args[0]\n\n                    if stage_name in self.stage:\n                        self.error(f\"stage '{stage_name}' already defined\")\n\n                    self.stage[stage_name] = {\n                        'parents': parents\n                    }\n\n                    continue\n\n                if section == \"script\":\n                    mode = \"script\"\n                    if len(self.script) != 0:\n                        self.error(f\"sby file syntax error: '[script]' section already defined\")\n                    if args is not None:\n                        self.error(f\"sby file syntax error: '[script]' section does not accept any arguments. got {args}\")\n\n                    continue\n\n                if section == \"autotune\":\n                    mode = \"autotune\"\n                    if self.autotune_config:\n                        self.error(f\"sby file syntax error: '[autotune]' section already defined\")\n\n                    import sby_autotune\n                    self.autotune_config = sby_autotune.SbyAutotuneConfig()\n                    continue\n                \n                if section == \"cancelledby\":\n                    mode = \"cancelledby\"\n                    if args is not None:\n                        self.error(f\"sby file syntax error: '[cancelledby]' section does not accept any arguments. got {args}\")\n                    continue\n\n                if section == \"file\":\n                    mode = \"file\"\n                    if args is None:\n                        self.error(f\"sby file syntax error: '[file]' section expects a file name argument\")\n\n                    section_args = args.split()\n\n                    if len(section_args) > 1:\n                        self.error(f\"sby file syntax error: '[file]' section expects exactly one file name argument, got {len(section_args)}\")\n                    current_verbatim_file = section_args[0]\n                    if current_verbatim_file in self.verbatim_files:\n                        self.error(f\"duplicate file: {current_verbatim_file}\")\n                    self.verbatim_files[current_verbatim_file] = list()\n                    continue\n\n                if section == \"files\":\n                    mode = \"files\"\n                    if args is not None:\n                        self.error(f\"sby file syntax error: '[files]' section does not accept any arguments. got {args}\")\n                    continue\n\n                self.error(f\"sby file syntax error: unexpected section '{section}', expected one of 'options, engines, script, autotune, file, files'\")\n\n            if mode == \"options\":\n                entries = line.strip().split(maxsplit = 1)\n                if len(entries) != 2:\n                    self.error(f\"sby file syntax error: '[options]' section entry does not have an argument '{line}'\")\n                self.options[entries[0]] = entries[1]\n                continue\n\n            if mode == \"autotune\":\n                self.autotune_config.config_line(self, line)\n                continue\n            \n            if mode == \"cancelledby\":\n                taskname = line.strip()\n                if taskname:\n                    self.cancelledby.append(taskname)\n                continue\n\n            if mode == \"engines\":\n                args = line.strip().split()\n                self.engines[engine_mode].append(args)\n                continue\n\n            if mode == \"setup\":\n                _valid_options = (\n                    \"cutpoint\", \"disable\", \"enable\", \"assume\", \"define\"\n                )\n\n                args = line.strip().split(maxsplit = 1)\n\n                if len(args) < 2:\n                    self.error(f\"sby file syntax error: entry in '[setup]' must have an argument, got '{' '.join(args)}'\")\n\n                if args[0] not in _valid_options:\n                    self.error(f\"sby file syntax error: expected one of '{', '.join(_valid_options)}' in '[setup]' section, got '{args[0]}'\")\n\n                else:\n                    opt_key = args[0]\n                    opt_args = args[1].strip().split()\n\n                    if opt_key == 'define':\n                        if 'define' not in self.setup:\n                            self.setup['define'] = {}\n\n                        if len(opt_args) != 2:\n                            self.error(f\"sby file syntax error: 'define' statement in '[setup]' section takes  exactly 2 arguments, got '{' '.join(opt_args)}'\")\n\n                        if opt_args[0][0] != '@':\n                            self.error(f\"sby file syntax error: 'define' statement in '[setup]' section expects an '@' prefixed name as the first parameter, got '{opt_args[0]}'\")\n\n                        name = opt_args[0][1:]\n                        self.setup['define'][name] =  opt_args[2:]\n                    else:\n                        self.setup[opt_key] =  opt_args[1:]\n                continue\n\n            if mode == \"stage\":\n                _valid_options = (\n                    \"mode\", \"depth\", \"timeout\", \"expect\", \"engine\",\n                    \"cutpoint\", \"enable\", \"disable\", \"assume\", \"skip\",\n                    \"check\", \"prove\", \"abstract\", \"setsel\"\n                )\n\n                args = line.strip().split(maxsplit = 1)\n\n                if args is None:\n                    self.error(f\"sby file syntax error: unknown key in '[stage]' section\")\n\n                if len(args) < 2:\n                    self.error(f\"sby file syntax error: entry in '[stage]' must have an argument, got {' '.join(args)}\")\n\n                if args[0] not in _valid_options:\n                    self.error(f\"sby file syntax error: expected one of '{', '.join(map(repr, _valid_options))}' in '[stage]' section, got '{args[0]}'\")\n                else:\n                    opt_key = args[0]\n                    opt_args = args[1].strip().split()\n                    if opt_key == 'setsel':\n\n                        if len(opt_args) != 2:\n                            self.error(f\"sby file syntax error: 'setsel' statement in '[stage]' section takes  exactly 2 arguments, got '{' '.join(opt_args)}'\")\n\n                        if opt_args[0][0] != '@':\n                            self.error(f\"sby file syntax error: 'setsel' statement in '[stage]' section expects an '@' prefixed name as the first parameter, got '{opt_args[0]}'\")\n\n                        name = opt_args[0][1:]\n\n                        if stage_name not in self.stage:\n                            self.stage[stage_name] = dict()\n\n                        self.stage[stage_name][opt_key] = {\n                            'name': name, 'pattern': opt_args[2:]\n                        }\n\n                    else:\n                        if stage_name not in self.stage:\n                            self.stage[stage_name] = dict()\n\n                        self.stage[stage_name][opt_key] = opt_args[1:]\n                continue\n\n            if mode == \"script\":\n                self.script.append(line)\n                continue\n\n            if mode == \"files\":\n                entries = line.split()\n                if len(entries) < 1 or len(entries) > 2:\n                    self.error(f\"sby file syntax error: '[files]' section entry expects up to 2 arguments, {len(entries)} specified\")\n\n                if len(entries) == 1:\n                    self.files[Path(entries[0]).name] = entries[0]\n                elif len(entries) == 2:\n                    self.files[entries[0]] = entries[1]\n\n                continue\n\n            if mode == \"file\":\n                self.verbatim_files[current_verbatim_file].append(raw_line)\n                continue\n\n            self.error(f\"sby file syntax error: In an incomprehensible mode '{mode}'\")\n\n        if len(self.stage.keys()) == 0:\n            self.stage['default'] = { 'enable': '*' }\n\n    def error(self, logmessage):\n        raise SbyAbort(logmessage)\n\n\nclass SbyTaskloop:\n    def __init__(self, jobclient=None):\n        self.procs_pending = []\n        self.procs_running = []\n        self.tasks = []\n        self.tasks_done = []\n        self.poll_now = False\n        self.jobclient = jobclient\n\n    def run(self):\n        for proc in self.procs_pending:\n            proc.poll()\n\n\n        waiting_for_jobslots = False\n        if self.jobclient:\n            waiting_for_jobslots = self.jobclient.has_pending_leases()\n\n        while self.procs_running or waiting_for_jobslots or self.poll_now:\n            fds = []\n            if self.jobclient:\n                fds.extend(self.jobclient.poll_fds())\n            for proc in self.procs_running:\n                if proc.running:\n                    fds.append(proc.p.stdout)\n\n            if not self.poll_now:\n                if os.name == \"posix\":\n                    try:\n                        select(fds, [], [], 1.0) == ([], [], [])\n                    except InterruptedError:\n                        pass\n                else:\n                    sleep(0.1)\n            self.poll_now = False\n\n            if self.jobclient:\n                self.jobclient.poll()\n\n            self.procs_waiting = []\n\n            for proc in self.procs_running:\n                proc.poll()\n\n            for proc in self.procs_pending:\n                proc.poll()\n\n            if self.jobclient:\n                waiting_for_jobslots = self.jobclient.has_pending_leases()\n\n            tasks = self.tasks\n            self.tasks = []\n            for task in tasks:\n                task.check_timeout()\n                if task.procs_pending or task.procs_running:\n                    self.tasks.append(task)\n                else:\n                    task.exit_callback()\n                    self.tasks_done.append(task)\n\n        for task in self.tasks:\n            task.exit_callback()\n\n@dataclass\nclass SbySummaryEvent:\n    engine_idx: int\n    trace: Optional[str] = field(default=None)\n    path: Optional[str] = field(default=None)\n    hdlname: Optional[str] = field(default=None)\n    type: Optional[str] = field(default=None)\n    src: Optional[str] = field(default=None)\n    step: Optional[int] = field(default=None)\n    prop: Optional[SbyProperty] = field(default=None)\n    engine_case: Optional[str] = field(default=None)\n\n    @property\n    def engine(self):\n        return f\"engine_{self.engine_idx}\"\n\n@dataclass\nclass SbyTraceSummary:\n    trace: str\n    path: Optional[str] = field(default=None)\n    engine_case: Optional[str] = field(default=None)\n    events: dict = field(default_factory=lambda: defaultdict(lambda: defaultdict(list)))\n    trace_ids: dict[str, int] = field(default_factory=lambda: dict())\n    last_ext: Optional[str] = field(default=None)\n\n    @property\n    def kind(self):\n        if '$assert' in self.events:\n            kind = 'counterexample trace'\n        elif '$cover' in self.events:\n            kind = 'cover trace'\n        else:\n            kind = 'trace'\n        return kind\n\n@dataclass\nclass SbyEngineSummary:\n    engine_idx: int\n    traces: dict = field(default_factory=dict)\n    status: Optional[str] = field(default=None)\n    unreached_covers: Optional[list] = field(default=None)\n\n    @property\n    def engine(self):\n        return f\"engine_{self.engine_idx}\"\n\nclass SbySummary:\n    def __init__(self, task):\n        self.task = task\n        self.timing = []\n        self.lines = []\n\n        self.engine_summaries = {}\n        self.traces = defaultdict(dict)\n        self.engine_status = {}\n        self.unreached_covers = None\n\n    def append(self, line):\n        self.lines.append(line)\n\n    def extend(self, lines):\n        self.lines.extend(lines)\n\n    def engine_summary(self, engine_idx):\n        if engine_idx not in self.engine_summaries:\n            self.engine_summaries[engine_idx] = SbyEngineSummary(engine_idx)\n        return self.engine_summaries[engine_idx]\n\n    def add_event(self, *args, update_status=True, **kwargs):\n        event = SbySummaryEvent(*args, **kwargs)\n\n        engine = self.engine_summary(event.engine_idx)\n\n        if update_status:\n            status_metadata = dict(source=\"summary_event\", engine=engine.engine)\n        if event.step:\n            status_metadata[\"step\"] = event.step\n\n        add_trace = False\n        if event.prop:\n            if event.type is None:\n                event.type = event.prop.celltype\n            elif event.type == \"$assert\":\n                event.prop.status = \"FAIL\"\n                add_trace = True\n            elif event.type == \"$cover\":\n                event.prop.status = \"PASS\"\n                add_trace = True\n\n        trace_id = None\n        trace_path = None\n        if event.trace:\n            # get or create trace summary\n            try:\n                trace_summary = engine.traces[event.trace]\n            except KeyError:\n                trace_summary = SbyTraceSummary(event.trace, path=event.path, engine_case=event.engine_case)\n                engine.traces[event.trace] = trace_summary\n\n            if event.path:\n                trace_path = Path(event.path)\n                trace_ext = trace_path.suffix\n                trace_summary.last_ext = trace_ext\n                try:\n                    # use existing tracefile for this extension\n                    trace_id = trace_summary.trace_ids[trace_ext]\n                except KeyError:\n                    # add tracefile to database\n                    trace_id = self.task.status_db.add_task_trace(event.trace, event.path, trace_ext[1:], event.engine_case)\n                    trace_summary.trace_ids[trace_ext] = trace_id\n            elif trace_summary.path:\n                # use existing tracefile for last extension\n                trace_path = Path(trace_summary.path)\n                trace_ext = trace_summary.last_ext\n                trace_id = trace_summary.trace_ids[trace_ext]\n\n            if event.type:\n                by_type = trace_summary.events[event.type]\n                if event.hdlname:\n                    by_type[event.hdlname].append(event)\n\n        if event.prop and update_status:\n            # update property status in database\n            self.task.status_db.set_task_property_status(\n                event.prop,\n                trace_id=trace_id,\n                data=status_metadata,\n            )\n\n        if trace_path and add_trace:\n            event.prop.tracefiles.append(str(trace_path))\n\n    def set_engine_status(self, engine_idx, status, case=None):\n        engine_summary = self.engine_summary(engine_idx)\n        if case is None:\n            self.task.log(f\"{click.style(f'engine_{engine_idx}', fg='magenta')}: Status returned by engine: {status}\")\n            self.engine_summary(engine_idx).status = status\n        else:\n            self.task.log(f\"{click.style(f'engine_{engine_idx}.{case}', fg='magenta')}: Status returned by engine for {case}: {status}\")\n            if engine_summary.status is None:\n                engine_summary.status = {}\n            engine_summary.status[case] = status\n\n    def summarize(self, short):\n        omitted_excess = False\n        for line in self.timing:\n            yield line\n\n        for engine_idx, engine_cmd in self.task.engine_list():\n            engine_cmd = ' '.join(engine_cmd)\n            trace_limit = 5\n            prop_limit = 5\n            step_limit = 5\n            engine = self.engine_summary(engine_idx)\n            if isinstance(engine.status, dict):\n                for case, status in sorted(engine.status.items()):\n                    yield f\"{engine.engine} ({engine_cmd}) returned {status} for {case}\"\n            elif engine.status:\n                yield f\"{engine.engine} ({engine_cmd}) returned {engine.status}\"\n            else:\n                yield f\"{engine.engine} ({engine_cmd}) did not return a status\"\n\n            produced_traces = False\n\n            for i, (trace_name, trace) in enumerate(sorted(engine.traces.items())):\n                if short and i == trace_limit:\n                    excess = len(engine.traces) - trace_limit\n                    omitted_excess = True\n                    yield f\"and {excess} further trace{'s' if excess != 1 else ''}\"\n                    break\n                case_suffix = f\" [{trace.engine_case}]\" if trace.engine_case else \"\"\n                if trace.path:\n                    # print single preferred trace\n                    preferred_exts = [\".fst\", \".vcd\"]\n                    if trace.last_ext not in preferred_exts: preferred_exts.append(trace.last_ext)\n                    for ext in trace.trace_ids.keys():\n                        if ext not in preferred_exts: preferred_exts.append(ext)\n                    for ext in preferred_exts:\n                        if ext not in trace.trace_ids:\n                            continue\n                        if short:\n                            path = Path(self.task.workdir) / trace.path\n                        else:\n                            path = Path(trace.path)\n                        yield f\"{trace.kind}{case_suffix}: {path.with_suffix(ext)}\"\n                        if short:\n                            break\n                else:\n                    yield f\"{trace.kind}{case_suffix}: <{trace.trace}>\"\n                produced_traces = True\n                for event_type, events in sorted(trace.events.items()):\n                    if event_type == '$assert':\n                        desc = \"failed assertion\"\n                        short_desc = 'assertion'\n                    elif event_type == '$cover':\n                        desc = \"reached cover statement\"\n                        short_desc = 'cover statement'\n                    elif event_type == '$assume':\n                        desc = \"violated assumption\"\n                        short_desc = 'assumption'\n                    else:\n                        continue\n                    for j, (hdlname, same_events) in enumerate(sorted(events.items())):\n                        if short and j == prop_limit:\n                            excess = len(events) - prop_limit\n                            yield f\"  and {excess} further {short_desc}{'s' if excess != 1 else ''}\"\n                            break\n\n                        event = same_events[0]\n                        # uniquify steps and ignore events with missing steps\n                        steps = sorted(set(e.step for e in same_events if e.step))\n                        if short and len(steps) > step_limit:\n                            excess = len(steps) - step_limit\n                            steps = [str(step) for step in steps[:step_limit]]\n                            omitted_excess = True\n                            steps[-1] += f\" and {excess} further step{'s' if excess != 1 else ''}\"\n\n                        event_string = f\"  {desc} {hdlname} at {event.src}\"\n                        if steps:\n                            event_string += f\" step{'s' if len(steps) > 1 else ''} {', '.join(map(str, steps))}\"\n                        yield event_string\n\n            if not produced_traces:\n                yield f\"{engine.engine} did not produce any traces\"\n\n        if self.unreached_covers is None and self.task.opt_mode == 'cover' and self.task.status != \"PASS\" and self.task.design:\n            self.unreached_covers = []\n            for prop in self.task.design.hierarchy:\n                if prop.type == prop.Type.COVER and prop.status in [\"UNKNOWN\", \"FAIL\"]:\n                    self.unreached_covers.append(prop)\n\n        if self.unreached_covers:\n            yield f\"unreached cover statements:\"\n            for j, prop in enumerate(self.unreached_covers):\n                if short and j == prop_limit:\n                    excess = len(self.unreached_covers) - prop_limit\n                    omitted_excess = True\n                    yield f\"  and {excess} further propert{'ies' if excess != 1 else 'y'}\"\n                    break\n                yield f\"  {prop.hdlname} at {prop.location}\"\n\n        for line in self.lines:\n            yield line\n\n        if omitted_excess:\n            yield f\"see {self.task.workdir}/{self.task.status} for a complete summary\"\n    def __iter__(self):\n        yield from self.summarize(True)\n\n\n\nclass SbyTask(SbyConfig):\n    def __init__(self, sbyconfig, workdir, early_logs, reusedir, status_cancels=False, taskloop=None, logfile=None, name=None, live_formats=[]):\n        super().__init__()\n        self.used_options = set()\n        self.models = dict()\n        self.workdir = workdir\n        self.reusedir = reusedir\n        self.status_cancels = status_cancels\n        self.name = name\n        self.live_formats = live_formats\n        self.status = \"UNKNOWN\"\n        self.total_time = 0\n        self.expect = list()\n        self.design = None\n        self.precise_prop_status = False\n        self.timeout_reached = False\n        self.task_local_abort = False\n        self.exit_callback = self.summarize\n\n        yosys_program_prefix = \"\" ##yosys-program-prefix##\n        self.exe_paths = {\n            \"yosys\": os.getenv(\"YOSYS\", yosys_program_prefix + \"yosys\"),\n            \"abc\": os.getenv(\"ABC\", yosys_program_prefix + \"yosys-abc\"),\n            \"smtbmc\": os.getenv(\"SMTBMC\", yosys_program_prefix + \"yosys-smtbmc\"),\n            \"witness\": os.getenv(\"WITNESS\", yosys_program_prefix + \"yosys-witness\"),\n            \"suprove\": os.getenv(\"SUPROVE\", \"suprove\"),\n            \"aigbmc\": os.getenv(\"AIGBMC\", \"aigbmc\"),\n            \"avy\": os.getenv(\"AVY\", \"avy\"),\n            \"rIC3\": os.getenv(\"RIC3\", \"rIC3\"),\n            \"btormc\": os.getenv(\"BTORMC\", \"btormc\"),\n            \"pono\": os.getenv(\"PONO\", \"pono\"),\n            \"imctk-eqy-engine\": os.getenv(\"IMCTK_EQY_ENGINE\", \"imctk-eqy-engine\"),\n            \"itp-bmc\": os.getenv(\"ITP_BMC\", \"itp-bmc\"),\n        }\n\n        self.taskloop = taskloop or SbyTaskloop()\n        self.taskloop.tasks.append(self)\n\n        self.procs_running = []\n        self.procs_pending = []\n\n        self.start_clock_time = monotonic()\n\n        if os.name == \"posix\":\n            ru = resource.getrusage(resource.RUSAGE_CHILDREN)\n            self.start_process_time = ru.ru_utime + ru.ru_stime\n\n        self.summary = SbySummary(self)\n\n        self.logfile = logfile or open(f\"{workdir}/logfile.txt\", \"a\")\n        self.log_targets = [sys.stdout, self.logfile]\n\n        for line in early_logs:\n            click.echo(line, file=self.logfile)\n\n        if not reusedir:\n            with open(f\"{workdir}/config.sby\", \"w\") as f:\n                for line in sbyconfig:\n                    click.echo(line, file=f)\n\n    def engine_list(self):\n        engines = self.engines.get(None, []) + self.engines.get(self.opt_mode, [])\n        return list(enumerate(engines))\n\n    def check_timeout(self):\n        if self.opt_timeout is not None:\n            total_clock_time = int(monotonic() - self.start_clock_time)\n            if total_clock_time > self.opt_timeout:\n                self.log(f\"Reached TIMEOUT ({self.opt_timeout} seconds). Terminating all subprocesses.\")\n                self.status = \"TIMEOUT\"\n                self.terminate(timeout=True)\n\n    def update_proc_pending(self, proc):\n        self.procs_pending.append(proc)\n        self.taskloop.procs_pending.append(proc)\n\n    def update_proc_running(self, proc):\n        self.procs_pending.remove(proc)\n        self.taskloop.procs_pending.remove(proc)\n\n        self.procs_running.append(proc)\n        self.taskloop.procs_running.append(proc)\n        all_procs_running.append(proc)\n\n    def update_proc_stopped(self, proc):\n        self.procs_running.remove(proc)\n        self.taskloop.procs_running.remove(proc)\n        all_procs_running.remove(proc)\n\n    def update_proc_canceled(self, proc):\n        self.procs_pending.remove(proc)\n        self.taskloop.procs_pending.remove(proc)\n\n    def log(self, logmessage):\n        tm = localtime()\n        line = dress_message(self.workdir, logmessage)\n        for target in self.log_targets:\n            click.echo(line, file=target)\n\n    def log_prefix(self, prefix, message=None):\n        prefix = f\"{click.style(prefix, fg='magenta')}: \"\n        def log(message):\n            self.log(f\"{prefix}{message}\")\n        if message is None:\n            return log\n        else:\n            log(message)\n\n    def error(self, logmessage):\n        tm = localtime()\n        self.log(click.style(f\"ERROR: {logmessage}\", fg=\"red\", bold=True))\n        self.status = \"ERROR\"\n        if \"ERROR\" not in self.expect:\n            self.retcode = 16\n        else:\n            self.retcode = 0\n        self.terminate()\n        with open(f\"{self.workdir}/{self.status}\", \"w\") as f:\n            click.echo(f\"ERROR: {logmessage}\", file=f)\n        raise SbyAbort(logmessage)\n\n    def makedirs(self, path):\n        path = Path(path)\n        if self.reusedir and path.is_dir():\n            rmtree(path, ignore_errors=True)\n        path.mkdir(parents=True, exist_ok=True)\n\n    def copy_src(self, linkmode=False):\n        outdir = Path(self.workdir) / \"src\"\n        self.makedirs(outdir)\n\n        for dstfile, lines in self.verbatim_files.items():\n            dstfile = outdir / dstfile\n            self.log(f\"Writing '{dstfile.absolute()}'.\")\n            dstfile.parent.mkdir(parents=True, exist_ok=True)\n\n            with open(dstfile, \"w\") as f:\n                for line in lines:\n                    f.write(line)\n\n        for dstfile, srcfile in self.files.items():\n            dstfile = Path(dstfile)\n            if dstfile.is_absolute() or \"..\" in dstfile.parts:\n                self.error(f\"destination filename must be a relative path without /../: {dstfile}\")\n            dstfile = outdir / dstfile\n\n            srcfile = process_filename(srcfile)\n\n            basedir = dstfile.parent\n            basedir.mkdir(parents=True, exist_ok=True)\n\n            if linkmode:\n                verb = \"Link\"\n            else:\n                verb = \"Copy\"\n            self.log(f\"{verb} '{srcfile.absolute()}' to '{dstfile.absolute()}'.\")\n\n            if linkmode:\n                os.symlink(srcfile.resolve(), dstfile)\n            elif srcfile.is_dir():\n                copytree(srcfile, dstfile, dirs_exist_ok=True)\n            else:\n                copyfile(srcfile, dstfile)\n\n    def handle_str_option(self, option_name, default_value):\n        if option_name in self.options:\n            self.__dict__[\"opt_\" + option_name] = self.options[option_name]\n            self.used_options.add(option_name)\n        else:\n            self.__dict__[\"opt_\" + option_name] = default_value\n\n    def handle_int_option(self, option_name, default_value):\n        if option_name in self.options:\n            self.__dict__[\"opt_\" + option_name] = int(self.options[option_name])\n            self.used_options.add(option_name)\n        else:\n            self.__dict__[\"opt_\" + option_name] = default_value\n\n    def handle_bool_option(self, option_name, default_value):\n        if option_name in self.options:\n            if self.options[option_name] not in [\"on\", \"off\"]:\n                self.error(f\"Invalid value '{self.options[option_name]}' for boolean option {option_name}.\")\n            self.__dict__[\"opt_\" + option_name] = self.options[option_name] == \"on\"\n            self.used_options.add(option_name)\n        else:\n            self.__dict__[\"opt_\" + option_name] = default_value\n\n    def make_model(self, model_name):\n        modeldir = Path(self.workdir) / \"model\"\n        modeldir.mkdir(exist_ok=True)\n\n        if model_name == \"prep\":\n            with open(modeldir / \"design_prep.ys\", \"w\") as f:\n                print(f\"# running in {modeldir}/\", file=f)\n                print(f\"\"\"read_rtlil design.il\"\"\", file=f)\n                if not self.opt_skip_prep:\n                    print(\"scc -select; simplemap; select -clear\", file=f)\n                    print(\"memory_nordff\", file=f)\n                    if self.opt_multiclock:\n                        print(\"clk2fflogic\", file=f)\n                    else:\n                        print(\"async2sync\", file=f)\n                        if self.opt_assume_early:\n                            print(\"chformal -assume -early\", file=f)\n                    print(\"opt_clean\", file=f)\n                    print(\"formalff -setundef -clk2ff -ff2anyinit -hierarchy\", file=f)\n                    if self.opt_mode in [\"bmc\", \"prove\"]:\n                        print(\"chformal -live -fair -cover -remove\", file=f)\n                    if self.opt_mode == \"cover\":\n                        if self.opt_cover_assert:\n                            print(\"chformal -live -fair -remove\", file=f)\n                        else:\n                            print(\"chformal -live -fair -assert -remove\", file=f)\n                    if self.opt_mode == \"live\":\n                        print(\"chformal -assert2assume\", file=f)\n                        print(\"chformal -cover -remove\", file=f)\n                    print(\"opt_clean\", file=f)\n                    print(\"check\", file=f)  # can't detect undriven wires past this point\n                    print(\"setundef -undriven -anyseq\", file=f)\n                    print(\"opt -fast\", file=f)\n                    if self.opt_witrename:\n                        # we need to run this a second time to handle anything added by prep\n                        print(\"rename -witness\", file=f)\n                        print(\"opt_clean\", file=f)\n                print(f\"\"\"write_rtlil ../model/design_prep.il\"\"\", file=f)\n\n            proc = SbyProc(\n                self,\n                model_name,\n                self.model(\"base\"),\n                \"cd {}/model; {} -ql design_{s}.log design_{s}.ys\".format(self.workdir, self.exe_paths[\"yosys\"], s=model_name)\n            )\n            proc.checkretcode = True\n\n            return [proc]\n\n        if model_name == \"base\":\n            with open(modeldir / \"design.ys\", \"w\") as f:\n                print(f\"# running in {self.workdir}/src/\", file=f)\n                for cmd in self.script:\n                    print(cmd, file=f)\n                # the user must designate a top module in [script]\n                print(\"hierarchy -smtcheck\", file=f)\n                # we need to give flatten-preserved names before write_jny\n                if self.opt_witrename:\n                    print(\"rename -witness\", file=f)\n                print(f\"\"\"write_jny -no-connections ../model/design.json\"\"\", file=f)\n                print(f\"\"\"write_rtlil ../model/design.il\"\"\", file=f)\n\n            proc = SbyProc(\n                self,\n                model_name,\n                [],\n                \"cd {}/src; {} -ql ../model/design.log ../model/design.ys\".format(self.workdir, self.exe_paths[\"yosys\"])\n            )\n            proc.checkretcode = True\n\n            def instance_hierarchy_callback(retcode):\n                if self.design == None:\n                    with open(modeldir / \"design.json\") as f:\n                        self.design = design_hierarchy(f)\n                        self.status_db.create_task_properties([\n                            prop for prop in self.design.properties_by_path.values()\n                            if not prop.type.assume_like\n                        ])\n\n            def instance_hierarchy_error_callback(retcode):\n                self.precise_prop_status = False\n\n            proc.register_exit_callback(instance_hierarchy_callback)\n            proc.error_callback = instance_hierarchy_error_callback\n\n            return [proc]\n\n        if re.match(r\"^smt2(_syn)?(_nomem)?(_stbv|_stdt)?$\", model_name):\n            with open(modeldir / f\"design_{model_name}.ys\", \"w\") as f:\n                print(f\"# running in {modeldir}/\", file=f)\n                print(f\"\"\"read_rtlil design_prep.il\"\"\", file=f)\n                print(\"hierarchy -smtcheck\", file=f)\n                print(\"delete */t:$print\", file=f)\n                print(\"formalff -assume\", file=f)\n                if \"_nomem\" in model_name:\n                    print(\"memory_map -formal\", file=f)\n                    print(\"formalff -setundef -clk2ff -ff2anyinit\", file=f)\n                if \"_syn\" in model_name:\n                    print(\"techmap\", file=f)\n                    print(\"opt -fast\", file=f)\n                    print(\"abc\", file=f)\n                    print(\"opt_clean\", file=f)\n                print(\"dffunmap\", file=f)\n                print(\"stat\", file=f)\n                if \"_stbv\" in model_name:\n                    print(f\"write_smt2 -stbv -wires design_{model_name}.smt2\", file=f)\n                elif \"_stdt\" in model_name:\n                    print(f\"write_smt2 -stdt -wires design_{model_name}.smt2\", file=f)\n                else:\n                    print(f\"write_smt2 -wires design_{model_name}.smt2\", file=f)\n\n            proc = SbyProc(\n                self,\n                model_name,\n                self.model(\"prep\"),\n                \"cd {}/model; {} -ql design_{s}.log design_{s}.ys\".format(self.workdir, self.exe_paths[\"yosys\"], s=model_name)\n            )\n            proc.checkretcode = True\n\n            return [proc]\n\n        if re.match(r\"^btor(_syn)?(_nomem)?$\", model_name):\n            with open(modeldir / f\"design_{model_name}.ys\", \"w\") as f:\n                print(f\"# running in {modeldir}/\", file=f)\n                print(f\"\"\"read_rtlil design_prep.il\"\"\", file=f)\n                print(\"hierarchy -simcheck\", file=f)\n                print(\"delete */t:$print\", file=f)\n                print(\"formalff -assume\", file=f)\n                if \"_nomem\" in model_name:\n                    print(\"memory_map -formal\", file=f)\n                    print(\"formalff -setundef -clk2ff -ff2anyinit\", file=f)\n                print(\"flatten\", file=f)\n                print(\"setundef -undriven -anyseq\", file=f)\n                if \"_syn\" in model_name:\n                    print(\"opt -full\", file=f)\n                    print(\"techmap\", file=f)\n                    print(\"opt -fast\", file=f)\n                    print(\"abc\", file=f)\n                    print(\"opt_clean\", file=f)\n                else:\n                    print(\"opt -fast\", file=f)\n                print(\"delete -output\", file=f)\n                print(\"dffunmap\", file=f)\n                print(\"stat\", file=f)\n                print(\"write_btor {}-i design_{m}.info -ywmap design_btor.ywb design_{m}.btor\".format(\"-c \" if self.opt_mode == \"cover\" else \"\", m=model_name), file=f)\n                print(\"write_btor -s {}-i design_{m}_single.info -ywmap design_btor_single.ywb design_{m}_single.btor\".format(\"-c \" if self.opt_mode == \"cover\" else \"\", m=model_name), file=f)\n\n            proc = SbyProc(\n                self,\n                model_name,\n                self.model(\"prep\"),\n                \"cd {}/model; {} -ql design_{s}.log design_{s}.ys\".format(self.workdir, self.exe_paths[\"yosys\"], s=model_name)\n            )\n            proc.checkretcode = True\n\n            return [proc]\n\n        if model_name == \"aig\":\n            with open(modeldir / \"design_aiger.ys\", \"w\") as f:\n                print(f\"# running in {modeldir}/\", file=f)\n                print(\"read_rtlil design_prep.il\", file=f)\n                print(\"delete */t:$print\", file=f)\n                print(\"hierarchy -simcheck\", file=f)\n                print(\"formalff -assume\", file=f)\n                print(\"flatten\", file=f)\n                print(\"setundef -undriven -anyseq\", file=f)\n                print(\"setattr -unset keep\", file=f)\n                print(\"delete -output\", file=f)\n                print(\"opt -full\", file=f)\n                print(\"techmap\", file=f)\n                print(\"opt -fast\", file=f)\n                print(\"memory_map -formal\", file=f)\n                print(\"formalff -clk2ff -ff2anyinit\", file=f)\n                print(\"simplemap\", file=f)\n                print(\"dffunmap\", file=f)\n                print(\"aigmap\", file=f)\n                print(\"opt_clean\", file=f)\n                print(\"stat\", file=f)\n                print(f\"write_aiger -I -B -zinit -no-startoffset {'-vmap' if self.opt_aigvmap else '-map'} design_aiger.aim\" +\n                        f\"{' -symbols' if self.opt_aigsyms else ''} -ywmap design_aiger.ywa design_aiger.aig\", file=f)\n\n            proc = SbyProc(\n                self,\n                \"aig\",\n                self.model(\"prep\"),\n                f\"\"\"cd {modeldir}; {self.exe_paths[\"yosys\"]} -ql design_aiger.log design_aiger.ys\"\"\"\n            )\n            proc.checkretcode = True\n\n            return [proc]\n\n        if model_name == \"aig_fold\":\n            proc = SbyProc(\n                self,\n                model_name,\n                self.model(\"aig\"),\n                f\"\"\"cd {modeldir}; {self.exe_paths[\"abc\"]} -c 'read_aiger design_aiger.aig; fold{\" -s\" if self.opt_aigfolds else \"\"}; strash; write_aiger design_aiger_fold.aig'\"\"\",\n                logfile=open(f\"{modeldir}/design_aiger_fold.log\", \"w\")\n            )\n            proc.checkretcode = True\n\n            return [proc]\n\n        self.error(f\"Invalid model name: {model_name}\")\n\n    def model(self, model_name):\n        if model_name not in self.models:\n            self.models[model_name] = self.make_model(model_name)\n        return self.models[model_name]\n\n    def terminate(self, timeout=False, cancel=False):\n        if timeout:\n            self.timeout_reached = True\n        for proc in list(self.procs_running):\n            proc.terminate(timeout or cancel)\n        for proc in list(self.procs_pending):\n            proc.terminate(timeout or cancel)\n        if timeout:\n            self.update_unknown_props(dict(source=\"timeout\"))\n            \n    def cancel(self):\n        self.terminate(cancel=True)\n        self.update_status(\"CANCELLED\")\n\n    def proc_failed(self, proc):\n        # proc parameter used by autotune override\n        self.status = \"ERROR\"\n        self.terminate()\n\n    def update_unknown_props(self, data):\n        for prop in self.design.hierarchy:\n            if prop.status != \"UNKNOWN\":\n                continue\n            if ((prop.type == prop.Type.ASSERT and self.opt_mode in [\"bmc\", \"prove\"]) or\n                (prop.type == prop.Type.COVER and self.opt_mode == \"cover\")):\n                self.status_db.set_task_property_status(prop, data=data)\n\n    def pass_unknown_asserts(self, data):\n        for prop in self.design.pass_unknown_asserts():\n            self.status_db.set_task_property_status(prop, data=data)\n\n    def update_status(self, new_status, step = None):\n        assert new_status in [\"PASS\", \"FAIL\", \"UNKNOWN\", \"ERROR\", \"CANCELLED\"]\n        self.status_db.set_task_status(new_status)\n\n        if new_status == \"UNKNOWN\":\n            return\n\n        if self.status == \"ERROR\":\n            return\n\n        if new_status == \"PASS\":\n            assert self.status != \"FAIL\"\n            self.status = \"PASS\"\n            if self.opt_mode in (\"bmc\", \"prove\") and self.design:\n                data = {\"source\": \"task_status\"}\n                if step:\n                    data[\"step\"] = step\n                self.pass_unknown_asserts(data)\n\n        elif new_status == \"FAIL\":\n            assert self.status != \"PASS\"\n            self.status = \"FAIL\"\n\n        elif new_status == \"ERROR\":\n            self.status = \"ERROR\"\n\n        elif new_status == \"CANCELLED\":\n            self.status = \"CANCELLED\"\n\n        else:\n            assert 0\n\n    def handle_non_engine_options(self):\n        with open(f\"{self.workdir}/config.sby\", \"r\") as f:\n            self.parse_config(f)\n\n        self.handle_str_option(\"mode\", None)\n\n        if self.opt_mode is None:\n            self.error(\"Missing mode. Please specify a `mode` in the [options] section.\")\n        elif self.opt_mode not in [\"bmc\", \"prove\", \"cover\", \"live\", \"prep\"]:\n            self.error(f\"Invalid mode: {self.opt_mode}\")\n\n        self.expect = [\"PASS\"]\n        if \"expect\" in self.options:\n            self.expect = self.options[\"expect\"].upper().split(\",\")\n            self.used_options.add(\"expect\")\n\n        for s in self.expect:\n            if s not in [\"PASS\", \"FAIL\", \"UNKNOWN\", \"ERROR\", \"TIMEOUT\", \"CANCELLED\"]:\n                self.error(f\"Invalid expect value: {s}\")\n\n        if self.opt_mode != \"live\":\n            self.handle_int_option(\"depth\", 20)\n\n        self.handle_bool_option(\"multiclock\", False)\n        self.handle_bool_option(\"wait\", False)\n        self.handle_int_option(\"timeout\", None)\n\n        self.handle_bool_option(\"vcd\", True)\n        self.handle_bool_option(\"vcd_sim\", False)\n        self.handle_bool_option(\"fst\", False)\n        self.handle_int_option(\"cycle_width\", 10)\n        if self.opt_cycle_width < 2 or self.opt_cycle_width % 2 != 0:\n                self.error(f\"cycle_width option must be an even number >= 2, but is {self.opt_cycle_width}\")\n\n        self.handle_bool_option(\"witrename\", True)\n        self.handle_bool_option(\"aigfolds\", False)\n        self.handle_bool_option(\"aigvmap\", False)\n        self.handle_bool_option(\"aigsyms\", False)\n\n        self.handle_str_option(\"smtc\", None)\n        self.handle_int_option(\"skip\", None)\n        self.handle_str_option(\"tbtop\", None)\n\n        if self.opt_mode != \"live\":\n            self.handle_int_option(\"append\", 0)\n            self.handle_bool_option(\"append_assume\", True)\n\n        self.handle_str_option(\"make_model\", None)\n        self.handle_bool_option(\"skip_prep\", False)\n\n        self.handle_bool_option(\"assume_early\", True)\n        \n        if self.opt_mode == \"cover\":\n            self.handle_bool_option(\"cover_assert\", False)\n\n    def setup_status_db(self, status_path=None):\n        if hasattr(self, 'status_db'):\n            return\n\n        if status_path is None:\n            try:\n                with open(f\"{self.workdir}/status.path\", \"r\") as status_path_file:\n                    status_path = f\"{self.workdir}/{status_path_file.read().rstrip()}\"\n            except FileNotFoundError:\n                status_path = f\"{self.workdir}/status.sqlite\"\n\n        self.status_db = SbyStatusDb(status_path, self, live_formats=self.live_formats)\n\n    def setup_procs(self, setupmode, linkmode=False):\n        self.handle_non_engine_options()\n        if self.opt_smtc is not None:\n            for engine_idx, engine in self.engine_list():\n                if engine[0] != \"smtbmc\":\n                    self.error(\"Option smtc is only valid for smtbmc engine.\")\n\n        if self.opt_skip is not None:\n            if self.opt_skip == 0:\n                self.opt_skip = None\n            else:\n                for engine_idx, engine in self.engine_list():\n                    if engine[0] not in [\"smtbmc\", \"btor\"]:\n                        self.error(\"Option skip is only valid for smtbmc and btor engines.\")\n\n        if len(self.engine_list()) == 0 and self.opt_mode != \"prep\":\n            self.error(\"Config file is lacking engine configuration.\")\n\n        if self.reusedir:\n            rmtree(f\"{self.workdir}/model\", ignore_errors=True)\n        else:\n            self.copy_src(linkmode)\n\n        if setupmode:\n            self.retcode = 0\n            return\n\n        self.setup_status_db()\n\n        if self.opt_make_model is not None:\n            for name in self.opt_make_model.split(\",\"):\n                self.model(name.strip())\n\n            for proc in self.procs_pending:\n                proc.wait = True\n\n        if self.opt_mode == \"bmc\":\n            import sby_mode_bmc\n            sby_mode_bmc.run(self)\n\n        elif self.opt_mode == \"prove\":\n            import sby_mode_prove\n            sby_mode_prove.run(self)\n\n        elif self.opt_mode == \"live\":\n            import sby_mode_live\n            sby_mode_live.run(self)\n\n        elif self.opt_mode == \"cover\":\n            import sby_mode_cover\n            sby_mode_cover.run(self)\n\n        elif self.opt_mode == \"prep\":\n            self.model(\"prep\")\n            self.update_status(\"PASS\")\n\n        else:\n            assert False\n\n        for opt in self.options.keys():\n            if opt not in self.used_options:\n                self.error(f\"Unused option: {opt}\")\n\n    def summarize(self):\n        total_clock_time = int(monotonic() - self.start_clock_time)\n\n        if os.name == \"posix\":\n            ru = resource.getrusage(resource.RUSAGE_CHILDREN)\n            total_process_time = int((ru.ru_utime + ru.ru_stime) - self.start_process_time)\n            self.total_time = total_process_time\n\n            # TODO process time is incorrect when running in parallel\n\n            self.summary.timing = [\n                \"Elapsed clock time [H:MM:SS (secs)]: {}:{:02d}:{:02d} ({})\".format\n                        (total_clock_time // (60*60), (total_clock_time // 60) % 60, total_clock_time % 60, total_clock_time),\n                \"Elapsed process time [H:MM:SS (secs)]: {}:{:02d}:{:02d} ({})\".format\n                        (total_process_time // (60*60), (total_process_time // 60) % 60, total_process_time % 60, total_process_time),\n            ]\n        else:\n            self.summary.timing = [\n                \"Elapsed clock time [H:MM:SS (secs)]: {}:{:02d}:{:02d} ({})\".format\n                        (total_clock_time // (60*60), (total_clock_time // 60) % 60, total_clock_time % 60, total_clock_time),\n                \"Elapsed process time unvailable on Windows\"\n            ]\n\n        for line in self.summary:\n            if line.startswith(\"Elapsed\"):\n                self.log(f\"summary: {line}\")\n            else:\n                self.log(\"summary: \" + click.style(line, fg=\"green\" if self.status in self.expect else \"red\", bold=True))\n\n        assert self.status in [\"PASS\", \"FAIL\", \"UNKNOWN\", \"ERROR\", \"TIMEOUT\", \"CANCELLED\"]\n\n        if self.status in self.expect:\n            self.retcode = 0\n        else:\n            if self.status == \"PASS\": self.retcode = 1\n            if self.status == \"FAIL\": self.retcode = 2\n            if self.status == \"UNKNOWN\": self.retcode = 4\n            if self.status == \"TIMEOUT\": self.retcode = 8\n            if self.status == \"ERROR\": self.retcode = 16\n            if self.status == \"CANCELLED\": self.retcode = 32\n\n    def write_summary_file(self):\n        with open(f\"{self.workdir}/{self.status}\", \"w\") as f:\n            for line in self.summary.summarize(short=False):\n                click.echo(line, file=f)\n\n    def print_junit_result(self, f, junit_ts_name, junit_tc_name, junit_format_strict=False):\n        junit_time = strftime('%Y-%m-%dT%H:%M:%S')\n        if not self.design:\n            self.precise_prop_status = False\n        if self.precise_prop_status:\n            checks = self.design.hierarchy.get_property_list()\n            junit_tests = len(checks)\n            junit_failures = 0\n            junit_errors = 0\n            junit_skipped = 0\n            for check in checks:\n                if check.status == \"PASS\":\n                    pass\n                elif check.status == \"FAIL\":\n                    junit_failures += 1\n                elif check.status == \"UNKNOWN\":\n                    junit_skipped += 1\n                else:\n                    junit_errors += 1\n            if self.retcode == 16:\n                junit_errors += 1\n            elif self.retcode != 0:\n                junit_failures += 1\n        else:\n            junit_tests = 1\n            junit_errors = 1 if self.retcode == 16 else 0\n            junit_failures = 1 if self.retcode != 0 and junit_errors == 0 else 0\n            junit_skipped = 0\n        print(f'<?xml version=\"1.0\" encoding=\"UTF-8\"?>', file=f)\n        print(f'<testsuites>', file=f)\n        print(f'<testsuite timestamp=\"{junit_time}\" hostname=\"{platform.node()}\" package=\"{junit_ts_name}\" id=\"0\" name=\"{junit_tc_name}\" tests=\"{junit_tests}\" errors=\"{junit_errors}\" failures=\"{junit_failures}\" time=\"{self.total_time}\" skipped=\"{junit_skipped}\">', file=f)\n        print(f'<properties>', file=f)\n        print(f'<property name=\"os\" value=\"{platform.system()}\"/>', file=f)\n        print(f'<property name=\"expect\" value=\"{\", \".join(self.expect)}\"/>', file=f)\n        print(f'<property name=\"status\" value=\"{self.status}\"/>', file=f)\n        print(f'</properties>', file=f)\n        if self.precise_prop_status:\n            print(f'<testcase classname=\"{junit_tc_name}\" name=\"build execution\" time=\"0\">', file=f)\n            if self.retcode == 16:\n                print(f'<error type=\"ERROR\"/>', file=f) # type mandatory, message optional\n            elif self.retcode != 0:\n                if len(self.expect) > 1 or \"PASS\" not in self.expect:\n                    expected = \" \".join(self.expect)\n                    print(f'<failure type=\"EXPECT\" message=\"Task returned status {self.status}. Expected values were: {expected}\" />', file=f)\n                else:\n                    print(f'<failure type=\"{self.status}\" message=\"Task returned status {self.status}.\" />', file=f)\n            print(f'</testcase>', file=f)\n\n            for check in checks:\n                if junit_format_strict:\n                    detail_attrs = ''\n                else:\n                    detail_attrs = f' type=\"{check.type}\" location=\"{check.location}\" id=\"{check.name}\"'\n                    if check.tracefile:\n                        detail_attrs += f' tracefile=\"{check.tracefile}\"'\n                if check.location:\n                    junit_prop_name = f\"Property {check.type} in {check.hierarchy} at {check.location}\"\n                else:\n                    junit_prop_name = f\"Property {check.type} {check.name} in {check.hierarchy}\"\n                print(f'<testcase classname=\"{junit_tc_name}\" name=\"{junit_prop_name}\" time=\"0\"{detail_attrs}>', file=f)\n                if check.status == \"PASS\":\n                    pass\n                elif check.status == \"UNKNOWN\":\n                    print(f'<skipped />', file=f)\n                elif check.status == \"FAIL\":\n                    traceinfo = f' Trace file: {check.tracefile}' if check.type == check.Type.ASSERT else ''\n                    print(f'<failure type=\"{check.type}\" message=\"{junit_prop_name} failed.{traceinfo}\" />', file=f)\n                elif check.status == \"ERROR\":\n                    print(f'<error type=\"ERROR\"/>', file=f) # type mandatory, message optional\n                print(f'</testcase>', file=f)\n        else:\n            print(f'<testcase classname=\"{junit_tc_name}\" name=\"{junit_tc_name}\" time=\"{self.total_time}\">', file=f)\n            if junit_errors:\n                print(f'<error type=\"ERROR\"/>', file=f) # type mandatory, message optional\n            elif junit_failures:\n                junit_type = \"assert\" if self.opt_mode in [\"bmc\", \"prove\"] else self.opt_mode\n                print(f'<failure type=\"{junit_type}\" message=\"{self.status}\" />', file=f)\n            print(f'</testcase>', file=f)\n        print('<system-out>', end=\"\", file=f)\n        with open(f\"{self.workdir}/logfile.txt\", \"r\") as logf:\n            for line in logf:\n                print(line.replace(\"&\", \"&amp;\").replace(\"<\", \"&lt;\").replace(\">\", \"&gt;\").replace(\"\\\"\", \"&quot;\"), end=\"\", file=f)\n        print('</system-out>', file=f)\n        print('<system-err>', file=f)\n        #TODO: can we handle errors and still output this file?\n        print('</system-err>', file=f)\n        print(f'</testsuite>', file=f)\n        print(f'</testsuites>', file=f)\n"
  },
  {
    "path": "sbysrc/sby_design.py",
    "content": "#\n# SymbiYosys (sby) -- Front-end for Yosys-based formal verification flows\n#\n# Copyright (C) 2022  N. Engelhardt <nak@yosyshq.com>\n#\n# Permission to use, copy, modify, and/or distribute this software for any\n# purpose with or without fee is hereby granted, provided that the above\n# copyright notice and this permission notice appear in all copies.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n#\n\nimport json, re\nfrom enum import Enum, auto\nfrom dataclasses import dataclass, field\nfrom typing import Optional, Tuple\n\n\naddr_re = re.compile(r'\\\\\\[[0-9]+\\]$')\npublic_name_re = re.compile(r\"\\\\([a-zA-Z_][a-zA-Z0-9_]*(\\[[0-9]+\\])?|\\[[0-9]+\\])$\")\n\ndef pretty_name(id):\n    if public_name_re.match(id):\n        return id.lstrip(\"\\\\\")\n    else:\n        return id\n\ndef pretty_path(path):\n    out = \"\"\n    for name in path:\n        name = pretty_name(name)\n        if name.startswith(\"[\"):\n            out += name\n            continue\n        if out:\n            out += \".\"\n        if name.startswith(\"\\\\\") or name.startswith(\"$\"):\n            out += name + \" \"\n        else:\n            out += name\n\n    return out\n\n\n@dataclass\nclass SbyProperty:\n    class Type(Enum):\n        ASSUME = auto()\n        ASSERT = auto()\n        COVER = auto()\n        LIVE = auto()\n        FAIR = auto()\n\n        def __str__(self):\n            return self.name\n\n        @classmethod\n        def from_cell(c, name):\n            if name == \"$assume\":\n                return c.ASSUME\n            if name == \"$assert\":\n                return c.ASSERT\n            if name == \"$cover\":\n                return c.COVER\n            if name == \"$live\":\n                return c.LIVE\n            if name == \"$fair\":\n                return c.FAIR\n            raise ValueError(\"Unknown property type: \" + name)\n\n        @classmethod\n        def from_flavor(c, name):\n            if name == \"assume\":\n                return c.ASSUME\n            if name == \"assert\":\n                return c.ASSERT\n            if name == \"cover\":\n                return c.COVER\n            if name == \"live\":\n                return c.LIVE\n            if name == \"fair\":\n                return c.FAIR\n            raise ValueError(\"Unknown property type: \" + name)\n\n        @property\n        def assume_like(self):\n            return self in [self.ASSUME, self.FAIR]\n\n    name: str\n    path: Tuple[str, ...]\n    type: Type\n    location: str\n    hierarchy: str\n    status: str = field(default=\"UNKNOWN\")\n    tracefiles: str = field(default_factory=list)\n\n    @property\n    def tracefile(self):\n        if self.tracefiles:\n            return self.tracefiles[0]\n        else:\n            return \"\"\n\n    @property\n    def celltype(self):\n        return f\"${str(self.type).lower()}\"\n\n    @property\n    def kind(self):\n        return str(self.type)\n\n    @property\n    def hdlname(self):\n        return pretty_path(self.path).rstrip()\n\n\n    def __repr__(self):\n        return f\"SbyProperty<{self.type} {self.name} {self.path} at {self.location}: status={self.status}, tracefile=\\\"{self.tracefile}\\\">\"\n\n@dataclass\nclass SbyModule:\n    name: str\n    path: Tuple[str, ...]\n    type: str\n    submodules: dict = field(default_factory=dict)\n    properties: list = field(default_factory=list)\n\n    def __repr__(self):\n        return f\"SbyModule<{self.name} : {self.type}, submodules={self.submodules}, properties={self.properties}>\"\n\n    def __iter__(self):\n        for prop in self.properties:\n            yield prop\n        for submod in self.submodules.values():\n            yield from submod.__iter__()\n\n    def get_property_list(self):\n        return [p for p in self if p.type != p.Type.ASSUME]\n\n    def find_property(self, path, cell_name, trans_dict=dict()):\n        # backends may need to mangle names irreversibly, so allow applying\n        # the same transformation here\n        trans = str.maketrans(trans_dict)\n        path_iter = iter(path)\n\n        mod = next(path_iter).translate(trans)\n        if self.name.translate(trans) != mod:\n            raise ValueError(f\"{self.name} is not the first module in hierarchical path {pretty_path(path)}.\")\n\n        mod_hier = self\n        for mod in path_iter:\n            mod_hier = next((v for k, v in mod_hier.submodules.items() if mod.translate(trans) == k.translate(trans)), None)\n            if not mod_hier:\n                raise KeyError(f\"Could not find {pretty_path(path)} in design hierarchy!\")\n\n        prop = next((p for p in mod_hier.properties if cell_name == p.name.translate(trans)), None)\n        if not prop:\n            raise KeyError(f\"Could not find property {cell_name} at location {pretty_print(path)} in properties list!\")\n        return prop\n\n\n@dataclass\nclass SbyDesign:\n    hierarchy: SbyModule = None\n    memory_bits: int = 0\n    forall: bool = False\n    properties_by_path: dict = field(default_factory=dict)\n\n    def pass_unknown_asserts(self):\n        updated = []\n        for prop in self.hierarchy:\n            if prop.type == prop.Type.ASSERT and prop.status == \"UNKNOWN\":\n                prop.status = \"PASS\"\n                updated.append(prop)\n        return updated\n\n\ndef cell_path(cell):\n    path = cell[\"attributes\"].get(\"hdlname\")\n    if path is None:\n        if cell[\"name\"].startswith('$'):\n            return (cell[\"name\"],)\n        else:\n            return (\"\\\\\" + cell[\"name\"],)\n    else:\n        return tuple(f\"\\\\{segment}\" for segment in path.split())\n\n\ndef design_hierarchy(filename):\n    design = SbyDesign(hierarchy=None)\n    design_json = json.load(filename)\n    def make_mod_hier(instance_name, module_name, hierarchy=\"\", path=()):\n        # print(instance_name,\":\", module_name)\n        sub_hierarchy=f\"{hierarchy}/{instance_name}\" if hierarchy else instance_name\n        mod = SbyModule(name=instance_name, path=path, type=module_name)\n\n        for m in design_json[\"modules\"]:\n            if m[\"name\"] == module_name:\n                cell_sorts = m[\"cell_sorts\"]\n                break\n        else:\n            raise ValueError(f\"Cannot find module {module_name}\")\n\n        for sort in cell_sorts:\n            if sort[\"type\"] in [\"$assume\", \"$assert\", \"$cover\", \"$live\", \"$fair\"]:\n                for cell in sort[\"cells\"]:\n                    try:\n                        location = cell[\"attributes\"][\"src\"]\n                    except KeyError:\n                        location = \"\"\n                    p = SbyProperty(\n                        name=cell[\"name\"],\n                        path=(*path, *cell_path(cell)),\n                        type=SbyProperty.Type.from_cell(sort[\"type\"]),\n                        location=location,\n                        hierarchy=sub_hierarchy)\n                    mod.properties.append(p)\n            if sort[\"type\"] == \"$check\":\n                for cell in sort[\"cells\"]:\n                    try:\n                        location = cell[\"attributes\"][\"src\"]\n                    except KeyError:\n                        location = \"\"\n                    p = SbyProperty(\n                        name=cell[\"name\"],\n                        path=(*path, *cell_path(cell)),\n                        type=SbyProperty.Type.from_flavor(cell[\"parameters\"][\"FLAVOR\"]),\n                        location=location,\n                        hierarchy=sub_hierarchy)\n                    mod.properties.append(p)\n\n            if sort[\"type\"][0] != '$' or sort[\"type\"].startswith(\"$paramod\"):\n                for cell in sort[\"cells\"]:\n                    mod.submodules[cell[\"name\"]] = make_mod_hier(\n                        cell[\"name\"], sort[\"type\"], sub_hierarchy, (*path, *cell_path(cell)))\n            if sort[\"type\"] in [\"$mem\", \"$mem_v2\"]:\n                for cell in sort[\"cells\"]:\n                    design.memory_bits += int(cell[\"parameters\"][\"WIDTH\"], 2) * int(cell[\"parameters\"][\"SIZE\"], 2)\n            if sort[\"type\"] in [\"$allconst\", \"$allseq\"]:\n                design.forall = True\n\n        return mod\n\n    for m in design_json[\"modules\"]:\n        attrs = m[\"attributes\"]\n        if \"top\" in attrs and int(attrs[\"top\"]) == 1:\n            design.hierarchy = make_mod_hier(m[\"name\"], m[\"name\"], \"\", (m[\"name\"],))\n\n            for prop in design.hierarchy:\n                design.properties_by_path[prop.path[1:]] = prop\n            return design\n    else:\n        raise ValueError(\"Cannot find top module\")\n\ndef main():\n    import sys\n    if len(sys.argv) != 2:\n        print(f\"\"\"Usage: {sys.argv[0]} design.json\"\"\")\n    with open(sys.argv[1]) as f:\n        design = design_hierarchy(f)\n        print(\"Design Hierarchy:\", design.hierarchy)\n        for p in design.hierarchy.get_property_list():\n            print(\"Property:\", p)\n        print(\"Memory Bits:\", design.memory_bits)\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "sbysrc/sby_engine_abc.py",
    "content": "#\n# SymbiYosys (sby) -- Front-end for Yosys-based formal verification flows\n#\n# Copyright (C) 2016  Claire Xenia Wolf <claire@yosyshq.com>\n#\n# Permission to use, copy, modify, and/or distribute this software for any\n# purpose with or without fee is hereby granted, provided that the above\n# copyright notice and this permission notice appear in all copies.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n#\n\nimport re, getopt\nimport json\nimport os\nfrom sby_core import SbyProc\nfrom sby_engine_aiger import aigsmt_exit_callback, aigsmt_trace_callback\n\n\ndef abc_getopt(args, long):\n    long = set(long)\n    output = []\n    parsed = []\n    toggles = set()\n    pos = 0\n\n    while pos < len(args):\n        arg = args[pos]\n        pos += 1\n        if not arg.startswith('-'):\n            output.append(arg)\n        elif arg == '--':\n            output.extend(args[pos:])\n            break\n        elif arg.startswith('--'):\n            if '=' in arg:\n                prefix, param = arg.split('=', 1)\n                if prefix + \"=\" in long:\n                    parsed.append(prefix, param)\n            elif arg[2:] in long:\n                parsed.append((arg, ''))\n            elif arg[2:] + \"=\" in long:\n                parsed.append((arg, args[pos]))\n                pos += 1\n            else:\n                output.append(arg)\n        elif arg.startswith('-'):\n            output.append(arg)\n            for c in arg[1:]:\n                if 'A' <= c <= 'Z':\n                    if pos < len(args):\n                        output.append(args[pos])\n                        pos += 1\n                else:\n                    toggles.symmetric_difference_update([c])\n\n    return output, parsed, toggles\n\n\ndef run(mode, task, engine_idx, engine):\n    keep_going = False\n\n    fold_command = \"fold\"\n    if task.opt_aigfolds:\n        fold_command += \" -s\"\n\n    prep_commands = []\n\n    for i, arg in reversed(list(enumerate(engine[1:], 1))):\n        if arg.endswith(';'):\n            prep_commands = engine[1:i + 1]\n            engine[1:] = engine[i + 1:]\n            break\n\n    abc_command, custom_options, toggles = abc_getopt(engine[1:], [\n        \"keep-going\",\n    ])\n\n    if len(abc_command) == 0:\n        task.error(\"Missing ABC command.\")\n\n    if abc_command[0].startswith('-'):\n        task.error(f\"Unexpected ABC engine option '{abc_command[0]}'.\")\n\n    if abc_command[0] == \"bmc3\":\n        if mode != \"bmc\":\n            task.error(\"ABC command 'bmc3' is only valid in bmc mode.\")\n        for o, a in custom_options:\n            task.error(f\"Option {o} not supported by 'abc {abc_command[0]}'\")\n        abc_command[0] += f\" -F {task.opt_depth} -v\"\n\n    elif abc_command[0] == \"sim3\":\n        if mode != \"bmc\":\n            task.error(\"ABC command 'sim3' is only valid in bmc mode.\")\n        for o, a in custom_options:\n            task.error(f\"Option {o} not supported by 'abc {abc_command[0]}'\")\n        abc_command[0] += f\" -F {task.opt_depth} -v\"\n\n    elif abc_command[0] == \"pdr\":\n        if mode != \"prove\":\n            task.error(\"ABC command 'pdr' is only valid in prove mode.\")\n\n        for o, a in custom_options:\n            if o == '--keep-going':\n                keep_going = True\n            else:\n                task.error(f\"Option {o} not supported by 'abc {abc_command[0]}'\")\n\n        abc_command[0] += \" -v -l\"\n\n        if keep_going:\n            abc_command += [\"-a\", \"-X\", f\"engine_{engine_idx}/trace_\"]\n\n        if 'd' in toggles:\n            abc_command += [\"-I\", f\"engine_{engine_idx}/invariants.pla\"]\n            if not task.opt_aigfolds:\n                fold_command += \" -s\"\n\n    else:\n        task.error(f\"Invalid ABC command {abc_command[0]}.\")\n\n    abc_command[0:0] = prep_commands\n\n    smtbmc_vcd = task.opt_vcd and not task.opt_vcd_sim\n    run_aigsmt = smtbmc_vcd or (task.opt_append and task.opt_append_assume)\n    smtbmc_append = 0\n    sim_append = 0\n    log = task.log_prefix(f\"engine_{engine_idx}\")\n\n    if task.opt_append_assume:\n        smtbmc_append = task.opt_append\n    elif smtbmc_vcd:\n        if not task.opt_append_assume:\n            log(\"For VCDs generated by smtbmc the option 'append_assume off' is ignored\")\n        smtbmc_append = task.opt_append\n    else:\n        sim_append = task.opt_append\n\n    proc = SbyProc(\n        task,\n        f\"engine_{engine_idx}\",\n        task.model(\"aig\"),\n        f\"\"\"cd {task.workdir}; {task.exe_paths[\"abc\"]} -c 'read_aiger model/design_aiger.aig; {\n            fold_command}; strash; {\" \".join(abc_command)}; write_cex -a engine_{engine_idx}/trace.aiw'\"\"\",\n        logfile=open(f\"{task.workdir}/engine_{engine_idx}/logfile.txt\", \"w\")\n    )\n    proc.checkretcode = True\n\n    proc.noprintregex = re.compile(r\"^\\.+$\")\n    proc_status = \"UNKNOWN\"\n\n    procs_running = 1\n\n    aiger_props = None\n    disproved = set()\n    proved = set()\n\n    def output_callback(line):\n        nonlocal proc_status\n        nonlocal procs_running\n        nonlocal aiger_props\n\n        if aiger_props is None:\n            with open(f\"{task.workdir}/model/design_aiger.ywa\") as ywa_file:\n                ywa = json.load(ywa_file)\n                aiger_props = []\n                for path in ywa[\"asserts\"]:\n                    aiger_props.append(task.design.properties_by_path.get(tuple(path)))\n\n        if keep_going:\n            match = re.match(r\"Writing CEX for output ([0-9]+) to (engine_[0-9]+/(.*)\\.aiw)\", line)\n            if match:\n                output = int(match[1])\n                tracefile = match[2]\n                name = match[3]\n                trace, _ = os.path.splitext(name)\n                task.summary.add_event(engine_idx=engine_idx, trace=trace, path=tracefile)\n                prop = aiger_props[output]\n                if prop:\n                    prop.status = \"FAIL\"\n                    task.summary.add_event(\n                        engine_idx=engine_idx, trace=trace,\n                        hdlname=prop.hdlname, src=prop.location, prop=prop,\n                    )\n                disproved.add(output)\n                proc_status = \"FAIL\"\n                proc = aigsmt_trace_callback(task, engine_idx, proc_status,\n                    run_aigsmt=run_aigsmt, smtbmc_vcd=smtbmc_vcd, smtbmc_append=smtbmc_append, sim_append=sim_append,\n                    name=name,\n                )\n                proc.register_exit_callback(exit_callback)\n                procs_running += 1\n        else:\n            match = re.match(r\"^Output [0-9]+ of miter .* was asserted in frame [0-9]+.\", line)\n            if match: proc_status = \"FAIL\"\n\n        match = re.match(r\"^Proved output +([0-9]+) in frame +-?[0-9]+\", line)\n        if match:\n            output = int(match[1])\n            prop = aiger_props[output] if aiger_props else None\n            if prop:\n                prop.status = \"PASS\"\n                task.summary.add_event(\n                    engine_idx=engine_idx, trace=None,\n                    hdlname=prop.hdlname, src=prop.location, prop=prop,\n                )\n            proved.add(output)\n\n        match = re.match(r\"^Simulation of [0-9]+ frames for [0-9]+ rounds with [0-9]+ restarts did not assert POs.\", line)\n        if match: proc_status = \"UNKNOWN\"\n\n        match = re.match(r\"^Stopping BMC because all 2\\^[0-9]+ reachable states are visited.\", line)\n        if match: proc_status = \"PASS\"\n\n        match = re.match(r\"^No output asserted in [0-9]+ frames.\", line)\n        if match: proc_status = \"PASS\"\n\n        match = re.match(r\"^Property proved.\", line)\n        if match: proc_status = \"PASS\"\n\n        if keep_going:\n            match = re.match(r\"^Properties:  All = (\\d+). Proved = (\\d+). Disproved = (\\d+). Undecided = (\\d+).\", line)\n            if match:\n                all_count = int(match[1])\n                proved_count = int(match[2])\n                disproved_count = int(match[3])\n                undecided_count = int(match[4])\n                if (\n                    (aiger_props and all_count != len(aiger_props)) or\n                    all_count != proved_count + disproved_count + undecided_count or\n                    disproved_count != len(disproved) or\n                    proved_count != len(proved)\n                ):\n                    log(\"WARNING: inconsistent status output\")\n                    proc_status = \"UNKNOWN\"\n                elif proved_count == all_count:\n                    proc_status = \"PASS\"\n                elif disproved_count == 0:\n                    proc_status = \"UNKNOWN\"\n                else:\n                    proc_status = \"FAIL\"\n\n        match = re.match(\"Error: (Does not work|Only works) for (sequential|combinational) networks.\", line)\n        if match: proc_status = \"ERROR\"\n\n        return line\n\n    def exit_callback(retcode):\n        nonlocal procs_running\n        if keep_going:\n            procs_running -= 1\n            if not procs_running:\n                if proc_status == \"FAIL\" and mode == \"bmc\" and keep_going:\n                    task.pass_unknown_asserts(dict(source=\"abc pdr\", keep_going=True, engine=f\"engine_{engine_idx}\"))\n                task.update_status(proc_status)\n                task.summary.set_engine_status(engine_idx, proc_status)\n                if proc_status != \"UNKNOWN\" and not keep_going:\n                    task.terminate()\n        else:\n            aigsmt_exit_callback(task, engine_idx, proc_status,\n                run_aigsmt=run_aigsmt, smtbmc_vcd=smtbmc_vcd, smtbmc_append=smtbmc_append, sim_append=sim_append)\n\n    proc.output_callback = output_callback\n    proc.register_exit_callback(exit_callback)\n"
  },
  {
    "path": "sbysrc/sby_engine_aiger.py",
    "content": "#\n# SymbiYosys (sby) -- Front-end for Yosys-based formal verification flows\n#\n# Copyright (C) 2016  Claire Xenia Wolf <claire@yosyshq.com>\n#\n# Permission to use, copy, modify, and/or distribute this software for any\n# purpose with or without fee is hereby granted, provided that the above\n# copyright notice and this permission notice appear in all copies.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n#\n\nimport re, os, getopt, click, json\nfrom sby_core import SbyProc\nfrom sby_sim import sim_witness_trace\n\ndef run(mode, task, engine_idx, engine):\n    opts, solver_args = getopt.getopt(engine[1:], \"\", [])\n\n    if len(solver_args) == 0:\n        task.error(\"Missing solver command.\")\n\n    for o, a in opts:\n        task.error(\"Unexpected AIGER engine options.\")\n\n    status_2 = \"UNKNOWN\"\n\n    model_variant = \"\"\n    json_output = False\n\n    if solver_args[0] == \"suprove\":\n        if mode not in [\"live\", \"prove\"]:\n            task.error(\"The aiger solver 'suprove' is only supported in live and prove modes.\")\n        if mode == \"live\" and (len(solver_args) == 1 or solver_args[1][0] != \"+\"):\n            solver_args.insert(1, \"+simple_liveness\")\n        solver_cmd = \" \".join([task.exe_paths[\"suprove\"]] + solver_args[1:])\n\n    elif solver_args[0] == \"avy\":\n        model_variant = \"_fold\"\n        if mode != \"prove\":\n            task.error(\"The aiger solver 'avy' is only supported in prove mode.\")\n        solver_cmd = \" \".join([task.exe_paths[\"avy\"], \"--cex\", \"-\"] + solver_args[1:])\n    \n    elif solver_args[0] == \"rIC3\":\n        if mode not in [\"bmc\", \"prove\"]:\n            task.error(\"The aiger solver 'rIC3' is only supported in bmc and prove mode.\")\n        if mode == \"prove\":\n            solver_cmd = \" \".join([task.exe_paths[\"rIC3\"], \"--witness\"] + solver_args[1:])\n        if mode == \"bmc\":\n            solver_cmd = \" \".join([task.exe_paths[\"rIC3\"], \"--bmc-max-k {}\".format(task.opt_depth - 1), \"-e bmc\", \"-v 0\", \"--witness\"] + solver_args[1:])\n            status_2 = \"PASS\"  # rIC3 outputs status 2 when BMC passes\n\n    elif solver_args[0] == \"aigbmc\":\n        if mode != \"bmc\":\n            task.error(\"The aiger solver 'aigbmc' is only supported in bmc mode.\")\n        solver_cmd = \" \".join([task.exe_paths[\"aigbmc\"], str(task.opt_depth - 1)] + solver_args[1:])\n        status_2 = \"PASS\"  # aigbmc outputs status 2 when BMC passes\n\n    elif solver_args[0] == \"imctk-eqy-engine\":\n        model_variant = \"_fold\"\n        json_output = True\n        if mode != \"prove\":\n            task.error(\"The aiger solver 'imctk-eqy-engine' is only supported in prove mode.\")\n        args = [\"--bmc-depth\", str(task.opt_depth), \"--jsonl-output\"]\n        solver_cmd = \" \".join([task.exe_paths[\"imctk-eqy-engine\"], *args, *solver_args[1:]])\n\n    else:\n        task.error(f\"Invalid solver command {solver_args[0]}.\")\n\n    smtbmc_vcd = task.opt_vcd and not task.opt_vcd_sim\n    run_aigsmt = (mode != \"live\") and (smtbmc_vcd or (task.opt_append and task.opt_append_assume))\n    smtbmc_append = 0\n    sim_append = 0\n    log = task.log_prefix(f\"engine_{engine_idx}\")\n\n    if mode != \"live\":\n        if task.opt_append_assume:\n            smtbmc_append = task.opt_append\n        elif smtbmc_vcd:\n            if not task.opt_append_assume:\n                log(\"For VCDs generated by smtbmc the option 'append_assume off' is ignored\")\n            smtbmc_append = task.opt_append\n        else:\n            sim_append = task.opt_append\n\n    proc = SbyProc(\n        task,\n        f\"engine_{engine_idx}\",\n        task.model(f\"aig{model_variant}\"),\n        f\"cd {task.workdir}; {solver_cmd} model/design_aiger{model_variant}.aig\",\n        logfile=open(f\"{task.workdir}/engine_{engine_idx}/logfile.txt\", \"w\")\n    )\n    if solver_args[0] not in [\"avy\", \"rIC3\"]:\n        proc.checkretcode = True\n\n    proc_status = None\n    produced_cex = False\n    end_of_cex = False\n    aiw_file = open(f\"{task.workdir}/engine_{engine_idx}/trace.aiw\", \"w\")\n\n    def output_callback(line):\n        nonlocal proc_status\n        nonlocal produced_cex\n        nonlocal end_of_cex\n\n        if json_output:\n            # Forward log messages, but strip the prefix containing runtime and memory stats\n            if not line.startswith('{'):\n                print(line, file=proc.logfile, flush=True)\n                matched = re.match(r\".*(TRACE|DEBUG|INFO|WARN|ERROR) (.*)\", line)\n                if matched:\n                    if matched[1] == \"INFO\":\n                        task.log(matched[2])\n                    else:\n                        task.log(f\"{matched[1]} {matched[2]}\")\n                return None\n            event = json.loads(line)\n            if \"aiw\" in event:\n                print(event[\"aiw\"], file=aiw_file)\n            if \"status\" in event:\n                if event[\"status\"] == \"pass\":\n                    proc_status = \"PASS\"\n                elif event[\"status\"] == \"fail\":\n                    proc_status = \"FAIL\"\n            return None\n\n        if proc_status is not None:\n            if not end_of_cex and not produced_cex and line.isdigit():\n                produced_cex = True\n            if not end_of_cex:\n                print(line, file=aiw_file)\n            if line == \".\":\n                end_of_cex = True\n            return None\n\n        if line.startswith(\"u\"):\n            return f\"No CEX up to depth {int(line[1:])-1}.\"\n\n        if line in [\"0\", \"1\", \"2\"]:\n            print(line, file=aiw_file)\n            if line == \"0\": proc_status = \"PASS\"\n            if line == \"1\": proc_status = \"FAIL\"\n            if line == \"2\": proc_status = status_2\n\n        return None\n\n    def exit_callback(retcode):\n        aiw_file.close()\n        aigsmt_exit_callback(task, engine_idx, proc_status,\n            run_aigsmt=run_aigsmt, smtbmc_vcd=smtbmc_vcd, smtbmc_append=smtbmc_append, sim_append=sim_append, )\n\n    proc.output_callback = output_callback\n    proc.register_exit_callback(exit_callback)\n\n\ndef aigsmt_exit_callback(task, engine_idx, proc_status, *, run_aigsmt, smtbmc_vcd, smtbmc_append, sim_append):\n    if proc_status is None:\n        task.error(f\"engine_{engine_idx}: Could not determine engine status.\")\n\n    task.update_status(proc_status)\n    task.summary.set_engine_status(engine_idx, proc_status)\n    task.terminate()\n    if proc_status == \"FAIL\" and (not run_aigsmt or task.opt_aigsmt != \"none\"):\n        aigsmt_trace_callback(task, engine_idx, proc_status, run_aigsmt=run_aigsmt, smtbmc_vcd=smtbmc_vcd, smtbmc_append=smtbmc_append, sim_append=sim_append)\n\ndef aigsmt_trace_callback(task, engine_idx, proc_status, *, run_aigsmt, smtbmc_vcd, smtbmc_append, sim_append, name=\"trace\"):\n\n    trace_prefix = f\"engine_{engine_idx}/{name}\"\n\n    aiw2yw_suffix = '_aiw' if run_aigsmt else ''\n\n    witness_proc = SbyProc(\n        task, f\"engine_{engine_idx}\", [],\n        f\"cd {task.workdir}; {task.exe_paths['witness']} aiw2yw engine_{engine_idx}/{name}.aiw model/design_aiger.ywa engine_{engine_idx}/{name}{aiw2yw_suffix}.yw\",\n    )\n    final_proc = witness_proc\n\n    if run_aigsmt:\n        smtbmc_opts = []\n        smtbmc_opts += [\"-s\", task.opt_aigsmt]\n        if task.opt_tbtop is not None:\n            smtbmc_opts  += [\"--vlogtb-top\", task.opt_tbtop]\n        smtbmc_opts += [\"--noprogress\", f\"--append {smtbmc_append}\"]\n        if smtbmc_vcd:\n            smtbmc_opts += [f\"--dump-vcd {trace_prefix}.vcd\"]\n        smtbmc_opts += [f\"--dump-yw {trace_prefix}.yw\", f\"--dump-vlogtb {trace_prefix}_tb.v\", f\"--dump-smtc {trace_prefix}.smtc\"]\n\n        proc2 = SbyProc(\n            task,\n            f\"engine_{engine_idx}\",\n            [*task.model(\"smt2\"), witness_proc],\n            f\"cd {task.workdir}; {task.exe_paths['smtbmc']} {' '.join(smtbmc_opts)} --yw engine_{engine_idx}/{name}{aiw2yw_suffix}.yw model/design_smt2.smt2\",\n            logfile=open(f\"{task.workdir}/engine_{engine_idx}/logfile2.txt\", \"w\"),\n        )\n\n        proc2_status = None\n\n        last_prop = []\n        recorded_last = False\n        current_step = None\n\n        def output_callback2(line):\n            nonlocal proc2_status\n            nonlocal last_prop\n            nonlocal recorded_last\n            nonlocal current_step\n\n            smt2_trans = {'\\\\':'/', '|':'/'}\n\n            def parse_mod_path(path_string):\n                # Match a path with . as delimiter, allowing escaped tokens in\n                # verilog `\\name ` format\n                return [m[1] or m[0] for m in re.findall(r\"(\\\\([^ ]*) |[^\\.]+)(?:\\.|$)\", path_string)]\n\n            match = re.match(r\"^## [0-9: ]+ .* in step ([0-9]+)\\.\\.\", line)\n            if match:\n                last_prop = []\n                recorded_last = False\n                current_step = int(match[1])\n                return line\n\n            match = re.match(r\"^## [0-9: ]+ Status: FAILED\", line)\n            if match: proc2_status = \"FAIL\"\n\n            match = re.match(r\"^## [0-9: ]+ Status: PASSED\", line)\n            if match: proc2_status = \"PASS\"\n\n            match = re.match(r\"^## [0-9: ]+ Assert failed in ([^:]+): (\\S+)(?: \\((\\S+)\\))?\", line)\n            if match:\n                path = parse_mod_path(match[1])\n                cell_name = match[3] or match[2]\n                prop = task.design.hierarchy.find_property(path, cell_name, trans_dict=smt2_trans)\n                prop.status = \"FAIL\"\n                last_prop.append(prop)\n                return line\n\n            match = re.match(r\"^## [0-9: ]+ Writing trace to (VCD|Yosys witness) file: (\\S+)\", line)\n            if match:\n                tracefile = match[2]\n                trace, _ = os.path.splitext(os.path.basename(tracefile))\n                task.summary.add_event(engine_idx=engine_idx, trace=trace, path=tracefile)\n                for p in last_prop:\n                    task.summary.add_event(\n                        engine_idx=engine_idx, trace=trace,\n                        type=p.celltype, hdlname=p.hdlname, src=p.location,\n                        step=current_step, prop=p,\n                    )\n                recorded_last = True\n                return line\n\n            return line\n\n        def exit_callback2(retcode):\n            nonlocal last_prop, recorded_last\n            if proc2_status is None:\n                task.error(f\"engine_{engine_idx}: Could not determine aigsmt status.\")\n            if proc2_status != \"FAIL\":\n                task.error(f\"engine_{engine_idx}: Unexpected aigsmt status.\")\n            if len(last_prop) and not recorded_last:\n                task.error(f\"engine_{engine_idx}: Found properties without trace.\")\n\n        proc2.output_callback = output_callback2\n        proc2.register_exit_callback(exit_callback2)\n\n        final_proc = proc2\n\n    if task.opt_fst or (task.opt_vcd and task.opt_vcd_sim):\n        final_proc = sim_witness_trace(f\"engine_{engine_idx}\", task, engine_idx, f\"engine_{engine_idx}/{name}.yw\", append=sim_append, deps=[final_proc])\n    elif not run_aigsmt:\n        task.log(f\"{click.style(f'engine_{engine_idx}', fg='magenta')}: Engine did not produce a counter example.\")\n\n    return final_proc\n"
  },
  {
    "path": "sbysrc/sby_engine_btor.py",
    "content": "#\n# SymbiYosys (sby) -- Front-end for Yosys-based formal verification flows\n#\n# Copyright (C) 2016  Claire Xenia Wolf <claire@yosyshq.com>\n#\n# Permission to use, copy, modify, and/or distribute this software for any\n# purpose with or without fee is hereby granted, provided that the above\n# copyright notice and this permission notice appear in all copies.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n#\n\nimport re, os, getopt, click\nfrom types import SimpleNamespace\nfrom sby_core import SbyProc\nfrom sby_sim import sim_witness_trace\n\ndef run(mode, task, engine_idx, engine):\n    random_seed = None\n\n    opts, solver_args = getopt.getopt(engine[1:], \"\", [\"seed=\"])\n\n    if len(solver_args) == 0:\n        task.error(\"Missing solver command.\")\n\n    for o, a in opts:\n        if o == \"--seed\":\n            random_seed = a\n        else:\n            task.error(\"Unexpected BTOR engine options.\")\n\n    if solver_args[0] == \"btormc\":\n        solver_cmd = \"\"\n        if random_seed:\n            solver_cmd += f\"BTORSEED={random_seed} \"\n        solver_cmd += task.exe_paths[\"btormc\"] + f\"\"\" --stop-first {0 if mode == \"cover\" else 1} -v 1 -kmax {task.opt_depth - 1}\"\"\"\n        if task.opt_skip is not None:\n            solver_cmd += f\" -kmin {task.opt_skip}\"\n        solver_cmd += \" \".join([\"\"] + solver_args[1:])\n\n    elif solver_args[0] == \"pono\":\n        if random_seed:\n            task.error(\"Setting the random seed is not available for the pono solver.\")\n        if task.opt_skip is not None:\n            task.error(\"The btor engine supports the option skip only for the btormc solver.\")\n        solver_cmd = task.exe_paths[\"pono\"] + f\" --witness -v 1 -e bmc -k {task.opt_depth - 1}\"\n        solver_cmd += \" \".join([\"\"] + solver_args[1:])\n\n    else:\n        task.error(f\"Invalid solver command {solver_args[0]}.\")\n\n    log = task.log_prefix(f\"engine_{engine_idx}\")\n\n    btorsim_vcd = task.opt_vcd and not task.opt_vcd_sim\n    run_sim = task.opt_fst or not btorsim_vcd\n    sim_append = 0\n\n    if task.opt_append and btorsim_vcd:\n        log(\"The BTOR engine does not support the 'append' option when using btorsim.\")\n    else:\n        sim_append = task.opt_append\n\n    if task.opt_append and task.opt_append_assume:\n        log(\"The BTOR engine does not support enforcing assumptions in appended time steps.\")\n\n\n    common_state = SimpleNamespace()\n    common_state.solver_status = None\n    common_state.produced_cex = 0\n    common_state.expected_cex = 1\n    common_state.wit_file = None\n    common_state.assert_fail = False\n    common_state.running_procs = 0\n    common_state.current_step = None\n\n    def print_traces_and_terminate():\n        if mode == \"cover\":\n            if common_state.assert_fail:\n                proc_status = \"FAIL\"\n            elif common_state.expected_cex == 0:\n                proc_status = \"pass\"\n            elif common_state.solver_status == \"sat\":\n                proc_status = \"pass\"\n            elif common_state.solver_status == \"unsat\":\n                proc_status = \"FAIL\"\n            else:\n                task.error(f\"engine_{engine_idx}: Engine terminated without status.\")\n            task.update_unknown_props(dict(source=\"btor\", engine=f\"engine_{engine_idx}\"))\n        else:\n            if common_state.expected_cex == 0:\n                proc_status = \"pass\"\n            elif common_state.solver_status == \"sat\":\n                proc_status = \"FAIL\"\n            elif common_state.solver_status == \"unsat\":\n                proc_status = \"pass\"\n            else:\n                task.error(f\"engine_{engine_idx}: Engine terminated without status.\")\n\n        task.update_status(proc_status.upper(), common_state.current_step)\n        task.summary.set_engine_status(engine_idx, proc_status)\n\n        task.terminate()\n\n    if mode == \"cover\":\n        def output_callback2(line):\n            match = re.search(r\"Assert failed in test\", line)\n            if match:\n                common_state.assert_fail = True\n            return line\n    else:\n        def output_callback2(line):\n            return line\n\n    def make_exit_callback(suffix):\n        def exit_callback2(retcode):\n            trace = f\"trace{suffix}\"\n            vcdpath = f\"engine_{engine_idx}/{trace}.vcd\"\n            trace_path = f\"{task.workdir}/{vcdpath}\"\n            if os.path.exists(trace_path):\n                task.summary.add_event(engine_idx=engine_idx, trace=trace, path=vcdpath, type=\"$cover\" if mode == \"cover\" else \"$assert\")\n\n            common_state.running_procs -= 1\n            if (common_state.running_procs == 0):\n                print_traces_and_terminate()\n\n        return exit_callback2\n\n    def simple_exit_callback(retcode):\n        common_state.running_procs -= 1\n        if (common_state.running_procs == 0):\n            print_traces_and_terminate()\n\n    def output_callback(line):\n        if mode == \"cover\":\n            if solver_args[0] == \"btormc\":\n                match = re.search(r\"calling BMC on ([0-9]+) properties\", line)\n                if match:\n                    common_state.expected_cex = int(match[1])\n                    if common_state.produced_cex != 0:\n                        task.error(f\"engine_{engine_idx}: Unexpected engine output (property count).\")\n                    task.update_unknown_props(dict(source=\"btor_init\", engine=f\"engine_{engine_idx}\"))\n\n            else:\n                task.error(f\"engine_{engine_idx}: BTOR solver '{solver_args[0]}' is currently not supported in cover mode.\")\n\n        if (common_state.produced_cex < common_state.expected_cex) and line == \"sat\":\n            if common_state.wit_file != None:\n                task.error(f\"engine_{engine_idx}: Unexpected engine output (sat).\")\n            if common_state.expected_cex == 1:\n                common_state.wit_file = open(f\"{task.workdir}/engine_{engine_idx}/trace.wit\", \"w\")\n            else:\n                common_state.wit_file = open(f\"\"\"{task.workdir}/engine_{engine_idx}/trace{common_state.produced_cex}.wit\"\"\", \"w\")\n            if solver_args[0] != \"btormc\":\n                proc.log(\"Found satisfiability witness.\")\n\n        if common_state.wit_file:\n            print(line, file=common_state.wit_file)\n            if line == \".\":\n                if common_state.expected_cex == 1:\n                    suffix = \"\"\n                else:\n                    suffix = common_state.produced_cex\n\n                model = f\"design_btor{'_single' if solver_args[0] == 'pono' else ''}\"\n\n                yw_proc = SbyProc(\n                    task, f\"engine_{engine_idx}.trace{suffix}\", [],\n                    f\"cd {task.workdir}; {task.exe_paths['witness']} wit2yw engine_{engine_idx}/trace{suffix}.wit model/{model}.ywb engine_{engine_idx}/trace{suffix}.yw\",\n                )\n                common_state.running_procs += 1\n                yw_proc.register_exit_callback(simple_exit_callback)\n\n                btorsim_vcd = (task.opt_vcd and not task.opt_vcd_sim)\n\n                if btorsim_vcd:\n                    # TODO cover runs btorsim not only for trace generation, can we run it without VCD generation in that case?\n                    proc2 = SbyProc(\n                        task,\n                        f\"engine_{engine_idx}.trace{suffix}\",\n                        task.model(\"btor\"),\n                        \"cd {dir} ; btorsim -c --vcd engine_{idx}/trace{i}{i2}.vcd --hierarchical-symbols --info model/design_btor{s}.info model/design_btor{s}.btor engine_{idx}/trace{i}.wit\".format(dir=task.workdir, idx=engine_idx, i=suffix, i2='' if btorsim_vcd else '_btorsim', s='_single' if solver_args[0] == 'pono' else ''),\n                        logfile=open(f\"{task.workdir}/engine_{engine_idx}/logfile2.txt\", \"w\")\n                    )\n                    proc2.output_callback = output_callback2\n                    if run_sim:\n                        proc2.register_exit_callback(simple_exit_callback)\n                    else:\n                        proc2.register_exit_callback(make_exit_callback(suffix))\n                    proc2.checkretcode = True\n                    common_state.running_procs += 1\n\n                if run_sim:\n                    sim_proc = sim_witness_trace(f\"engine_{engine_idx}\", task, engine_idx, f\"engine_{engine_idx}/trace{suffix}.yw\", append=sim_append, deps=[yw_proc])\n                    sim_proc.register_exit_callback(simple_exit_callback)\n                    common_state.running_procs += 1\n\n                common_state.produced_cex += 1\n                common_state.wit_file.close()\n                common_state.wit_file = None\n                if common_state.produced_cex == common_state.expected_cex:\n                    common_state.solver_status = \"sat\"\n\n        else:\n            if solver_args[0] == \"btormc\":\n                if \"calling BMC on\" in line:\n                    return line\n                match = re.match(r\".*at bound k = (\\d+).*\", line)\n                if match:\n                    common_state.current_step = int(match[1])\n                if \"SATISFIABLE\" in line:\n                    return line\n                if \"bad state properties at bound\" in line:\n                    return line\n                if \"deleting model checker:\" in line:\n                    if common_state.solver_status is None:\n                        common_state.solver_status = \"unsat\"\n                    return line\n\n            elif solver_args[0] == \"pono\":\n                match = re.match(r\".*at bound (\\d+).*\", line)\n                if match:\n                    common_state.current_step = int(match[1])\n                if line == \"unknown\":\n                    if common_state.solver_status is None:\n                        common_state.solver_status = \"unsat\"\n                    return \"No CEX found.\"\n                if line not in [\"b0\"]:\n                    return line\n\n            print(line, file=proc.logfile)\n\n        return None\n\n    def exit_callback(retcode):\n        if common_state.expected_cex != 0:\n            if common_state.solver_status is None:\n                task.error(f\"engine_{engine_idx}: Could not determine engine status.\")\n\n        if common_state.solver_status == \"unsat\":\n            if common_state.expected_cex == 1:\n                with open(f\"\"\"{task.workdir}/engine_{engine_idx}/trace.wit\"\"\", \"w\") as wit_file:\n                    print(\"unsat\", file=wit_file)\n            else:\n                for i in range(common_state.produced_cex, common_state.expected_cex):\n                    with open(f\"{task.workdir}/engine_{engine_idx}/trace{i}.wit\", \"w\") as wit_file:\n                        print(\"unsat\", file=wit_file)\n\n        common_state.running_procs -= 1\n        if (common_state.running_procs == 0):\n            print_traces_and_terminate()\n\n    proc = SbyProc(\n        task,\n        f\"engine_{engine_idx}\", task.model(\"btor\"),\n        f\"cd {task.workdir}; {solver_cmd} model/design_btor{'_single' if solver_args[0]=='pono' else ''}.btor\",\n        logfile=open(f\"{task.workdir}/engine_{engine_idx}/logfile.txt\", \"w\")\n    )\n    proc.checkretcode = True\n    if solver_args[0] == \"pono\":\n        proc.retcodes = [0, 1, 255] # UNKNOWN = -1, FALSE = 0, TRUE = 1, ERROR = 2\n    proc.output_callback = output_callback\n    proc.register_exit_callback(exit_callback)\n    common_state.running_procs += 1\n"
  },
  {
    "path": "sbysrc/sby_engine_itp.py",
    "content": "#\n# SymbiYosys (sby) -- Front-end for Yosys-based formal verification flows\n# ITP engine: interpolation-based model checking using Craig interpolants\n#\n# Contributor:  Pratik Deshmukh <deshmukhpratik931@gmail.com>\n#\n# Permission to use, copy, modify, and/or distribute this software for any\n# purpose with or without fee is hereby granted, provided that the above\n# copyright notice and this permission notice appear in all copies.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n#\n# Usage in .sby file:\n#   [engines]\n#   itp [bound] [skip]\n#\n#   bound: maximum unrolling depth (default: task opt_depth or 20)\n#   skip:  number of initial timeframes to skip before checking bad states\n#          (default: task opt_skip or 0)\n#\n# The engine uses the 'itp-bmc' binary which must be either:\n#   1. Available in PATH as 'itp-bmc'\n#   2. Specified via the ITP_BMC environment variable\n#\n# Note: This engine currently does not produce counterexample witness traces.\n# When a property violation is found (FAIL), no .aiw or .yw trace file is\n# generated. Trace generation support is planned for a future version.\n#\n\nimport os, getopt\nfrom sby_core import SbyProc\n\ndef run(mode, task, engine_idx, engine):\n    if mode not in [\"prove\", \"bmc\"]:\n        task.error(f\"engine_{engine_idx}: The itp engine is only supported in prove and bmc modes.\")\n\n    # Parse options\n    args = engine[1:]\n\n    # Defaults from task options\n    bound = task.opt_depth\n    skip  = task.opt_skip\n\n    # Positional args override: itp [bound] [skip]\n    if len(args) > 0:\n        try:\n            bound = int(args[0])\n        except ValueError:\n            task.error(f\"engine_{engine_idx}: Invalid bound value '{args[0]}', expected integer.\")\n    if len(args) > 1:\n        try:\n            skip = int(args[1])\n        except ValueError:\n            task.error(f\"engine_{engine_idx}: Invalid skip value '{args[1]}', expected integer.\")\n    if len(args) > 2:\n        task.error(f\"engine_{engine_idx}: Too many arguments to itp engine.\")\n\n    if skip >= bound:\n        task.error(f\"engine_{engine_idx}: skip ({skip}) must be less than bound ({bound}).\")\n\n    # Locate binary and derive workdir (for minisat relative path)\n    bmc_binary = task.exe_paths[\"itp-bmc\"]\n    bmc_workdir = os.path.dirname(os.path.realpath(bmc_binary))\n\n    log = task.log_prefix(f\"engine_{engine_idx}\")\n\n    # Input: binary AIGER model generated by sby\n    aig_src = f\"{task.workdir}/model/design_aiger.aig\"\n\n    log(f\"Running interpolation-based model checker: bound={bound}, skip={skip}\")\n\n    proc_status = None\n\n    def output_callback(line):\n        nonlocal proc_status\n        if \"Fixpoint reached\" in line:\n            log(\"Interpolation fixpoint reached — property proved SAFE\")\n            proc_status = \"PASS\"\n        elif \"Counterexample found\" in line:\n            log(\"Counterexample found — property UNSAFE\")\n        elif \"Safe up to bound\" in line:\n            log(f\"Bounded safety result (fixpoint not reached): {line.strip()}\")\n            proc_status = \"PASS\"\n        return line\n\n    def exit_callback(retcode):\n        nonlocal proc_status\n\n        # If output_callback already determined status (e.g. from output lines),\n        # trust that over the exit code\n        if proc_status is None:\n            proc_status = \"PASS\" if retcode == 0 else \"FAIL\"\n\n        task.update_status(proc_status)\n        task.summary.set_engine_status(engine_idx, proc_status)\n        task.terminate()\n\n    proc = SbyProc(\n        task,\n        f\"engine_{engine_idx}\",\n        task.model(\"aig\"),\n        f\"BMC_WORKDIR={bmc_workdir} {bmc_binary} {bound} {aig_src} {skip}\",\n        logfile=open(f\"{task.workdir}/engine_{engine_idx}/logfile.txt\", \"w\"),\n    )\n\n    proc.output_callback = output_callback\n    proc.register_exit_callback(exit_callback)"
  },
  {
    "path": "sbysrc/sby_engine_smtbmc.py",
    "content": "#\n# SymbiYosys (sby) -- Front-end for Yosys-based formal verification flows\n#\n# Copyright (C) 2016  Claire Xenia Wolf <claire@yosyshq.com>\n#\n# Permission to use, copy, modify, and/or distribute this software for any\n# purpose with or without fee is hereby granted, provided that the above\n# copyright notice and this permission notice appear in all copies.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n#\n\nimport re, os, getopt, click, glob\nfrom sby_core import SbyProc\nfrom sby_sim import sim_witness_trace\n\ndef run(mode, task, engine_idx, engine):\n    smtbmc_opts = []\n    nomem_opt = False\n    presat_opt = True\n    unroll_opt = None\n    syn_opt = False\n    stbv_opt = False\n    stdt_opt = False\n    dumpsmt2 = False\n    progress = False\n    basecase_only = False\n    induction_only = False\n    keep_going = False\n    random_seed = None\n    task.precise_prop_status = True\n\n    opts, args = getopt.getopt(engine[1:], \"\", [\"nomem\", \"syn\", \"stbv\", \"stdt\", \"presat\",\n            \"nopresat\", \"unroll\", \"nounroll\", \"dumpsmt2\", \"progress\", \"basecase\", \"induction\", \"keep-going\", \"seed=\"])\n\n    for o, a in opts:\n        if o == \"--nomem\":\n            nomem_opt = True\n        elif o == \"--syn\":\n            syn_opt = True\n        elif o == \"--stbv\":\n            stbv_opt = True\n        elif o == \"--stdt\":\n            stdt_opt = True\n        elif o == \"--presat\":\n            presat_opt = True\n        elif o == \"--nopresat\":\n            presat_opt = False\n        elif o == \"--unroll\":\n            unroll_opt = True\n        elif o == \"--nounroll\":\n            unroll_opt = False\n        elif o == \"--dumpsmt2\":\n            dumpsmt2 = True\n        elif o == \"--progress\":\n            progress = True\n        elif o == \"--basecase\":\n            if induction_only:\n                task.error(\"smtbmc options --basecase and --induction are exclusive.\")\n            basecase_only = True\n        elif o == \"--induction\":\n            if basecase_only:\n                task.error(\"smtbmc options --basecase and --induction are exclusive.\")\n            induction_only = True\n        elif o == \"--keep-going\":\n            keep_going = True\n        elif o == \"--seed\":\n            random_seed = a\n        else:\n            task.error(f\"Invalid smtbmc options {o}.\")\n\n    xtra_opts = False\n    for i, a in enumerate(args):\n        if i == 0 and a == \"z3\" and unroll_opt is None:\n                unroll_opt = False\n        if a == \"--\":\n            xtra_opts = True\n            continue\n        if xtra_opts:\n            smtbmc_opts.append(a)\n        else:\n            smtbmc_opts += [\"-s\" if i == 0 else \"-S\", a]\n\n    if presat_opt:\n        smtbmc_opts += [\"--presat\"]\n\n    if unroll_opt is None or unroll_opt:\n        smtbmc_opts += [\"--unroll\"]\n\n    if task.opt_smtc is not None:\n        smtbmc_opts += [\"--smtc\", f\"src/{task.opt_smtc}\"]\n\n    if task.opt_tbtop is not None:\n         smtbmc_opts += [\"--vlogtb-top\", task.opt_tbtop]\n\n    model_name = \"smt2\"\n    if syn_opt: model_name += \"_syn\"\n    if nomem_opt: model_name += \"_nomem\"\n    if stbv_opt: model_name += \"_stbv\"\n    if stdt_opt: model_name += \"_stdt\"\n\n    if mode == \"prove\":\n        if not induction_only:\n            run(\"prove_basecase\", task, engine_idx, engine)\n        if not basecase_only:\n            run(\"prove_induction\", task, engine_idx, engine)\n        return\n\n    procname = f\"engine_{engine_idx}\"\n    trace_prefix = f\"engine_{engine_idx}/trace\"\n    logfile_prefix = f\"{task.workdir}/engine_{engine_idx}/logfile\"\n\n    if mode == \"prove_basecase\":\n        procname += \".basecase\"\n        logfile_prefix += \"_basecase\"\n\n    if mode == \"prove_induction\":\n        procname += \".induction\"\n        trace_prefix += \"_induct\"\n        logfile_prefix += \"_induction\"\n        smtbmc_opts.append(\"-i\")\n\n    if mode == \"cover\":\n        smtbmc_opts.append(\"-c\")\n        trace_prefix += \"%\"\n\n    if keep_going and mode != \"prove_induction\":\n        smtbmc_opts.append(\"--keep-going\")\n        if mode != \"cover\":\n            trace_prefix += \"%\"\n\n    if dumpsmt2:\n        smtbmc_opts += [\"--dump-smt2\", trace_prefix.replace(\"%\", \"\") + \".smt2\"]\n\n    if not progress:\n        smtbmc_opts.append(\"--noprogress\")\n\n    if task.opt_skip is not None:\n        t_opt = \"{}:{}\".format(task.opt_skip, task.opt_depth)\n    else:\n        t_opt = \"{}\".format(task.opt_depth)\n\n    smtbmc_vcd = task.opt_vcd and not task.opt_vcd_sim\n\n    smtbmc_append = 0\n    sim_append = 0\n\n    log = task.log_prefix(f\"engine_{engine_idx}\")\n\n    if task.opt_append_assume:\n        smtbmc_append = task.opt_append\n    elif smtbmc_vcd:\n        if not task.opt_append_assume:\n            log(\"For VCDs generated by smtbmc the option 'append_assume off' is ignored\")\n        smtbmc_append = task.opt_append\n    else:\n        sim_append = task.opt_append\n\n    trace_ext = 'fst' if task.opt_fst else 'vcd'\n\n    random_seed = f\"--info \\\"(set-option :random-seed {random_seed})\\\"\" if random_seed else \"\"\n    dump_flags = f\"--dump-vcd {trace_prefix}.vcd \" if smtbmc_vcd else \"\"\n    dump_flags += f\"--dump-yw {trace_prefix}.yw --dump-vlogtb {trace_prefix}_tb.v --dump-smtc {trace_prefix}.smtc\"\n    proc = SbyProc(\n        task,\n        procname,\n        task.model(model_name),\n        f\"\"\"cd {task.workdir}; {task.exe_paths[\"smtbmc\"]} {\" \".join(smtbmc_opts)} -t {t_opt} {random_seed} --append {smtbmc_append} {dump_flags} model/design_{model_name}.smt2\"\"\",\n        logfile=open(logfile_prefix + \".txt\", \"w\"),\n        logstderr=(not progress)\n    )\n\n    if mode == \"prove_basecase\":\n        task.basecase_procs.append(proc)\n\n    if mode == \"prove_induction\":\n        task.induction_procs.append(proc)\n\n    proc_status = None\n    last_prop = []\n    recorded_last = False\n    pending_sim = None\n    current_step = None\n    procs_running = 1\n    failed_assert = False\n\n    def output_callback(line):\n        nonlocal proc_status\n        nonlocal last_prop\n        nonlocal recorded_last\n        nonlocal pending_sim\n        nonlocal current_step\n        nonlocal procs_running\n        nonlocal failed_assert\n\n        if pending_sim:\n            sim_proc = sim_witness_trace(procname, task, engine_idx, pending_sim, append=sim_append, inductive=mode == \"prove_induction\")\n            sim_proc.register_exit_callback(simple_exit_callback)\n            procs_running += 1\n            pending_sim = None\n\n        smt2_trans = {'\\\\':'/', '|':'/'}\n\n        def parse_mod_path(path_string):\n            # Match a path with . as delimiter, allowing escaped tokens in\n            # verilog `\\name ` format\n            return [m[1] or m[0] for m in re.findall(r\"(\\\\([^ ]*) |[^\\.]+)(?:\\.|$)\", path_string)]\n\n        match = re.match(r\"^## [0-9: ]+ .* in step ([0-9]+)\\.\\.\", line)\n        if match:\n            last_prop = []\n            recorded_last = False\n            if mode == \"prove_induction\":\n                return line\n            last_step = current_step\n            current_step = int(match[1])\n            if current_step != last_step and last_step is not None:\n                task.update_unknown_props(dict(source=\"smtbmc\", engine=f\"engine_{engine_idx}\", step=last_step))\n            return line\n\n        match = re.match(r\"^## [0-9: ]+ Status: FAILED\", line)\n        if match:\n            proc_status = \"FAIL\"\n            return line.replace(\"FAILED\", \"failed\")\n\n        match = re.match(r\"^## [0-9: ]+ Status: PASSED\", line)\n        if match:\n            proc_status = \"PASS\"\n            return line.replace(\"PASSED\", \"passed\")\n\n        match = re.match(r\"^## [0-9: ]+ Status: PREUNSAT\", line)\n        if match:\n            proc_status = \"ERROR\"\n            return line\n\n        match = re.match(r\"^## [0-9: ]+ Unexpected response from solver:\", line)\n        if match:\n            proc_status = \"ERROR\"\n            return line\n\n        match = re.match(r\"^## [0-9: ]+ Assert failed in ([^:]+): (\\S+)(?: \\((\\S+)\\))?\", line)\n        if match:\n            failed_assert = not keep_going\n            path = parse_mod_path(match[1])\n            cell_name = match[3] or match[2]\n            prop = task.design.hierarchy.find_property(path, cell_name, trans_dict=smt2_trans)\n            prop.status = \"FAIL\"\n            last_prop.append(prop)\n            return line\n\n        match = re.match(r\"^## [0-9: ]+ Reached cover statement in step \\d+ at ([^:]+): (\\S+)(?: \\((\\S+)\\))?\", line)\n        if match:\n            path = parse_mod_path(match[1])\n            cell_name = match[3] or match[2]\n            prop = task.design.hierarchy.find_property(path, cell_name, trans_dict=smt2_trans)\n            prop.status = \"PASS\"\n            last_prop.append(prop)\n            return line\n\n        match = re.match(r\"^## [0-9: ]+ Writing trace to (VCD|Yosys witness) file: (\\S+)\", line)\n        if match:\n            tracefile = match[2]\n            if match[1] == \"Yosys witness\" and (task.opt_fst or task.opt_vcd_sim):\n                pending_sim = tracefile\n            trace, _ = os.path.splitext(os.path.basename(tracefile))\n            engine_case = mode.split('_')[1] if '_' in mode else None\n            task.summary.add_event(engine_idx=engine_idx, trace=trace, path=tracefile, engine_case=engine_case)\n            for p in last_prop:\n                task.summary.add_event(\n                    engine_idx=engine_idx, trace=trace,\n                    type=p.celltype, hdlname=p.hdlname, src=p.location,\n                    step=current_step, prop=p,\n                )\n            recorded_last = True\n            return line\n\n        match = re.match(r\"^## [0-9: ]+ Unreached cover statement at ([^:]+): (\\S+)(?: \\((\\S+)\\))?\", line)\n        if match and not failed_assert:\n            path = parse_mod_path(match[1])\n            cell_name = match[3] or match[2]\n            prop = task.design.hierarchy.find_property(path, cell_name, trans_dict=smt2_trans)\n            prop.status = \"FAIL\"\n            task.summary.add_event(\n                engine_idx=engine_idx, trace=None,\n                hdlname=prop.hdlname, src=prop.location,\n                step=current_step, prop=prop,\n            )\n\n        return line\n\n    def simple_exit_callback(retcode):\n        nonlocal procs_running\n        procs_running -= 1\n        if not procs_running:\n            last_exit_callback()\n\n    def exit_callback(retcode):\n        nonlocal last_prop, recorded_last\n        if proc_status is None:\n            task.error(f\"engine_{engine_idx}: Engine terminated without status.\")\n        if len(last_prop) and not recorded_last:\n            task.error(f\"engine_{engine_idx}: Found properties without trace.\")\n        simple_exit_callback(retcode)\n\n    def last_exit_callback():\n        nonlocal current_step\n        if mode == \"bmc\" or mode == \"cover\":\n            task.update_status(proc_status, current_step)\n            if proc_status == \"FAIL\" and mode == \"bmc\" and keep_going:\n                task.pass_unknown_asserts(dict(source=\"smtbmc\", keep_going=True, engine=f\"engine_{engine_idx}\", step=current_step))\n            proc_status_lower = proc_status.lower() if proc_status == \"PASS\" else proc_status\n            task.summary.set_engine_status(engine_idx, proc_status_lower)\n            if not keep_going:\n                task.terminate()\n\n        elif mode in [\"prove_basecase\", \"prove_induction\"]:\n            proc_status_lower = proc_status.lower() if proc_status == \"PASS\" else proc_status\n            task.summary.set_engine_status(engine_idx, proc_status_lower, mode.split(\"_\")[1])\n\n            if mode == \"prove_basecase\":\n                for proc in task.basecase_procs:\n                    proc.terminate()\n\n                if proc_status == \"PASS\":\n                    task.basecase_pass = True\n\n                else:\n                    task.update_status(proc_status)\n                    task.terminate()\n\n            elif mode == \"prove_induction\":\n                for proc in task.induction_procs:\n                    proc.terminate()\n\n                if proc_status == \"PASS\":\n                    task.induction_pass = True\n\n            else:\n                assert False\n\n            if task.basecase_pass and task.induction_pass:\n                task.update_status(\"PASS\", current_step)\n                task.summary.append(\"successful proof by k-induction.\")\n                if not keep_going:\n                    task.terminate()\n\n        else:\n            assert False\n\n    proc.output_callback = output_callback\n    proc.register_exit_callback(exit_callback)\n"
  },
  {
    "path": "sbysrc/sby_jobserver.py",
    "content": "#\n# SymbiYosys (sby) -- Front-end for Yosys-based formal verification flows\n#\n# Copyright (C) 2022  Jannis Harder <jix@yosyshq.com> <me@jix.one>\n#\n# Permission to use, copy, modify, and/or distribute this software for any\n# purpose with or without fee is hereby granted, provided that the above\n# copyright notice and this permission notice appear in all copies.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n#\n\nimport atexit\nimport os\nimport select\nimport shlex\nimport subprocess\nimport sys\nimport weakref\nimport signal\n\nif os.name == \"posix\":\n    import fcntl\n\ninherited_jobcount = None\ninherited_jobserver_auth = None\ninherited_jobserver_auth_present = None\n\ndef process_jobserver_environment():\n    \"\"\"Process the environment looking for a make jobserver. This should be called\n    early (when only inherited fds are present) to reliably detect whether the jobserver\n    specified in the environment is accessible.\"\"\"\n    global inherited_jobcount\n    global inherited_jobserver_auth\n    global inherited_jobserver_auth_present\n\n    if len(sys.argv) >= 2 and sys.argv[1] == '--jobserver-helper':\n        jobserver_helper(*map(int, sys.argv[2:]))\n        exit(0)\n\n    inherited_jobserver_auth_present = False\n\n    for flag in shlex.split(os.environ.get(\"MAKEFLAGS\", \"\")):\n        if flag.startswith(\"-j\"):\n            if flag == \"-j\":\n                inherited_jobcount = 0\n            else:\n                try:\n                    inherited_jobcount = int(flag[2:])\n                except ValueError:\n                    pass\n        elif flag.startswith(\"--jobserver-auth=\") or flag.startswith(\"--jobserver-fds=\"):\n            inherited_jobserver_auth_present = True\n            if os.name == \"posix\":\n                arg = flag.split(\"=\", 1)[1]\n                if arg.startswith(\"fifo:\"):\n                    try:\n                        fd = os.open(arg[5:], os.O_RDWR)\n                    except FileNotFoundError:\n                        pass\n                    else:\n                        inherited_jobserver_auth = fd, fd\n                else:\n                    arg = arg.split(\",\")\n                    try:\n                        jobserver_fds = int(arg[0]), int(arg[1])\n                        for fd in jobserver_fds:\n                            fcntl.fcntl(fd, fcntl.F_GETFD)\n                    except (ValueError, OSError):\n                        pass\n                    else:\n                        inherited_jobserver_auth = jobserver_fds\n\n\ndef jobserver_helper(jobserver_read_fd, jobserver_write_fd, request_fd, response_fd):\n    \"\"\"Helper process to handle blocking jobserver pipes.\"\"\"\n    def handle_sigusr1(*args):\n        # Since Python doesn't allow user code to handle EINTR anymore, we replace the\n        # jobserver fd with an fd at EOF to interrupt a blocking read in a way that\n        # cannot lose any read data\n        r, w = os.pipe()\n        os.close(w)\n        os.dup2(r, jobserver_read_fd)\n        os.close(r)\n    signal.signal(signal.SIGINT, signal.SIG_IGN)\n    signal.signal(signal.SIGUSR1, handle_sigusr1)\n    pending = 0\n    while True:\n        try:\n            new_pending = len(os.read(request_fd, 1024))\n            if new_pending == 0:\n                pending = 0\n                break\n            else:\n                pending += new_pending\n                continue\n        except BlockingIOError:\n            if pending == 0:\n                select.select([request_fd], [], [])\n                continue\n\n        if pending > 0:\n            try:\n                # Depending on the make version (4.3 vs 4.2) this is blocking or\n                # non-blocking. As this is an attribute of the pipe not the fd, we\n                # cannot change it without affecting other processes. Older versions of\n                # gnu make require this to be blocking, and produce errors if it is\n                # non-blocking. Newer versions of gnu make set this non-blocking, both,\n                # as client and as server. The documentation still says it is blocking.\n                # This leaves us no choice but to handle both cases, which is the reason\n                # we have this helper process in the first place.\n                token = os.read(jobserver_read_fd, 1)\n            except BlockingIOError:\n                select.select([jobserver_read_fd], [], [])\n                continue\n            if not token:\n                break\n\n            pending -= 1\n\n            try:\n                os.write(response_fd, token)\n            except:\n                os.write(jobserver_write_fd, token)\n                raise\n    os.close(jobserver_write_fd)\n\n\nclass SbyJobLease:\n    def __init__(self, client):\n        self.client = client\n        self.is_ready = False\n        self.is_done = False\n\n    def done(self):\n        if self.is_ready and not self.is_done:\n            self.client.return_lease()\n\n        self.is_done = True\n\n    def __repr__(self):\n        return f\"is_ready={self.is_ready} is_done={self.is_done}\"\n\n    def __del__(self):\n        self.done()\n\n\nclass SbyJobServer:\n    def __init__(self, jobcount):\n        assert jobcount >= 1\n        # TODO support unlimited parallelism?\n        self.jobcount = jobcount\n        if jobcount == 1:\n            self.read_fd, self.write_fd = None, None\n            self.makeflags = None\n        elif jobcount > 1:\n            self.read_fd, self.write_fd = os.pipe()\n            if os.getenv('SBY_BLOCKING_JOBSERVER') != '1':\n                os.set_blocking(self.read_fd, False)\n            os.write(self.write_fd, b\"*\" * (jobcount - 1))\n            self.makeflags = f\"-j{jobcount} --jobserver-auth={self.read_fd},{self.write_fd} --jobserver-fds={self.read_fd},{self.write_fd}\"\n\n\nclass SbyJobClient:\n    def __init__(self, fallback_jobcount=None):\n        self.jobcount = None\n        self.read_fd = self.write_fd = None\n        self.helper_process = None\n\n        self.local_slots = 1\n        self.acquired_slots = []\n        self.pending_leases = []\n\n        assert inherited_jobserver_auth_present is not None, \"process_jobserver_environment was not called\"\n\n        have_jobserver = inherited_jobserver_auth_present\n\n        if os.name == \"nt\" and inherited_jobserver_auth_present:\n            # There are even more incompatible variants of the make jobserver on\n            # windows, none of them are supported for now.\n            print(\"WARNING: Found jobserver in MAKEFLAGS, this is not supported on windows.\")\n            have_jobserver = False\n\n        if have_jobserver and inherited_jobserver_auth is None:\n            print(\"WARNING: Could not connect to jobserver specified in MAKEFLAGS, disabling parallel execution.\")\n            have_jobserver = False\n            fallback_jobcount = 1\n\n        if have_jobserver:\n            jobcount = inherited_jobcount\n        elif fallback_jobcount is not None:\n            jobcount = fallback_jobcount\n        elif inherited_jobcount is not None and inherited_jobcount > 0:\n            jobcount = inherited_jobcount\n        else:\n            try:\n                jobcount = len(os.sched_getaffinity(0))\n            except AttributeError:\n                jobcount = os.cpu_count()\n\n        if have_jobserver:\n            self.read_fd, self.write_fd = inherited_jobserver_auth\n        elif os.name == \"nt\":\n            # On Windows, without a jobserver, use only local slots\n            self.local_slots = jobcount\n        else:\n            self.sby_jobserver = SbyJobServer(jobcount)\n            self.read_fd = self.sby_jobserver.read_fd\n            self.write_fd = self.sby_jobserver.write_fd\n\n        self.jobcount = jobcount\n\n        if self.read_fd is not None:\n            if os.get_blocking(self.read_fd):\n                request_read_fd, self.request_write_fd = os.pipe()\n                self.response_read_fd, response_write_fd = os.pipe()\n                os.set_blocking(self.response_read_fd, False)\n                os.set_blocking(request_read_fd, False)\n\n                pass_fds = [self.read_fd, self.write_fd, request_read_fd, response_write_fd]\n\n                self.helper_process = subprocess.Popen(\n                    [sys.executable, sys.modules['__main__'].__file__, '--jobserver-helper', *map(str, pass_fds)],\n                    stdin=subprocess.DEVNULL,\n                    pass_fds=pass_fds,\n                )\n\n                os.close(request_read_fd)\n                os.close(response_write_fd)\n\n                atexit.register(self.atexit_blocking)\n            else:\n                atexit.register(self.atexit_nonblocking)\n\n    def atexit_nonblocking(self):\n        while self.acquired_slots:\n            os.write(self.write_fd, self.acquired_slots.pop())\n\n    def atexit_blocking(self):\n        # Return all slot tokens we are currently holding\n        while self.acquired_slots:\n            os.write(self.write_fd, self.acquired_slots.pop())\n\n        if self.helper_process:\n            # Closing the request pipe singals the helper that we want to exit\n            os.close(self.request_write_fd)\n\n            # Additionally we send a signal to interrupt a blocking read within the\n            # helper\n            self.helper_process.send_signal(signal.SIGUSR1)\n\n            # The helper might have been in the process of sending us some tokens, which\n            # we still need to return\n            while True:\n                try:\n                    token = os.read(self.response_read_fd, 1)\n                except BlockingIOError:\n                    select.select([self.response_read_fd], [], [])\n                    continue\n                if not token:\n                    break\n                os.write(self.write_fd, token)\n            os.close(self.response_read_fd)\n\n            # Wait for the helper to exit, should be immediate at this point\n            self.helper_process.wait()\n\n    def request_lease(self):\n        pending = SbyJobLease(self)\n\n        if self.local_slots > 0:\n            self.local_slots -= 1\n            pending.is_ready = True\n        else:\n            self.pending_leases.append(weakref.ref(pending))\n            if self.helper_process:\n                os.write(self.request_write_fd, b\"!\")\n\n        return pending\n\n    def return_lease(self):\n        if self.acquired_slots:\n            os.write(self.write_fd, self.acquired_slots.pop())\n            return\n\n        if self.activate_pending_lease():\n            return\n\n        self.local_slots += 1\n\n    def activate_pending_lease(self):\n        while self.pending_leases:\n            pending = self.pending_leases.pop(0)()\n            if pending is None:\n                continue\n            pending.is_ready = True\n            return True\n        return False\n\n    def has_pending_leases(self):\n        while self.pending_leases and not self.pending_leases[-1]():\n            self.pending_leases.pop()\n        return bool(self.pending_leases)\n\n    def poll_fds(self):\n        if self.helper_process:\n            return [self.response_read_fd]\n        elif self.read_fd is not None and self.has_pending_leases():\n            return [self.read_fd]\n        else:\n            return []\n\n    def poll(self):\n        read_fd = self.response_read_fd if self.helper_process else self.read_fd\n        if read_fd is None:\n            return\n\n        while self.helper_process or self.has_pending_leases():\n            try:\n                token = os.read(read_fd, 1)\n            except BlockingIOError:\n                break\n\n            self.got_token(token)\n\n    def got_token(self, token):\n        self.acquired_slots.append(token)\n\n        if self.activate_pending_lease():\n            return\n\n        self.return_lease()\n"
  },
  {
    "path": "sbysrc/sby_mode_bmc.py",
    "content": "#\n# SymbiYosys (sby) -- Front-end for Yosys-based formal verification flows\n#\n# Copyright (C) 2016  Claire Xenia Wolf <claire@yosyshq.com>\n#\n# Permission to use, copy, modify, and/or distribute this software for any\n# purpose with or without fee is hereby granted, provided that the above\n# copyright notice and this permission notice appear in all copies.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n#\n\nimport re, os, getopt, click\nfrom sby_core import SbyProc\n\ndef run(task):\n    task.handle_int_option(\"depth\", 20)\n    task.handle_str_option(\"aigsmt\", \"yices\")\n\n    for engine_idx, engine in task.engine_list():\n        task.log(f\"{click.style(f'engine_{engine_idx}', fg='magenta')}: {' '.join(engine)}\")\n        task.makedirs(f\"{task.workdir}/engine_{engine_idx}\")\n\n        if engine[0] == \"smtbmc\":\n            import sby_engine_smtbmc\n            sby_engine_smtbmc.run(\"bmc\", task, engine_idx, engine)\n\n        elif engine[0] == \"abc\":\n            import sby_engine_abc\n            sby_engine_abc.run(\"bmc\", task, engine_idx, engine)\n\n        elif engine[0] == \"aiger\":\n            import sby_engine_aiger\n            sby_engine_aiger.run(\"bmc\", task, engine_idx, engine)\n\n        elif engine[0] == \"btor\":\n            import sby_engine_btor\n            sby_engine_btor.run(\"bmc\", task, engine_idx, engine)\n\n        elif engine[0] == \"itp\":\n            import sby_engine_itp\n            sby_engine_itp.run(\"bmc\", task, engine_idx, engine)\n\n        elif engine[0] == \"none\":\n            pass\n\n        else:\n            task.error(f\"Invalid engine '{engine[0]}' for bmc mode.\")\n"
  },
  {
    "path": "sbysrc/sby_mode_cover.py",
    "content": "#\n# SymbiYosys (sby) -- Front-end for Yosys-based formal verification flows\n#\n# Copyright (C) 2016  Claire Xenia Wolf <claire@yosyshq.com>\n#\n# Permission to use, copy, modify, and/or distribute this software for any\n# purpose with or without fee is hereby granted, provided that the above\n# copyright notice and this permission notice appear in all copies.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n#\n\nimport re, os, getopt, click\nfrom sby_core import SbyProc\n\ndef run(task):\n    task.handle_int_option(\"depth\", 20)\n\n    for engine_idx, engine in task.engine_list():\n        task.log(f\"{click.style(f'engine_{engine_idx}', fg='magenta')}: {' '.join(engine)}\")\n        task.makedirs(f\"{task.workdir}/engine_{engine_idx}\")\n\n        if engine[0] == \"smtbmc\":\n            import sby_engine_smtbmc\n            sby_engine_smtbmc.run(\"cover\", task, engine_idx, engine)\n\n        elif engine[0] == \"btor\":\n            import sby_engine_btor\n            sby_engine_btor.run(\"cover\", task, engine_idx, engine)\n\n        elif engine[0] == \"none\":\n            pass\n\n        else:\n            task.error(f\"Invalid engine '{engine[0]}' for cover mode.\")\n"
  },
  {
    "path": "sbysrc/sby_mode_live.py",
    "content": "#\n# SymbiYosys (sby) -- Front-end for Yosys-based formal verification flows\n#\n# Copyright (C) 2016  Claire Xenia Wolf <claire@yosyshq.com>\n#\n# Permission to use, copy, modify, and/or distribute this software for any\n# purpose with or without fee is hereby granted, provided that the above\n# copyright notice and this permission notice appear in all copies.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n#\n\nimport re, os, getopt, click\nfrom sby_core import SbyProc\n\ndef run(task):\n    task.handle_str_option(\"aigsmt\", \"yices\")\n\n    task.status = \"UNKNOWN\"\n\n    for engine_idx, engine in task.engine_list():\n        task.log(f\"{click.style(f'engine_{engine_idx}', fg='magenta')}: {' '.join(engine)}\")\n        task.makedirs(f\"{task.workdir}/engine_{engine_idx}\")\n\n        if engine[0] == \"aiger\":\n            import sby_engine_aiger\n            sby_engine_aiger.run(\"live\", task, engine_idx, engine)\n\n        elif engine[0] == \"none\":\n            pass\n\n        else:\n            task.error(f\"Invalid engine '{engine[0]}' for live mode.\")\n"
  },
  {
    "path": "sbysrc/sby_mode_prove.py",
    "content": "#\n# SymbiYosys (sby) -- Front-end for Yosys-based formal verification flows\n#\n# Copyright (C) 2016  Claire Xenia Wolf <claire@yosyshq.com>\n#\n# Permission to use, copy, modify, and/or distribute this software for any\n# purpose with or without fee is hereby granted, provided that the above\n# copyright notice and this permission notice appear in all copies.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n#\n\nimport re, os, getopt, click\nfrom sby_core import SbyProc\n\ndef run(task):\n    task.handle_int_option(\"depth\", 20)\n    task.handle_str_option(\"aigsmt\", \"yices\")\n\n    task.status = \"UNKNOWN\"\n\n    task.basecase_pass = False\n    task.induction_pass = False\n    task.basecase_procs = list()\n    task.induction_procs = list()\n\n    for engine_idx, engine in task.engine_list():\n        task.log(f\"{click.style(f'engine_{engine_idx}', fg='magenta')}: {' '.join(engine)}\")\n        task.makedirs(f\"{task.workdir}/engine_{engine_idx}\")\n\n        if engine[0] == \"smtbmc\":\n            import sby_engine_smtbmc\n            sby_engine_smtbmc.run(\"prove\", task, engine_idx, engine)\n\n        elif engine[0] == \"aiger\":\n            import sby_engine_aiger\n            sby_engine_aiger.run(\"prove\", task, engine_idx, engine)\n\n        elif engine[0] == \"abc\":\n            import sby_engine_abc\n            sby_engine_abc.run(\"prove\", task, engine_idx, engine)\n\n        elif engine[0] == \"itp\":\n            import sby_engine_itp\n            sby_engine_itp.run(\"prove\", task, engine_idx, engine)\n\n        elif engine[0] == \"none\":\n            pass\n\n        else:\n            task.error(f\"Invalid engine '{engine[0]}' for prove mode.\")\n"
  },
  {
    "path": "sbysrc/sby_sim.py",
    "content": "#\n# SymbiYosys (sby) -- Front-end for Yosys-based formal verification flows\n#\n# Copyright (C) 2022  Jannis Harder <jix@yosyshq.com> <me@jix.one>\n#\n# Permission to use, copy, modify, and/or distribute this software for any\n# purpose with or without fee is hereby granted, provided that the above\n# copyright notice and this permission notice appear in all copies.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n#\n\nimport os, re, glob, json\nfrom sby_core import SbyProc\nfrom sby_design import pretty_path\n\ndef sim_witness_trace(prefix, task, engine_idx, witness_file, *, append, inductive=False, deps=()):\n    trace_name = os.path.basename(witness_file)[:-3]\n    formats = []\n    tracefile = None\n    if task.opt_vcd and task.opt_vcd_sim:\n        tracefile = f\"engine_{engine_idx}/{trace_name}.vcd\"\n        formats.append(f\"-vcd {trace_name}.vcd\")\n    if task.opt_fst:\n        tracefile = f\"engine_{engine_idx}/{trace_name}.fst\"\n        formats.append(f\"-fst {trace_name}.fst\")\n\n    # for warnings / error messages\n    error_tracefile = f\"{task.workdir}/{tracefile}\" or f\"{task.workdir}/engine_{engine_idx}/{trace_name}.yw\"\n\n    sim_log = task.log_prefix(f\"{prefix}.{trace_name}\")\n\n    sim_log(f\"Generating simulation trace for witness file: {witness_file}\")\n\n    with open(f\"{task.workdir}/engine_{engine_idx}/{trace_name}.ys\", \"w\") as f:\n        print(f\"# running in {task.workdir}/engine_{engine_idx}/\", file=f)\n        print(\"read_rtlil ../model/design_prep.il\", file=f)\n        sim_args = \"\"\n        if inductive:\n            sim_args += \" -noinitstate\"\n        if task.opt_cycle_width != 10:\n            formats.append(f\"-width {task.opt_cycle_width}\")\n        print(f\"sim -hdlname -summary {trace_name}.json -append {append}{sim_args} -r {trace_name}.yw {' '.join(formats)}\", file=f)\n\n    def exit_callback(retval):\n\n        if task.design:\n            task.precise_prop_status = True\n\n        assertion_types = set()\n\n        with open(f\"{task.workdir}/engine_{engine_idx}/{trace_name}.json\") as summary:\n            summary = json.load(summary)\n            for assertion in summary[\"assertions\"]:\n                assertion[\"path\"] = tuple(assertion[\"path\"])\n\n        first_appended = summary[\"steps\"] + 1 - append\n\n        printed_assumption_warning = False\n\n        task.summary.add_event(engine_idx=engine_idx, trace=trace_name, path=tracefile)\n\n        for assertion in summary[\"assertions\"]:\n            if task.design:\n                try:\n                    prop = task.design.properties_by_path[tuple(assertion[\"path\"])]\n                except KeyError:\n                    prop = None\n            else:\n                prop = None\n\n            hdlname = pretty_path((summary['top'], *assertion['path'])).rstrip()\n            task.summary.add_event(\n                engine_idx=engine_idx,\n                trace=trace_name, path=tracefile, hdlname=hdlname,\n                type=assertion[\"type\"], src=assertion.get(\"src\"), step=assertion[\"step\"],\n                prop=prop)\n\n            assertion_types.add(assertion[\"type\"])\n\n            if assertion[\"type\"] == '$assume':\n                if assertion[\"step\"] < first_appended:\n                    task.error(f\"produced trace {error_tracefile!r} violates assumptions during simulation\")\n                elif not printed_assumption_warning:\n                    sim_log(f\"Warning: trace {error_tracefile!r} violates assumptions during simulation of the appended time steps.\")\n                    if not task.opt_append_assume:\n                        sim_log(\"For supported engines, the option 'append_assume on' can be used to find inputs that uphold assumptions during appended time steps.\")\n                    printed_assumption_warning = True\n\n    proc = SbyProc(\n        task,\n        f\"{prefix}.{trace_name}\",\n        deps,\n        f\"\"\"cd {task.workdir}/engine_{engine_idx}; {task.exe_paths[\"yosys\"]} -ql {trace_name}.log {trace_name}.ys\"\"\",\n    )\n    proc.noprintregex = re.compile(r\"Warning: Assert .* failed.*\")\n    proc.register_exit_callback(exit_callback)\n    return proc\n"
  },
  {
    "path": "sbysrc/sby_status.py",
    "content": "from __future__ import annotations\n\nimport sqlite3\nimport os\nimport time\nimport json\nimport click\nimport re\nfrom collections import defaultdict\nfrom functools import wraps\nfrom pathlib import Path\nfrom typing import Any, Callable, TypeVar, Optional, Iterable\nfrom sby_design import SbyProperty, pretty_path\n\n\nFn = TypeVar(\"Fn\", bound=Callable[..., Any])\n\nSQLSCRIPT = \"\"\"\\\nCREATE TABLE task (\n    id INTEGER PRIMARY KEY,\n    workdir TEXT,\n    name TEXT,\n    mode TEXT,\n    created REAL\n);\nCREATE TABLE task_status (\n    id INTEGER PRIMARY KEY,\n    task INTEGER,\n    status TEXT,\n    data TEXT,\n    created REAL,\n    FOREIGN KEY(task) REFERENCES task(id)\n);\nCREATE TABLE task_property (\n    id INTEGER PRIMARY KEY,\n    task INTEGER,\n    src TEXT,\n    name TEXT,\n    hdlname TEXT,\n    kind TEXT,\n    created REAL,\n    FOREIGN KEY(task) REFERENCES task(id)\n);\nCREATE TABLE task_property_status (\n    id INTEGER PRIMARY KEY,\n    task_property INTEGER,\n    task_trace INTEGER,\n    status TEXT,\n    data TEXT,\n    created REAL,\n    FOREIGN KEY(task_property) REFERENCES task_property(id),\n    FOREIGN KEY(task_trace) REFERENCES task_trace(id)\n);\nCREATE TABLE task_trace (\n    id INTEGER PRIMARY KEY,\n    task INTEGER,\n    trace TEXT,\n    path TEXT,\n    kind TEXT,\n    engine_case TEXT,\n    created REAL,\n    FOREIGN KEY(task) REFERENCES task(id)\n);\"\"\"\n\ndef transaction(method: Fn) -> Fn:\n    @wraps(method)\n    def wrapper(self: SbyStatusDb, *args: Any, **kwargs: Any) -> Any:\n        if self.con.in_transaction:\n            return method(self, *args, **kwargs)\n\n        try:\n            with self.con:\n                self.log_debug(f\"begin {method.__name__!r} transaction\")\n                self.db.execute(\"begin\")\n                result = method(self, *args, **kwargs)\n        except Exception as err:\n            self.log_debug(f\"failed {method.__name__!r} transaction {err}\")\n            if not isinstance(err, sqlite3.OperationalError):\n                raise\n            if re.match(r\"table \\w+ has no column named \\w+\", err.args[0]):\n                err.add_note(\"SBY status database can be reset with --statusreset\")\n                raise\n        else:\n            self.log_debug(f\"comitted {method.__name__!r} transaction\")\n            return result\n\n        try:\n            with self.con:\n                self.log_debug(\n                    f\"retrying {method.__name__!r} transaction once in immediate mode\"\n                )\n                self.db.execute(\"begin immediate\")\n                result = method(self, *args, **kwargs)\n        except Exception as err:\n            self.log_debug(f\"failed {method.__name__!r} transaction {err}\")\n            raise\n        else:\n            self.log_debug(f\"comitted {method.__name__!r} transaction\")\n            return result\n\n    return wrapper  # type: ignore\n\nclass FileInUseError(Exception):\n    def __init__(self, *args, file: Path|str = \"file\"):\n        super().__init__(f\"Found {file}, try again later\", *args)\n\n\nclass SbyStatusDb:\n    def __init__(self, path: Path, task, timeout: float = 5.0, live_formats = []):\n        self.debug = False\n        self.task = task\n        self.live_formats = live_formats\n\n        self.con = sqlite3.connect(path, isolation_level=None, timeout=timeout)\n        self.db = self.con.cursor()\n        self.db.row_factory = sqlite3.Row\n        err_count = 0\n        err_max = 3\n        while True:\n            try:\n                self.db.execute(\"PRAGMA journal_mode=WAL\")\n                self.db.execute(\"PRAGMA synchronous=0\")\n                self.db.execute(\"PRAGMA foreign_keys=ON\")\n            except sqlite3.OperationalError as err:\n                if \"database is locked\" not in err.args[0]:\n                    raise\n                err_count += 1\n                if err_count > err_max:\n                    err.add_note(f\"Failed to acquire lock after {err_count} attempts, aborting\")\n                    raise\n                backoff = err_count / 10.0\n                self.log_debug(f\"Database locked, retrying in {backoff}s\")\n                time.sleep(backoff)\n            else:\n                break\n\n        self._setup()\n\n        if task is not None:\n            self.start_time = time.time()\n            self.task_id = self.create_task(workdir=task.workdir, name=task.name, mode=task.opt_mode, now=self.start_time)\n\n    def log_debug(self, *args):\n        if self.debug:\n            if self.task:\n                self.task.log(\" \".join(str(arg) for arg in args))\n            else:\n                print(*args)\n\n    @transaction\n    def _setup(self):\n        for statement in SQLSCRIPT.split(\";\\n\"):\n            statement = statement.strip().replace(\"CREATE TABLE\", \"CREATE TABLE IF NOT EXISTS\")\n            if statement:\n                self.db.execute(statement)\n\n    def test_schema(self) -> bool:\n        schema = self.db.execute(\"SELECT sql FROM sqlite_master;\").fetchall()\n        schema_script = '\\n'.join(str(sql[0] + ';') for sql in schema)\n        self._tables = re.findall(r\"CREATE TABLE (\\w+) \\(\", schema_script)\n        return schema_script != SQLSCRIPT\n\n    @transaction\n    def create_task(self, workdir: str, name: str, mode: str, now:float) -> int:\n        return self.db.execute(\n            \"\"\"\n                INSERT INTO task (workdir, name, mode, created)\n                VALUES (:workdir, :name, :mode, :now)\n            \"\"\",\n            dict(workdir=workdir, name=name, mode=mode, now=now),\n        ).lastrowid\n\n    @transaction\n    def create_task_properties(\n        self, properties: Iterable[SbyProperty], *, task_id: Optional[int] = None\n    ):\n        if task_id is None:\n            task_id = self.task_id\n        now = time.time()\n        self.db.executemany(\n            \"\"\"\n                INSERT INTO task_property (name, src, hdlname, task, kind, created)\n                VALUES (:name, :src, :hdlname, :task, :kind, :now)\n            \"\"\",\n            [\n                dict(\n                    name=json.dumps(prop.path),\n                    src=prop.location or \"\",\n                    hdlname=prop.hdlname,\n                    task=task_id,\n                    kind=prop.kind,\n                    now=now,\n                )\n                for prop in properties\n            ],\n        )\n\n    @transaction\n    def set_task_status(\n        self,\n        status: Optional[str] = None,\n        data: Any = None,\n    ):\n        if status is None:\n            status = property.status\n\n        now = time.time()\n        self.db.execute(\n            \"\"\"\n                INSERT INTO task_status (\n                    task, status, data, created\n                )\n                VALUES (\n                    :task, :status, :data, :now\n                )\n            \"\"\",\n            dict(\n                task=self.task_id,\n                status=status,\n                data=json.dumps(data),\n                now=now,\n            ),\n        )\n\n    @transaction\n    def set_task_property_status(\n        self,\n        property: SbyProperty,\n        trace_id: Optional[int] = None,\n        data: Any = None,\n    ):\n        now = time.time()\n        self.db.execute(\n            \"\"\"\n                INSERT INTO task_property_status (\n                    task_property, task_trace, status, data, created\n                )\n                VALUES (\n                    (SELECT id FROM task_property WHERE task = :task AND name = :name),\n                    :trace_id, :status, :data, :now\n                )\n            \"\"\",\n            dict(\n                task=self.task_id,\n                trace_id=trace_id,\n                name=json.dumps(property.path),\n                status=property.status,\n                data=json.dumps(data),\n                now=now,\n            ),\n        )\n\n        if self.live_formats:\n            row = self.get_status_data_joined(self.db.lastrowid)\n            for fmt in self.live_formats:\n                fmtline = format_status_data_fmtline(row, fmt)\n                self.task.log(f\"{click.style(fmt, fg='yellow')}: {fmtline}\")\n        \n    @transaction\n    def add_task_trace(\n        self,\n        trace: str,\n        path: str,\n        kind: str,\n        engine_case: Optional[str] = None,\n        task_id: Optional[int] = None,\n    ):\n        if task_id is None:\n            task_id = self.task_id\n        now = time.time()\n        return self.db.execute(\n            \"\"\"\n                INSERT INTO task_trace (\n                    trace, task, path, engine_case, kind, created\n                )\n                VALUES (\n                    :trace, :task, :path, :engine_case, :kind, :now\n                )\n            \"\"\",\n            dict(\n                trace=trace,\n                task=task_id,\n                path=path,\n                engine_case=engine_case,\n                kind=kind,\n                now=now\n            )\n        ).lastrowid\n\n    def all_tasks(self):\n        rows = self.db.execute(\n            \"\"\"\n                SELECT id, workdir, created FROM task\n            \"\"\"\n        ).fetchall()\n\n        return {row[\"id\"]: dict(row) for row in rows}\n\n    def all_tasks_status(self):\n        rows = self.db.execute(\n            \"\"\"\n                SELECT task.id, task.name, task.created,\n                task_status.status, task_status.created as 'status_created'\n                FROM task\n                LEFT JOIN task_status ON task_status.task=task.id\n            \"\"\"\n        ).fetchall()\n\n        return {row[\"id\"]: dict(row) for row in rows}\n\n    def all_task_properties(self):\n        rows = self.db.execute(\n            \"\"\"\n                SELECT id, task, src, name, created FROM task_property\n            \"\"\"\n        ).fetchall()\n\n        def get_result(row):\n            row = dict(row)\n            row[\"name\"] = tuple(json.loads(row.get(\"name\", \"[]\")))\n            return row\n\n        return {row[\"id\"]: get_result(row) for row in rows}\n\n    def all_task_property_statuses(self):\n        rows = self.db.execute(\n            \"\"\"\n                SELECT id, task_property, status, data, created\n                FROM task_property_status\n            \"\"\"\n        ).fetchall()\n\n        def get_result(row):\n            row = dict(row)\n            row[\"data\"] = json.loads(row.get(\"data\", \"null\"))\n            return row\n\n        return {row[\"id\"]: get_result(row) for row in rows}\n\n    def all_status_data(self):\n        return (\n            self.all_tasks(),\n            self.all_task_properties(),\n            self.all_task_property_statuses(),\n        )\n\n    @transaction\n    def _reset(self):\n        hard_reset = self.test_schema()\n        # table names can't be parameters, so we need to use f-strings\n        # but it is safe to use here because it comes from the regex \"\\w+\"\n        for table in self._tables:\n            if hard_reset:\n                self.log_debug(f\"dropping {table}\")\n                self.db.execute(f\"DROP TABLE {table}\")\n            else:\n                self.log_debug(f\"clearing {table}\")\n                self.db.execute(f\"DELETE FROM {table}\")\n        if hard_reset:\n            self._setup()\n\n    def reset(self):\n        self.db.execute(\"PRAGMA foreign_keys=OFF\")\n        self._reset()\n        self.db.execute(\"PRAGMA foreign_keys=ON\")\n\n    def print_status_summary(self, latest: bool):\n        tasks, task_properties, task_property_statuses = self.all_status_data()\n        latest_task_ids = filter_latest_task_ids(tasks)\n        properties = defaultdict(set)\n\n        uniquify_paths = defaultdict(dict)\n\n        def add_status(task_property, status):\n            if latest and task_property[\"task\"] not in latest_task_ids:\n                return\n\n            display_name = task_property[\"name\"]\n            if display_name[-1].startswith(\"$\"):\n                counters = uniquify_paths[task_property[\"src\"]]\n                counter = counters.setdefault(display_name[-1], len(counters) + 1)\n                if task_property[\"src\"]:\n                    if counter < 2:\n                        path_based = f\"<unnamed at {task_property['src']}>\"\n                    else:\n                        path_based = f\"<unnamed #{counter} at {task_property['src']}>\"\n                else:\n                    path_based = f\"<unnamed #{counter}>\"\n                display_name = (*display_name[:-1], path_based)\n\n            properties[display_name].add(status)\n\n        for task_property in task_properties.values():\n            add_status(task_property, \"UNKNOWN\")\n\n        for status in task_property_statuses.values():\n            task_property = task_properties[status[\"task_property\"]]\n            add_status(task_property, status[\"status\"])\n\n        for display_name, statuses in sorted(properties.items()):\n            print(pretty_path(display_name), combine_statuses(statuses))\n\n    def print_task_summary(self):\n        tasks = self.all_tasks_status()\n        task_status = defaultdict(set)\n        for task in tasks.values():\n            task_status[task[\"name\"]].add(task[\"status\"] or \"UNKNOWN\")\n        for task_name, statuses in sorted(task_status.items()):\n            print(task_name, combine_statuses(statuses))\n\n    def get_status_data_joined(self, status_id: int):\n        row = self.db.execute(\n            \"\"\"\n                SELECT task.name as 'task_name', task.mode, task.workdir, task.created, task_property.kind,\n                task_property.src as 'location', task_property.name, task_property.hdlname, task_property_status.status,\n                task_property_status.data, task_property_status.created as 'status_created',\n                task_property_status.id, task_trace.path, task_trace.kind as trace_kind\n                FROM task\n                INNER JOIN task_property ON task_property.task=task.id\n                INNER JOIN task_property_status ON task_property_status.task_property=task_property.id\n                LEFT JOIN task_trace ON task_property_status.task_trace=task_trace.id\n                WHERE task_property_status.id=:status_id;\n            \"\"\",\n            dict(status_id=status_id)\n        ).fetchone()\n        return parse_status_data_row(row)\n\n    def all_status_data_joined(self):\n        rows = self.db.execute(\n            \"\"\"\n                SELECT task.id as 'task_id', task.name as 'task_name', task.mode, task.workdir, task.created, task_property.kind,\n                task_property.src as 'location', task_property.name, task_property.hdlname, task_property_status.status,\n                task_property_status.data, task_property_status.created as 'status_created',\n                task_property_status.id, task_trace.path, task_trace.kind as trace_kind\n                FROM task\n                INNER JOIN task_property ON task_property.task=task.id\n                INNER JOIN task_property_status ON task_property_status.task_property=task_property.id\n                LEFT JOIN task_trace ON task_property_status.task_trace=task_trace.id;\n            \"\"\"\n        ).fetchall()\n\n        return {row[\"id\"]: parse_status_data_row(row) for row in rows}\n\n    def print_status_summary_fmt(self, tasknames: list[str], status_format: str, latest: bool):\n        # get all statuses\n        all_properties = self.all_status_data_joined()\n        latest_task_ids = filter_latest_task_ids(self.all_tasks())\n\n        # print header\n        header = format_status_data_fmtline(None, status_format)\n        if header:\n            print(header)\n\n        # find summary for each task/property combo\n        prop_map: dict[(str, str, str), dict[str, (int, int)]] = {}\n        for row, prop_status in all_properties.items():\n            if tasknames and prop_status['task_name'] not in tasknames:\n                continue\n            if latest and prop_status['task_id'] not in latest_task_ids:\n                continue\n            status = prop_status['status']\n            this_depth = prop_status['data'].get('step')\n            this_kind = prop_status['trace_kind']\n            key = (prop_status['task_name'], prop_status['hdlname'])\n            try:\n                prop_status_map = prop_map[key]\n            except KeyError:\n                prop_map[key] = prop_status_map = {}\n\n            try:\n                current_depth, _, current_kind = prop_status_map[status]\n            except KeyError:\n                prop_status_map[status] = (this_depth, row, this_kind)\n                continue\n\n            update_map = False\n            if current_depth is None and current_kind is None:\n                # no depth or kind to compare, just take latest data\n                update_map = True\n            elif this_depth is not None and this_depth != current_depth:\n                if current_depth is None:\n                    # always prefer a known depth to an unknown\n                    update_map = True\n                elif status == 'FAIL' and this_depth < current_depth:\n                    # earliest fail\n                    update_map = True\n                elif status != 'FAIL' and this_depth > current_depth:\n                    # latest non-FAIL\n                    update_map = True\n            elif this_kind in ['fst', 'vcd']:\n                # prefer traces over witness files\n                update_map = True\n            if update_map:\n                prop_status_map[status] = (this_depth, row, this_kind)\n\n        for prop in prop_map.values():\n            # ignore UNKNOWNs if there are other statuses\n            if len(prop) > 1 and \"UNKNOWN\" in prop:\n                del prop[\"UNKNOWN\"]\n\n            for _, row, _ in prop.values():\n                line = format_status_data_fmtline(all_properties[row], status_format)\n                print(line)\n\ndef combine_statuses(statuses):\n    statuses = set(statuses)\n\n    if len(statuses) > 1:\n        statuses.discard(\"UNKNOWN\")\n\n    return \",\".join(sorted(statuses))\n\ndef parse_status_data_row(raw: sqlite3.Row):\n    row_dict = dict(raw)    \n    row_dict[\"name\"] = json.loads(row_dict.get(\"name\", \"null\"))\n    row_dict[\"data\"] = json.loads(row_dict.get(\"data\") or \"{}\")\n    return row_dict\n\nfmtline_columns = [\n    \"time\",\n    \"task_name\",\n    \"mode\",\n    \"engine\",\n    \"name\",\n    \"location\",\n    \"kind\",\n    \"status\",\n    \"trace\",\n    \"depth\",\n]\n\ndef format_status_data_fmtline(row: dict|None, fmt: str = \"csv\") -> str:\n    if row is None:\n        data = None\n    else:\n        engine = row['data'].get('engine', row['data'].get('source'))\n        name = row['hdlname']\n        depth = row['data'].get('step')\n\n        data = {\n            \"task_name\": row['task_name'],\n            \"mode\": row['mode'],\n            \"engine\": engine,\n            \"name\": name or pretty_path(row['name']),\n            \"location\": row['location'],\n            \"kind\": row['kind'],\n            \"status\": row['status'] or \"UNKNOWN\",\n            \"depth\": depth,\n        }\n        try:\n            data[\"trace\"] = str(Path(row['workdir']) / row['path'])\n        except TypeError:\n            pass\n        try:\n            data['time'] = round(row['status_created'] - row['created'], 2)\n        except TypeError:\n            pass\n    if fmt == \"csv\":\n        if data is None:\n            csv_line = fmtline_columns\n        else:\n            csv_line = [data.get(column) for column in fmtline_columns]\n        def csv_field(value):\n            if value is None:\n                return \"\"\n            value = str(value).replace('\"', '\"\"')\n            if any(c in value for c in '\",\\n'):\n                value = f'\"{value}\"'\n            return value\n        return ','.join(map(csv_field, csv_line))\n    elif fmt == \"jsonl\":\n        if data is None:\n            return \"\"\n        # field order\n        data = {column: data[column] for column in fmtline_columns if data.get(column)}\n        return json.dumps(data)\n\ndef filter_latest_task_ids(all_tasks: dict[int, dict[str]]):\n    latest: dict[str, int] = {}\n    for task_id, task_dict in all_tasks.items():\n        latest[task_dict[\"workdir\"]] = task_id\n    return list(latest.values())\n\ndef remove_db(path):\n    path = Path(path)\n    lock_exts = [\".sqlite-wal\", \".sqlite-shm\"]\n    maybe_locked = False\n    for lock_file in [path.with_suffix(ext) for ext in lock_exts]:\n        if lock_file.exists():\n            # lock file may be a false positive if it wasn't cleaned up\n            maybe_locked = True\n            break\n\n    if not maybe_locked:\n        # safe to delete\n        os.remove(path)\n        return\n\n    # test database directly\n    with sqlite3.connect(path, isolation_level=\"EXCLUSIVE\", timeout=1) as con:\n        cur = con.cursor()\n        # single result rows\n        cur.row_factory = lambda _, r: r[0]\n\n        # changing journal_mode is disallowed if there are multiple connections\n        try:\n            cur.execute(\"PRAGMA journal_mode=DELETE\")\n        except sqlite3.OperationalError as err:\n            if \"database is locked\" in err.args[0]:\n                raise FileInUseError(file=path)\n            else:\n                raise\n\n        # no other connections, delete all tables\n        drop_script = cur.execute(\"SELECT name FROM sqlite_master WHERE type = 'table';\").fetchall()\n        for table in drop_script:\n            cur.execute(f\"DROP TABLE {table}\")\n"
  },
  {
    "path": "tests/.gitignore",
    "content": "/make/rules\n__pycache__\n"
  },
  {
    "path": "tests/Makefile",
    "content": "test:\n\n.PHONY: test clean refresh help\n\nOS_NAME := $(shell python3 -c \"import os;print(os.name)\")\nifeq (nt,$(OS_NAME))\nifeq (quoted,$(shell echo \"quoted\"))\nOS_NAME := nt-unix-like\nendif\nendif\n\nifeq (nt,$(OS_NAME))\n$(error This Makefile requires unix-like tools and shell, e.g. MSYS2.)\nendif\n\nhelp:\n\t@cat make/help.txt\n\nexport SBY_WORKDIR_GITIGNORE := 1\n\nifeq ($(SBY_CMD),)\nSBY_MAIN := $(realpath $(dir $(firstword $(MAKEFILE_LIST)))/../sbysrc/sby.py)\nelse\nSBY_MAIN := $(realpath $(dir $(firstword $(MAKEFILE_LIST)))/make/run_sby.py)\nendif\n\nifeq ($(SBY_EXAMPLES),)\nEXAMPLE_DIR := ../docs/examples\nelse\nEXAMPLE_DIR := $(SBY_EXAMPLES)\nendif\n\nCHECK_COLLECT := $(shell python3 make/collect_tests.py --check --output make/rules/collect.mk --examples $(EXAMPLE_DIR))\nifneq (,$(CHECK_COLLECT))\n$(warning $(CHECK_COLLECT))\nendif\n\n\nifeq (nt-unix-like,$(OS_NAME))\nSBY_MAIN := $(shell cygpath -w $(SBY_MAIN))\nendif\nexport SBY_MAIN\n\nmake/rules/collect.mk: make/collect_tests.py\n\tpython3 make/collect_tests.py --output $@ --examples $(EXAMPLE_DIR)\n\nmake/rules/test/%.mk:\n\tpython3 make/test_rules.py --rule $@ --source $<\n\nifneq (help,$(MAKECMDGOALS))\n\n# This should run every time but only trigger anything depending on it whenever\n# the script overwrites make/rules/found_tools. This doesn't really match how\n# make targets usually work, so we manually shell out here.\n\nFIND_TOOLS := $(shell python3 make/required_tools.py || echo error)\n\nifneq (,$(findstring error,$(FIND_TOOLS)))\n$(error could not run 'python3 make/required_tools.py')\nendif\n\nifneq (,$(FIND_TOOLS))\n$(warning $(FIND_TOOLS))\nendif\n\ninclude make/rules/collect.mk\n\nendif\n"
  },
  {
    "path": "tests/autotune/Makefile",
    "content": "SUBDIR=autotune\ninclude ../make/subdir.mk\n"
  },
  {
    "path": "tests/autotune/autotune_div.sby",
    "content": "[tasks]\nbmc\ncover\nprove\n\n[options]\nbmc: mode bmc\ncover: mode cover\nprove: mode prove\nexpect pass\n\n[engines]\nsmtbmc boolector\n\n[script]\nread -sv autotune_div.sv\nprep -top top\n\n[file autotune_div.sv]\nmodule top #(\n    parameter WIDTH = 4 // Reduce this if it takes too long on CI\n) (\n    input clk,\n    input load,\n    input [WIDTH-1:0] a,\n    input [WIDTH-1:0] b,\n    output reg [WIDTH-1:0] q,\n    output reg [WIDTH-1:0] r,\n    output reg done\n);\n\n    reg [WIDTH-1:0] a_reg = 0;\n    reg [WIDTH-1:0] b_reg = 1;\n\n    initial begin\n        q <= 0;\n        r <= 0;\n        done <= 1;\n    end\n\n    reg [WIDTH-1:0] q_step = 1;\n    reg [WIDTH-1:0] r_step = 1;\n\n    // This is not how you design a good divider circuit!\n    always @(posedge clk) begin\n        if (load) begin\n            a_reg <= a;\n            b_reg <= b;\n            q <= 0;\n            r <= a;\n            q_step <= 1;\n            r_step <= b;\n            done <= b == 0;\n        end else begin\n            if (r_step <= r) begin\n                q <= q + q_step;\n                r <= r - r_step;\n\n                if (!r_step[WIDTH-1]) begin\n                    r_step <= r_step << 1;\n                    q_step <= q_step << 1;\n                end\n            end else begin\n                if (!q_step[0]) begin\n                    r_step <= r_step >> 1;\n                    q_step <= q_step >> 1;\n                end else begin\n                    done <= 1;\n                end\n            end\n        end\n    end\n\n    always @(posedge clk) begin\n        assert (r_step == b_reg * q_step); // Helper invariant\n\n        assert (q * b_reg + r == a_reg); // Main invariant & correct output relationship\n        if (done) assert (r <= b_reg - 1); // Output range\n\n        cover (done);\n        cover (done && b_reg == 0);\n        cover (r != a_reg);\n        cover (r == a_reg);\n    end\nendmodule\n"
  },
  {
    "path": "tests/autotune/autotune_div.sh",
    "content": "#!/bin/bash\nset -e\npython3 $SBY_MAIN --autotune -f $SBY_FILE $TASK\n"
  },
  {
    "path": "tests/autotune/autotune_options.sby",
    "content": "[tasks]\na\nb\nc\nd\n\n[options]\nmode bmc\nexpect fail\n\n[engines]\nsmtbmc boolector\n\n[script]\nread -sv autotune_div.sv\nprep -top top\n\n[autotune]\na: timeout 60\na: wait 10%+20\na: parallel 1\na: presat on\na: incr on\na: mem on\na: forall on\n\nb: timeout none\nb: parallel auto\nb: presat off\nb: incr off\nb: mem auto\nb: mem_threshold 20\nb: forall any\n\nc: presat any\nc: incr any\nc: mem any\nc: forall auto\n\nd: incr auto\nd: incr_threshold 10\n\n[file autotune_div.sv]\nmodule top (input clk);\n    reg [7:0] counter = 0;\n    always @(posedge clk) begin\n        counter <= counter + 1;\n        assert (counter != 4);\n    end\nendmodule\n"
  },
  {
    "path": "tests/autotune/autotune_options.sh",
    "content": "#!/bin/bash\nset -e\npython3 $SBY_MAIN --autotune -f $SBY_FILE $TASK\n"
  },
  {
    "path": "tests/blackbox/Makefile",
    "content": "SUBDIR=blackbox\ninclude ../make/subdir.mk\n"
  },
  {
    "path": "tests/blackbox/blackbox.sby",
    "content": "[options]\nmode bmc\ndepth 1\nexpect fail\n\n[engines]\nsmtbmc\n\n[script]\nread -formal test.v\nprep -top top\ncutpoint -blackbox\n\n[file test.v]\n(* blackbox *)\nmodule submod(a, b);\n\tinput [7:0] a;\n\toutput [7:0] b;\nendmodule\n\nmodule top;\n\t(*anyconst*) wire [7:0] a;\n\twire [7:0] b;\n\n\tsubmod submod(\n\t\t.a(a),\n\t\t.b(b)\n\t);\n\n\talways @* begin\n\t\tassert(~a == b);\n\tend\nendmodule\n"
  },
  {
    "path": "tests/blackbox/parameter.sby",
    "content": "[options]\nmode bmc\ndepth 1\nexpect fail\n\n[engines]\nsmtbmc\n\n[script]\nread -formal test.v\nprep -top top\ncutpoint -blackbox\n\n[file test.v]\n(* blackbox *)\nmodule submod #(parameter WIDTH = 24) (input [WIDTH-1:0] a, output [WIDTH-1:0] b);\nendmodule\n\nmodule top;\n\t(*anyconst *) wire [7:0] a;\n    wire [7:0] b;\n\n\tsubmod #(.WIDTH(8)) submod(\n\t\t.a(a),\n\t\t.b(b)\n\t);\n\n\talways @* begin\n\t\tassert(~a == b);\n\tend\nendmodule\n"
  },
  {
    "path": "tests/blackbox/unknown_cells.sby",
    "content": "[options]\nmode bmc\ndepth 1\nexpect error\n\n[engines]\nsmtbmc\n\n[script]\nread_rtlil test.il\ncutpoint -blackbox\n\n[file test.il]\nautoidx 31\nattribute \\keep 1\nattribute \\top 1\nattribute \\library \"work\"\nattribute \\hdlname \"top\"\nmodule \\top\n  wire $auto$rtlil.cc:2739:Not$26\n  wire $auto$rtlil.cc:2739:Not$28\n  wire width 8 $verific$n12$4\n  attribute \\anyconst 1\n  wire width 8 \\a\n  wire width 8 \\b\n  cell $assert $auto$verificsva.cc:1732:import$24\n    connect \\A $auto$rtlil.cc:2739:Not$28\n    connect \\EN $auto$rtlil.cc:2739:Not$26\n  end\n  cell $not $auto$verificsva.cc:1745:import$25\n    parameter \\A_SIGNED 0\n    parameter \\A_WIDTH 1\n    parameter \\Y_WIDTH 1\n    connect \\A $auto$rtlil.cc:2739:Not$28\n    connect \\Y $auto$rtlil.cc:2739:Not$26\n  end\n  cell $anyconst $verific$a$test.v:8$2\n    parameter \\WIDTH 8\n    connect \\Y \\a\n  end\n  cell $eq $verific$equal_4$test.v:17$22\n    parameter \\A_SIGNED 0\n    parameter \\A_WIDTH 8\n    parameter \\B_SIGNED 0\n    parameter \\B_WIDTH 8\n    parameter \\Y_WIDTH 1\n    connect \\A $verific$n12$4\n    connect \\B \\b\n    connect \\Y $auto$rtlil.cc:2739:Not$28\n  end\n  cell $not $verific$inv_3$test.v:17$21\n    parameter \\A_SIGNED 0\n    parameter \\A_WIDTH 8\n    parameter \\Y_WIDTH 8\n    connect \\A \\a\n    connect \\Y $verific$n12$4\n  end\n  cell \\submod \\submod\n    connect \\a \\a\n    connect \\b \\b\n  end\nend\n"
  },
  {
    "path": "tests/blackbox/wider.sby",
    "content": "[tasks]\nparameter_signed_unsigned: parameter port_signed\nparameter_signed_signed: parameter port_signed signal_signed\nparameter_unsigned_signed: parameter signal_signed\nparameter_unsigned_unsigned: parameter\nsigned_unsigned: port_signed\nsigned_signed: port_signed signal_signed\nunsigned_signed: signal_signed\nunsigned_unsigned:\n\n[options]\nmode bmc\ndepth 1\nexpect pass\n\n[engines]\nsmtbmc\n\n[script]\nport_signed: read -define PORT_SIGNED\nparameter: read -define PARAMETER\nread -formal test.v\nprep -top top\ncutpoint -blackbox\n\n[file test.v]\n`ifdef PARAMETER\n`define DEF_PARAMETER #(parameter WIDTH = 24)\n`define USE_PARAMETER #(.WIDTH(8))\n`define PORT_WIDTH WIDTH\n`else\n`define DEF_PARAMETER\n`define USE_PARAMETER\n`define PORT_WIDTH 8\n`endif\n\n`ifdef PORT_SIGNED\n`define PORT_SIGNED_WORD signed\n`else\n`define PORT_SIGNED_WORD\n`endif\n\n`ifdef SIGNAL_SIGNED\n`define SIGNAL_SIGNED_WORD signed\n`else\n`define SIGNAL_SIGNED_WORD\n`endif\n\n\n(* blackbox *)\nmodule submod `DEF_PARAMETER (a, b);\n\tinput [`PORT_WIDTH - 1:0] a;\n\toutput `PORT_SIGNED_WORD [`PORT_WIDTH - 1:0] b;\nendmodule\n\nmodule top;\n\t(*anyconst *) wire [7:0] a;\n    wire `SIGNAL_SIGNED_WORD [9:0] b;\n\n\tsubmod `USE_PARAMETER submod(\n\t\t.a(a),\n\t\t.b(b)\n\t);\n\n\talways @* begin\n`ifdef PORT_SIGNED\n        assert(b[9] == b[7] && b[8] == b[7]);\n`else\n\t\tassert(b[9:8] == 0);\n`endif\n\tend\nendmodule\n"
  },
  {
    "path": "tests/intertask/Makefile",
    "content": "SUBDIR=intertask\ninclude ../make/subdir.mk\n"
  },
  {
    "path": "tests/intertask/cancelledby.sby",
    "content": "[tasks]\nc\nb\na\n\n[cancelledby]\na: b\nb: c\nc: a\n\n[options]\nmode bmc\ndepth 20\na: expect pass,cancelled\nb: expect fail,cancelled\nc: expect fail,cancelled\n\n[engines]\nbtor btormc\n\n[script]\na: read -define MAX=32\nb: read -define MAX=12\nc: read -define MAX=3\nread -formal demo.sv\nprep -top demo\n\n[file demo.sv]\nmodule demo (\n  input clk,\n  output reg [5:0] counter\n);\n  initial counter = 0;\n\n  always @(posedge clk) begin\n    if (counter == 15)\n      counter <= 0;\n    else\n      counter <= counter + 1;\n  end\n\n`ifdef FORMAL\n  always @(posedge clk) begin\n    assert (counter < `MAX);\n  end\n`endif\nendmodule\n"
  },
  {
    "path": "tests/intertask/cancelledby.sh",
    "content": "#!/bin/bash\nset -e\n\nif [[ $TASK == a ]]; then\n    # different process, no cancellations\n    rm -rf ${WORKDIR}_*\n    python3 $SBY_MAIN --prefix $WORKDIR $SBY_FILE a\n    python3 $SBY_MAIN --prefix $WORKDIR $SBY_FILE b\n    python3 $SBY_MAIN --prefix $WORKDIR $SBY_FILE c\n    test -e ${WORKDIR}_a/PASS -a -e ${WORKDIR}_b/FAIL -a -e ${WORKDIR}_c/FAIL\nelif [[ $TASK == b ]]; then\n    # same process, a cancels c cancels b\n    # use statusdb so that the different taskloops from using --sequential doesn't matter\n    python3 $SBY_MAIN --prefix $WORKDIR -f $SBY_FILE a c b --sequential --statuscancels\n    test -e ${WORKDIR}_a/PASS -a -e ${WORKDIR}_b/CANCELLED -a -e ${WORKDIR}_c/CANCELLED\nelse\n    # different process, b cancels a, c completes before a\n    rm -rf ${WORKDIR} ${WORKDIR}_*\n    python3 $SBY_MAIN --prefix $WORKDIR $SBY_FILE b --statuscancels\n    python3 $SBY_MAIN --prefix $WORKDIR $SBY_FILE c --statuscancels\n    python3 $SBY_MAIN --prefix $WORKDIR $SBY_FILE a --statuscancels\n    echo test -e ${WORKDIR}_a/CANCELLED -a -e ${WORKDIR}_b/FAIL -a -e ${WORKDIR}_c/FAIL\nfi\n"
  },
  {
    "path": "tests/intertask/longrunning.sby",
    "content": "[tasks]\nbmc\nprove\n\n[cancelledby]\nbmc: prove\n\n[options]\nbmc:\nmode bmc\ndepth 20000\nexpect cancelled\n\nprove:\nmode prove\nexpect pass\n--\n\n[engines]\nsmtbmc boolector\n\n[script]\nread -sv autotune_div.sv\nprep -top top\n\n[file autotune_div.sv]\nmodule top #(\n    parameter WIDTH = 4 // Reduce this if it takes too long on CI\n) (\n    input clk,\n    input load,\n    input [WIDTH-1:0] a,\n    input [WIDTH-1:0] b,\n    output reg [WIDTH-1:0] q,\n    output reg [WIDTH-1:0] r,\n    output reg done\n);\n\n    reg [WIDTH-1:0] a_reg = 0;\n    reg [WIDTH-1:0] b_reg = 1;\n\n    initial begin\n        q <= 0;\n        r <= 0;\n        done <= 1;\n    end\n\n    reg [WIDTH-1:0] q_step = 1;\n    reg [WIDTH-1:0] r_step = 1;\n\n    // This is not how you design a good divider circuit!\n    always @(posedge clk) begin\n        if (load) begin\n            a_reg <= a;\n            b_reg <= b;\n            q <= 0;\n            r <= a;\n            q_step <= 1;\n            r_step <= b;\n            done <= b == 0;\n        end else begin\n            if (r_step <= r) begin\n                q <= q + q_step;\n                r <= r - r_step;\n\n                if (!r_step[WIDTH-1]) begin\n                    r_step <= r_step << 1;\n                    q_step <= q_step << 1;\n                end\n            end else begin\n                if (!q_step[0]) begin\n                    r_step <= r_step >> 1;\n                    q_step <= q_step >> 1;\n                end else begin\n                    done <= 1;\n                end\n            end\n        end\n    end\n\n    always @(posedge clk) begin\n        assert (r_step == b_reg * q_step); // Helper invariant\n\n        assert (q * b_reg + r == a_reg); // Main invariant & correct output relationship\n        if (done) assert (r <= b_reg - 1); // Output range\n\n        cover (done);\n        cover (done && b_reg == 0);\n        cover (r != a_reg);\n        cover (r == a_reg);\n    end\nendmodule\n"
  },
  {
    "path": "tests/intertask/longrunning.sh",
    "content": "#!/bin/bash\nset -e\n\nif [[ $TASK == bmc ]]; then\n    python3 $SBY_MAIN --prefix $WORKDIR -f $SBY_FILE prove bmc\nelse\n    rm -rf ${WORKDIR} ${WORKDIR}_*\n    python3 $SBY_MAIN --prefix $WORKDIR $SBY_FILE bmc --statuscancels & bmc_pid=\"$!\"\n    # make sure we don't leave the background task running\n    trap 'kill \"$bmc_pid\" 2>/dev/null || true' EXIT\n    python3 $SBY_MAIN --prefix $WORKDIR $SBY_FILE prove\n    sleep 10\n    test -e ${WORKDIR}_bmc/CANCELLED\nfi\n"
  },
  {
    "path": "tests/junit/JUnit.xsd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xs:schema\n\txmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n\t elementFormDefault=\"qualified\"\n\t attributeFormDefault=\"unqualified\">\n\t<xs:annotation>\n\t\t<xs:documentation xml:lang=\"en\">JUnit test result schema for the Apache Ant JUnit and JUnitReport tasks\nCopyright © 2011, Windy Road Technology Pty. Limited\nThe Apache Ant JUnit XML Schema is distributed under the terms of the Apache License Version 2.0 http://www.apache.org/licenses/\nPermission to waive conditions of this license may be requested from Windy Road Support (http://windyroad.org/support).</xs:documentation>\n\t</xs:annotation>\n\t<xs:element name=\"testsuite\" type=\"testsuite\"/>\n\t<xs:simpleType name=\"ISO8601_DATETIME_PATTERN\">\n\t\t<xs:restriction base=\"xs:dateTime\">\n\t\t\t<xs:pattern value=\"[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\"/>\n\t\t</xs:restriction>\n\t</xs:simpleType>\n\t<xs:element name=\"testsuites\">\n\t\t<xs:annotation>\n\t\t\t<xs:documentation xml:lang=\"en\">Contains an aggregation of testsuite results</xs:documentation>\n\t\t</xs:annotation>\n\t\t<xs:complexType>\n\t\t\t<xs:sequence>\n\t\t\t\t<xs:element name=\"testsuite\" minOccurs=\"0\" maxOccurs=\"unbounded\">\n\t\t\t\t\t<xs:complexType>\n\t\t\t\t\t\t<xs:complexContent>\n\t\t\t\t\t\t\t<xs:extension base=\"testsuite\">\n\t\t\t\t\t\t\t\t<xs:attribute name=\"package\" type=\"xs:token\" use=\"required\">\n\t\t\t\t\t\t\t\t\t<xs:annotation>\n\t\t\t\t\t\t\t\t\t\t<xs:documentation xml:lang=\"en\">Derived from testsuite/@name in the non-aggregated documents</xs:documentation>\n\t\t\t\t\t\t\t\t\t</xs:annotation>\n\t\t\t\t\t\t\t\t</xs:attribute>\n\t\t\t\t\t\t\t\t<xs:attribute name=\"id\" type=\"xs:int\" use=\"required\">\n\t\t\t\t\t\t\t\t\t<xs:annotation>\n\t\t\t\t\t\t\t\t\t\t<xs:documentation xml:lang=\"en\">Starts at '0' for the first testsuite and is incremented by 1 for each following testsuite</xs:documentation>\n\t\t\t\t\t\t\t\t\t</xs:annotation>\n\t\t\t\t\t\t\t\t</xs:attribute>\n\t\t\t\t\t\t\t</xs:extension>\n\t\t\t\t\t\t</xs:complexContent>\n\t\t\t\t\t</xs:complexType>\n\t\t\t\t</xs:element>\n\t\t\t</xs:sequence>\n\t\t</xs:complexType>\n\t</xs:element>\n\t<xs:complexType name=\"testsuite\">\n\t\t<xs:annotation>\n\t\t\t<xs:documentation xml:lang=\"en\">Contains the results of exexuting a testsuite</xs:documentation>\n\t\t</xs:annotation>\n\t\t<xs:sequence>\n\t\t\t<xs:element name=\"properties\">\n\t\t\t\t<xs:annotation>\n\t\t\t\t\t<xs:documentation xml:lang=\"en\">Properties (e.g., environment settings) set during test execution</xs:documentation>\n\t\t\t\t</xs:annotation>\n\t\t\t\t<xs:complexType>\n\t\t\t\t\t<xs:sequence>\n\t\t\t\t\t\t<xs:element name=\"property\" minOccurs=\"0\" maxOccurs=\"unbounded\">\n\t\t\t\t\t\t\t<xs:complexType>\n\t\t\t\t\t\t\t\t<xs:attribute name=\"name\" use=\"required\">\n\t\t\t\t\t\t\t\t\t<xs:simpleType>\n\t\t\t\t\t\t\t\t\t\t<xs:restriction base=\"xs:token\">\n\t\t\t\t\t\t\t\t\t\t\t<xs:minLength value=\"1\"/>\n\t\t\t\t\t\t\t\t\t\t</xs:restriction>\n\t\t\t\t\t\t\t\t\t</xs:simpleType>\n\t\t\t\t\t\t\t\t</xs:attribute>\n\t\t\t\t\t\t\t\t<xs:attribute name=\"value\" type=\"xs:string\" use=\"required\"/>\n\t\t\t\t\t\t\t</xs:complexType>\n\t\t\t\t\t\t</xs:element>\n\t\t\t\t\t</xs:sequence>\n\t\t\t\t</xs:complexType>\n\t\t\t</xs:element>\n\t\t\t<xs:element name=\"testcase\" minOccurs=\"0\" maxOccurs=\"unbounded\">\n\t\t\t\t<xs:complexType>\n\t\t\t\t\t<xs:choice minOccurs=\"0\">\n\t\t\t\t\t\t<xs:element name=\"skipped\" />\n\t\t\t\t\t\t<xs:element name=\"error\" minOccurs=\"0\" maxOccurs=\"1\">\n\t\t\t\t\t\t\t<xs:annotation>\n\t\t\t\t\t\t\t\t<xs:documentation xml:lang=\"en\">Indicates that the test errored.  An errored test is one that had an unanticipated problem. e.g., an unchecked throwable; or a problem with the implementation of the test. Contains as a text node relevant data for the error, e.g., a stack trace</xs:documentation>\n\t\t\t\t\t\t\t</xs:annotation>\n\t\t\t\t\t\t\t<xs:complexType>\n\t\t\t\t\t\t\t\t<xs:simpleContent>\n\t\t\t\t\t\t\t\t\t<xs:extension base=\"pre-string\">\n\t\t\t\t\t\t\t\t\t\t<xs:attribute name=\"message\" type=\"xs:string\">\n\t\t\t\t\t\t\t\t\t\t\t<xs:annotation>\n\t\t\t\t\t\t\t\t\t\t\t\t<xs:documentation xml:lang=\"en\">The error message. e.g., if a java exception is thrown, the return value of getMessage()</xs:documentation>\n\t\t\t\t\t\t\t\t\t\t\t</xs:annotation>\n\t\t\t\t\t\t\t\t\t\t</xs:attribute>\n\t\t\t\t\t\t\t\t\t\t<xs:attribute name=\"type\" type=\"xs:string\" use=\"required\">\n\t\t\t\t\t\t\t\t\t\t\t<xs:annotation>\n\t\t\t\t\t\t\t\t\t\t\t\t<xs:documentation xml:lang=\"en\">The type of error that occured. e.g., if a java execption is thrown the full class name of the exception.</xs:documentation>\n\t\t\t\t\t\t\t\t\t\t\t</xs:annotation>\n\t\t\t\t\t\t\t\t\t\t</xs:attribute>\n\t\t\t\t\t\t\t\t\t</xs:extension>\n\t\t\t\t\t\t\t\t</xs:simpleContent>\n\t\t\t\t\t\t\t</xs:complexType>\n\t\t\t\t\t\t</xs:element>\n\t\t\t\t\t\t<xs:element name=\"failure\">\n\t\t\t\t\t\t\t<xs:annotation>\n\t\t\t\t\t\t\t\t<xs:documentation xml:lang=\"en\">Indicates that the test failed. A failure is a test which the code has explicitly failed by using the mechanisms for that purpose. e.g., via an assertEquals. Contains as a text node relevant data for the failure, e.g., a stack trace</xs:documentation>\n\t\t\t\t\t\t\t</xs:annotation>\n\t\t\t\t\t\t\t<xs:complexType>\n\t\t\t\t\t\t\t\t<xs:simpleContent>\n\t\t\t\t\t\t\t\t\t<xs:extension base=\"pre-string\">\n\t\t\t\t\t\t\t\t\t\t<xs:attribute name=\"message\" type=\"xs:string\">\n\t\t\t\t\t\t\t\t\t\t\t<xs:annotation>\n\t\t\t\t\t\t\t\t\t\t\t\t<xs:documentation xml:lang=\"en\">The message specified in the assert</xs:documentation>\n\t\t\t\t\t\t\t\t\t\t\t</xs:annotation>\n\t\t\t\t\t\t\t\t\t\t</xs:attribute>\n\t\t\t\t\t\t\t\t\t\t<xs:attribute name=\"type\" type=\"xs:string\" use=\"required\">\n\t\t\t\t\t\t\t\t\t\t\t<xs:annotation>\n\t\t\t\t\t\t\t\t\t\t\t\t<xs:documentation xml:lang=\"en\">The type of the assert.</xs:documentation>\n\t\t\t\t\t\t\t\t\t\t\t</xs:annotation>\n\t\t\t\t\t\t\t\t\t\t</xs:attribute>\n\t\t\t\t\t\t\t\t\t</xs:extension>\n\t\t\t\t\t\t\t\t</xs:simpleContent>\n\t\t\t\t\t\t\t</xs:complexType>\n\t\t\t\t\t\t</xs:element>\n\t\t\t\t\t</xs:choice>\n\t\t\t\t\t<xs:attribute name=\"name\" type=\"xs:token\" use=\"required\">\n\t\t\t\t\t\t<xs:annotation>\n\t\t\t\t\t\t\t<xs:documentation xml:lang=\"en\">Name of the test method</xs:documentation>\n\t\t\t\t\t\t</xs:annotation>\n\t\t\t\t\t</xs:attribute>\n\t\t\t\t\t<xs:attribute name=\"classname\" type=\"xs:token\" use=\"required\">\n\t\t\t\t\t\t<xs:annotation>\n\t\t\t\t\t\t\t<xs:documentation xml:lang=\"en\">Full class name for the class the test method is in.</xs:documentation>\n\t\t\t\t\t\t</xs:annotation>\n\t\t\t\t\t</xs:attribute>\n\t\t\t\t\t<xs:attribute name=\"time\" type=\"xs:decimal\" use=\"required\">\n\t\t\t\t\t\t<xs:annotation>\n\t\t\t\t\t\t\t<xs:documentation xml:lang=\"en\">Time taken (in seconds) to execute the test</xs:documentation>\n\t\t\t\t\t\t</xs:annotation>\n\t\t\t\t\t</xs:attribute>\n\t\t\t\t\t<xs:attribute name=\"id\" type=\"xs:string\" use=\"optional\">\n\t\t\t\t\t\t<xs:annotation>\n\t\t\t\t\t\t\t<xs:documentation xml:lang=\"en\">Cell ID of the property</xs:documentation>\n\t\t\t\t\t\t</xs:annotation>\n\t\t\t\t\t</xs:attribute>\n\t\t\t\t\t<xs:attribute name=\"type\" type=\"xs:token\" use=\"optional\">\n\t\t\t\t\t\t<xs:annotation>\n\t\t\t\t\t\t\t<xs:documentation xml:lang=\"en\">Kind of property (assert, cover, live)</xs:documentation>\n\t\t\t\t\t\t</xs:annotation>\n\t\t\t\t\t</xs:attribute>\n\t\t\t\t\t<xs:attribute name=\"location\" type=\"xs:token\" use=\"optional\">\n\t\t\t\t\t\t<xs:annotation>\n\t\t\t\t\t\t\t<xs:documentation xml:lang=\"en\">Source location of the property</xs:documentation>\n\t\t\t\t\t\t</xs:annotation>\n\t\t\t\t\t</xs:attribute>\n\t\t\t\t\t<xs:attribute name=\"tracefile\" type=\"xs:token\" use=\"optional\">\n\t\t\t\t\t\t<xs:annotation>\n\t\t\t\t\t\t\t<xs:documentation xml:lang=\"en\">Tracefile for the property</xs:documentation>\n\t\t\t\t\t\t</xs:annotation>\n\t\t\t\t\t</xs:attribute>\n\t\t\t\t</xs:complexType>\n\t\t\t</xs:element>\n\t\t\t<xs:element name=\"system-out\">\n\t\t\t\t<xs:annotation>\n\t\t\t\t\t<xs:documentation xml:lang=\"en\">Data that was written to standard out while the test was executed</xs:documentation>\n\t\t\t\t</xs:annotation>\n\t\t\t\t<xs:simpleType>\n\t\t\t\t\t<xs:restriction base=\"pre-string\">\n\t\t\t\t\t\t<xs:whiteSpace value=\"preserve\"/>\n\t\t\t\t\t</xs:restriction>\n\t\t\t\t</xs:simpleType>\n\t\t\t</xs:element>\n\t\t\t<xs:element name=\"system-err\">\n\t\t\t\t<xs:annotation>\n\t\t\t\t\t<xs:documentation xml:lang=\"en\">Data that was written to standard error while the test was executed</xs:documentation>\n\t\t\t\t</xs:annotation>\n\t\t\t\t<xs:simpleType>\n\t\t\t\t\t<xs:restriction base=\"pre-string\">\n\t\t\t\t\t\t<xs:whiteSpace value=\"preserve\"/>\n\t\t\t\t\t</xs:restriction>\n\t\t\t\t</xs:simpleType>\n\t\t\t</xs:element>\n\t\t</xs:sequence>\n\t\t<xs:attribute name=\"name\" use=\"required\">\n\t\t\t<xs:annotation>\n\t\t\t\t<xs:documentation xml:lang=\"en\">Full class name of the test for non-aggregated testsuite documents. Class name without the package for aggregated testsuites documents</xs:documentation>\n\t\t\t</xs:annotation>\n\t\t\t<xs:simpleType>\n\t\t\t\t<xs:restriction base=\"xs:token\">\n\t\t\t\t\t<xs:minLength value=\"1\"/>\n\t\t\t\t</xs:restriction>\n\t\t\t</xs:simpleType>\n\t\t</xs:attribute>\n\t\t<xs:attribute name=\"timestamp\" type=\"ISO8601_DATETIME_PATTERN\" use=\"required\">\n\t\t\t<xs:annotation>\n\t\t\t\t<xs:documentation xml:lang=\"en\">when the test was executed. Timezone may not be specified.</xs:documentation>\n\t\t\t</xs:annotation>\n\t\t</xs:attribute>\n\t\t<xs:attribute name=\"hostname\" use=\"required\">\n\t\t\t<xs:annotation>\n\t\t\t\t<xs:documentation xml:lang=\"en\">Host on which the tests were executed. 'localhost' should be used if the hostname cannot be determined.</xs:documentation>\n\t\t\t</xs:annotation>\n\t\t\t<xs:simpleType>\n\t\t\t\t<xs:restriction base=\"xs:token\">\n\t\t\t\t\t<xs:minLength value=\"1\"/>\n\t\t\t\t</xs:restriction>\n\t\t\t</xs:simpleType>\n\t\t</xs:attribute>\n\t\t<xs:attribute name=\"tests\" type=\"xs:int\" use=\"required\">\n\t\t\t<xs:annotation>\n\t\t\t\t<xs:documentation xml:lang=\"en\">The total number of tests in the suite</xs:documentation>\n\t\t\t</xs:annotation>\n\t\t</xs:attribute>\n\t\t<xs:attribute name=\"failures\" type=\"xs:int\" use=\"required\">\n\t\t\t<xs:annotation>\n\t\t\t\t<xs:documentation xml:lang=\"en\">The total number of tests in the suite that failed. A failure is a test which the code has explicitly failed by using the mechanisms for that purpose. e.g., via an assertEquals</xs:documentation>\n\t\t\t</xs:annotation>\n\t\t</xs:attribute>\n\t\t<xs:attribute name=\"errors\" type=\"xs:int\" use=\"required\">\n\t\t\t<xs:annotation>\n\t\t\t\t<xs:documentation xml:lang=\"en\">The total number of tests in the suite that errored. An errored test is one that had an unanticipated problem. e.g., an unchecked throwable; or a problem with the implementation of the test.</xs:documentation>\n\t\t\t</xs:annotation>\n\t\t</xs:attribute>\n\t\t<xs:attribute name=\"skipped\" type=\"xs:int\" use=\"optional\">\n\t\t\t<xs:annotation>\n\t\t\t\t<xs:documentation xml:lang=\"en\">The total number of ignored or skipped tests in the suite.</xs:documentation>\n\t\t\t</xs:annotation>\n\t\t</xs:attribute>\n\t\t<xs:attribute name=\"time\" type=\"xs:decimal\" use=\"required\">\n\t\t\t<xs:annotation>\n\t\t\t\t<xs:documentation xml:lang=\"en\">Time taken (in seconds) to execute the tests in the suite</xs:documentation>\n\t\t\t</xs:annotation>\n\t\t</xs:attribute>\n\t</xs:complexType>\n\t<xs:simpleType name=\"pre-string\">\n\t\t<xs:restriction base=\"xs:string\">\n\t\t\t<xs:whiteSpace value=\"preserve\"/>\n\t\t</xs:restriction>\n\t</xs:simpleType>\n</xs:schema>\n"
  },
  {
    "path": "tests/junit/Makefile",
    "content": "SUBDIR=junit\ninclude ../make/subdir.mk\n"
  },
  {
    "path": "tests/junit/junit_assert.sby",
    "content": "[tasks]\npass\nfail\npreunsat\n\n[options]\nmode bmc\ndepth 1\n\npass: expect pass\nfail: expect fail\npreunsat: expect error\n\n[engines]\nsmtbmc boolector\n\n[script]\nfail: read -define FAIL\npreunsat: read -define PREUNSAT\nread -sv test.sv\nprep -top top\n\n[file test.sv]\nmodule test(input foo);\nalways @* assert(foo);\n`ifdef FAIL\nalways @* assert(!foo);\n`endif\n`ifdef PREUNSAT\nalways @* assume(!foo);\n`endif\nendmodule\n\nmodule top();\ntest test_i (\n.foo(1'b1)\n);\nendmodule\n"
  },
  {
    "path": "tests/junit/junit_assert.sh",
    "content": "#!/bin/bash\nset -e\npython3 $SBY_MAIN -f $SBY_FILE $TASK\npython3 validate_junit.py $WORKDIR/$WORKDIR.xml\n"
  },
  {
    "path": "tests/junit/junit_cover.sby",
    "content": "[tasks]\npass\nuncovered fail\nassert fail\npreunsat\n\n[options]\nmode cover\ndepth 1\ncover_assert on\n\npass: expect pass\nfail: expect fail\npreunsat: expect fail\n\n[engines]\nsmtbmc boolector\n\n[script]\nuncovered: read -define FAIL\nassert: read -define FAIL_ASSERT\npreunsat: read -define PREUNSAT\nread -sv test.sv\nprep -top top\n\n[file test.sv]\nmodule test(input foo);\n`ifdef PREUNSAT\nalways @* assume(!foo);\n`endif\nalways @* cover(foo);\n`ifdef FAIL\nalways @* cover(!foo);\n`endif\n`ifdef FAIL_ASSERT\nalways @* assert(!foo);\n`endif\nendmodule\n\nmodule top();\ntest test_i (\n.foo(1'b1)\n);\nendmodule\n"
  },
  {
    "path": "tests/junit/junit_cover.sh",
    "content": "#!/bin/bash\nset -e\npython3 $SBY_MAIN -f $SBY_FILE $TASK\npython3 validate_junit.py $WORKDIR/$WORKDIR.xml\n"
  },
  {
    "path": "tests/junit/junit_expect.sby",
    "content": "[options]\nmode bmc\ndepth 1\nexpect fail,timeout\n\n[engines]\nsmtbmc\n\n[script]\nread -formal foo.v\nprep -top foo\n\n[file foo.v]\nmodule foo;\nalways_comb assert(1);\nendmodule\n"
  },
  {
    "path": "tests/junit/junit_expect.sh",
    "content": "#!/bin/bash\nset -e\n! python3 $SBY_MAIN -f $SBY_FILE $TASK\ngrep '<failure type=\"EXPECT\" message=\"Task returned status PASS. Expected values were: FAIL TIMEOUT\" />' $WORKDIR/$WORKDIR.xml\n"
  },
  {
    "path": "tests/junit/junit_nocodeloc.sby",
    "content": "[options]\nmode bmc\n\nexpect fail\n\n[engines]\nsmtbmc boolector\n\n[script]\nread -sv multi_assert.v\nprep -top test\nsetattr -unset src\n\n[file multi_assert.v]\nmodule test();\nalways @* begin\nassert (1);\nassert (0);\nend\nendmodule\n"
  },
  {
    "path": "tests/junit/junit_nocodeloc.sh",
    "content": "#!/bin/bash\nset -e\npython3 $SBY_MAIN -f $SBY_FILE $TASK\npython3 validate_junit.py $WORKDIR/$WORKDIR.xml\n"
  },
  {
    "path": "tests/junit/junit_timeout_error.sby",
    "content": "[tasks]\nsyntax error\nsolver error\ntimeout\n\n[options]\nmode cover\ndepth 1\ntimeout: timeout 1\nerror: expect error\ntimeout: expect timeout\n\n[engines]\n~solver: smtbmc --dumpsmt2 --progress --stbv z3\nsolver: smtbmc foo\n\n[script]\nread -noverific\nsyntax: read -define SYNTAX_ERROR\nread -sv diophantine.sv\nprep -top diophantine\n\n[file diophantine.sv]\nmodule diophantine;\n\t(* anyconst *) reg [15:0] a, b, c;\n\n\t`ifdef SYNTAX_ERROR\n\tfoo\n\t`endif\n\n\twire [48:0] x = a*a*a + b*b*b;\n\twire [48:0] y = c*c*c;\n\n\talways @* begin\n\t\tassume (a > 0);\n\t\tassume (b > 0);\n\t\tassume (c > 0);\n\t\tassume (x == y);\n\t\tcover (1);\n\tend\nendmodule\n"
  },
  {
    "path": "tests/junit/junit_timeout_error.sh",
    "content": "#!/bin/bash\nset -e\npython3 $SBY_MAIN -f $SBY_FILE $TASK\npython3 validate_junit.py $WORKDIR/$WORKDIR.xml\n"
  },
  {
    "path": "tests/junit/validate_junit.py",
    "content": "try:\n    from xmlschema import XMLSchema, XMLSchemaValidationError\nexcept ImportError:\n    import os\n    if \"NOSKIP\" not in os.environ.get(\"MAKEFLAGS\", \"\"):\n        print()\n        print(\"SKIPPING python library xmlschema not found, skipping JUnit output validation\")\n        print()\n        exit(0)\n\nimport argparse\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"Validate JUnit output\")\n    parser.add_argument('xml')\n    parser.add_argument('--xsd', default=\"JUnit.xsd\")\n\n    args = parser.parse_args()\n\n    schema = XMLSchema(args.xsd)\n    try:\n        schema.validate(args.xml)\n    except XMLSchemaValidationError as e:\n        print(e)\n        exit(1)\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tests/keepgoing/Makefile",
    "content": "SUBDIR=keepgoing\ninclude ../make/subdir.mk\n"
  },
  {
    "path": "tests/keepgoing/check_output.py",
    "content": "import re\n\n\ndef line_ref(dir, filename, pattern):\n    with open(dir + \"/src/\" + filename) as file:\n        if isinstance(pattern, str):\n            pattern_re = re.compile(re.escape(pattern))\n        else:\n            pattern_re = pattern\n            pattern = pattern.pattern\n\n        for number, line in enumerate(file, 1):\n            if pattern_re.search(line):\n                # Needs to match source locations for both verilog frontends\n                return fr\"{filename}:(?:{number}|\\d+\\.\\d+-{number}\\.\\d+)\"\n\n        raise RuntimeError(\"%s: pattern `%s` not found\" % (filename, pattern))\n"
  },
  {
    "path": "tests/keepgoing/keepgoing_multi_step.py",
    "content": "import sys\nfrom check_output import *\n\nsrc = \"keepgoing_multi_step.sv\"\n\nworkdir = sys.argv[1]\n\nassert_0 = line_ref(workdir, src, \"assert(0)\")\nstep_3_7 = line_ref(workdir, src, \"step 3,7\")\nstep_5 = line_ref(workdir, src, \"step 5\")\nstep_7 = line_ref(workdir, src, \"step 7\")\n\nlog = open(workdir + \"/logfile.txt\").read()\n\nif \"_abc]\" not in log:\n    log_per_trace = log.split(\"Writing trace to Yosys witness file\")[:-1]\n    assert len(log_per_trace) == 4\n    assert re.search(r\"Assert failed in test: %s \\(.*\\)$\" % assert_0, log_per_trace[0], re.M)\n\n    for i in range(1, 4):\n        assert re.search(r\"Assert failed in test: %s \\(.*\\) \\[failed before\\]$\" % assert_0, log_per_trace[i], re.M)\n\n\n    assert re.search(r\"Assert failed in test: %s \\(.*\\)$\" % step_3_7, log_per_trace[1], re.M)\n    assert re.search(r\"Assert failed in test: %s \\(.*\\)$\" % step_5, log_per_trace[2], re.M)\n    assert re.search(r\"Assert failed in test: %s \\(.*\\) \\[failed before\\]$\" % step_3_7, log_per_trace[3], re.M)\n    assert re.search(r\"Assert failed in test: %s \\(.*\\)$\" % step_7, log_per_trace[3], re.M)\n\n    pattern = f\"Property ASSERT in test at {assert_0} failed. Trace file: engine_0/trace0.(vcd|fst)\"\n    assert re.search(pattern, open(f\"{workdir}/{workdir}.xml\").read())\n\nlog_per_trace = log.split(\"summary: counterexample trace\")[1:]\nassert len(log_per_trace) == 4\n\nfor i in range(4):\n    assert re.search(r\"failed assertion test\\..* at %s\" % assert_0, log_per_trace[i], re.M)\n\nstep_3_7_traces = [i for i, t in enumerate(log_per_trace) if re.search(r\"failed assertion test\\..* at %s\" % step_3_7, t, re.M)]\nstep_5_traces = [i for i, t in enumerate(log_per_trace) if re.search(r\"failed assertion test\\..* at %s\" % step_5, t, re.M)]\nstep_7_traces = [i for i, t in enumerate(log_per_trace) if re.search(r\"failed assertion test\\..* at %s\" % step_7, t, re.M)]\n\nassert len(step_3_7_traces) == 2\nassert len(step_5_traces) == 1\nassert len(step_7_traces) == 1\n"
  },
  {
    "path": "tests/keepgoing/keepgoing_multi_step.sby",
    "content": "[tasks]\nbmc\nprove\nabc : prove\n\n[options]\nbmc: mode bmc\nprove: mode prove\nexpect fail\n\n[engines]\n~abc: smtbmc --keep-going boolector\nabc: abc --keep-going pdr\n\n[script]\nread -sv keepgoing_multi_step.sv\nprep -top test\n\n[files]\nkeepgoing_multi_step.sv\n"
  },
  {
    "path": "tests/keepgoing/keepgoing_multi_step.sh",
    "content": "#!/bin/bash\nset -e\npython3 $SBY_MAIN -f $SBY_FILE $TASK\npython3 ${SBY_FILE%.sby}.py $WORKDIR\n"
  },
  {
    "path": "tests/keepgoing/keepgoing_multi_step.sv",
    "content": "module test (\n\tinput clk, a\n);\n\treg [7:0] counter = 0;\n\n\talways @(posedge clk) begin\n\t\tcounter <= counter + 1;\n\tend\n\n\talways @(posedge clk) begin\n\t\tassert(0);\n\t\tif (counter == 3 || counter == 7) begin\n\t\t\tassert(a); // step 3,7\n\t\tend\n\t\tif (counter == 5) begin\n\t\t\tassert(a); // step 5\n\t\tend\n\t\tif (counter == 7) begin\n\t\t\tassert(a); // step 7\n\t\tend\n\t\tassert(1);\n\tend\nendmodule\n"
  },
  {
    "path": "tests/keepgoing/keepgoing_same_step.py",
    "content": "import sys\nfrom check_output import *\n\nworkdir = sys.argv[1]\nsrc = \"keepgoing_same_step.sv\"\n\nassert_a = line_ref(workdir, src, \"assert(a)\")\nassert_not_a = line_ref(workdir, src, \"assert(!a)\")\nassert_0 = line_ref(workdir, src, \"assert(0)\")\n\nlog = open(workdir + \"/logfile.txt\").read()\nlog_per_trace = log.split(\"Writing trace to Yosys witness file\")[:-1]\n\nassert len(log_per_trace) == 2\n\nassert re.search(r\"Assert failed in test: %s \\(.*\\)$\" % assert_a, log, re.M)\nassert re.search(r\"Assert failed in test: %s \\(.*\\)$\" % assert_not_a, log, re.M)\nassert re.search(r\"Assert failed in test: %s \\(.*\\)$\" % assert_0, log_per_trace[0], re.M)\nassert re.search(r\"Assert failed in test: %s \\(.*\\) \\[failed before\\]$\" % assert_0, log_per_trace[1], re.M)\n"
  },
  {
    "path": "tests/keepgoing/keepgoing_same_step.sby",
    "content": "[options]\nmode bmc\nexpect fail\n\n[engines]\nsmtbmc --keep-going boolector\n\n[script]\nread -sv keepgoing_same_step.sv\nprep -top test\n\n[files]\nkeepgoing_same_step.sv\n"
  },
  {
    "path": "tests/keepgoing/keepgoing_same_step.sh",
    "content": "#!/bin/bash\nset -e\npython3 $SBY_MAIN -f $SBY_FILE $TASK\npython3 ${SBY_FILE%.sby}.py $WORKDIR\n"
  },
  {
    "path": "tests/keepgoing/keepgoing_same_step.sv",
    "content": "module test (\n\tinput clk, a\n);\n\treg [7:0] counter = 0;\n\n\talways @(posedge clk) begin\n\t\tcounter <= counter + 1;\n\tend\n\n\talways @(posedge clk) begin\n\t\tif (counter == 3) begin\n\t\t\tassert(a);\n\t\t\tassert(!a);\n\t\t\tassert(0);\n\t\tend\n\tend\nendmodule\n"
  },
  {
    "path": "tests/keepgoing/keepgoing_smtc.py",
    "content": "import sys\nfrom check_output import *\n\nworkdir = sys.argv[1]\nsrc = \"keepgoing_same_step.sv\"\n\nassert_a = line_ref(workdir, src, \"assert(a)\")\nassert_not_a = line_ref(workdir, src, \"assert(!a)\")\nassert_0 = line_ref(workdir, src, \"assert(0)\")\n\nassert_false = line_ref(workdir, \"extra.smtc\", \"assert false\")\nassert_distinct = line_ref(workdir, \"extra.smtc\", \"assert (distinct\")\n\nlog = open(workdir + \"/logfile.txt\").read()\nlog_per_trace = log.split(\"Writing trace to Yosys witness file\")[:-1]\n\nassert len(log_per_trace) == 4\n\nassert re.search(r\"Assert failed in test: %s \\(.*\\)$\" % assert_a, log, re.M)\nassert re.search(r\"Assert failed in test: %s \\(.*\\)$\" % assert_not_a, log, re.M)\n\nassert re.search(r\"Assert src/%s failed: false\" % assert_false, log_per_trace[0], re.M)\nassert re.search(r\"Assert failed in test: %s \\(.*\\)$\" % assert_0, log_per_trace[1], re.M)\nassert re.search(r\"Assert failed in test: %s \\(.*\\) \\[failed before\\]$\" % assert_0, log_per_trace[2], re.M)\nassert re.search(r\"Assert src/%s failed: \\(distinct\" % assert_distinct, log_per_trace[3], re.M)\n"
  },
  {
    "path": "tests/keepgoing/keepgoing_smtc.sby",
    "content": "[options]\nmode bmc\nexpect fail\n\n[engines]\nsmtbmc --keep-going boolector -- --smtc src/extra.smtc\n\n[script]\nread -sv keepgoing_same_step.sv\nprep -top test\n\n[files]\nkeepgoing_same_step.sv\n\n[file extra.smtc]\nstate 2\nassert false\nalways\nassert (distinct [counter] #b00000111)\n"
  },
  {
    "path": "tests/keepgoing/keepgoing_smtc.sh",
    "content": "#!/bin/bash\nset -e\npython3 $SBY_MAIN -f $SBY_FILE $TASK\npython3 ${SBY_FILE%.sby}.py $WORKDIR\n"
  },
  {
    "path": "tests/links/Makefile",
    "content": "SUBDIR=links\ninclude ../make/subdir.mk\n"
  },
  {
    "path": "tests/links/dir/script.ys",
    "content": "read -sv picorv32.v\nread -sv prv32fmcmp.v\nprep -top prv32fmcmp\n"
  },
  {
    "path": "tests/links/more_dirs.sby",
    "content": "[tasks]\nlink\ncopy\n\n[options]\nmode prep\n\n[engines]\nbtor btormc\n\n[script]\nread -noverific\nscript dir/script.ys\n\n[files]\nhere/dir ${WORKDIR}/../dir\na/b/c.v prv32fmcmp.v\n\n[file here/doc]\nlog foo\n"
  },
  {
    "path": "tests/links/more_dirs.sh",
    "content": "#!/bin/bash\nset -e\nif [[ $TASK == link ]]; then\n    flags=\"--setup --link\"\nelse\n    flags=\"--setup\"\nfi\npython3 $SBY_MAIN -f $SBY_FILE $TASK $flags\n\ntest -e ${WORKDIR}/src/here/dir -a -e ${WORKDIR}/src/a/b/c.v -a -e ${WORKDIR}/src/here/doc\n"
  },
  {
    "path": "tests/links/symlink.py",
    "content": "from pathlib import Path\nimport sys\n\ndef main():\n    workdir, task = sys.argv[1:]\n    src = Path(workdir) / \"src\"\n    count = 0\n    for srcfile in src.iterdir():\n        if srcfile.name == \"heredoc\":\n            assert(not srcfile.is_symlink())\n            with open(srcfile, \"r\") as f:\n                local_contents = f.readline()\n                assert(local_contents.strip() == 'log foo')\n        else:\n            assert(srcfile.is_symlink() == (task == \"link\"))\n            assert(srcfile.name != \"script.ys\")\n        count += 1\n    assert(count == 4)\n    script_ys = src / \"dir\" / \"script.ys\"\n    assert(script_ys.exists())\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "tests/links/symlink.sby",
    "content": "[tasks]\nlink\ncopy\ndir_implicit: dir\ndir_explicit: dir\n\n[options]\nmode prep\n\n[engines]\nbtor btormc\n\n[script]\nread -noverific\nscript dir/script.ys\n\n[files]\n../../docs/examples/demos/picorv32.v\nprv32fmcmp.v\n~dir: dir\ndir_implicit: dir/\ndir_explicit: dir/ dir/\n\n[file heredoc]\nlog foo\n"
  },
  {
    "path": "tests/links/symlink.sh",
    "content": "#!/bin/bash\nset -e\nif [[ $TASK == link ]]; then\n    flags=\"--setup --link\"\nelse\n    flags=\"--setup\"\nfi\npython3 $SBY_MAIN -f $SBY_FILE $TASK $flags\npython3 ${SBY_FILE%.sby}.py $WORKDIR $TASK\n"
  },
  {
    "path": "tests/make/collect_tests.py",
    "content": "from pathlib import Path\nimport re\nimport argparse\n\nparser = argparse.ArgumentParser()\nparser.add_argument(\"--output\", required=True)\nparser.add_argument(\"--examples\")\nparser.add_argument(\"--check\", action='store_true')\nargs = parser.parse_args()\n\ntests = []\nchecked_dirs = []\n\nSAFE_PATH = re.compile(r\"^[a-zA-Z0-9_./\\\\]*$\")\n\nout_file = Path(args.output)\nheader_line = f\"# example dir = {args.examples}\"\n\nif args.check:\n    try:\n        with out_file.open(\"r\") as f:\n            found_header = f.readline().strip()\n        if found_header != header_line:\n            out_file.unlink()\n    except FileNotFoundError:\n        pass\n    exit()\n\nout_file.parent.mkdir(exist_ok=True)\n\ndef collect(path):\n    # don't pick up any paths that need escaping nor any sby workdirs\n    if (\n        not SAFE_PATH.match(str(path))\n        or (path / \"config.sby\").exists()\n        or (path / \"status.sqlite\").exists()\n        or (path / \"config.mcy\").exists()\n        or path.name == \"__pycache__\"\n        or path == out_file.parent\n    ):\n        return\n\n    checked_dirs.append(path)\n    for entry in path.glob(\"*.sby\"):\n        filename = str(entry)\n        if not SAFE_PATH.match(filename):\n            print(f\"skipping {filename!r}, use only [a-zA-Z0-9_./] in filenames\")\n            continue\n        if entry.name.startswith(\"skip_\"):\n            continue\n        if entry.with_suffix(\".ivy\").exists():\n            continue\n        tests.append(entry)\n    for entry in path.glob(\"*\"):\n        if entry.with_suffix(\".sby\").exists():\n            continue\n        if entry.is_dir():\n            collect(entry)\n\n\ndef unix_path(path):\n    source_path = \"/\".join(path.parts)\n    if source_path.startswith(\"//\"):\n        source_path = source_path[1:]\n    if args.examples:\n        try:\n            relative = path.relative_to(args.examples)\n        except ValueError:\n            pass\n        else:\n            if '..' not in relative.parts:\n                return source_path, \"examples/\" + \"/\".join(relative.parts)\n    if '..' in path.parts:\n        raise RuntimeError(\"path escapes collect directories\")\n    return source_path, source_path\n\n\ncollect(Path('.'))\nif args.examples:\n    collect(Path(args.examples))\n\nwith out_file.open(\"w\") as output:\n    print(header_line, file=output)\n\n    for checked_dir in checked_dirs:\n        print(f\"{out_file}: {checked_dir}\", file=output)\n\n    for test in tests:\n        test_unix_source, test_unix_rule = unix_path(test)\n        print(f\"make/rules/test/{test_unix_rule}.mk: {test_unix_source}\", file=output)\n        for ext in [\".sh\", \".py\"]:\n            script_file = test.parent / (test.stem + ext)\n            if script_file.exists():\n                script_file_unix_source, script_file_unix_rule = unix_path(script_file)\n                print(f\"make/rules/test/{test_unix_rule}.mk: {script_file_unix_source}\", file=output)\n        print(f\"make/rules/test/{test_unix_rule}.mk: make/test_rules.py\", file=output)\n    for test in tests:\n        test_unix_source, test_unix_rule = unix_path(test)\n        print(f\"-include make/rules/test/{test_unix_rule}.mk\", file=output)\n"
  },
  {
    "path": "tests/make/help.txt",
    "content": "make test:\n    run all tests (default)\n\nmake clean:\n    remove all sby workdirs\n\nmake test[_m_<mode>][_e_<engine>][_s_<solver>]:\n    run all tests that use a specific mode, engine and solver\n\nmake <name>:\n    run the test for <name>.sby\n\nmake refresh:\n    do nothing apart from refreshing generated make rules\n\nmake help:\n    show this help\n\nrunning make in a subdirectory or prefixing the target with the subdirectory\nlimits the test selection to that directory\n"
  },
  {
    "path": "tests/make/required_tools.py",
    "content": "import shutil\n\nREQUIRED_TOOLS = {\n    (\"smtbmc\", \"yices\"): [\"yices-smt2\"],\n    (\"smtbmc\", \"z3\"): [\"z3\"],\n    (\"smtbmc\", \"cvc4\"): [\"cvc4\"],\n    (\"smtbmc\", \"cvc5\"): [\"cvc5\"],\n    (\"smtbmc\", \"mathsat\"): [\"mathsat\"],\n    (\"smtbmc\", \"boolector\"): [\"boolector\"],\n    (\"smtbmc\", \"bitwuzla\"): [\"bitwuzla\"],\n    (\"smtbmc\", \"abc\"): [\"yosys-abc\"],\n    (\"aiger\", \"suprove\"): [\"suprove\", \"yices\"],\n    (\"aiger\", \"avy\"): [\"avy\", \"yices\"],\n    (\"aiger\", \"aigbmc\"): [\"aigbmc\", \"yices\"],\n    (\"btor\", \"btormc\"): [\"btormc\", \"btorsim\"],\n    (\"btor\", \"pono\"): [\"pono\", \"btorsim\"],\n    (\"abc\"): [\"yices\"],\n}\n\n\ndef found_tools():\n    with open(\"make/rules/found_tools\") as found_tools_file:\n        return [tool.strip() for tool in found_tools_file.readlines()]\n\n\nif __name__ == \"__main__\":\n    import subprocess\n    import sys\n    import os\n    from pathlib import Path\n\n    args = sys.argv[1:]\n\n    if args and args[0] == \"run\":\n        target, command, *required_tools = args[1:]\n\n        with open(\"make/rules/found_tools\") as found_tools_file:\n            found_tools = set(tool.strip() for tool in found_tools_file.readlines())\n\n        if 'verific' in required_tools:\n            result = subprocess.run([\"yosys\", \"-qp\", \"read -verific\"], capture_output=True)\n            if result.returncode:\n                print()\n                print(f\"SKIPPING {target}: requires yosys with verific support\")\n                print()\n                exit()\n            required_tools.remove('verific')\n\n        missing_tools = sorted(\n            f\"`{tool}`\" for tool in required_tools if tool not in found_tools\n        )\n        if missing_tools:\n            noskip = \"NOSKIP\" in os.environ.get(\"MAKEFLAGS\", \"\")\n            print()\n            print(f\"SKIPPING {target}: {', '.join(missing_tools)} not found\")\n            if noskip:\n                print(\"NOSKIP was set, treating this as an error\")\n            print()\n            exit(noskip)\n\n        print(command, flush=True)\n        exit(subprocess.call(command, shell=True, close_fds=False))\n\n    found_tools = []\n    check_tools = set()\n    for tools in REQUIRED_TOOLS.values():\n        check_tools.update(tools)\n\n    for tool in sorted(check_tools):\n        if not shutil.which(tool):\n            continue\n\n        if tool == \"btorsim\":\n            error_msg = subprocess.run(\n                [\"btorsim\", \"--vcd\"],\n                capture_output=True,\n                text=True,\n            ).stderr\n            if \"invalid command line option\" in error_msg:\n                print(\n                    \"found `btorsim` binary is too old \"\n                    \"to support the `--vcd` option, ignoring\"\n                )\n                continue\n\n        found_tools.append(tool)\n\n    found_tools = \"\\n\".join(found_tools + [\"\"])\n\n    try:\n        with open(\"make/rules/found_tools\") as found_tools_file:\n            if found_tools_file.read() == found_tools:\n                exit(0)\n    except FileNotFoundError:\n        pass\n\n    Path(\"make/rules\").mkdir(exist_ok=True)\n\n    with open(\"make/rules/found_tools\", \"w\") as found_tools_file:\n        found_tools_file.write(found_tools)\n"
  },
  {
    "path": "tests/make/run_sby.py",
    "content": "import os\nimport sys\nprog = os.environ.get(\"SBY_CMD\", \"sby\")\nos.execvp(prog, [prog, *sys.argv[1:]])\n"
  },
  {
    "path": "tests/make/subdir.mk",
    "content": "TESTDIR ?= ..\n\ntest:\n\t@$(MAKE) -C $(TESTDIR) $(SUBDIR)/$@\n\n.PHONY: test refresh IMPLICIT_PHONY\n\nMakefile: IMPLICIT_PHONY\n%.mk: IMPLICIT_PHONY\n\nIMPLICIT_PHONY:\n\nrefresh:\n\t@$(MAKE) -C $(TESTDIR) refresh\n\nhelp:\n\t@$(MAKE) -C $(TESTDIR) help\n\n%: IMPLICIT_PHONY\n\t@$(MAKE) -C $(TESTDIR) $(SUBDIR)/$@\n"
  },
  {
    "path": "tests/make/test_rules.py",
    "content": "import sys\nimport os\nimport subprocess\nimport json\nimport shlex\nimport argparse\nfrom pathlib import Path\n\nfrom required_tools import REQUIRED_TOOLS\n\nparser = argparse.ArgumentParser()\nparser.add_argument(\"--rule\", required=True)\nparser.add_argument(\"--source\", required=True)\nargs = parser.parse_args()\n\n\ndef unix_path(path):\n    source_path = \"/\".join(path.parts)\n    if source_path.startswith(\"//\"):\n        source_path = source_path[1:]\n    return source_path\n\n\nsby_file = Path(args.source)\nrules_file = Path(args.rule)\nrules_base = rules_file.parent.parts[3:]\nsby_dir = sby_file.parent\n\n\ntaskinfo = json.loads(\n    subprocess.check_output(\n        [sys.executable, os.getenv(\"SBY_MAIN\"), \"--dumptaskinfo\", sby_file.name],\n        cwd=sby_dir,\n    )\n)\n\n\ndef parse_engine(engine):\n    engine, *args = engine\n    default_solvers = {\"smtbmc\": \"yices\"}\n    for arg in args:\n        if not arg.startswith(\"-\"):\n            return engine, arg\n    return engine, default_solvers.get(engine)\n\n\nrules_file.parent.mkdir(exist_ok=True, parents=True)\n\nwith rules_file.open(\"w\") as rules:\n    name = unix_path(sby_dir / sby_file.stem)\n    rule_name = \"/\".join(rules_base) + f\"/{sby_file.stem}\"\n\n    for task, info in taskinfo.items():\n        target = name\n        rule_target = rule_name\n        workdirname = sby_file.stem\n        if task:\n            target += f\"_{task}\"\n            rule_target += f\"_{task}\"\n            workdirname += f\"_{task}\"\n\n        engines = set()\n        solvers = set()\n        engine_solvers = set()\n\n        required_tools = set()\n\n        for mode_engines in info[\"engines\"].values():\n            for engine in mode_engines:\n                engine, solver = parse_engine(engine)\n                engines.add(engine)\n                required_tools.update(\n                    REQUIRED_TOOLS.get((engine, solver), REQUIRED_TOOLS.get(engine, ()))\n                )\n                if solver:\n                    solvers.add(solver)\n                    engine_solvers.add((engine, solver))\n\n        if any(\n            line.startswith(\"read -verific\") or line.startswith(\"verific\")\n            for line in info[\"script\"]\n        ):\n            required_tools.add(\"verific\")\n\n        required_tools = sorted(required_tools)\n\n        print(f\".PHONY: {rule_target}\", file=rules)\n        print(f\"{rule_target}:\", file=rules)\n\n        shell_script = sby_dir / f\"{sby_file.stem}.sh\"\n\n        sby_dir_unix = unix_path(sby_dir)\n\n        if shell_script.exists():\n            command = f\"cd {sby_dir_unix} && env SBY_FILE={sby_file.name} WORKDIR={workdirname} TASK={task} bash {shell_script.name}\"\n        else:\n            command = (\n                f\"cd {sby_dir_unix} && python3 $(SBY_MAIN) -f {sby_file.name} {task}\"\n            )\n\n        print(\n            f\"\\t+@python3 make/required_tools.py run {target} {shlex.quote(command)} {shlex.join(required_tools)}\",\n            file=rules,\n        )\n\n        print(f\".PHONY: clean-{rule_target}\", file=rules)\n        print(f\"clean-{rule_target}:\", file=rules)\n        print(f\"\\trm -rf {target}\", file=rules)\n\n        test_groups = []\n\n        mode = info[\"mode\"]\n\n        test_groups.append(f\"test_m_{mode}\")\n\n        for engine in sorted(engines):\n            test_groups.append(f\"test_e_{engine}\")\n            test_groups.append(f\"test_m_{mode}_e_{engine}\")\n\n        for solver in sorted(solvers):\n            test_groups.append(f\"test_s_{solver}\")\n            test_groups.append(f\"test_m_{mode}_s_{solver}\")\n\n        for engine, solver in sorted(engine_solvers):\n            test_groups.append(f\"test_e_{engine}_s_{solver}\")\n            test_groups.append(f\"test_m_{mode}_e_{engine}_s_{solver}\")\n\n        prefix = \"\"\n\n        for part in [*rules_base, \"\"]:\n            print(f\".PHONY: {prefix}clean {prefix}test\", file=rules)\n            print(f\"{prefix}clean: clean-{rule_target}\", file=rules)\n            print(f\"{prefix}test: {rule_target}\", file=rules)\n\n            for test_group in test_groups:\n                print(f\".PHONY: {prefix}{test_group}\", file=rules)\n                print(f\"{prefix}{test_group}: {rule_target}\", file=rules)\n            prefix += f\"{part}/\"\n\n    tasks = [task for task in taskinfo.keys() if task]\n\n    if tasks:\n        print(f\".PHONY: {rule_name}\", file=rules)\n        print(f\"{rule_name}:\", *(f\"{rule_name}_{task}\" for task in tasks), file=rules)\n"
  },
  {
    "path": "tests/parser/.gitignore",
    "content": "*\n!Makefile\n!.gitignore\n!*.sby\n"
  },
  {
    "path": "tests/parser/Makefile",
    "content": "SUBDIR=parser\ninclude ../make/subdir.mk\n"
  },
  {
    "path": "tests/regression/Makefile",
    "content": "SUBDIR=regression\ninclude ../make/subdir.mk\n"
  },
  {
    "path": "tests/regression/aim_vs_smt2_nonzero_start_offset.sby",
    "content": "[tasks]\nabc_bmc3 bmc\nabc_sim3 bmc\naiger_avy prove\naiger_suprove prove\nabc_pdr prove\n\n[options]\nbmc: mode bmc\nprove: mode prove\nexpect fail\nwait on\n\n[engines]\nabc_bmc3: abc bmc3\nabc_sim3: abc sim3\naiger_avy: aiger avy\naiger_suprove: aiger suprove\nabc_pdr: abc pdr\n\n[script]\nread -sv test.sv\nprep -top test\n\n[file test.sv]\nmodule test (\n    input clk,\n    input [8:1] nonzero_offset\n);\n    reg [7:0] counter = 0;\n\n    always @(posedge clk) begin\n        if (counter == 3) assert(nonzero_offset[1]);\n        counter <= counter + 1;\n    end\nendmodule\n"
  },
  {
    "path": "tests/regression/const_clocks.sby",
    "content": "[tasks]\nbtor\nsmt\nbtor_m btor multiclock\nsmt_m smt multiclock\n\n[options]\nmode bmc\n\nmulticlock: multiclock on\n\n[engines]\n#smtbmc\nbtor: btor btormc\nsmt: smtbmc boolector\n\n[script]\nread_verilog -formal const_clocks.sv\nprep -flatten -top top\n\n[file const_clocks.sv]\nmodule top(\n    input clk,\n    input [7:0] d\n);\n\n    (* keep *)\n    wire [7:0] some_const = $anyconst;\n\n    wire [7:0] q;\n\n    ff ff1(.clk(1'b0), .d(d), .q(q));\n\n    initial assume (some_const == q);\n    initial assume (q != 0);\n\n\n    always @(posedge clk) assert(some_const == q);\nendmodule\n\nmodule ff(input clk, input [7:0] d, (* keep *) output reg [7:0] q);\n    always @(posedge clk) q <= d;\nendmodule\n"
  },
  {
    "path": "tests/regression/fake_loop.sby",
    "content": "[options]\nmode cover\n\n[engines]\nsmtbmc boolector\n\n[script]\nread -formal fake_loop.sv\nhierarchy -top fake_loop\nproc\n\n[file fake_loop.sv]\nmodule fake_loop(input clk, input a, input b, output [9:0] x);\n    wire [9:0] ripple;\n    reg [9:0] prev_ripple = 9'b0;\n\n    always @(posedge clk) prev_ripple <= ripple;\n\n    assign ripple = {ripple[8:0], a} ^ prev_ripple; // only cyclic at the coarse-grain level\n    assign x = ripple[9] + b;\n\n    always @(posedge clk) cover(ripple[9]);\nendmodule\n"
  },
  {
    "path": "tests/regression/ff_xinit_opt.sby",
    "content": "[options]\nmode bmc\n\n[engines]\nsmtbmc boolector\n\n[script]\nread_verilog -formal ff_xinit_opt.sv\nprep -flatten -top top\n\nopt -fast -keepdc\n\n[file ff_xinit_opt.sv]\nmodule top(\n    input clk,\n    input [7:0] d\n);\n\n    (* keep *)\n    wire [7:0] some_const = $anyconst;\n\n    wire [7:0] q1;\n    wire [7:0] q2;\n\n    ff ff1(.clk(clk), .d(q1), .q(q1));\n    ff ff2(.clk(1'b0), .d(d), .q(q2));\n\n    initial assume (some_const == q1);\n    initial assume (some_const == q2);\n    initial assume (q1 != 0);\n    initial assume (q2 != 0);\n\n    always @(posedge clk) assert(some_const == q1);\n    always @(posedge clk) assert(some_const == q2);\nendmodule\n\nmodule ff(input clk, input [7:0] d, (* keep *) output reg [7:0] q);\n    always @(posedge clk) q <= d;\nendmodule\n"
  },
  {
    "path": "tests/regression/invalid_ff_dcinit_merge.sby",
    "content": "[options]\nmode bmc\ndepth 4\nexpect fail\n\n[engines]\nsmtbmc\n\n[script]\nread -formal top.sv\nprep -top top\n\n[file top.sv]\nmodule top(\ninput clk, d\n);\n\nreg q1;\nreg q2;\n\nalways @(posedge clk) begin\n    q1 <= d;\n    q2 <= d;\nend;\n\n// q1 and q2 are unconstrained in the initial state, so this should fail\nalways @(*) assert(q1 == q2);\n\nendmodule\n"
  },
  {
    "path": "tests/regression/option_skip.sby",
    "content": "[tasks]\nsmtbmc_pass: smtbmc pass\nsmtbmc_fail: smtbmc fail\nbtormc_pass: btormc pass\nbtormc_fail: btormc fail\n\n[options]\nmode bmc\npass: expect pass\nfail: expect fail\npass: depth 5\nfail: depth 6\n\nskip 2\n\n[engines]\nsmtbmc: smtbmc boolector\n[engines bmc]\nbtormc: btor btormc\n\n[script]\nread -formal top.sv\nprep -top top\n\n[file top.sv]\nmodule top(input clk);\n    reg [7:0] counter = 0;\n\n    always @(posedge clk) begin\n        counter <= counter + 1;\n        assert (counter < 4);\n    end\nendmodule\n"
  },
  {
    "path": "tests/regression/smt_dynamic_index_assign.sby",
    "content": "[options]\nmode cover\ndepth 36\n\n[engines]\nsmtbmc boolector\n\n[script]\nread -formal top.sv\nprep -top top\n\n[file top.sv]\nmodule top(input clk);\n    reg [33:0] bits = 0;\n    reg [5:0] counter = 0;\n\n    always @(posedge clk) begin\n        counter <= counter + 1;\n        bits[counter] <= 1;\n        cover (&bits);\n    end\nendmodule\n"
  },
  {
    "path": "tests/regression/unroll_noincr_traces.sby",
    "content": "[tasks]\nboolector\nyices\nz3\n\n[options]\nmode bmc\nexpect fail\n\n[engines]\nboolector: smtbmc boolector -- --noincr\nyices: smtbmc --unroll yices -- --noincr\nz3: smtbmc --unroll z3 -- --noincr\n\n\n[script]\nread -formal top.sv\nprep -top top\n\n[file top.sv]\nmodule top(input clk);\n    reg [7:0] counter = 0;\n    wire derived = counter * 7;\n\n    always @(posedge clk) begin\n        counter <= counter + 1;\n        assert (counter < 4);\n    end\nendmodule\n"
  },
  {
    "path": "tests/regression/verilog_hier_path.sby",
    "content": "[options]\nmode bmc\ndepth 1\nexpect fail\n\n[engines]\nsmtbmc\n\n[script]\nread_verilog -formal sub.v\nread_verilog -formal top.v\nprep -top \\\\(foo)\n\n[file top.v]\nmodule \\\\(foo) (input a);\n    always @* begin\n        assert_foo: assert (a);\n    end\n    \\\\(bar) \\\\(bar)=i= (.a(a));\nendmodule\n\n[file sub.v]\nmodule \\\\(bar) (input a);\n    always @* begin\n        assert_bar: assert (a);\n    end\nendmodule\n"
  },
  {
    "path": "tests/regression/vhdl_hier_path.sby",
    "content": "[options]\nmode bmc\ndepth 1\nexpect fail\n\n[engines]\nsmtbmc\n\n[script]\nverific -vhdl subsub.vhd\nverific -vhdl sub.vhd\nverific -vhdl top.vhd\nhierarchy -top top\nhierarchy -top \\\\sub(p=41)\\(rtl)\nprep\n\n[file top.vhd]\nlibrary ieee;\nuse ieee.std_logic_1164.all;\nuse ieee.numeric_std.all;\n\nentity top is\n  port (\n    a : in integer\n  );\nend entity;\n\narchitecture rtl of top is\ncomponent sub is\n  generic (\n    p : integer\n  );\n  port (\n    a : in integer\n  );\nend component;\nbegin\n  sub_i: sub\n    generic map (\n      p => 41\n    )\n    port map (\n      a => a\n    );\nend architecture;\n\n[file sub.vhd]\nlibrary ieee;\nuse ieee.std_logic_1164.all;\nuse ieee.numeric_std.all;\n\nentity sub is\n  generic (\n    p : integer := 99\n  );\n  port (\n    a : in integer\n  );\nend entity;\n\narchitecture rtl of sub is\ncomponent subsub is\n  generic (\n    p : integer\n  );\n  port (\n    a : in integer\n  );\nend component;\nbegin\n  subsub_i: subsub\n    generic map (\n      p => p + 1\n    )\n    port map (\n      a => a\n    );\nend architecture;\n\n[file subsub.vhd]\nlibrary ieee;\nuse ieee.std_logic_1164.all;\nuse ieee.numeric_std.all;\n\nentity subsub is\n  generic (\n    p : integer := 99\n  );\n  port (\n    a : in integer\n  );\nend entity;\n\narchitecture rtl of subsub is\nbegin\n  assert (p > a);\nend architecture;\n"
  },
  {
    "path": "tests/staged_sim_and_verif/README.md",
    "content": "Staged simulation + verification example demonstrating staged verification using simulation and writeback via `sim -w` pass.\n\nThis test mirrors what is described in <https://yosyshq.readthedocs.io/projects/ap130/en/latest/>, and should be kept up to date with that appnote.\n\n- Stage 1: run cover to reach “req sent, ack pending”, producing `trace0.yw`.\n- Stage 2A (cover branch): replay the witness with `sim -w` to bake state, then run another cover that requires the ack.\n- Stage 2B (assert branch): replay the same baked state and assert there is at most one further ack after the second req.\n- Uses labeled properties (`phase1_*`, `phase2_shared_*`, `phase2a_*`, `phase2b_*`) and a selector script to strip irrelevant properties per stage.\n- Needs Yosys with Verific (`verific -formal` in the scripts).\n\nRun via the wrapper: from the root directory, call `make -C tests staged_sim_and_verif/staged_sim_and_verif`, which calls `staged_sim_and_verif.sh` and exercises all five tasks in `skip_staged_flow.sby`. You may also run each task manually; simply ensure you run the tasks in the correct order shown in the `.sh` file.\n\nDesign notes\n- Two-phase flow with a branch in phase 2: phase 1 reaches “two reqs seen” and emits a witness; phase 2 replays that witness with `sim -w` and then splits into a cover branch (ack) and an assert branch (single remaining ack). This covers both SCY-like sequential covers and an assertion path that goes beyond SCY’s current cover-only sequencing.\n- Phase separation uses labeled properties (`phase1_*`, `phase2_shared_*`, `phase2a_*`, `phase2b_*`), matching the SCY approach. Each branch prunes with `select */c:phase* ... %u %d` to keep only the shared + branch-specific labels for that task.\n- Tooling: needs Yosys with Verific (`verific -formal`) for SVA parsing. The minimal `staged_sim_and_verif.sby` exists just so the test harness discovers the Verific requirement.\n- Harness glue: the current make harness can’t express “run these SBY tasks sequentially from one .sby”; it spins each task as an independent target. We keep the multi-task config in `skip_staged_flow.sby` (prefixed so collect skips it) and use `staged_sim_and_verif.sh` to drive the five tasks in order. The tiny `staged_sim_and_verif.sby` is only there so collect/required_tools see the test and recognize that verific is required. The `.sh` is what actually runs and enforces ordering.\n"
  },
  {
    "path": "tests/staged_sim_and_verif/Req_Ack.sv",
    "content": "module DUT (\n        input  logic clk,\n        output logic req,\n        output logic ack\n);\n\n`ifdef FORMAL\n\n        logic [31:0] reqs_seen;\n        logic [31:0] acks_seen;\n        logic [31:0] cycle_count;\n\n        // Deterministic initial state\n        initial begin\n                reqs_seen = 32'b0;\n                acks_seen = 32'b0;\n                cycle_count = 32'b0;\n        end\n\n        always @ (posedge clk) begin\n                if (req) reqs_seen <= reqs_seen + 1;\n                if (ack && !$past(ack)) acks_seen <= acks_seen + 1;\n                cycle_count <= cycle_count + 1;\n        end\n\n        // Req is only high for one cycle\n        assume property (@(posedge clk)\n                req |-> ##1 !req\n        );\n\n        // Reqs are at least 8 cycles apart\n        assume property (@(posedge clk)\n                req |-> ##1 (!req [*7])\n        );\n\n        // ack comes exactly 4 cycles after req\n        assume property (@(posedge clk) \n                req |-> ##4 ack\n        );\n\n        // ack must remain low if no req 4 cycles ago\n        assume property (@(posedge clk)\n                !$past(req,4) |-> !ack\n        );\n\n        // For the purpose of demonstration, stop exactly when second req pulse\n        // occurs. This leaves us in a state where we're waiting for the second\n        // ack.\n        always @(posedge clk) begin\n                stage1_reqs_seen: cover(reqs_seen == 2);\n        end\n\n        // In stage 2, cover that the first ack arrives within a bounded window\n        // after the first req + another req arrives.\n        stage2_cover_ack_and_new_req: cover property (@(posedge clk)\n                $rose(ack) ##[1:$] (reqs_seen == 3)\n        );\n\n\n        // In stage 3, assume that there's no more reqs.\n        always @ (posedge clk) begin\n                stage3_shared_no_new_req: assume(!req);\n        end\n\n        // In stage 3a, cover the second ack arriving eventually.\n        always @(posedge clk) begin\n                stage3a_cover_ack: cover(ack);\n        end\n\n        // In stage 3b, assert that once we've seen 3 acks, we stay at 3 acks.\n        stage3b_acks_remains_3: assert property (\n                @(posedge clk) $rose(acks_seen == 3) |-> (acks_seen == 3)[*1:$]\n        );\n\n`endif\n\nendmodule\n"
  },
  {
    "path": "tests/staged_sim_and_verif/skip_staged_flow.sby",
    "content": "[tasks]\nprep\nstage_1 cover\nstage_2 cover\nstage_3_init\nstage_3a_cover cover\nstage_3b_assert\n\n[options]\nprep:\nmode prep\n\ncover:\nmode cover\nskip_prep on\n\nstage_3_init:\nmode prep\nskip_prep on\n\nstage_3b_assert:\nmode prove\nskip_prep on\n\n--\n\n[engines]\n\nprep:\nnone\n\ncover:\nsmtbmc\n\nstage_3_init:\nnone\n\nstage_3b_assert:\nsmtbmc\n\n--\n\n[script]\n\nprep:\nverific -formal Req_Ack.sv\nhierarchy -top DUT\nprep\n\nstage_1:\nread_rtlil design_prep.il\n\nwrite_rtlil stage_1_init.il\n\nselect */c:stage* */c:stage1* %d\ndelete\n\nstage_2:\nread_rtlil stage_1_init.il\nsim -a -w -scope DUT -r trace0.yw\nwrite_rtlil stage_2_init.il\n\nselect */c:stage* */c:stage2* %d\ndelete\n\nstage_3_init:\nread_rtlil stage_2_init.il\n\nsim -a -w -scope DUT -r trace0.yw -noinitstate\n\nwrite_rtlil stage_3_init.il\n\nstage_3a_cover:\nread_rtlil stage_3_init.il\nselect */c:stage* */c:stage3_shared* */c:stage3a* %u %d\ndelete\n\nstage_3b_assert:\nread_rtlil stage_3_init.il\nselect */c:stage* */c:stage3_shared* */c:stage3b* %u %d\ndelete\n\n\n--\n\n[files]\n\nprep:\nReq_Ack.sv\n\nstage_1:\nskip_staged_flow_prep/model/design_prep.il\n\nstage_2:\nskip_staged_flow_stage_1/src/stage_1_init.il\nskip_staged_flow_stage_1/engine_0/trace0.yw\n\nstage_3_init:\nskip_staged_flow_stage_2/src/stage_2_init.il\nskip_staged_flow_stage_2/engine_0/trace0.yw\n\nstage_3a_cover:\nskip_staged_flow_stage_3_init/src/stage_3_init.il\n\nstage_3b_assert:\nskip_staged_flow_stage_3_init/src/stage_3_init.il\n"
  },
  {
    "path": "tests/staged_sim_and_verif/staged_sim_and_verif.sby",
    "content": "[options]\nmode cover\ndepth 1\n\n[engines]\nsmtbmc\n\n[script]\n# Minimal job so dumptaskinfo picks up the tools this flow requires.\nverific -formal Req_Ack.sv\nprep -top DUT\n\n[files]\nReq_Ack.sv\n"
  },
  {
    "path": "tests/staged_sim_and_verif/staged_sim_and_verif.sh",
    "content": "#!/bin/bash\nset -euo pipefail\n\nFLOW_FILE=\"skip_staged_flow.sby\"\n\nrun_task() {\n    python3 \"$SBY_MAIN\" -f \"$FLOW_FILE\" \"$1\"\n}\n\nrun_task prep\nrun_task stage_1\nrun_task stage_2\nrun_task stage_3_init\nrun_task stage_3a_cover\nrun_task stage_3b_assert\n"
  },
  {
    "path": "tests/statusdb/Makefile",
    "content": "SUBDIR=statusdb\ninclude ../make/subdir.mk\n"
  },
  {
    "path": "tests/statusdb/mixed.py",
    "content": "import json\nimport sqlite3\nimport sys\n\ndef get_prop_type(prop: str):\n    prop = json.loads(prop or \"[]\")\n    name_parts = prop[-1].split(\"_\")\n    if name_parts[0] == \"\\\\check\":\n        # read_verilog style\n        # \\check_cover_mixed_v...\n        return name_parts[1]\n    else:\n        # verific style\n        # \\assert_auto_verificsva_cc...\n        return name_parts[0][1:]\n\ndef main():\n    workdir = sys.argv[1]\n    with open(f\"{workdir}/status.path\", \"r\") as status_path_file:\n        status_path = f\"{workdir}/{status_path_file.read().rstrip()}\"\n    # read only database\n    con = sqlite3.connect(f\"file:{status_path}?mode=ro\", uri=True)\n    db = con.cursor()\n    # custom sql to get all task property statuses for the current workdir\n    rows = db.execute(\n        \"\"\"\n            SELECT task.id, task_property.id, task_property.name, task_property.src, task_property_status.status\n            FROM task\n            LEFT JOIN task_property ON task_property.task=task.id\n            LEFT JOIN task_property_status ON task_property_status.task_property=task_property.id\n            WHERE task.workdir=:workdir\n            ORDER BY task_property_status.id DESC;\n        \"\"\",\n        {\"workdir\": workdir}\n    ).fetchall()\n    # only check the most recent iteration of the test\n    last_id = 0\n    for row_id, _, _, _, _ in rows:\n        if row_id > last_id:\n            last_id = row_id\n    # only check the last status of a property\n    checked_props = set()\n    for row_id, prop_id, prop, src, status in rows:\n        if row_id != last_id:\n            continue\n        if prop_id in checked_props:\n            continue\n        checked_props.add(prop_id)\n        prop_type = get_prop_type(prop)\n        valid_status: list[None|str] = []\n        if workdir in [\"mixed_bmc\", \"mixed_assert\"] and prop_type == \"assert\":\n            valid_status = [\"FAIL\"]\n        elif workdir == \"mixed_bmc\" and prop_type == \"cover\":\n            valid_status = [None, \"UNKNOWN\"]\n        elif workdir == \"mixed_assert\" and prop_type == \"cover\":\n            valid_status = [\"PASS\", None, \"UNKNOWN\"]\n        elif workdir == \"mixed_no_assert\" and prop_type == \"assert\":\n            valid_status = [None, \"UNKNOWN\"]\n        elif workdir == \"mixed_no_assert\" and prop_type == \"cover\":\n            valid_status = [\"PASS\"]\n        assert status in valid_status, f\"Unexpected {prop_type} status {status} for {prop} ({src})\"\n        \n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "tests/statusdb/mixed.sby",
    "content": "[tasks]\nno_assert cover\nassert cover\nbmc\n\n[options]\ncover: mode cover\nbmc: mode bmc\nbmc: depth 1\n\nassert: cover_assert on\nno_assert: expect pass\n~no_assert: expect fail\n\n[engines]\ncover: smtbmc boolector\nbmc: smtbmc boolector\n\n[script]\nread -formal mixed.v\nprep -top test\n\n[files]\nmixed.v\n"
  },
  {
    "path": "tests/statusdb/mixed.sh",
    "content": "#!/bin/bash\nset -e\npython3 $SBY_MAIN -f $SBY_FILE $TASK\npython3 ${SBY_FILE%.sby}.py $WORKDIR\n"
  },
  {
    "path": "tests/statusdb/mixed.v",
    "content": "module test (input CP, CN, input A, B, output reg XP, XN);\n\treg [7:0] counter = 0;\n\talways @* begin\n\t\tassume (A || B);\n\t\tassume (!A || !B);\n\t\tassert (A == B);\n\t\tcover (counter == 3 && A);\n\t\tcover (counter == 3 && B);\n\tend\n\talways @(posedge CP)\n\t\tcounter <= counter + 1;\n\talways @(posedge CP)\n\t\tXP <= A;\n\talways @(negedge CN)\n\t\tXN <= B;\nendmodule\n"
  },
  {
    "path": "tests/statusdb/reset.sby",
    "content": "[options]\nmode bmc\ndepth 100\nexpect fail\n\n[engines]\nsmtbmc --keep-going boolector\n\n[script]\nread -formal reset.sv\nprep -top demo\n\n[files]\nreset.sv\n"
  },
  {
    "path": "tests/statusdb/reset.sh",
    "content": "#!/bin/bash\nset -e\n\n# run task\npython3 $SBY_MAIN -f $SBY_FILE $TASK\n# task has 3 properties\npython3 $SBY_MAIN -f $SBY_FILE --status | wc -l | grep -q '3'\n# resetting task should work\npython3 $SBY_MAIN -f $SBY_FILE --statusreset\n# clean database should have no properties\npython3 $SBY_MAIN -f $SBY_FILE --status | wc -l | grep -q '0'\n"
  },
  {
    "path": "tests/statusdb/reset.sv",
    "content": "module demo (\n  input clk,\n  output reg [5:0] counter\n);\n  initial counter = 0;\n\n  always @(posedge clk) begin\n    if (counter == 15)\n      counter <= 0;\n    else\n      counter <= counter + 1;\n  end\n\n`ifdef FORMAL\n  always @(posedge clk) begin\n    assert (1);\n    assert (counter < 7);\n    assert (0);\n  end\n`endif\nendmodule\n"
  },
  {
    "path": "tests/statusdb/timeout.sby",
    "content": "[tasks]\nbtor_bmc: btor bmc\nbtor_fin_bmc: btor bmc fin\npono_bmc: pono bmc\npono_fin_bmc: pono bmc fin\nbtor_cover: btor cover\nbtor_fin_cover: btor cover fin\nsmt_bmc: smt bmc\nsmt_fin_bmc: smt bmc fin\nsmt_cover: smt cover\nsmt_fin_cover: smt cover fin\nsmt_prove: smt prove\nsmt_fin_prove: smt prove fin\nsmt_fail: smtfail bmc fail\nsmt_fin_fail: smtfail bmc fail fin\naig_bmc: aigbmc bmc\naig_fin_bmc: aigbmc bmc fin\naig_prove: aiger prove\naig_fin_prove: aiger prove fin\nabc_bmc: abcbmc bmc\nabc_fin_bmc: abcbmc bmc fin\nabc_prove: abc prove\nabc_fin_prove: abc prove fin\nabc_fail: abcfail prove fail\nabc_fin_fail: abcfail prove fail fin\n\n[options]\nbmc: mode bmc\ncover: mode cover\nprove: mode prove\nfin:\nexpect PASS,FAIL,UNKNOWN\ndepth 10\n--\n~fin:\nexpect TIMEOUT\ndepth 40000\ntimeout 1\n--\n\n[engines]\nbtor: btor btormc\npono: btor pono\nsmt: smtbmc bitwuzla\nsmtfail: smtbmc --keep-going bitwuzla\naigbmc: aiger aigbmc\naiger: aiger suprove\nabcbmc: abc bmc3\nabc: abc pdr\nabcfail: abc --keep-going pdr\n\n[script]\nfin: read -define WIDTH=4\n~fin: read -define WIDTH=8\nfail: read -define FAIL=1\nread -sv timeout.sv\nprep -top top\n\n[file timeout.sv]\nmodule top #(\n    parameter WIDTH = `WIDTH\n) (\n    input clk,\n    input load,\n    input [WIDTH-1:0] a,\n    input [WIDTH-1:0] b,\n    output reg [WIDTH-1:0] q,\n    output reg [WIDTH-1:0] r,\n    output reg done\n);\n\n    reg [WIDTH-1:0] a_reg = 0;\n    reg [WIDTH-1:0] b_reg = 1;\n\n    initial begin\n        q <= 0;\n        r <= 0;\n        done <= 1;\n    end\n\n    reg [WIDTH-1:0] q_step = 1;\n    reg [WIDTH-1:0] r_step = 1;\n\n    // This is not how you design a good divider circuit!\n    always @(posedge clk) begin\n        if (load) begin\n            a_reg <= a;\n            b_reg <= b;\n            q <= 0;\n            r <= a;\n            q_step <= 1;\n            r_step <= b;\n            done <= b == 0;\n        end else begin\n            if (r_step <= r) begin\n                q <= q + q_step;\n                r <= r - r_step;\n\n                if (!r_step[WIDTH-1]) begin\n                    r_step <= r_step << 1;\n                    q_step <= q_step << 1;\n                end\n            end else begin\n                if (!q_step[0]) begin\n                    r_step <= r_step >> 1;\n                    q_step <= q_step >> 1;\n                end else begin\n                    done <= 1;\n                end\n            end\n        end\n    end\n\n    always @(posedge clk) begin\n        assert (1); // trivial\n    `ifdef FAIL\n        assert (0);\n    `endif\n        assert (r_step == b_reg * q_step); // Helper invariant\n\n        assert (q * b_reg + r == a_reg); // Main invariant & correct output relationship\n        if (done) assert (r <= b_reg - 1); // Output range\n\n        cover (done);\n        cover (done && b_reg == 0);\n        cover (r != a_reg);\n        cover (r == a_reg);\n        cover (0); // unreachable\n    end\nendmodule\n"
  },
  {
    "path": "tests/statusdb/timeout.sh",
    "content": "#!/bin/bash\nset -e\npython3 $SBY_MAIN -f $SBY_FILE $TASK\n\nSTATUS_CSV=${WORKDIR}/status.csv\npython3 $SBY_MAIN -f $SBY_FILE $TASK --statusfmt csv --latest | tee $STATUS_CSV\n\nif [[ $TASK =~ \"_cover\" ]]; then\n    wc -l $STATUS_CSV | grep -q '6'\n    grep \"COVER\" $STATUS_CSV | wc -l | grep -q '5'\nelif [[ $TASK =~ \"_fail\" ]]; then\n    wc -l $STATUS_CSV | grep -q '6'\n    grep \"ASSERT\" $STATUS_CSV | wc -l | grep -q '5'\n    grep \"FAIL\" $STATUS_CSV | wc -l | grep -q '1'\nelse\n    wc -l $STATUS_CSV | grep -q '5'\n    grep \"ASSERT\" $STATUS_CSV | wc -l | grep -q '4'\nfi\n\nif [[ $TASK == \"smt_cover\" ]]; then\n    grep \"PASS\" $STATUS_CSV | wc -l | grep -q '4'\nfi\n"
  },
  {
    "path": "tests/unsorted/2props1trace.sby",
    "content": "[options]\nmode bmc\ndepth 1\nexpect fail\n\n[engines]\nsmtbmc\n\n[script]\nread -sv top.sv\nprep -top top\n\n[file top.sv]\nmodule top(\ninput foo,\ninput bar\n);\nalways @(*) begin\n\tassert (foo);\n\tassert (bar);\nend\nendmodule\n"
  },
  {
    "path": "tests/unsorted/Makefile",
    "content": "SUBDIR=unsorted\ninclude ../make/subdir.mk\n"
  },
  {
    "path": "tests/unsorted/allconst.sby",
    "content": "[tasks]\nyices\nz3\n\n[options]\nmode cover\ndepth 1\n\n[engines]\nyices: smtbmc --stbv yices\nz3: smtbmc --stdt z3\n\n[script]\nread -noverific\nread -formal primegen.sv\nprep -top primegen\n\n[file primegen.sv]\n\nmodule primegen;\n\t(* anyconst *) reg [9:0] prime;\n\t(* allconst *) reg [4:0] factor;\n\n\talways @* begin\n\t\tif (1 < factor && factor < prime)\n\t\t\tassume ((prime % factor) != 0);\n\t\tassume (prime > 800);\n\t\tcover (1);\n\tend\nendmodule\n"
  },
  {
    "path": "tests/unsorted/bmc_len.sby",
    "content": "[tasks]\nsmtbmc_pass: smtbmc pass\nsmtbmc_fail: smtbmc fail\naigbmc_pass: aigbmc pass\naigbmc_fail: aigbmc fail\nbtormc_pass: btormc pass\nbtormc_fail: btormc fail\nabc_pass: abc pass\nabc_fail: abc fail\n\n[options]\nmode bmc\npass: expect pass\nfail: expect fail\npass: depth 5\nfail: depth 6\n\n[engines]\nsmtbmc: smtbmc boolector\naigbmc: aiger aigbmc\nbtormc: btor btormc\nabc: abc bmc3\n\n[script]\nread -formal top.sv\nprep -top top\n\n[file top.sv]\nmodule top(input clk);\n    reg [7:0] counter = 0;\n\n    always @(posedge clk) begin\n        counter <= counter + 1;\n        assert (counter < 4);\n    end\nendmodule\n"
  },
  {
    "path": "tests/unsorted/both_ex.sby",
    "content": "[tasks]\nbtormc bmc\npono bmc\ncover\n\n[options]\nbmc: mode bmc\ncover: mode cover\ndepth 5\nexpect pass\n\n[engines]\nbtormc: btor btormc\npono: btor pono\ncover: btor btormc\n\n[script]\nread -sv both_ex.v\nprep -top test\n\n[files]\nboth_ex.v\n"
  },
  {
    "path": "tests/unsorted/both_ex.v",
    "content": "module test(\n  input clk,\n  input [7:0] data\n  );\n\nlocalparam MAX_COUNT = 8'd111;\nreg [7:0] count = 8'd0;\nreg [7:0] margin = MAX_COUNT;\n\nalways @ (posedge clk) begin\n  if (data > margin) begin\n    count <= 8'd0;\n    margin <= MAX_COUNT;\n  end else begin\n    count <= count + data;\n    margin <= margin - data;\n  end\n\n  assume (data < 8'd40);\n  assert (count <= MAX_COUNT);\n  cover (count == 8'd42);\n  cover (count == 8'd111);\nend\n\nendmodule\n"
  },
  {
    "path": "tests/unsorted/btor_meminit.sby",
    "content": "[tasks]\nbtormc\n#pono\nsmtbmc\n\n[options]\nmode bmc\nexpect fail\n\n[engines]\nbtormc: btor btormc\n# pono: btor pono\nsmtbmc: smtbmc\n\n[script]\nread -formal top.sv\nprep -top top -flatten\n\n[file top.sv]\n\nmodule top(input clk);\n\n    inner inner(clk);\n\nendmodule\n\nmodule inner(input clk);\n    reg [7:0] counter = 0;\n\n    reg [1:0] mem [0:255];\n\n    initial begin\n        mem[0] = 0;\n        mem[1] = 1;\n        mem[2] = 2;\n        mem[3] = 2;\n        mem[4] = 0;\n        mem[7] = 0;\n    end\n\n    always @(posedge clk) begin\n        counter <= counter + 1;\n        foo: assert (mem[counter] < 3);\n        bar: assume (counter < 7);\n\n        mem[counter] <= 0;\n    end\nendmodule\n"
  },
  {
    "path": "tests/unsorted/cover.sby",
    "content": "[options]\nmode cover\nexpect pass\n\n[engines]\nbtor btormc\n\n[script]\nread -formal cover.sv\nprep -top top\n\n[files]\ncover.sv\n"
  },
  {
    "path": "tests/unsorted/cover.sv",
    "content": "module top (\n\tinput clk,\n\tinput [7:0] din\n);\n\treg [31:0] state = 0;\n\n\talways @(posedge clk) begin\n\t\tstate <= ((state << 5) + state) ^ din;\n\tend\n\n`ifdef FORMAL\n\talways @(posedge clk) begin\n\t\tcover (state == 'd 12345678);\n\t\tcover (state == 'h 12345678);\n\tend\n`endif\nendmodule\n"
  },
  {
    "path": "tests/unsorted/cover_fail.sby",
    "content": "[options]\nmode cover\ndepth 5\nexpect pass,fail\n\n[engines]\nsmtbmc boolector\n\n[script]\nread -sv test.v\nprep -top test\n\n[file test.v]\nmodule test(\ninput clk,\ninput rst,\noutput reg [3:0] count\n);\n\ninitial assume (rst == 1'b1);\n\nalways @(posedge clk) begin\nif (rst)\n  count <= 4'b0;\nelse\n  count <= count + 1'b1;\n\ncover (count == 0 && !rst);\ncover (count == 4'd11 && !rst);\nend\nendmodule\n"
  },
  {
    "path": "tests/unsorted/cover_unreachable.sby",
    "content": "[tasks]\nbtormc\nsmtbmc\n\n[options]\nmode cover\nexpect fail\n\n[engines]\nbtormc: btor btormc\nsmtbmc: smtbmc\n\n[script]\nread -formal top.sv\nprep -top top -flatten\n\n[file top.sv]\n\nmodule top(input clk);\n\n    inner inner(clk);\n\nendmodule\n\nmodule inner(input clk);\n    reg [7:0] counter = 0;\n\n    always @(posedge clk) begin\n        counter <= counter == 4 ? 0 : counter + 1;\n\n        reachable: cover (counter == 3);\n        unreachable: cover (counter == 5);\n    end\nendmodule\n"
  },
  {
    "path": "tests/unsorted/demo.sby",
    "content": "[tasks]\nbtormc\npono\ncvc4\ncvc5\n\n[options]\nmode bmc\ndepth 100\nexpect fail\n\n[engines]\nbtormc: btor btormc\npono: btor pono\ncvc4: smtbmc cvc4\ncvc5: smtbmc cvc5\n\n[script]\nread -formal demo.sv\nprep -top demo\n\n[files]\ndemo.sv\n"
  },
  {
    "path": "tests/unsorted/demo.sv",
    "content": "module demo (\n  input clk,\n  output reg [5:0] counter\n);\n  initial counter = 0;\n\n  always @(posedge clk) begin\n    if (counter == 15)\n      counter <= 0;\n    else\n      counter <= counter + 1;\n  end\n\n`ifdef FORMAL\n  always @(posedge clk) begin\n    assert (counter < 7);\n  end\n`endif\nendmodule\n"
  },
  {
    "path": "tests/unsorted/floor_divmod.sby",
    "content": "[options]\nmode bmc\ndepth 1\n\n[engines]\nsmtbmc\n\n[script]\nread_verilog -icells -formal test.v\nprep -top top\n\n[file test.v]\nmodule top;\n\twire [7:0] a = $anyconst, b = $anyconst, fdiv, fmod, a2;\n\tassign a2 = b * fdiv + fmod;\n\n\t\\$divfloor #(\n\t\t.A_WIDTH(8),\n\t\t.B_WIDTH(8),\n\t\t.A_SIGNED(1),\n\t\t.B_SIGNED(1),\n\t\t.Y_WIDTH(8),\n\t) fdiv_m (\n\t\t.A(a),\n\t\t.B(b),\n\t\t.Y(fdiv)\n\t);\n\n\t\\$modfloor #(\n\t\t.A_WIDTH(8),\n\t\t.B_WIDTH(8),\n\t\t.A_SIGNED(1),\n\t\t.B_SIGNED(1),\n\t\t.Y_WIDTH(8),\n\t) fmod_m (\n\t\t.A(a),\n\t\t.B(b),\n\t\t.Y(fmod)\n\t);\n\n\talways @* begin\n\t\tassume(b != 0);\n\t\tassert(a == a2);\n\tend\nendmodule\n"
  },
  {
    "path": "tests/unsorted/memory.sby",
    "content": "[tasks]\nbtormc\npono\n\n[options]\nmode bmc\ndepth 10\nexpect fail\n\n[engines]\nbtormc: btor btormc\npono: btor pono\n\n[script]\nread -formal memory.sv\nprep -top testbench\n\n[files]\nmemory.sv\n"
  },
  {
    "path": "tests/unsorted/memory.sv",
    "content": "module testbench (\n  input clk, wen,\n  input [9:0] addr,\n  input [7:0] wdata,\n  output [7:0] rdata\n);\n  memory uut (\n    .clk  (clk  ),\n    .wen  (wen  ),\n    .addr (addr ),\n    .wdata(wdata),\n    .rdata(rdata)\n  );\n\n  (* anyconst *) reg [9:0] test_addr;\n  reg test_data_valid = 0;\n  reg [7:0] test_data;\n\n  always @(posedge clk) begin\n    if (addr == test_addr) begin\n      if (wen) begin\n        test_data <= wdata;\n\ttest_data_valid <= 1;\n      end\n      if (test_data_valid) begin\n        assert(test_data == rdata);\n      end\n    end\n  end\nendmodule\n\nmodule memory (\n  input clk, wen,\n  input [9:0] addr,\n  input [7:0] wdata,\n  output [7:0] rdata\n);\n  reg [7:0] bank0 [0:255];\n  reg [7:0] bank1 [0:255];\n  reg [7:0] bank2 [0:255];\n  reg [7:0] bank3 [0:255];\n\n  wire [1:0] mem_sel = addr[9:8];\n  wire [7:0] mem_addr = addr[7:0];\n\n  always @(posedge clk) begin\n    case (mem_sel)\n      0: if (wen) bank0[mem_addr] <= wdata;\n      1: if (wen) bank1[mem_addr] <= wdata;\n      2: if (wen) bank1[mem_addr] <= wdata;  // BUG: Should assign to bank2\n      3: if (wen) bank3[mem_addr] <= wdata;\n    endcase\n  end\n\n  assign rdata =\n    mem_sel == 0 ? bank0[mem_addr] :\n    mem_sel == 1 ? bank1[mem_addr] :\n    mem_sel == 2 ? bank2[mem_addr] :\n    mem_sel == 3 ? bank3[mem_addr] : 'bx;\nendmodule\n"
  },
  {
    "path": "tests/unsorted/mixed.sby",
    "content": "[tasks]\ncover\nbtormc bmc\npono bmc\n\n[options]\ncover: mode cover\nbmc: mode bmc\nbmc: depth 1\n\ncover: expect pass\n~cover: expect fail\n\n[engines]\ncover: btor btormc\nbtormc: btor btormc\npono: btor pono\n\n[script]\nread -formal mixed.v\nprep -top test\n\n[files]\nmixed.v\n"
  },
  {
    "path": "tests/unsorted/mixed.v",
    "content": "module test (input CP, CN, input A, B, output reg XP, XN);\n\treg [7:0] counter = 0;\n\talways @* begin\n\t\tassume (A || B);\n\t\tassume (!A || !B);\n\t\tassert (A == B);\n\t\tcover (counter == 3 && A);\n\t\tcover (counter == 3 && B);\n\tend\n\talways @(posedge CP)\n\t\tcounter <= counter + 1;\n\talways @(posedge CP)\n\t\tXP <= A;\n\talways @(negedge CN)\n\t\tXN <= B;\nendmodule\n"
  },
  {
    "path": "tests/unsorted/multi_assert.sby",
    "content": "[tasks]\nbtormc\npono\n\n[options]\nmode bmc\ndepth 5\nexpect fail\n\n[engines]\nbtormc: btor btormc\npono: btor pono\n\n[script]\nread -sv multi_assert.v\nprep -top test\n\n[file multi_assert.v]\nmodule test();\nalways @* begin\nassert (1);\nassert (0);\nend\nendmodule\n"
  },
  {
    "path": "tests/unsorted/no_props.sby",
    "content": "[tasks]\nabc prove\nabc_keepgoing prove\nbtor_cover cover\n\n# \"Error: Does not work for combinational networks.\"\nabc_bmc3 bmc error\n# \"Error: Only works for sequential networks.\"\nabc_sim3 bmc error\n# \"Property index 0 is greater than the number of properties in file model/design_btor_single.btor (0)\"\nbtor_pono bmc error\n\n[options]\nprove: mode prove\nbmc: mode bmc\ncover: mode cover\nerror: expect ERROR\n\n[engines]\nabc: abc pdr\nabc_keepgoing: abc --keep-going pdr\nbtor_pono: btor pono\nbtor_cover: btor btormc\nabc_bmc3: abc bmc3\nabc_sim3: abc sim3\n\n[script]\nread -sv test.sv\nprep -top top\n\n[file test.sv]\nmodule top(input i, output o);\n\nassign o = ~i;\n\nendmodule\n"
  },
  {
    "path": "tests/unsorted/no_vcd.sby",
    "content": "[tasks]\nsmtbmc mode_bmc\nbtor_bmc engine_btor mode_bmc\nbtor_cover engine_btor mode_cover\nabc mode_bmc\naiger engine_aiger mode_prove\n\n[options]\nmode_bmc: mode bmc\nmode_prove: mode prove\nmode_cover: mode cover\nvcd off\n~mode_cover: expect fail\n\n[engines]\nsmtbmc: smtbmc\nengine_btor: btor btormc\nabc: abc bmc3\naiger: aiger suprove\n\n[script]\nread_verilog -formal no_vcd.sv\nprep -top top\n\n[file no_vcd.sv]\nmodule top(input clk, input force);\n\nreg [4:0] counter = 0;\n\nalways @(posedge clk) begin\n    if (!counter[4] || force)\n        counter <= counter + 1;\n    assert (counter < 10);\n    cover (counter == 4);\nend\n\nendmodule\n"
  },
  {
    "path": "tests/unsorted/preunsat.sby",
    "content": "[tasks]\nbtormc\nyices\n\n[options]\nmode bmc\nyices: expect error\nbtormc: expect pass\n\n[engines]\nbtormc: btor btormc\nyices: smtbmc yices\n\n[script]\nread -sv test.sv\nprep -top test\n\n[file test.sv]\nmodule test(input foo);\nalways @* assume(0);\nalways @* assert(foo);\nendmodule\n"
  },
  {
    "path": "tests/unsorted/prv32fmcmp.sby",
    "content": "[tasks]\nbtormc\npono\n\n[options]\nmode bmc\nexpect fail\n\n[engines]\nbtormc: btor btormc\npono: btor pono\n\n[script]\nread -noverific\nread -sv picorv32.v\nread -sv prv32fmcmp.v\nprep -top prv32fmcmp\n\n[files]\n../../docs/examples/demos/picorv32.v\nprv32fmcmp.v\n"
  },
  {
    "path": "tests/unsorted/prv32fmcmp.v",
    "content": "module prv32fmcmp (\n\tinput         clock,\n\tinput         resetn,\n\toutput        mem_valid_a, mem_valid_b,\n\toutput        mem_instr_a, mem_instr_b,\n\tinput         mem_ready_a, mem_ready_b,\n\toutput [31:0] mem_addr_a, mem_addr_b,\n\toutput [31:0] mem_wdata_a, mem_wdata_b,\n\toutput [ 3:0] mem_wstrb_a, mem_wstrb_b,\n\tinput  [31:0] mem_rdata_a, mem_rdata_b\n);\n\tpicorv32 #(\n\t\t.REGS_INIT_ZERO(1),\n\t\t.COMPRESSED_ISA(1)\n\t) prv32_a (\n\t\t.clk       (clock      ),\n\t\t.resetn    (resetn     ),\n\t\t.mem_valid (mem_valid_a),\n\t\t.mem_instr (mem_instr_a),\n\t\t.mem_ready (mem_ready_a),\n\t\t.mem_addr  (mem_addr_a ),\n\t\t.mem_wdata (mem_wdata_a),\n\t\t.mem_wstrb (mem_wstrb_a),\n\t\t.mem_rdata (mem_rdata_a)\n\t);\n\n\tpicorv32 #(\n\t\t.REGS_INIT_ZERO(1),\n\t\t.COMPRESSED_ISA(1)\n\t) prv32_b (\n\t\t.clk       (clock      ),\n\t\t.resetn    (resetn     ),\n\t\t.mem_valid (mem_valid_b),\n\t\t.mem_instr (mem_instr_b),\n\t\t.mem_ready (mem_ready_b),\n\t\t.mem_addr  (mem_addr_b ),\n\t\t.mem_wdata (mem_wdata_b),\n\t\t.mem_wstrb (mem_wstrb_b),\n\t\t.mem_rdata (mem_rdata_b)\n\t);\n\n\treg [31:0] rom [0:255];\n\n\tinteger mem_access_cnt_a = 0;\n\tinteger mem_access_cnt_b = 0;\n\n\talways @* begin\n\t\tassume(resetn == !$initstate);\n\n\t\tif (resetn) begin\n\t\t\t// only consider programs without data memory read/write\n\t\t\tif (mem_valid_a) assume(mem_instr_a);\n\t\t\tif (mem_valid_b) assume(mem_instr_b);\n\n\t\t\t// when the access cnt matches, the addresses must match\n\t\t\tif (mem_valid_a && mem_valid_b && mem_access_cnt_a == mem_access_cnt_b) begin\n\t\t\t\tassert(mem_addr_a == mem_addr_b);\n\t\t\tend\n\n\t\t\t// hook up to memory\n\t\t\tassume(mem_rdata_a == rom[mem_addr_a[9:2]]);\n\t\t\tassume(mem_rdata_b == rom[mem_addr_b[9:2]]);\n\t\tend\n\n\t\t// it will pass when this is enabled\n\t\t//assume(mem_ready_a == mem_ready_b);\n\tend\n\n\talways @(posedge clock) begin\n\t\tmem_access_cnt_a <= mem_access_cnt_a + (resetn && mem_valid_a && mem_ready_a);\n\t\tmem_access_cnt_b <= mem_access_cnt_b + (resetn && mem_valid_b && mem_ready_b);\n\tend\nendmodule\n"
  },
  {
    "path": "tests/unsorted/redxor.sby",
    "content": "[options]\nmode cover\nexpect pass\n\n[engines]\nbtor btormc\n\n[script]\nread -formal redxor.v\nprep -top test\n\n[files]\nredxor.v\n"
  },
  {
    "path": "tests/unsorted/redxor.v",
    "content": "module test(input [7:0] I, output O);\nassign O = ^I;\n\nalways @(*) begin\ncover(O==1'b0);\ncover(O==1'b1);\nend\nendmodule\n"
  },
  {
    "path": "tests/unsorted/smtlib2_module.sby",
    "content": "[options]\nmode bmc\ndepth 1\n\n[engines]\nsmtbmc\n\n[script]\nread_verilog -formal test.v\nprep -top top\n\n[file test.v]\n(* blackbox *)\n(* smtlib2_module *)\nmodule submod(a, b);\n\tinput [7:0] a;\n\t(* smtlib2_comb_expr = \"(bvnot a)\" *)\n\toutput [7:0] b;\nendmodule\n\nmodule top;\n\twire [7:0] a = $anyconst, b;\n\n\tsubmod submod(\n\t\t.a(a),\n\t\t.b(b)\n\t);\n\n\talways @* begin\n\t\tassert(~a == b);\n\tend\nendmodule\n"
  },
  {
    "path": "tests/unsorted/stopfirst.sby",
    "content": "[options]\nmode bmc\nexpect fail\n\n[engines]\nbtor btormc\n\n[script]\nread -sv test.sv\nprep -top test\n\n[file test.sv]\nmodule test(input foo);\nalways @* assert(foo);\nalways @* assert(!foo);\nendmodule\n"
  },
  {
    "path": "tests/unsorted/submod_props.sby",
    "content": "[tasks]\nbmc\ncover\nflatten\n\n[options]\nbmc: mode bmc\ncover: mode cover\nflatten: mode bmc\n\nexpect fail\n\n[engines]\nsmtbmc boolector\n\n[script]\nread -sv test.sv\nprep -top top\nflatten: flatten\n\n[file test.sv]\nmodule test(input foo);\nalways @* assert(foo);\nalways @* assert(!foo);\nalways @* cover(foo);\nalways @* cover(!foo);\nendmodule\n\nmodule top();\ntest test_i (\n.foo(1'b1)\n);\nendmodule\n"
  },
  {
    "path": "tools/README.md",
    "content": "# SBY - Additional Tools\n\nThis directory contains various tools that can be used in conjunction with SBY.\n\n* [`aigcexmin`](./aigcexmin) Counter-example minimization of AIGER witness (.aiw) files\n* [`cexenum`](./cexenum) Enumeration of minimized counter-examples\n"
  },
  {
    "path": "tools/aigcexmin/.gitignore",
    "content": "/target\n"
  },
  {
    "path": "tools/aigcexmin/Cargo.toml",
    "content": "[package]\nname = \"aigcexmin\"\nversion = \"0.1.0\"\nedition = \"2021\"\nauthors = [\"Jannis Harder <jix@yosyshq.com> <me@jix.one>\"]\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[profile.release]\ndebug = true # profiling\n\n[dependencies]\nclap = { version = \"4.4.8\", features = [\"derive\", \"cargo\", \"wrap_help\"] }\ncolor-eyre = \"0.6.2\"\nflussab = \"0.3.1\"\nflussab-aiger = \"0.1.0\"\nzwohash = \"0.1.2\"\n"
  },
  {
    "path": "tools/aigcexmin/src/aig_eval.rs",
    "content": "use flussab_aiger::{aig::OrderedAig, Lit};\n\nuse crate::util::unpack_lit;\n\npub trait AigValue<Context>: Copy {\n    fn invert_if(self, en: bool, ctx: &mut Context) -> Self;\n    fn and(self, other: Self, ctx: &mut Context) -> Self;\n    fn constant(value: bool, ctx: &mut Context) -> Self;\n\n    fn invert(self, ctx: &mut Context) -> Self {\n        self.invert_if(true, ctx)\n    }\n\n    fn or(self, other: Self, ctx: &mut Context) -> Self {\n        let not_self = self.invert(ctx);\n        let not_other = other.invert(ctx);\n        let not_or = not_self.and(not_other, ctx);\n        not_or.invert(ctx)\n    }\n}\n\npub fn initial_frame<L, V, Context>(\n    aig: &OrderedAig<L>,\n    state: &mut Vec<V>,\n    mut latch_init: impl FnMut(usize, &mut Context) -> V,\n    mut input: impl FnMut(usize, &mut Context) -> V,\n    ctx: &mut Context,\n) where\n    L: Lit,\n    V: AigValue<Context>,\n{\n    state.clear();\n    state.push(V::constant(false, ctx));\n\n    for i in 0..aig.input_count {\n        state.push(input(i, ctx));\n    }\n\n    for i in 0..aig.latches.len() {\n        state.push(latch_init(i, ctx));\n    }\n\n    for and_gate in aig.and_gates.iter() {\n        let [a, b] = and_gate.inputs.map(|lit| {\n            let (var, polarity) = unpack_lit(lit);\n            state[var].invert_if(polarity, ctx)\n        });\n\n        state.push(a.and(b, ctx));\n    }\n}\n\npub fn successor_frame<L, V, Context>(\n    aig: &OrderedAig<L>,\n    state: &mut Vec<V>,\n    mut input: impl FnMut(usize, &mut Context) -> V,\n    ctx: &mut Context,\n) where\n    L: Lit,\n    V: AigValue<Context>,\n{\n    assert_eq!(state.len(), 1 + aig.max_var_index);\n\n    for i in 0..aig.input_count {\n        state.push(input(i, ctx));\n    }\n\n    for latch in aig.latches.iter() {\n        let (var, polarity) = unpack_lit(latch.next_state);\n        state.push(state[var].invert_if(polarity, ctx));\n    }\n\n    state.drain(1..1 + aig.max_var_index);\n\n    for and_gate in aig.and_gates.iter() {\n        let [a, b] = and_gate.inputs.map(|lit| {\n            let (var, polarity) = unpack_lit(lit);\n            state[var].invert_if(polarity, ctx)\n        });\n\n        state.push(a.and(b, ctx));\n    }\n}\n\nimpl AigValue<()> for bool {\n    fn invert_if(self, en: bool, _ctx: &mut ()) -> Self {\n        self ^ en\n    }\n\n    fn and(self, other: Self, _ctx: &mut ()) -> Self {\n        self & other\n    }\n\n    fn constant(value: bool, _ctx: &mut ()) -> Self {\n        value\n    }\n}\n\nimpl AigValue<()> for Option<bool> {\n    fn invert_if(self, en: bool, _ctx: &mut ()) -> Self {\n        self.map(|b| b ^ en)\n    }\n\n    fn and(self, other: Self, _ctx: &mut ()) -> Self {\n        match (self, other) {\n            (Some(true), Some(true)) => Some(true),\n            (Some(false), _) | (_, Some(false)) => Some(false),\n            _ => None,\n        }\n    }\n\n    fn constant(value: bool, _ctx: &mut ()) -> Self {\n        Some(value)\n    }\n}\n"
  },
  {
    "path": "tools/aigcexmin/src/care_graph.rs",
    "content": "use std::{\n    cmp::Reverse,\n    collections::{BTreeSet, BinaryHeap},\n    mem::{replace, take},\n    num::NonZeroU32,\n};\n\nuse color_eyre::eyre::bail;\nuse flussab::DeferredWriter;\nuse flussab_aiger::{aig::OrderedAig, Lit};\nuse zwohash::HashMap;\n\nuse crate::{\n    aig_eval::{initial_frame, successor_frame, AigValue},\n    util::{unpack_lit, write_output_bit},\n};\n\n#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]\n#[repr(transparent)]\npub struct NodeRef {\n    code: Reverse<NonZeroU32>,\n}\n\nimpl std::fmt::Debug for NodeRef {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        if self.is_const() {\n            if *self == Self::TRUE {\n                f.debug_struct(\"NodeRef::TRUE\").finish()\n            } else {\n                f.debug_struct(\"NodeRef::FALSE\").finish()\n            }\n        } else {\n            f.debug_tuple(\"NodeRef::new\").field(&self.index()).finish()\n        }\n    }\n}\n\nimpl NodeRef {\n    const INVALID_INDEX: usize = u32::MAX as usize;\n    const TRUE_INDEX: usize = Self::INVALID_INDEX - 1;\n    const FALSE_INDEX: usize = Self::INVALID_INDEX - 2;\n\n    pub const TRUE: Self = Self::new(Self::TRUE_INDEX);\n    pub const FALSE: Self = Self::new(Self::FALSE_INDEX);\n\n    pub const fn new(index: usize) -> Self {\n        assert!(index < u32::MAX as usize);\n        let Some(code) = NonZeroU32::new(!(index as u32)) else {\n            unreachable!();\n        };\n        Self {\n            code: Reverse(code),\n        }\n    }\n\n    pub fn index(self) -> usize {\n        !(self.code.0.get()) as usize\n    }\n\n    pub fn is_const(self) -> bool {\n        self == Self::TRUE || self == Self::FALSE\n    }\n}\n\n#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]\nenum Gate {\n    And,\n    Or,\n}\n\n#[derive(Debug)]\nenum NodeDef {\n    Gate([NodeRef; 2]),\n    Input(u32),\n}\n\nimpl NodeDef {\n    fn and(inputs: [NodeRef; 2]) -> Self {\n        assert!(inputs[0] < inputs[1]);\n        Self::Gate(inputs)\n    }\n\n    fn or(inputs: [NodeRef; 2]) -> Self {\n        assert!(inputs[0] < inputs[1]);\n        Self::Gate([inputs[1], inputs[0]])\n    }\n\n    fn input(id: u32) -> Self {\n        Self::Input(id)\n    }\n\n    fn as_gate(&self) -> Result<(Gate, [NodeRef; 2]), u32> {\n        match *self {\n            NodeDef::Gate(inputs) => {\n                if inputs[0] < inputs[1] {\n                    Ok((Gate::And, inputs))\n                } else {\n                    Ok((Gate::Or, [inputs[1], inputs[0]]))\n                }\n            }\n            NodeDef::Input(input) => Err(input),\n        }\n    }\n}\n\n#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Debug)]\nenum NodeState {\n    #[default]\n    Unknown,\n    Nonselected,\n    Selected,\n    Required,\n}\n\n#[derive(Debug)]\nstruct Node {\n    def: NodeDef,\n    priority: u32,\n    state: NodeState,\n    renamed: Option<NodeRef>,\n}\n\nimpl Node {\n    fn update_state(&mut self, state: NodeState) -> NodeState {\n        let old_state = self.state;\n        self.state = self.state.max(state);\n        old_state\n    }\n}\n\n#[derive(Default)]\npub struct AndOrGraph {\n    find_input: HashMap<u32, NodeRef>,\n    find_and: HashMap<[NodeRef; 2], NodeRef>,\n    find_or: HashMap<[NodeRef; 2], NodeRef>,\n\n    // find_renamed: HashMap<NodeRef, NodeRef>,\n    nodes: Vec<Node>,\n    queue: BinaryHeap<NodeRef>,\n    stack: Vec<NodeRef>,\n\n    unknown_inputs: BTreeSet<u32>,\n    required_inputs: BTreeSet<u32>,\n    active_node_count: usize,\n\n    input_order: Vec<(NodeRef, u32)>,\n    cache: bool,\n}\n\nimpl AndOrGraph {\n    pub fn input(&mut self, id: u32) -> NodeRef {\n        assert!(id <= u32::MAX - 2);\n        *self.find_input.entry(id).or_insert_with(|| {\n            let node_ref = NodeRef::new(self.nodes.len());\n            let node = Node {\n                def: NodeDef::input(id),\n                priority: id,\n                state: NodeState::Unknown,\n                renamed: None,\n            };\n            self.nodes.push(node);\n            self.unknown_inputs.insert(id);\n            node_ref\n        })\n    }\n\n    pub fn and(&mut self, mut inputs: [NodeRef; 2]) -> NodeRef {\n        inputs.sort_unstable();\n        if inputs[1] == NodeRef::FALSE {\n            NodeRef::FALSE\n        } else if inputs[1] == NodeRef::TRUE || inputs[1] == inputs[0] {\n            inputs[0]\n        } else {\n            let [a, b] = inputs;\n            match inputs.map(|input| self.nodes[input.index()].def.as_gate()) {\n                [Ok((Gate::And, [a0, a1])), _] if b == a0 || b == a1 => {\n                    return a;\n                }\n                [_, Ok((Gate::And, [b0, b1]))] if a == b0 || a == b1 => {\n                    return b;\n                }\n\n                [Ok((Gate::Or, [a0, a1])), _] if b == a0 || b == a1 => {\n                    return b;\n                }\n                [_, Ok((Gate::Or, [b0, b1]))] if a == b0 || a == b1 => {\n                    return a;\n                }\n\n                _ => (),\n            }\n\n            let mut mknode = || {\n                let node_ref = NodeRef::new(self.nodes.len());\n\n                let [a, b] = inputs.map(|input| self.nodes[input.index()].priority);\n\n                let node = Node {\n                    def: NodeDef::and(inputs),\n                    priority: a.min(b),\n                    state: NodeState::Unknown,\n                    renamed: None,\n                };\n                self.nodes.push(node);\n                node_ref\n            };\n\n            if self.cache {\n                *self.find_and.entry(inputs).or_insert_with(mknode)\n            } else {\n                mknode()\n            }\n        }\n    }\n\n    pub fn or(&mut self, mut inputs: [NodeRef; 2]) -> NodeRef {\n        inputs.sort_unstable();\n\n        if inputs[1] == NodeRef::TRUE {\n            NodeRef::TRUE\n        } else if inputs[1] == NodeRef::FALSE || inputs[1] == inputs[0] {\n            inputs[0]\n        } else {\n            let [a, b] = inputs;\n            match inputs.map(|input| self.nodes[input.index()].def.as_gate()) {\n                [Ok((Gate::Or, [a0, a1])), _] if b == a0 || b == a1 => {\n                    return a;\n                }\n                [_, Ok((Gate::Or, [b0, b1]))] if a == b0 || a == b1 => {\n                    return b;\n                }\n\n                [Ok((Gate::And, [a0, a1])), _] if b == a0 || b == a1 => {\n                    return b;\n                }\n                [_, Ok((Gate::And, [b0, b1]))] if a == b0 || a == b1 => {\n                    return a;\n                }\n\n                _ => (),\n            }\n\n            let mut mknode = || {\n                let node_ref = NodeRef::new(self.nodes.len());\n\n                let [a, b] = inputs.map(|input| self.nodes[input.index()].priority);\n\n                let node = Node {\n                    def: NodeDef::or(inputs),\n                    priority: a.max(b),\n                    state: NodeState::Unknown,\n                    renamed: None,\n                };\n                self.nodes.push(node);\n                node_ref\n            };\n\n            if self.cache {\n                *self.find_or.entry(inputs).or_insert_with(mknode)\n            } else {\n                mknode()\n            }\n        }\n    }\n\n    pub fn pass(&mut self, target: NodeRef, shuffle: usize, mut enable_cache: bool) -> NodeRef {\n        if self.cache {\n            enable_cache = false;\n        }\n\n        self.nodes[target.index()].state = NodeState::Required;\n        self.queue.push(target);\n\n        let target_priority = self.nodes[target.index()].priority;\n\n        'queue: while let Some(current) = self.queue.pop() {\n            let node = &self.nodes[current.index()];\n            let state = node.state;\n\n            self.stack.push(current);\n\n            match node.def.as_gate() {\n                Ok((Gate::And, inputs)) => {\n                    if enable_cache {\n                        self.find_and.insert(inputs, current);\n                    }\n                    for input in inputs {\n                        let node = &mut self.nodes[input.index()];\n                        if node.update_state(state) == NodeState::Unknown {\n                            self.queue.push(input);\n                        }\n                    }\n                }\n                Ok((Gate::Or, inputs)) => {\n                    if enable_cache {\n                        self.find_or.insert(inputs, current);\n                    }\n                    for input in inputs {\n                        let node = &mut self.nodes[input.index()];\n                        if node.update_state(NodeState::Nonselected) == NodeState::Unknown {\n                            self.queue.push(input);\n                        }\n                    }\n\n                    if state <= NodeState::Nonselected {\n                        continue;\n                    }\n\n                    let input_priorities = inputs.map(|input| self.nodes[input.index()].priority);\n\n                    for (i, input_priority) in input_priorities.into_iter().enumerate() {\n                        if input_priority < target_priority {\n                            // The other input will be false, so propagate the state\n                            self.nodes[inputs[i ^ 1].index()].update_state(state);\n                            continue;\n                        }\n                    }\n\n                    for input in inputs {\n                        let input_state = self.nodes[input.index()].state;\n                        if input_state >= NodeState::Selected {\n                            // One input of the or is already marked, no need to mark the other\n                            continue 'queue;\n                        }\n                    }\n\n                    // Mark the highest priority input\n                    let input = inputs[(input_priorities[1] > input_priorities[0]) as usize];\n                    self.nodes[input.index()].update_state(NodeState::Selected);\n                }\n                Err(_input) => (),\n            }\n        }\n\n        if enable_cache {\n            self.cache = true;\n        }\n\n        let mut stack = take(&mut self.stack);\n\n        self.active_node_count = stack.len();\n\n        self.unknown_inputs.clear();\n\n        for current in stack.drain(..).rev() {\n            let node = &mut self.nodes[current.index()];\n            let state = replace(&mut node.state, NodeState::Unknown);\n            let priority = node.priority;\n\n            match node.def.as_gate() {\n                Ok((gate, inputs)) => {\n                    let new_inputs = inputs.map(|input| self.nodes[input.index()].renamed.unwrap());\n\n                    let output = if new_inputs == inputs {\n                        current\n                    } else {\n                        match gate {\n                            Gate::And => self.and(new_inputs),\n                            Gate::Or => self.or(new_inputs),\n                        }\n                    };\n\n                    if (shuffle > 0 || new_inputs != inputs)\n                        && output != NodeRef::FALSE\n                        && output != NodeRef::TRUE\n                    {\n                        if let Ok((gate, inputs)) = self.nodes[output.index()].def.as_gate() {\n                            let [a, b] = inputs.map(|input| self.nodes[input.index()].priority);\n\n                            self.nodes[output.index()].priority = match gate {\n                                Gate::And => a.min(b),\n                                Gate::Or => a.max(b),\n                            };\n                        }\n                    }\n\n                    self.nodes[current.index()].renamed = Some(output);\n                }\n                Err(input) => match priority.cmp(&target_priority) {\n                    std::cmp::Ordering::Less => {\n                        self.nodes[current.index()].renamed = Some(NodeRef::FALSE);\n                    }\n                    std::cmp::Ordering::Equal => {\n                        self.required_inputs.insert(input);\n                        self.nodes[current.index()].renamed = Some(NodeRef::TRUE);\n                    }\n                    std::cmp::Ordering::Greater => match state {\n                        NodeState::Required => {\n                            self.required_inputs.insert(input);\n                            self.nodes[current.index()].renamed = Some(NodeRef::TRUE);\n                        }\n                        NodeState::Selected => {\n                            self.unknown_inputs.insert(input);\n                            self.nodes[current.index()].renamed = Some(current);\n\n                            if shuffle > 0 {\n                                let priority = &mut self.nodes[current.index()].priority;\n                                let mask = !(u64::MAX << 32.min(shuffle - 1)) as u32;\n\n                                *priority ^=\n                                    !(*priority ^ priority.wrapping_mul(0x2c9277b5)) & mask;\n                            }\n                        }\n                        NodeState::Nonselected => {\n                            self.nodes[current.index()].renamed = Some(NodeRef::FALSE);\n                        }\n                        NodeState::Unknown => {\n                            unreachable!();\n                        }\n                    },\n                },\n            }\n        }\n\n        self.input_order.clear();\n\n        let result = self.nodes[target.index()].renamed.unwrap();\n        self.stack = stack;\n\n        result\n    }\n}\n\nimpl AigValue<AndOrGraph> for (Option<bool>, NodeRef) {\n    fn invert_if(self, en: bool, _: &mut AndOrGraph) -> Self {\n        let (value, care) = self;\n        (value.map(|b| b ^ en), care)\n    }\n\n    fn and(self, other: Self, ctx: &mut AndOrGraph) -> Self {\n        let (value_a, care_a) = self;\n        let (value_b, care_b) = other;\n\n        match (value_a, value_b) {\n            (Some(true), Some(true)) => (Some(true), ctx.and([care_a, care_b])),\n            (Some(false), Some(false)) => (Some(false), ctx.or([care_a, care_b])),\n            (Some(false), _) => (Some(false), care_a),\n            (_, Some(false)) => (Some(false), care_b),\n            _ => (None, NodeRef::FALSE),\n        }\n    }\n\n    fn constant(value: bool, _: &mut AndOrGraph) -> Self {\n        (Some(value), NodeRef::TRUE)\n    }\n}\n\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\npub enum Verification {\n    Cex,\n    Full,\n}\n\npub struct MinimizationOptions {\n    pub fixed_init: bool,\n    pub satisfy_assumptions: bool,\n    pub verify: Option<Verification>,\n}\n\npub fn minimize<L: Lit>(\n    aig: &OrderedAig<L>,\n    latch_init: &[Option<bool>],\n    frame_inputs: &[Vec<Option<bool>>],\n    writer: &mut DeferredWriter,\n    options: &MinimizationOptions,\n) -> color_eyre::Result<()> {\n    let Some(initial_inputs) = frame_inputs.first() else {\n        bail!(\"no inputs found\");\n    };\n\n    let mut state = vec![];\n\n    let mut graph = AndOrGraph::default();\n\n    let input_id = |frame: Option<usize>, index: usize| -> u32 {\n        (if let Some(frame) = frame {\n            latch_init.len() + frame * initial_inputs.len() + index\n        } else {\n            index\n        })\n        .try_into()\n        .unwrap()\n    };\n\n    let decode_input_id = |id: u32| -> (Option<usize>, usize) {\n        let id = id as usize;\n        if id < latch_init.len() {\n            (None, id)\n        } else {\n            let id = id - latch_init.len();\n            let frame = id / initial_inputs.len();\n            let index = id % initial_inputs.len();\n            (Some(frame), index)\n        }\n    };\n\n    initial_frame(\n        aig,\n        &mut state,\n        |i, ctx| {\n            (\n                latch_init[i],\n                if latch_init[i].is_some() {\n                    if options.fixed_init {\n                        NodeRef::TRUE\n                    } else {\n                        ctx.input(input_id(None, i))\n                    }\n                } else {\n                    NodeRef::FALSE\n                },\n            )\n        },\n        |i, ctx| {\n            (\n                initial_inputs[i],\n                if initial_inputs[i].is_some() {\n                    ctx.input(input_id(Some(0), i))\n                } else {\n                    NodeRef::FALSE\n                },\n            )\n        },\n        &mut graph,\n    );\n\n    let mut minimization_target = 'minimization_target: {\n        let mut invariant_failed = (Some(false), NodeRef::TRUE);\n        for (t, inputs) in frame_inputs.iter().enumerate() {\n            if t > 0 {\n                successor_frame(\n                    aig,\n                    &mut state,\n                    |i, ctx| {\n                        (\n                            inputs[i],\n                            if inputs[i].is_some() {\n                                ctx.input(input_id(Some(t), i))\n                            } else {\n                                NodeRef::FALSE\n                            },\n                        )\n                    },\n                    &mut graph,\n                );\n            }\n\n            if options.satisfy_assumptions {\n                for invariant in aig.invariant_constraints.iter() {\n                    let (var, polarity) = unpack_lit(*invariant);\n                    let inv_invariant = state[var].invert_if(!polarity, &mut graph);\n\n                    invariant_failed = invariant_failed.or(inv_invariant, &mut graph);\n                }\n            }\n\n            let mut good_state = (Some(true), NodeRef::TRUE);\n\n            for (i, bad) in aig.bad_state_properties.iter().enumerate() {\n                let (var, polarity) = unpack_lit(*bad);\n                let inv_bad = state[var].invert_if(!polarity, &mut graph);\n\n                if inv_bad.0 == Some(false) && invariant_failed.0 == Some(false) {\n                    println!(\"bad state property {i} active in frame {t}\");\n                }\n\n                good_state = good_state.and(inv_bad, &mut graph);\n            }\n            good_state = good_state.or(invariant_failed, &mut graph);\n            if good_state.0 == Some(false) {\n                println!(\"bad state found in frame {t}\");\n\n                break 'minimization_target good_state.1;\n            }\n\n            if t > 0 && t % 500 == 0 {\n                println!(\n                    \"traced frame {t}/{frames}: node count = {node_count}\",\n                    frames = frame_inputs.len(),\n                    node_count = graph.nodes.len(),\n                );\n            }\n        }\n\n        bail!(\"no bad state found\");\n    };\n\n    let node_count_width = (graph.nodes.len().max(2) - 1).ilog10() as usize + 1;\n    let input_count_width = (graph.unknown_inputs.len().max(2) - 1).ilog10() as usize + 1;\n\n    println!(\n        \"input: node count = {node_count:w0$}, defined inputs = {defined_inputs:w1$}\",\n        node_count = graph.nodes.len(),\n        defined_inputs = graph.unknown_inputs.len(),\n        w0 = node_count_width,\n        w1 = input_count_width,\n    );\n\n    let mut shuffle = 0;\n\n    let mut iteration = 0;\n\n    while minimization_target != NodeRef::TRUE {\n        let prev_unknown_inputs = graph.unknown_inputs.len();\n        minimization_target = graph.pass(minimization_target, shuffle, iteration >= 1);\n        let unknown_inputs = graph.unknown_inputs.len();\n        let required_inputs = graph.required_inputs.len();\n        println!(\n            concat!(\n                \"iter:  node count = {node_count:w0$}, defined inputs = {defined_inputs:w1$}, \",\n                \"required inputs = {required_inputs:w1$}, shuffle = {shuffle}\"\n            ),\n            node_count = graph.active_node_count,\n            required_inputs = required_inputs,\n            defined_inputs = unknown_inputs + required_inputs,\n            shuffle = shuffle,\n            w0 = node_count_width,\n            w1 = input_count_width,\n        );\n\n        if unknown_inputs + (unknown_inputs / 4) < prev_unknown_inputs {\n            shuffle = 0;\n        } else {\n            shuffle += 1;\n        }\n        iteration += 1;\n    }\n\n    println!(\"minimization complete\");\n\n    for i in 0..aig.latches.len() {\n        let bit = if options.fixed_init || graph.required_inputs.contains(&input_id(None, i)) {\n            latch_init[i]\n        } else {\n            None\n        };\n\n        write_output_bit(writer, bit);\n    }\n\n    writer.write_all_defer_err(b\"\\n\");\n\n    for (t, inputs) in frame_inputs.iter().enumerate() {\n        for i in 0..aig.input_count {\n            let bit = if graph.required_inputs.contains(&input_id(Some(t), i)) {\n                inputs[i]\n            } else {\n                None\n            };\n\n            write_output_bit(writer, bit);\n        }\n        writer.write_all_defer_err(b\"\\n\");\n    }\n\n    writer.write_all_defer_err(b\"# DONE\\n\");\n    writer.flush_defer_err();\n    writer.check_io_error()?;\n\n    let Some(verify) = options.verify else {\n        return Ok(());\n    };\n\n    let mut check_state: Vec<Option<bool>> = vec![];\n\n    let empty_set = BTreeSet::new();\n\n    let verify_from = match verify {\n        Verification::Cex => &empty_set,\n        Verification::Full => &graph.required_inputs,\n    };\n\n    for check in [None]\n        .into_iter()\n        .chain(verify_from.iter().copied().map(Some))\n    {\n        check_state.clear();\n\n        initial_frame(\n            aig,\n            &mut check_state,\n            |i, _| {\n                let input = input_id(None, i);\n                if options.fixed_init\n                    || (Some(input) != check && graph.required_inputs.contains(&input))\n                {\n                    latch_init[i]\n                } else {\n                    None\n                }\n            },\n            |i, _| {\n                let input = input_id(Some(0), i);\n                if Some(input) != check && graph.required_inputs.contains(&input) {\n                    initial_inputs[i]\n                } else {\n                    None\n                }\n            },\n            &mut (),\n        );\n\n        let mut bad_state = false;\n\n        'frame: for (t, inputs) in frame_inputs.iter().enumerate() {\n            if t > 0 {\n                successor_frame(\n                    aig,\n                    &mut check_state,\n                    |i, _| {\n                        let input = input_id(Some(t), i);\n                        if Some(input) != check && graph.required_inputs.contains(&input) {\n                            inputs[i]\n                        } else {\n                            None\n                        }\n                    },\n                    &mut (),\n                );\n            }\n\n            if options.satisfy_assumptions {\n                for invariant in aig.invariant_constraints.iter() {\n                    let (var, polarity) = unpack_lit(*invariant);\n                    let invariant_output = check_state[var].invert_if(polarity, &mut ());\n                    if invariant_output != Some(true) {\n                        break 'frame;\n                    }\n                }\n            }\n\n            for bad in aig.bad_state_properties.iter() {\n                let (var, polarity) = unpack_lit(*bad);\n                let bad_output = check_state[var].invert_if(polarity, &mut ());\n                if bad_output == Some(true) {\n                    bad_state = true;\n                    break 'frame;\n                }\n            }\n        }\n\n        if bad_state != check.is_none() {\n            if let Some(check) = check {\n                let (frame, input) = decode_input_id(check);\n                if let Some(frame) = frame {\n                    bail!(\"minimality verification wrt. frame {frame} input {input} failed\");\n                } else {\n                    bail!(\"minimality verification wrt. initial latch {input} failed\");\n                }\n            } else {\n                bail!(\"counter example verification failed\");\n            }\n        }\n\n        if let Some(check) = check {\n            let (frame, input) = decode_input_id(check);\n            if let Some(frame) = frame {\n                println!(\"verified minimality wrt. frame {frame} input {input}\");\n            } else {\n                println!(\"verified minimality wrt. initial latch {input}\");\n            }\n        } else {\n            println!(\"verified counter example\");\n        }\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "tools/aigcexmin/src/main.rs",
    "content": "#![allow(clippy::needless_range_loop)]\n\nuse std::{fs, mem::replace, path::PathBuf};\n\nuse clap::{Parser, ValueEnum};\nuse color_eyre::eyre::bail;\n\nuse flussab_aiger::binary;\n\npub mod aig_eval;\npub mod care_graph;\npub mod util;\n\n/// AIG counter example minimization\n#[derive(clap::Parser)]\n#[command(author, version, about, long_about = None, help_template=\"\\\n{before-help}{name} {version}\n{author-with-newline}{about-with-newline}\n{usage-heading} {usage}\n\n{all-args}{after-help}\n\")]\npub struct Options {\n    /// Input AIGER file\n    aig: PathBuf,\n    /// Input AIGER witness file\n    witness: PathBuf,\n    /// Output AIGER witness file\n    output: PathBuf,\n\n    /// Verify the minimized counter example\n    #[clap(long, default_value = \"cex\")]\n    verify: VerificationOption,\n\n    /// Minimize latch initialization values\n    ///\n    /// Without this option the latch initialization values of the witness file are assumed to be\n    /// fixed and will remain as-is in the minimized witness file.\n    ///\n    /// Note that some tools (including the current Yosys/SBY flow) do not use AIGER native latch\n    /// initialization but instead perform initialization using inputs in the first frame.\n    #[clap(long)]\n    latches: bool,\n\n    /// Require assumptions to stay satisfied during minimization\n    #[clap(long)]\n    satisfy_assumptions: bool\n}\n\n#[derive(Copy, Clone, ValueEnum)]\nenum VerificationOption {\n    /// Skip verification\n    Off,\n    /// Verify the counter example\n    Cex,\n    /// Verify the counter example and that it is minimal (expensive)\n    Full,\n}\n\nfn main() -> color_eyre::Result<()> {\n    let options = Options::parse();\n\n    color_eyre::install()?;\n\n    let file_input = fs::File::open(options.aig)?;\n    let file_witness = fs::File::open(options.witness)?;\n    let file_output = fs::File::create(options.output)?;\n\n    let mut writer_output = flussab::DeferredWriter::from_write(file_output);\n\n    let mut read_witness_owned = flussab::DeferredReader::from_read(file_witness);\n    let read_witness = &mut read_witness_owned;\n\n    let aig_reader = binary::Parser::<u32>::from_read(file_input, binary::Config::default())?;\n\n    let aig = aig_reader.parse()?;\n\n    let mut offset = 0;\n    offset = flussab::text::next_newline(read_witness, offset);\n\n    if offset == 2 {\n        read_witness.advance(replace(&mut offset, 0));\n        offset = flussab::text::next_newline(read_witness, offset);\n        read_witness.advance(replace(&mut offset, 0));\n\n        offset = flussab::text::next_newline(read_witness, offset);\n    }\n\n    if offset != aig.latches.len() + 1 {\n        bail!(\n            \"unexpected number of initial latch states, found {} expected {}\",\n            offset.saturating_sub(1),\n            aig.latches.len()\n        );\n    }\n\n    let latch_init = read_witness.buf()[..aig.latches.len()]\n        .iter()\n        .copied()\n        .map(util::parse_input_bit)\n        .collect::<Result<Vec<_>, _>>()?;\n\n    read_witness.advance(replace(&mut offset, 0));\n\n    let mut frame_inputs = vec![];\n\n    loop {\n        offset = flussab::text::next_newline(read_witness, offset);\n\n        if matches!(read_witness.buf().first(), None | Some(b'.') | Some(b'#')) {\n            read_witness.check_io_error()?;\n            break;\n        }\n\n        if offset != aig.input_count + 1 {\n            bail!(\n                \"unexpected number of input bits, found {} expected {}\",\n                offset.saturating_sub(1),\n                aig.input_count\n            );\n        }\n\n        frame_inputs.push(\n            read_witness.buf()[..aig.input_count]\n                .iter()\n                .copied()\n                .map(util::parse_input_bit)\n                .collect::<Result<Vec<_>, _>>()?,\n        );\n        read_witness.advance(replace(&mut offset, 0));\n    }\n\n    care_graph::minimize(\n        &aig,\n        &latch_init,\n        &frame_inputs,\n        &mut writer_output,\n        &care_graph::MinimizationOptions {\n            fixed_init: !options.latches,\n            satisfy_assumptions: options.satisfy_assumptions,\n            verify: match options.verify {\n                VerificationOption::Off => None,\n                VerificationOption::Cex => Some(care_graph::Verification::Cex),\n                VerificationOption::Full => Some(care_graph::Verification::Full),\n            },\n        },\n    )?;\n\n    Ok(())\n}\n"
  },
  {
    "path": "tools/aigcexmin/src/util.rs",
    "content": "use color_eyre::eyre::bail;\nuse flussab::DeferredWriter;\nuse flussab_aiger::Lit;\n\npub fn unpack_lit<L: Lit>(lit: L) -> (usize, bool) {\n    let lit = lit.code();\n    (lit >> 1, lit & 1 != 0)\n}\n\npub fn parse_input_bit(byte: u8) -> color_eyre::Result<Option<bool>> {\n    Ok(match byte {\n        b'0' => Some(false),\n        b'1' => Some(true),\n        b'x' => None,\n        _ => bail!(\"unexpected input bit {byte:?}\"),\n    })\n}\n\npub fn write_output_bit(writer: &mut DeferredWriter, bit: Option<bool>) {\n    writer.write_all_defer_err(match bit {\n        Some(false) => b\"0\",\n        Some(true) => b\"1\",\n        None => b\"x\",\n    })\n}\n"
  },
  {
    "path": "tools/cexenum/cexenum.py",
    "content": "#!/usr/bin/env tabbypy3\nfrom __future__ import annotations\nimport asyncio\n\nimport json\nimport threading\nimport traceback\nimport argparse\nimport shutil\nimport shlex\nimport os\nimport sys\nimport urllib.parse\nimport random\nfrom collections import Counter, defaultdict, deque\nfrom pathlib import Path\nfrom typing import Any, Awaitable, Iterable, Literal\n\ntry:\n    import readline  # type: ignore # noqa\nexcept ImportError:\n    pass\n\n\nlibexec = Path(__file__).parent.resolve() / \"libexec\"\n\nif libexec.exists():\n    os.environb[b\"PATH\"] = bytes(libexec) + b\":\" + os.environb[b\"PATH\"]\n    sys.path.insert(0, str(libexec))\n\nif True:\n    import yosys_mau.task_loop.job_server as job\n    from yosys_mau import task_loop as tl\n    from yosys_mau.stable_set import StableSet\n\n\ndef safe_smtlib_id(name: str) -> str:\n    return f\"|{urllib.parse.quote_plus(name).replace('+', ' ')}|\"\n\n\ndef arg_parser():\n    parser = argparse.ArgumentParser(\n        prog=\"cexenum\", usage=\"%(prog)s [options] <sby_workdir>\"\n    )\n\n    parser.add_argument(\n        \"work_dir\",\n        metavar=\"<workdir>\",\n        help=\"existing SBY work directory\",\n        type=Path,\n    )\n\n    parser.add_argument(\n        \"--depth\",\n        type=int,\n        metavar=\"N\",\n        help=\"BMC depth for the initial assertion failure (default: %(default)s)\",\n        default=100,\n    )\n\n    parser.add_argument(\n        \"--enum-depth\",\n        type=int,\n        metavar=\"N\",\n        help=\"number of time steps to run enumeration for, starting with\"\n        \" and including the time step of the first assertion failure\"\n        \" (default: %(default)s)\",\n        default=10,\n    )\n\n    parser.add_argument(\n        \"--no-sim\",\n        dest=\"sim\",\n        action=\"store_false\",\n        help=\"do not run sim to obtain .fst traces for the enumerated counter examples\",\n    )\n\n    parser.add_argument(\n        \"--smtbmc-options\",\n        metavar='\"...\"',\n        type=shlex.split,\n        help='command line options to pass to smtbmc (default: \"%(default)s\")',\n        default=\"-s yices --unroll\",\n    )\n\n    parser.add_argument(\n        \"--callback\",\n        metavar='\"...\"',\n        type=shlex.split,\n        help=\"command that will receive enumerated traces on stdin and can control \"\n        \"the enumeration via stdout (pass '-' to handle callbacks interactively)\",\n        default=\"\",\n    )\n\n    parser.add_argument(\n        \"--track-assumes\",\n        help=\"track individual assumptions and in case the assumptions cannot be \"\n        \"satisfied, report a subset of used assumptions that are in conflict\",\n        action=\"store_true\",\n    )\n    parser.add_argument(\n        \"--minimize-assumes\",\n        help=\"when using --track-assumes, solve for a minimal set of sufficient \"\n        \"assumptions\",\n        action=\"store_true\",\n    )\n    parser.add_argument(\n        \"--satisfy-assumes\",\n        help=\"when minimizing counter examples, keep design assumptions satisfied\",\n        action=\"store_true\",\n    )\n    parser.add_argument(\n        \"--diversify\",\n        help=\"produce a more diverse set of counter examples early on\",\n        action=\"store_true\",\n    )\n\n    parser.add_argument(\n        \"--diversify-seed\",\n        metavar=\"<N>\",\n        help=\"random seed used for --diversify\",\n        type=int,\n        default=1,\n    )\n    parser.add_argument(\n        \"--diversify-depth\",\n        help=\"stop diversification after enumerating the given number of traces\",\n        type=int,\n    )\n\n    parser.add_argument(\n        \"--max-per-step\",\n        metavar=\"<N>\",\n        help=\"advance to the next step after enumerating N traces\",\n        type=int,\n    )\n\n    parser.add_argument(\"--debug\", action=\"store_true\", help=\"enable debug logging\")\n    parser.add_argument(\n        \"--debug-events\", action=\"store_true\", help=\"enable debug event logging\"\n    )\n\n    parser.add_argument(\n        \"-j\",\n        metavar=\"<N>\",\n        type=int,\n        dest=\"jobs\",\n        help=\"maximum number of processes to run in parallel\",\n        default=None,\n    )\n\n    return parser\n\n\ndef lines(*args: Any):\n    return \"\".join(f\"{line}\\n\" for line in args)\n\n\n@tl.task_context\nclass App:\n    raw_args: argparse.Namespace\n\n    debug: bool = False\n    debug_events: bool = False\n\n    track_assumes: bool = False\n    minimize_assumes: bool = False\n    satisfy_assumes: bool = False\n\n    depth: int\n    enum_depth: int\n    sim: bool\n    diversify: bool\n    diversify_seed: int\n    diversify_depth: int | None = None\n    max_per_step: int = 0\n\n    callback: list[str]\n\n    smtbmc_options: list[str]\n\n    work_dir: Path\n\n    work_subdir: Path\n    trace_dir_full: Path\n    trace_dir_min: Path\n    cache_dir: Path\n\n\ndef main() -> None:\n    args = arg_parser().parse_args()\n\n    job.global_client(args.jobs)\n\n    # Move command line arguments into the App context\n    for name in dir(args):\n        if name in type(App).__mro__[1].__annotations__:\n            setattr(App, name, getattr(args, name))\n\n    App.raw_args = args\n\n    try:\n        tl.run_task_loop(task_loop_main)\n    except tl.TaskCancelled:\n        exit(1)\n    except BaseException as e:\n        if App.debug or App.debug_events:\n            traceback.print_exc()\n        tl.log_exception(e, raise_error=False)  # Automatically avoids double logging\n        exit(1)\n\n\ndef setup_logging():\n    tl.LogContext.app_name = \"CEXENUM\"\n    tl.logging.start_logging()\n\n    if App.debug_events:\n        tl.logging.start_debug_event_logging()\n    if App.debug:\n        tl.LogContext.level = \"debug\"\n\n    def error_handler(err: BaseException):\n        if isinstance(err, tl.TaskCancelled):\n            return\n        tl.log_exception(err, raise_error=True)\n\n    tl.current_task().set_error_handler(None, error_handler)\n\n\nasync def batch(*args: Awaitable[Any]):\n    result = None\n    for arg in args:\n        if arg is not None:\n            result = await arg\n    return result\n\n\nasync def task_loop_main() -> None:\n    setup_logging()\n\n    cached = False\n\n    App.cache_dir = App.work_dir / \"cexenum_cache\"\n    try:\n        App.cache_dir.mkdir()\n    except FileExistsError:\n        if (App.cache_dir / \"done\").exists():\n            cached = True\n        else:\n            shutil.rmtree(App.cache_dir)\n            App.cache_dir.mkdir()\n\n    App.work_subdir = App.work_dir / \"cexenum\"\n    try:\n        App.work_subdir.mkdir()\n    except FileExistsError:\n        shutil.rmtree(App.work_subdir)\n        App.work_subdir.mkdir()\n\n    App.trace_dir_full = App.work_subdir / \"full\"\n    App.trace_dir_full.mkdir()\n    App.trace_dir_min = App.work_subdir / \"min\"\n    App.trace_dir_min.mkdir()\n\n    if cached:\n        tl.log(\"Reusing cached AIGER model\")\n        aig_model = tl.Task()\n    else:\n        aig_model = AigModel()\n\n    Enumeration(aig_model)\n\n\nclass AigModel(tl.process.Process):\n    def __init__(self):\n        self[tl.LogContext].scope = \"aiger\"\n        (App.cache_dir / \"design_aiger.ys\").write_text(\n            lines(\n                \"read_rtlil ../model/design_prep.il\",\n                \"hierarchy -simcheck\",\n                \"flatten\",\n                \"setundef -undriven -anyseq\",\n                \"setattr -set keep 1 w:\\\\*\",\n                \"delete -output\",\n                \"opt -full\",\n                \"techmap\",\n                \"opt -fast\",\n                \"memory_map -formal\",\n                \"formalff -clk2ff -ff2anyinit\",\n                \"simplemap\",\n                \"dffunmap\",\n                \"aigmap\",\n                \"opt_clean\",\n                \"stat\",\n                \"write_rtlil design_aiger.il\",\n                \"write_aiger -I -B -zinit\"\n                \" -map design_aiger.aim -ywmap design_aiger.ywa design_aiger.aig\",\n            )\n        )\n        super().__init__(\n            [\"yosys\", \"-ql\", \"design_aiger.log\", \"design_aiger.ys\"], cwd=App.cache_dir\n        )\n        self.name = \"aiger\"\n        self.log_output()\n\n    async def on_run(self) -> None:\n        await super().on_run()\n        (App.cache_dir / \"done\").write_text(\"\")\n\n\nclass MinimizeTrace(tl.Task):\n    def __init__(self, trace_name: str, aig_model: tl.Task):\n        super().__init__()\n        self.trace_name = trace_name\n\n        full_yw = App.trace_dir_full / self.trace_name\n        min_yw = App.trace_dir_min / self.trace_name\n        min_x_yw = min_yw.with_suffix(\".x.yw\")\n\n        stem = full_yw.stem\n\n        full_aiw = full_yw.with_suffix(\".aiw\")\n        min_aiw = min_yw.with_suffix(\".aiw\")\n\n        yw2aiw = YosysWitness(\n            \"yw2aiw\",\n            full_yw,\n            App.cache_dir / \"design_aiger.ywa\",\n            full_aiw,\n            cwd=App.trace_dir_full,\n        )\n        yw2aiw.depends_on(aig_model)\n        yw2aiw[tl.LogContext].scope = f\"yw2aiw[{stem}]\"\n\n        aigcexmin = AigCexMin(\n            App.cache_dir / \"design_aiger.aig\",\n            full_aiw,\n            min_aiw,\n            cwd=App.trace_dir_min,\n        )\n        aigcexmin.depends_on(yw2aiw)\n        aigcexmin[tl.LogContext].scope = f\"aigcexmin[{stem}]\"\n\n        self.aiw2yw = aiw2yw = YosysWitness(\n            \"aiw2yw\",\n            min_aiw,\n            App.cache_dir / \"design_aiger.ywa\",\n            min_yw,\n            cwd=App.trace_dir_min,\n            options=(\"--skip-x\", \"--present-only\"),\n        )\n        aiw2yw[tl.LogContext].scope = f\"aiw2yw[{stem}]\"\n        aiw2yw.depends_on(aigcexmin)\n\n        self.aiw2yw_x = aiw2yw_x = YosysWitness(\n            \"aiw2yw\",\n            min_aiw,\n            App.cache_dir / \"design_aiger.ywa\",\n            min_x_yw,\n            cwd=App.trace_dir_min,\n            options=(\"--present-only\",),\n        )\n        aiw2yw_x[tl.LogContext].scope = f\"aiw2yw[{stem}(x)]\"\n        aiw2yw_x.depends_on(aigcexmin)\n\n        sim = SimTrace(\n            App.cache_dir / \"design_aiger.il\",\n            full_yw,\n            full_yw.with_suffix(\".fst\"),\n            cwd=App.trace_dir_full,\n            script_only=not App.sim,\n        )\n        sim.depends_on(aig_model)\n        sim[tl.LogContext].scope = f\"full-sim[{stem}]\"\n\n        sim = SimTrace(\n            App.cache_dir / \"design_aiger.il\",\n            min_x_yw,\n            min_yw.with_suffix(\".fst\"),\n            cwd=App.trace_dir_min,\n            script_only=not App.sim,\n        )\n\n        sim[tl.LogContext].scope = f\"sim[{stem}]\"\n        sim.depends_on(aiw2yw_x)\n\n\ndef relative_to(target: Path, cwd: Path) -> Path:\n    prefix = Path(\"\")\n    target = target.resolve()\n    cwd = cwd.resolve()\n\n    ok = False\n    for limit in (Path.cwd(), App.work_dir):\n        limit = Path.cwd().resolve()\n        try:\n            target.relative_to(limit)\n            ok = True\n        except ValueError:\n            pass\n    if not ok:\n        return target\n\n    while True:\n        try:\n            return prefix / (target.relative_to(cwd))\n        except ValueError:\n            prefix = prefix / \"..\"\n            if cwd == cwd.parent:\n                return target\n            cwd = cwd.parent\n\n\nclass YosysWitness(tl.process.Process):\n    def __init__(\n        self,\n        mode: Literal[\"yw2aiw\", \"aiw2yw\"],\n        input: Path,\n        mapfile: Path,\n        output: Path,\n        cwd: Path,\n        options: Iterable[str] = (),\n    ):\n        super().__init__(\n            [\n                \"yosys-witness\",\n                mode,\n                *(options or []),\n                str(relative_to(input, cwd)),\n                str(relative_to(mapfile, cwd)),\n                str(relative_to(output, cwd)),\n            ],\n            cwd=cwd,\n        )\n\n        def handler(event: tl.process.OutputEvent):\n            tl.log_debug(event.output.rstrip(\"\\n\"))\n\n        self.sync_handle_events(tl.process.OutputEvent, handler)\n\n\nclass AigCexMin(tl.process.Process):\n    def __init__(self, design_aig: Path, input_aiw: Path, output_aiw: Path, cwd: Path):\n        aigcexmin_args = []\n        if App.satisfy_assumes:\n            aigcexmin_args.append(\"--satisfy-assumptions\")\n        super().__init__(\n            [\n                \"aigcexmin\",\n                *aigcexmin_args,\n                str(relative_to(design_aig, cwd)),\n                str(relative_to(input_aiw, cwd)),\n                str(relative_to(output_aiw, cwd)),\n            ],\n            cwd=cwd,\n        )\n\n        self.log_path = output_aiw.with_suffix(\".log\")\n        self.log_file = None\n\n        def handler(event: tl.process.OutputEvent):\n            if self.log_file is None:\n                self.log_file = self.log_path.open(\"w\")\n            self.log_file.write(event.output)\n            self.log_file.flush()\n            tl.log_debug(event.output.rstrip(\"\\n\"))\n\n        self.sync_handle_events(tl.process.OutputEvent, handler)\n\n    def on_cleanup(self):\n        if self.log_file is not None:\n            self.log_file.close()\n        super().on_cleanup()\n\n\nclass SimTrace(tl.process.Process):\n    def __init__(\n        self,\n        design_il: Path,\n        input_yw: Path,\n        output_fst: Path,\n        cwd: Path,\n        script_only: bool = False,\n    ):\n        self[tl.LogContext].scope = \"sim\"\n        self._script_only = script_only\n\n        script_file = output_fst.with_suffix(\".fst.ys\")\n        log_file = output_fst.with_suffix(\".fst.log\")\n\n        script_file.write_text(\n            lines(\n                f\"read_rtlil {relative_to(design_il, cwd)}\",\n                \"logger -nowarn\"\n                ' \"Yosys witness trace has an unexpected value for the clock input\"',\n                f\"sim -zinit -r {relative_to(input_yw, cwd)} -hdlname\"\n                f\" -fst {relative_to(output_fst, cwd)}\",\n            )\n        )\n        super().__init__(\n            [\n                \"yosys\",\n                \"-ql\",\n                str(relative_to(log_file, cwd)),\n                str(relative_to(script_file, cwd)),\n            ],\n            cwd=cwd,\n        )\n        self.name = \"sim\"\n        self.log_output()\n\n    async def on_run(self):\n        if self._script_only:\n            tl.log(f\"manually run {self.shell_command} to generate a full trace\")\n        else:\n            await super().on_run()\n\n\nclass Callback(tl.TaskGroup):\n    recover_from_errors = False\n\n    def __init__(self, enumeration: Enumeration):\n        super().__init__()\n        self[tl.LogContext].scope = \"callback\"\n        self.search_next = False\n        self.enumeration = enumeration\n        self.tmp_counter = 0\n\n    async def step_callback(self, step: int) -> Literal[\"advance\", \"search\"]:\n        with self.as_current_task():\n            return await self._callback(step=step)\n\n    async def unsat_callback(self, step: int) -> Literal[\"advance\", \"search\"]:\n        with self.as_current_task():\n            return await self._callback(step=step, unsat=True)\n\n    async def trace_callback(\n        self, step: int, path: Path\n    ) -> Literal[\"advance\", \"search\"]:\n        with self.as_current_task():\n            return await self._callback(step=step, trace_path=path)\n\n    def exit(self):\n        pass\n\n    async def _callback(\n        self,\n        step: int,\n        trace_path: Path | None = None,\n        unsat: bool = False,\n    ) -> Literal[\"advance\", \"search\"]:\n        if not self.is_active():\n            if unsat:\n                return \"advance\"\n            return \"search\"\n        if trace_path is None and self.search_next:\n            if unsat:\n                return \"advance\"\n            return \"search\"\n        self.search_next = False\n\n        info = dict(step=step, enabled=sorted(self.enumeration.active_assumptions))\n\n        if trace_path:\n            self.callback_write(\n                {**info, \"event\": \"trace\", \"trace_path\": str(trace_path)}\n            )\n        elif unsat:\n            self.callback_write({**info, \"event\": \"unsat\"})\n        else:\n            self.callback_write({**info, \"event\": \"step\"})\n\n        while True:\n            try:\n                response: dict[Any, Any] | Any = await self.callback_read()\n                if not isinstance(response, (Exception, dict)):\n                    raise ValueError(\n                        f\"expected JSON object, got: {json.dumps(response)}\"\n                    )\n                if isinstance(response, Exception):\n                    raise response\n\n                did_something = False\n\n                if \"block_yw\" in response:\n                    did_something = True\n                    block_yw: Any = response[\"block_yw\"]\n\n                    if not isinstance(block_yw, str):\n                        raise ValueError(\n                            \"'block_yw' must be a string containing a file path, \"\n                            f\"got: {json.dumps(block_yw)}\"\n                        )\n                    name: Any = response.get(\"name\")\n                    if name is not None and not isinstance(name, str):\n                        raise ValueError(\n                            \"optional 'name' must be a string when present, \"\n                            \"got: {json.dumps(name)}\"\n                        )\n                    self.enumeration.block_trace(Path(block_yw), name=name)\n\n                if \"block_aiw\" in response:\n                    did_something = True\n                    block_aiw: Any = response[\"block_aiw\"]\n                    if not isinstance(block_aiw, str):\n                        raise ValueError(\n                            \"'block_yw' must be a string containing a file path, \"\n                            f\"got: {json.dumps(block_aiw)}\"\n                        )\n                    name: Any = response.get(\"name\")\n                    if name is not None and not isinstance(name, str):\n                        raise ValueError(\n                            \"optional 'name' must be a string when present, \"\n                            \"got: {json.dumps(name)}\"\n                        )\n\n                    tmpdir = App.work_subdir / \"tmp\"\n                    tmpdir.mkdir(exist_ok=True)\n                    self.tmp_counter += 1\n                    block_yw = tmpdir / f\"callback_{self.tmp_counter}.yw\"\n\n                    aiw2yw = YosysWitness(\n                        \"aiw2yw\",\n                        Path(block_aiw),\n                        App.cache_dir / \"design_aiger.ywa\",\n                        Path(block_yw),\n                        cwd=tmpdir,\n                        options=(\"--skip-x\", \"--present-only\"),\n                    )\n                    aiw2yw[tl.LogContext].scope = f\"aiw2yw[callback_{self.tmp_counter}]\"\n\n                    await aiw2yw.finished\n                    self.enumeration.block_trace(Path(block_yw), name=name)\n\n                if \"disable\" in response:\n                    did_something = True\n                    name = response[\"disable\"]\n                    if not isinstance(name, str):\n                        raise ValueError(\n                            \"'disable' must be a string representing an assumption, \"\n                            f\"got: {json.dumps(name)}\"\n                        )\n                    self.enumeration.disable_assumption(name)\n                if \"enable\" in response:\n                    did_something = True\n                    name = response[\"enable\"]\n                    if not isinstance(name, str):\n                        raise ValueError(\n                            \"'disable' must be a string representing an assumption, \"\n                            f\"got: {json.dumps(name)}\"\n                        )\n                    self.enumeration.enable_assumption(name)\n                action: Any = response.get(\"action\")\n                if action == \"next\":\n                    did_something = True\n                    self.search_next = True\n                    action = \"search\"\n                if action in (\"search\", \"advance\"):\n                    return action\n                if not did_something:\n                    raise ValueError(\n                        f\"could not interpret callback response: {response}\"\n                    )\n            except Exception as e:\n                tl.log_exception(e, raise_error=False)\n                if not self.recover_from_errors:\n                    raise\n\n    def is_active(self) -> bool:\n        return False\n\n    def callback_write(self, data: Any):\n        return\n\n    async def callback_read(self) -> Any:\n        raise NotImplementedError(\"must be implemented in Callback subclass\")\n\n\nclass InteractiveCallback(Callback):\n    recover_from_errors = True\n\n    interactive_shortcuts = {\n        \"n\": '{\"action\": \"next\"}',\n        \"s\": '{\"action\": \"search\"}',\n        \"a\": '{\"action\": \"advance\"}',\n    }\n\n    def __init__(self, enumeration: Enumeration):\n        super().__init__(enumeration)\n        self.__eof_reached = False\n\n    def is_active(self) -> bool:\n        return not self.__eof_reached\n\n    def callback_write(self, data: Any):\n        print(f\"callback: {json.dumps(data)}\")\n\n    async def callback_read(self) -> Any:\n        future: asyncio.Future[Any] = asyncio.Future()\n\n        loop = asyncio.get_event_loop()\n\n        def blocking_read():\n            try:\n                try:\n                    result = \"\"\n                    while not result:\n                        result = input(\n                            \"callback> \" if sys.stdout.isatty() else \"\"\n                        ).strip()\n                    result = self.interactive_shortcuts.get(result, result)\n                    result_data = json.loads(result)\n                except EOFError:\n                    print()\n                    self.__eof_reached = True\n                    result_data = dict(action=\"next\")\n                loop.call_soon_threadsafe(lambda: future.set_result(result_data))\n            except Exception as exc:\n                exception = exc\n                loop.call_soon_threadsafe(lambda: future.set_exception(exception))\n\n        thread = threading.Thread(target=blocking_read, daemon=True)\n        thread.start()\n\n        return await future\n\n\nclass ProcessCallback(Callback):\n    def __init__(self, enumeration: Enumeration, command: list[str]):\n        super().__init__(enumeration)\n        self[tl.LogContext].scope = \"callback\"\n        self.__eof_reached = False\n        self._command = command\n        self._lines: list[asyncio.Future[str]] = [asyncio.Future()]\n\n    def exit(self):\n        self.process.close_stdin()\n\n    async def on_prepare(self) -> None:\n        self.process = tl.Process(self._command, cwd=Path.cwd(), interact=True)\n        self.process.use_lease = False\n\n        future_line: asyncio.Future[str] = self._lines[-1]\n\n        def stdout_handler(event: tl.process.StdoutEvent):\n            nonlocal future_line\n            future_line.set_result(event.output)\n            future_line = asyncio.Future()\n            self._lines.append(future_line)\n\n        self.process.sync_handle_events(tl.process.StdoutEvent, stdout_handler)\n\n        def stderr_handler(event: tl.process.StderrEvent):\n            tl.log(event.output)\n\n        self.process.sync_handle_events(tl.process.StderrEvent, stderr_handler)\n\n        def exit_handler(event: tl.process.ExitEvent):\n            future_line.set_exception(EOFError(\"callback process exited\"))\n            # Mark the exception as retrieved\n            future_line.exception()\n\n        self.process.sync_handle_events(tl.process.ExitEvent, exit_handler)\n\n    def is_active(self) -> bool:\n        return not self.__eof_reached\n\n    def callback_write(self, data: Any):\n        if not self.process.is_finished:\n            self.process.write(json.dumps(data) + \"\\n\")\n\n    async def callback_read(self) -> Any:\n        future_line = self._lines.pop(0)\n        try:\n            data = json.loads(await future_line)\n            tl.log_debug(f\"callback action: {data}\")\n            return data\n        except EOFError:\n            self._lines.insert(0, future_line)\n            self.__eof_reached = True\n            return dict(action=\"next\")\n\n\ndef enter_task(fn):\n    # TODO move into mau\n    import functools\n\n    @functools.wraps(fn)\n    def wrapped_fn(self, *args, **kwargs):\n        with self.as_current_task():\n            return fn(self, *args, **kwargs)\n\n    return wrapped_fn\n\n\ndef inv_signal(sig):\n    return (*sig[:-1], \"0\" if sig[-1] != \"0\" else \"1\")\n\n\nclass Diversifier(tl.TaskGroup):\n    def __init__(self, seed, enumeration_depth):\n        super().__init__()\n        self._rng = random.Random(seed)\n        self._failed: StableSet[Any] = StableSet()\n        self._failed_sets: dict[Any, list[StableSet[Any]]] = defaultdict(list)\n        self._active: StableSet[Any] = StableSet()\n\n        self._current_id = 0\n\n        self._targets: dict[int, StableSet[Any]] = {}\n\n        self._sig_index: dict[Any, StableSet[int]] = defaultdict(StableSet)\n\n        self._depth_limit = 0\n\n        self._enumeration_depth_left = enumeration_depth\n\n        self._unsat_counter = 0\n\n        self[tl.LogContext].scope = \"diversifier\"\n        # self[tl.LogContext].level = \"debug\"\n\n    def _new_id(self):\n        self._current_id += 1\n        return self._current_id\n\n    @enter_task\n    def new_step(self):\n        self._failed = StableSet()\n        self._failed_sets = defaultdict(list)\n        self._active = StableSet()\n        self._depth_limit = 0\n        self._unsat_counter = 0\n\n    @enter_task\n    def diversify(self):\n        if self._enumeration_depth_left is not None:\n            if self._enumeration_depth_left <= 0:\n                return StableSet()\n\n        selected = StableSet()\n        blocked = StableSet()\n\n        pending = list(self._targets.values())\n\n        tl.log_debug(f\"{len(pending)=} initial\")\n\n        depth = 0\n\n        deferred = []\n        while pending or (deferred and depth < self._depth_limit):\n            if not pending:\n                for subspace in deferred:\n                    new_subspace = StableSet(\n                        sig for sig in subspace if sig[:-1] not in blocked\n                    )\n                    if new_subspace:\n                        pending.append(new_subspace)\n                deferred = []\n                depth += 1\n                continue\n\n            counter = Counter()\n\n            for subspace in self._shuffle_list(pending):\n                counter.update(self._shuffle_list(subspace))\n\n            sig, sig_count = max(counter.items(), key=lambda item: item[1])\n\n            tl.log_debug(f\"{sig=} {sig_count=}\")\n\n            selected.add(sig)\n            blocked.add(sig[:-1])\n\n            failed = False\n\n            for candidate in self._failed_sets[sig]:\n                if candidate.issubset(selected):\n                    failed = True\n                    tl.log_debug(f\"failed {candidate=}\")\n                    break\n\n            if not failed:\n                for candidate in self._failed_sets[sig]:\n                    target = next(s for s in candidate if s not in selected)\n                    self._failed_sets[target].append(candidate)\n\n                self._failed_sets.pop(sig, None)\n\n            if failed:\n                selected.remove(sig)\n                sig = None\n\n            new_pending = []\n\n            for subspace in pending:\n                new_subspace = StableSet(\n                    sig for sig in subspace if sig[:-1] not in blocked\n                )\n                if not new_subspace:\n                    continue\n                if sig in subspace:\n                    deferred.append(new_subspace)\n                else:\n                    new_pending.append(new_subspace)\n            pending = new_pending\n\n            tl.log_debug(f\"{len(pending)=} {selected=}\")\n\n        return selected\n\n    def _add_target(self, signals):\n        signals.difference_update(self._failed)\n        if signals:\n            id = self._new_id()\n            self._targets[id] = signals\n            for sig in signals:\n                self._sig_index[sig].add(id)\n\n    def _remove_target(self, id):\n        signals = self._targets.pop(id)\n        for sig in signals:\n            self._sig_index[sig].remove(id)\n\n    def _remove_target_sig(self, id, sig):\n        self._targets[id].remove(sig)\n        self._sig_index[sig].remove(id)\n\n    @enter_task\n    def update(self, signals):\n        if self._enumeration_depth_left is not None:\n            if self._enumeration_depth_left > 0:\n                self._enumeration_depth_left -= 1\n                if self._enumeration_depth_left == 0:\n                    tl.log(\n                        \"reached diversification depth, \"\n                        \"disabling diversification for future traces\"\n                    )\n        self._unsat_counter = 0\n        inv_signals = StableSet(map(inv_signal, signals))\n        self._add_target(inv_signals)\n        self._depth_limit += 1\n        tl.log_debug(f\"increased {self._depth_limit=}\")\n\n    @enter_task\n    def failed(self, failed):\n        self._unsat_counter += 1\n        self._depth_limit = max(0, self._depth_limit - 1)\n        tl.log_debug(f\"decreased {self._depth_limit=}\")\n\n        failed_sig = next(iter(failed))\n        self._failed_sets[failed_sig].append(failed)\n\n        if len(failed) == 1:\n            self._failed.add(failed_sig)\n\n            for id in list(self._sig_index[failed_sig]):\n                self._remove_target_sig(id, failed_sig)\n\n            tl.log(f\"found {len(self._failed)} settled inputs\")\n\n    def should_minimize(self):\n        return self._unsat_counter > 2\n\n    def _shuffle_set(self, items):\n        return StableSet(self._shuffle_list(items))\n\n    def _shuffle_list(self, items):\n        as_list = list(items)\n        self._rng.shuffle(as_list)\n        return as_list\n\n\ndef yw_to_dicts(yw_trace):\n    \"\"\"Convert a .yw JSON object to a list of dicts.\n\n    Every item of the list corresponds to one step. For every step it contains a dict\n    mapping (*path, index) pairs to the bit value for bits that are present.\n    \"\"\"\n\n    signal_bits = []\n    for signal in yw_trace[\"signals\"]:\n        chunk_offset = signal[\"offset\"]\n        path = tuple(signal[\"path\"])\n        for i in range(signal[\"width\"]):\n            signal_bits.append((*path, chunk_offset + i))\n\n    return [\n        {\n            signal: value\n            for signal, value in zip(signal_bits, step[\"bits\"][::-1])\n            if value in \"01\"\n        }\n        for step in yw_trace[\"steps\"]\n    ]\n\n\nclass Enumeration(tl.Task):\n    callback_mode: Literal[\"off\", \"interactive\", \"process\"]\n    callback_auto_search: bool = False\n\n    def __init__(self, aig_model: tl.Task):\n        self.aig_model = aig_model\n        self._pending_blocks: list[tuple[str | None, Path, StableSet[Any] | None]] = []\n        self.named_assumptions: StableSet[str] = StableSet()\n        self.active_assumptions: StableSet[str] = StableSet()\n\n        self._probes: dict[Any, str] = {}\n        self._probe_defs: dict[str, Any] = {}\n        if App.diversify:\n            self._diversifier = Diversifier(App.diversify_seed, App.diversify_depth)\n\n        self._last_id: int = 0\n\n        super().__init__()\n\n    def _new_id(self) -> int:\n        self._last_id += 1\n        return self._last_id\n\n    async def on_prepare(self) -> None:\n        if App.callback:\n            if App.callback == [\"-\"]:\n                self.callback_task = InteractiveCallback(self)\n            else:\n                self.callback_task = ProcessCallback(self, App.callback)\n        else:\n            self.callback_task = Callback(self)\n\n    async def on_run(self) -> None:\n        self.smtbmc = smtbmc = Smtbmc(App.work_dir / \"model\" / \"design_smt2.smt2\")\n        self._push_level = 0\n\n        await smtbmc.ping()\n\n        try:\n            await self._bmc_loop()\n        finally:\n            smtbmc.close_stdin()\n            self.callback_task.exit()\n\n    async def _bmc_loop(self) -> None:\n        smtbmc = self.smtbmc\n        i = -1\n        limit = App.depth\n        first_failure = None\n\n        checked = \"skip\"\n\n        counter = 0\n\n        while i <= limit or limit < 0:\n            if checked != \"skip\":\n                checked = await self._search_counter_example(i)\n\n            if checked == \"unsat\":\n                if i >= 0:\n                    action = await self.callback_task.unsat_callback(i)\n                    if action == \"search\":\n                        continue\n                checked = \"skip\"\n            if checked == \"skip\":\n                checked = \"unsat\"\n                i += 1\n                if App.diversify:\n                    self._diversifier.new_step()\n                if i > limit and limit >= 0:\n                    break\n                action = await self.callback_task.step_callback(i)\n\n                pending = batch(\n                    self._top(),\n                    smtbmc.bmc_step(\n                        i,\n                        initial=i == 0,\n                        assertions=False,\n                        pred=i - 1 if i else None,\n                    ),\n                )\n\n                if action == \"advance\":\n                    tl.log(f\"Skipping step {i}\")\n                    await batch(pending, smtbmc.assertions(i))\n                    checked = \"skip\"\n                    continue\n                assert action == \"search\"\n\n                tl.log(f\"Checking assumptions in step {i}..\")\n                presat_checked = await batch(\n                    pending,\n                    smtbmc.check(),\n                )\n                if presat_checked != \"sat\":\n\n                    if App.track_assumes:\n                        tl.log(\"No further counter-examples are reachable\")\n\n                        tl.log(\"Conflicting assumptions:\")\n                        unsat_assumptions = await smtbmc.get_unsat_assumptions(\n                            App.minimize_assumes\n                        )\n\n                        for key in unsat_assumptions:\n                            desc = await smtbmc.incremental_command(\n                                cmd=\"get_design_assume\", key=key\n                            )\n                            if desc is None:\n                                tl.log(f\"  Callback blocked trace {key[1]!r}\")\n                                continue\n                            expr, path, info = desc\n                            tl.log(f\"  In {path}: {info}\")\n\n                        if not self.active_assumptions:\n                            return\n                    if first_failure is None and not self.active_assumptions:\n                        tl.log_error(\"Assumptions are not satisfiable\")\n                    else:\n                        tl.log(\"No further counter-examples are reachable\")\n                        if not self.active_assumptions:\n                            return\n\n                tl.log(f\"Checking assertions in step {i}..\")\n                counter = 0\n                continue\n            elif checked == \"sat\":\n                if first_failure is None:\n                    first_failure = i\n                    if App.enum_depth < 0:\n                        limit = -1\n                    else:\n                        limit = i + App.enum_depth\n                    tl.log(\"BMC failed! Enumerating counter-examples..\")\n\n                path = App.trace_dir_full / f\"trace{i}_{counter}.yw\"\n                await smtbmc.incremental_command(cmd=\"write_yw_trace\", path=str(path))\n                tl.log(f\"Written counter-example to {path.name}\")\n\n                minimize = MinimizeTrace(path.name, self.aig_model)\n                minimize.depends_on(self.aig_model)\n\n                await minimize.aiw2yw.finished\n\n                min_path = App.trace_dir_min / f\"trace{i}_{counter}.yw\"\n\n                action = await self.callback_task.trace_callback(i, min_path)\n\n                if action == \"search\" and counter + 1 == App.max_per_step:\n                    tl.log(f\"Reached trace limit {App.max_per_step} for this step\")\n                    action = \"advance\"\n\n                if action == \"advance\":\n                    tl.log(\"Skipping remaining counter-examples for this step\")\n                    checked = \"skip\"\n                    continue\n                assert action == \"search\"\n\n                self.block_trace(min_path, diversify=App.diversify)\n\n                counter += 1\n            else:\n                tl.log_error(f\"Unexpected solver result: {checked!r}\")\n\n    def block_trace(self, path: Path, name: str | None = None, diversify: bool = False):\n        if name is not None:\n            if name in self.named_assumptions:\n                raise ValueError(f\"an assumption with name {name} was already defined\")\n            self.named_assumptions.add(name)\n            self.active_assumptions.add(name)\n\n        self._pending_blocks.append((name, path.absolute(), diversify))\n\n    def enable_assumption(self, name: str):\n        if name not in self.named_assumptions:\n            raise ValueError(f\"unknown assumption {name!r}\")\n        self.active_assumptions.add(name)\n\n    def disable_assumption(self, name: str):\n        if name not in self.named_assumptions:\n            raise ValueError(f\"unknown assumption {name!r}\")\n        self.active_assumptions.discard(name)\n        self._diversifier.new_step()\n\n    def _top(self) -> Awaitable[Any]:\n        return batch(*(self._pop() for _ in range(self._push_level)))\n\n    def _pop(self) -> Awaitable[Any]:\n        self._push_level -= 1\n        tl.log_debug(f\"pop to {self._push_level}\")\n        return self.smtbmc.pop()\n\n    def _push(self) -> Awaitable[Any]:\n        self._push_level += 1\n        tl.log_debug(f\"push to {self._push_level}\")\n        return self.smtbmc.push()\n\n    async def _search_counter_example(self, step: int) -> str:\n        smtbmc = self.smtbmc\n        pending_blocks, self._pending_blocks = self._pending_blocks, []\n\n        pending = self._top()\n\n        add_to_pending = []\n\n        for name, block_path, diversify in pending_blocks:\n            result = smtbmc.incremental_command(\n                cmd=\"read_yw_trace\",\n                name=\"last\",\n                path=str(block_path),\n                skip_x=True,\n            )\n\n            if diversify:\n\n                signals = StableSet(\n                    (s, step_signal, value)\n                    for s, step_signals in enumerate(\n                        yw_to_dicts(json.loads(block_path.read_text()))\n                    )\n                    for step_signal, value in step_signals.items()\n                )\n\n                self._diversifier.update(signals)\n\n            async def check_yw_trace_len():\n                last_step = (await result).get(\"last_step\", step)\n                if last_step > step:\n                    tl.log_warning(\n                        f\"Ignoring future time steps \"\n                        f\"{step + 1} to {last_step} of \"\n                        f\"{relative_to(block_path, Path.cwd())}\"\n                    )\n                return last_step\n\n            expr = [\n                \"not\",\n                [\"and\", *([\"yw\", \"last\", k] for k in range(step + 1))],\n            ]\n\n            if name is not None:\n                name_id = safe_smtlib_id(f\"cexenum trace {name}\")\n                add_to_pending.append(smtbmc.smtlib(f\"(declare-const {name_id} Bool)\"))\n                expr = [\"=\", expr, [\"smtlib\", name_id, \"Bool\"]]\n\n            add_to_pending.append(check_yw_trace_len())\n            add_to_pending.append(smtbmc.assert_(expr))\n\n        active_diversifications = StableSet()\n\n        try:\n            while True:\n                diversifications = StableSet()\n                if App.diversify:\n\n                    diversifications = StableSet(self._diversifier.diversify())\n\n                    for signal_def in active_diversifications.difference(\n                        diversifications\n                    ):\n                        add_to_pending.append(\n                            smtbmc.incremental_command(\n                                cmd=\"update_assumptions\",\n                                key=(\"cexenum probe\", self._probes[signal_def]),\n                                expr=None,\n                            )\n                        )\n\n                    new_probes = False\n\n                    for signal_def in diversifications:\n                        if signal_def in self._probes:\n                            continue\n\n                        sig_step, (*path, offset), value = signal_def\n                        probe_result = smtbmc.incremental_command(\n                            cmd=\"define\",\n                            expr=[\n                                \"=\",\n                                [\"yw_sig\", sig_step, path, offset],\n                                [\"bv\", value],\n                            ],\n                        )\n\n                        async def store_probe(signal_def, probe_result):\n                            defined = await probe_result\n                            self._probes[signal_def] = defined[\"name\"]\n                            self._probe_defs[defined[\"name\"]] = signal_def\n\n                        add_to_pending.append(probe_result)\n                        add_to_pending.append(store_probe(signal_def, probe_result))\n                        new_probes = True\n\n                    if new_probes:\n                        await batch(pending, *add_to_pending)\n                        pending, add_to_pending = None, []\n\n                    for signal_def in diversifications.difference(\n                        active_diversifications\n                    ):\n                        add_to_pending.append(\n                            smtbmc.incremental_command(\n                                cmd=\"update_assumptions\",\n                                key=(\"cexenum probe\", self._probes[signal_def]),\n                                expr=[\"def\", self._probes[signal_def]],\n                            )\n                        )\n\n                active_diversifications = diversifications\n\n                add_to_pending.append(self._push())\n                add_to_pending.append(smtbmc.assertions(step, False))\n\n                for name in self.active_assumptions:\n                    name_id = safe_smtlib_id(f\"cexenum trace {name}\")\n                    expr = [\"smtlib\", name_id, \"Bool\"]\n                    if App.track_assumes:\n                        add_to_pending.append(\n                            smtbmc.incremental_command(\n                                cmd=\"update_assumptions\",\n                                key=(\"cexenum trace\", name),\n                                expr=expr,\n                            )\n                        )\n                    else:\n                        add_to_pending.append(smtbmc.assert_(expr))\n\n                pending = batch(pending, *add_to_pending)\n\n                result = await batch(\n                    pending,\n                    smtbmc.check(),\n                )\n                pending, add_to_pending = None, []\n\n                if result == \"sat\" or not active_diversifications:\n                    return result\n\n                failed_assumptions = StableSet(\n                    map(\n                        tuple,\n                        await smtbmc.get_unsat_assumptions(\n                            minimize=self._diversifier.should_minimize()\n                        ),\n                    )\n                )\n\n                failed_diversifications = StableSet()\n\n                for key, expr in failed_assumptions:\n                    if key == \"cexenum probe\":\n                        failed_diversifications.add(self._probe_defs[expr])\n\n                if failed_diversifications:\n                    self._diversifier.failed(failed_diversifications)\n                else:\n                    return result\n\n                add_to_pending.append(self._top())\n        finally:\n            for signal_def in active_diversifications:\n                add_to_pending.append(\n                    smtbmc.incremental_command(\n                        cmd=\"update_assumptions\",\n                        key=(\"cexenum probe\", self._probes[signal_def]),\n                        expr=None,\n                    )\n                )\n\n            await batch(pending, *add_to_pending)\n\n\nclass Smtbmc(tl.process.Process):\n    def __init__(self, smt2_model: Path):\n        self[tl.LogContext].scope = \"smtbmc\"\n\n        smtbmc_options = []\n        if App.track_assumes:\n            smtbmc_options += [\"--track-assumes\"]\n        if App.minimize_assumes:\n            smtbmc_options += [\"--minimize-assumes\"]\n        if App.diversify:\n            smtbmc_options += [\"--smt2-option\", \":produce-unsat-assumptions=true\"]\n\n        smtbmc_options += App.smtbmc_options\n        super().__init__(\n            [\n                \"yosys-smtbmc\",\n                \"--incremental\",\n                \"--noprogress\",\n                *smtbmc_options,\n                str(smt2_model),\n            ],\n            interact=True,\n        )\n        self.name = \"smtbmc\"\n\n        self.expected_results: list[asyncio.Future[Any]] = []\n\n    async def on_run(self) -> None:\n        def output_handler(event: tl.process.StdoutEvent):\n            line = event.output.strip()\n            if line.startswith(\"{\"):\n                result = json.loads(event.output)\n            else:\n                result = dict(msg=line)\n            tl.log_debug(f\"smtbmc > {result!r}\")\n            if \"err\" in result:\n                exception = tl.logging.LoggedError(\n                    tl.log_error(result[\"err\"], raise_error=False)\n                )\n                self.expected_results.pop(0).set_exception(exception)\n            if \"msg\" in result:\n                tl.log(result[\"msg\"])\n            if \"ok\" in result:\n                assert self.expected_results\n                self.expected_results.pop(0).set_result(result[\"ok\"])\n\n        self.sync_handle_events(tl.process.StdoutEvent, output_handler)\n\n        return await super().on_run()\n\n    def ping(self) -> Awaitable[None]:\n        return self.incremental_command(cmd=\"ping\")\n\n    def incremental_command(self, **command: Any) -> Awaitable[Any]:\n        tl.log_debug(f\"smtbmc < {command!r}\")\n        self.write(json.dumps(command))\n        self.write(\"\\n\")\n        result: asyncio.Future[Any] = asyncio.Future()\n        self.expected_results.append(result)\n\n        return result\n\n    def new_step(self, step: int) -> Awaitable[None]:\n        return self.incremental_command(cmd=\"new_step\", step=step)\n\n    def push(self) -> Awaitable[None]:\n        return self.incremental_command(cmd=\"push\")\n\n    def pop(self) -> Awaitable[None]:\n        return self.incremental_command(cmd=\"pop\")\n\n    def check(self) -> Awaitable[str]:\n        return self.incremental_command(cmd=\"check\")\n\n    def smtlib(self, command: str, response=False) -> Awaitable[str]:\n        return self.incremental_command(\n            cmd=\"smtlib\", command=command, response=response\n        )\n\n    def assert_antecedent(self, expr: Any) -> Awaitable[None]:\n        return self.incremental_command(cmd=\"assert_antecedent\", expr=expr)\n\n    def assert_consequent(self, expr: Any) -> Awaitable[None]:\n        return self.incremental_command(cmd=\"assert_consequent\", expr=expr)\n\n    def assert_(self, expr: Any) -> Awaitable[None]:\n        return self.incremental_command(cmd=\"assert\", expr=expr)\n\n    def hierarchy(self, step: int) -> Awaitable[None]:\n        return self.assert_antecedent([\"mod_h\", [\"step\", step]])\n\n    def assert_design_assumes(self, step: int) -> Awaitable[None]:\n        return self.incremental_command(cmd=\"assert_design_assumes\", step=step)\n\n    def get_unsat_assumptions(self, minimize: bool) -> Awaitable[list[Any]]:\n        return self.incremental_command(cmd=\"get_unsat_assumptions\", minimize=minimize)\n\n    def assumptions(self, step: int, valid: bool = True) -> Awaitable[None]:\n        if valid:\n            return self.assert_design_assumes(step)\n\n        expr = [\"mod_u\", [\"step\", step]]\n        if not valid:\n            expr = [\"not\", expr]\n        return self.assert_consequent(expr)\n\n    def assertions(self, step: int, valid: bool = True) -> Awaitable[None]:\n        expr = [\"mod_a\", [\"step\", step]]\n        if not valid:\n            expr = [\"not\", expr]\n        return self.assert_(expr)\n\n    def initial(self, step: int, initial: bool) -> Awaitable[None]:\n        if initial:\n            return batch(\n                self.assert_antecedent([\"mod_i\", [\"step\", step]]),\n                self.assert_antecedent([\"mod_is\", [\"step\", step]]),\n            )\n        else:\n            return self.assert_antecedent([\"not\", [\"mod_is\", [\"step\", step]]])\n\n    def transition(self, pred: int, succ: int) -> Awaitable[None]:\n        return self.assert_antecedent([\"mod_t\", [\"step\", pred], [\"step\", succ]])\n\n    def bmc_step(\n        self,\n        step: int,\n        initial: bool = False,\n        assertions: bool = True,\n        pred: int | None = None,\n        assumptions: bool = True,\n    ) -> Awaitable[None]:\n        futures: list[Awaitable[None]] = []\n        futures.append(self.new_step(step))\n        futures.append(self.hierarchy(step))\n        if assumptions:\n            futures.append(self.assumptions(step))\n        futures.append(self.initial(step, initial))\n\n        if pred is not None:\n            futures.append(self.transition(pred, step))\n\n        if assertions:\n            futures.append(self.assertions(step))\n\n        return batch(*futures)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "tools/cexenum/examples/.gitignore",
    "content": ""
  },
  {
    "path": "tools/cexenum/examples/factor.sby",
    "content": "# Run using:\n#\n#   sby -f factor.sby\n#   tabbypy3 cexenum.py factor --enum-depth=0\n#\n[options]\nmode bmc\nmake_model prep,smt2\nexpect unknown\n\n[engines]\nnone\n\n[script]\nread_verilog -sv top.sv\nprep -top top\n\n[file top.sv]\nmodule top(input clk, input b_bit, output [15:0] acc);\n    reg [7:0] a;\n    reg [7:0] b_mask = 8'hff;\n\n\n    reg [15:0] a_shift = 0;\n    reg [15:0] acc = 0;\n\n\n    always @(posedge clk) begin\n        assume (!clk);\n        if ($initstate) begin\n            a_shift <= a;\n            acc <= 0;\n        end else begin\n\n            if (b_bit) begin\n                acc <= acc + a_shift;\n            end\n            a_shift <= a_shift << 1;\n            b_mask <= b_mask >> 1;\n        end\n\n        if (b_mask == 0) begin\n            a <= 0;\n            assert (acc != 100);\n        end;\n\n    end\n\nendmodule\n"
  }
]