3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2026-03-23 04:49:15 +00:00

Merge branch 'main' into related_load_data

This commit is contained in:
Arnim Läuger 2026-03-12 16:22:28 +01:00 committed by GitHub
commit f8b5da5058
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
226 changed files with 16144 additions and 2736 deletions

View file

@ -6,6 +6,8 @@ body:
attributes:
value: >
Learn more [here](https://yosyshq.readthedocs.io/projects/yosys/en/latest/yosys_internals/extending_yosys/contributing.html#reporting-bugs) about how to report bugs. We fix well-reported bugs the fastest.
If you have a general question, please ask it on the [Discourse forum](https://yosyshq.discourse.group/).

View file

@ -42,7 +42,7 @@ runs:
if: runner.os == 'Linux' && inputs.get-build-deps == 'true'
uses: awalsh128/cache-apt-pkgs-action@v1.6.0
with:
packages: bison clang flex libffi-dev libfl-dev libreadline-dev pkg-config tcl-dev zlib1g-dev
packages: bison clang flex libffi-dev libfl-dev libreadline-dev pkg-config tcl-dev zlib1g-dev libgtest-dev
version: ${{ inputs.runs-on }}-buildys
- name: Linux docs dependencies
@ -52,15 +52,6 @@ runs:
packages: graphviz xdot
version: ${{ inputs.runs-on }}-docsys
# if updating test dependencies, make sure to update
# docs/source/yosys_internals/extending_yosys/test_suites.rst to match.
- name: Linux test dependencies
if: runner.os == 'Linux' && inputs.get-test-deps == 'true'
uses: awalsh128/cache-apt-pkgs-action@v1.6.0
with:
packages: libgtest-dev
version: ${{ inputs.runs-on }}-testys
- name: Install macOS Dependencies
if: runner.os == 'macOS'
shell: bash

View file

@ -1,22 +1,20 @@
name: Test extra build flows
on:
# always test main
push:
branches:
- main
# test PRs
pull_request:
# allow triggering tests, ignores skip check
merge_group:
#push:
# branches: [ main ]
workflow_dispatch:
jobs:
pre_job:
runs-on: ubuntu-latest
outputs:
should_skip: ${{ steps.skip_check.outputs.should_skip }}
should_skip: ${{ steps.set_output.outputs.should_skip }}
steps:
- id: skip_check
if: ${{ github.event_name != 'merge_group' }}
uses: fkirc/skip-duplicate-actions@v5
with:
# don't run on documentation changes
@ -25,11 +23,19 @@ jobs:
# but never cancel main
cancel_others: ${{ github.ref != 'refs/heads/main' }}
- id: set_output
run: |
if [ "${{ github.event_name }}" = "merge_group" ]; then
echo "should_skip=false" >> $GITHUB_OUTPUT
else
echo "should_skip=${{ steps.skip_check.outputs.should_skip }}" >> $GITHUB_OUTPUT
fi
vs-prep:
name: Prepare Visual Studio build
runs-on: ubuntu-latest
needs: [pre_job]
if: needs.pre_job.outputs.should_skip != 'true'
if: (github.event_name == 'merge_group' || github.event_name == 'workflow_dispatch') && needs.pre_job.outputs.should_skip != 'true'
steps:
- uses: actions/checkout@v4
with:
@ -47,7 +53,7 @@ jobs:
name: Visual Studio build
runs-on: windows-latest
needs: [vs-prep, pre_job]
if: needs.pre_job.outputs.should_skip != 'true'
if: (github.event_name == 'merge_group' || github.event_name == 'workflow_dispatch') && needs.pre_job.outputs.should_skip != 'true'
steps:
- uses: actions/download-artifact@v4
with:
@ -64,7 +70,7 @@ jobs:
wasi-build:
name: WASI build
needs: pre_job
if: needs.pre_job.outputs.should_skip != 'true'
if: (github.event_name == 'merge_group' || github.event_name == 'workflow_dispatch') && needs.pre_job.outputs.should_skip != 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@ -102,6 +108,7 @@ jobs:
ENABLE_ZLIB := 0
CXXFLAGS += -I$(pwd)/flex-prefix/include
LINKFLAGS += -Wl,-z,stack-size=8388608 -Wl,--stack-first -Wl,--strip-all
END
make -C build -f ../Makefile CXX=clang -j$(nproc)
@ -109,7 +116,7 @@ jobs:
nix-build:
name: "Build nix flake"
needs: pre_job
if: needs.pre_job.outputs.should_skip != 'true'
if: (github.event_name == 'merge_group' || github.event_name == 'workflow_dispatch') && needs.pre_job.outputs.should_skip != 'true'
runs-on: ${{ matrix.os }}
strategy:
matrix:
@ -124,3 +131,13 @@ jobs:
with:
install_url: https://releases.nixos.org/nix/nix-2.30.0/install
- run: nix build .?submodules=1 -L
extra-builds-result:
runs-on: ubuntu-latest
needs:
- vs-build
- wasi-build
- nix-build
if: always() && !contains(join(needs.*.result, ','), 'failure') && !contains(join(needs.*.result, ','), 'cancelled')
steps:
- run: echo "All good"

View file

@ -1,17 +1,24 @@
name: Build docs artifact with Verific
on: [push, pull_request]
on:
pull_request:
merge_group:
push:
branches: [ main, "docs-preview/**", "docs-preview*" ]
tags: [ "*" ]
workflow_dispatch:
jobs:
check_docs_rebuild:
runs-on: ubuntu-latest
outputs:
skip_check: ${{ steps.skip_check.outputs.should_skip }}
should_skip: ${{ steps.set_output.outputs.should_skip }}
docs_export: ${{ steps.docs_var.outputs.docs_export }}
env:
docs_export: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/docs-preview') || startsWith(github.ref, 'refs/tags/') }}
steps:
- id: skip_check
if: ${{ github.event_name != 'merge_group' }}
uses: fkirc/skip-duplicate-actions@v5
with:
paths_ignore: '["**/README.md"]'
@ -22,11 +29,19 @@ jobs:
- id: docs_var
run: echo "docs_export=${docs_export}" >> $GITHUB_OUTPUT
- id: set_output
run: |
if [ "${{ github.event_name }}" = "merge_group" ]; then
echo "should_skip=false" >> $GITHUB_OUTPUT
else
echo "should_skip=${{ steps.skip_check.outputs.should_skip }}" >> $GITHUB_OUTPUT
fi
prepare-docs:
# docs builds are needed for anything on main, any tagged versions, and any tag
# or branch starting with docs-preview
needs: check_docs_rebuild
if: ${{ needs.check_docs_rebuild.outputs.should_skip != 'true' && github.repository == 'YosysHQ/Yosys' }}
if: ${{ needs.check_docs_rebuild.outputs.should_skip != 'true' && github.repository_owner == 'YosysHQ' }}
runs-on: [self-hosted, linux, x64, fast]
steps:
- name: Checkout Yosys
@ -75,7 +90,7 @@ jobs:
make -C docs html -j$procs TARGETS= EXTRA_TARGETS=
- name: Trigger RTDs build
if: ${{ needs.check_docs_rebuild.outputs.docs_export == 'true' }}
if: ${{ needs.check_docs_rebuild.outputs.docs_export == 'true' && github.repository == 'YosysHQ/Yosys' }}
uses: dfm/rtds-action@v1.1.0
with:
webhook_url: ${{ secrets.RTDS_WEBHOOK_URL }}

View file

@ -1,6 +1,11 @@
name: Create source archive with vendored dependencies
on: [push, workflow_dispatch]
on:
pull_request:
merge_group:
push:
branches: [ main ]
workflow_dispatch:
jobs:
vendor-sources:

View file

@ -1,22 +1,20 @@
name: Build and run tests
on:
# always test main
push:
branches:
- main
# test PRs
pull_request:
# allow triggering tests, ignores skip check
merge_group:
#push:
# branches: [ main ]
workflow_dispatch:
jobs:
pre_job:
runs-on: ubuntu-latest
outputs:
should_skip: ${{ steps.skip_check.outputs.should_skip }}
should_skip: ${{ steps.set_output.outputs.should_skip }}
steps:
- id: skip_check
if: ${{ github.event_name != 'merge_group' }}
uses: fkirc/skip-duplicate-actions@v5
with:
# don't run on documentation changes
@ -25,12 +23,21 @@ jobs:
# but never cancel main
cancel_others: ${{ github.ref != 'refs/heads/main' }}
- id: set_output
run: |
if [ "${{ github.event_name }}" = "merge_group" ]; then
echo "should_skip=false" >> $GITHUB_OUTPUT
else
echo "should_skip=${{ steps.skip_check.outputs.should_skip }}" >> $GITHUB_OUTPUT
fi
pre_docs_job:
runs-on: ubuntu-latest
outputs:
should_skip: ${{ steps.skip_check.outputs.should_skip }}
should_skip: ${{ steps.set_output.outputs.should_skip }}
steps:
- id: skip_check
if: ${{ github.event_name != 'merge_group' }}
uses: fkirc/skip-duplicate-actions@v5
with:
# don't run on readme changes
@ -39,6 +46,14 @@ jobs:
# but never cancel main
cancel_others: ${{ github.ref != 'refs/heads/main' }}
- id: set_output
run: |
if [ "${{ github.event_name }}" = "merge_group" ]; then
echo "should_skip=false" >> $GITHUB_OUTPUT
else
echo "should_skip=${{ steps.skip_check.outputs.should_skip }}" >> $GITHUB_OUTPUT
fi
build-yosys:
name: Reusable build
runs-on: ${{ matrix.os }}
@ -71,6 +86,7 @@ jobs:
cd build
make -f ../Makefile config-$CC
make -f ../Makefile -j$procs
make -f ../Makefile unit-test -j$procs
- name: Log yosys-config output
run: |
@ -80,7 +96,7 @@ jobs:
shell: bash
run: |
cd build
tar -cvf ../build.tar share/ yosys yosys-*
tar -cvf ../build.tar share/ yosys yosys-* libyosys.so
- name: Store build artifact
uses: actions/upload-artifact@v4
@ -130,7 +146,7 @@ jobs:
- name: Run tests
shell: bash
run: |
make -j$procs test TARGETS= EXTRA_TARGETS= CONFIG=$CC
make -j$procs vanilla-test TARGETS= EXTRA_TARGETS= CONFIG=$CC
- name: Report errors
if: ${{ failure() }}
@ -224,7 +240,7 @@ jobs:
name: Try build docs
runs-on: [self-hosted, linux, x64, fast]
needs: [pre_docs_job]
if: ${{ needs.pre_docs_job.outputs.should_skip != 'true' && github.repository == 'YosysHQ/Yosys' }}
if: ${{ needs.pre_docs_job.outputs.should_skip != 'true' && github.repository_owner == 'YosysHQ' }}
strategy:
matrix:
docs-target: [html, latexpdf]
@ -263,3 +279,14 @@ jobs:
name: docs-build-${{ matrix.docs-target }}
path: docs/build/
retention-days: 7
test-build-result:
runs-on: ubuntu-latest
needs:
- test-yosys
- test-cells
- test-docs
- test-docs-build
if: always() && !contains(join(needs.*.result, ','), 'failure') && !contains(join(needs.*.result, ','), 'cancelled')
steps:
- run: echo "All good"

View file

@ -1,22 +1,20 @@
name: Compiler testing
on:
# always test main
push:
branches:
- main
# test PRs
pull_request:
# allow triggering tests, ignores skip check
merge_group:
#push:
# branches: [ main ]
workflow_dispatch:
jobs:
pre_job:
runs-on: ubuntu-latest
outputs:
should_skip: ${{ steps.skip_check.outputs.should_skip }}
should_skip: ${{ steps.set_output.outputs.should_skip }}
steps:
- id: skip_check
if: ${{ github.event_name != 'merge_group' }}
uses: fkirc/skip-duplicate-actions@v5
with:
# don't run on documentation changes
@ -25,10 +23,18 @@ jobs:
# but never cancel main
cancel_others: ${{ github.ref != 'refs/heads/main' }}
- id: set_output
run: |
if [ "${{ github.event_name }}" = "merge_group" ]; then
echo "should_skip=false" >> $GITHUB_OUTPUT
else
echo "should_skip=${{ steps.skip_check.outputs.should_skip }}" >> $GITHUB_OUTPUT
fi
test-compile:
runs-on: ${{ matrix.os }}
needs: pre_job
if: needs.pre_job.outputs.should_skip != 'true'
if: (github.event_name == 'merge_group' || github.event_name == 'workflow_dispatch') && needs.pre_job.outputs.should_skip != 'true'
env:
CXXFLAGS: ${{ startsWith(matrix.compiler, 'gcc') && '-Wp,-D_GLIBCXX_ASSERTIONS' || ''}}
CC_SHORT: ${{ startsWith(matrix.compiler, 'gcc') && 'gcc' || 'clang' }}
@ -89,3 +95,11 @@ jobs:
run: |
make config-$CC_SHORT
make -j$procs CXXSTD=c++20 compile-only
test-compile-result:
runs-on: ubuntu-latest
needs:
- test-compile
if: always() && !contains(join(needs.*.result, ','), 'failure') && !contains(join(needs.*.result, ','), 'cancelled')
steps:
- run: echo "All good"

View file

@ -1,31 +1,41 @@
name: Check clang sanitizers
on:
# always test main
push:
branches:
- main
# ignore PRs due to time needed
# allow triggering tests, ignores skip check
pull_request:
merge_group:
#push:
# branches: [ main ]
workflow_dispatch:
jobs:
pre_job:
runs-on: ubuntu-latest
outputs:
should_skip: ${{ steps.skip_check.outputs.should_skip }}
should_skip: ${{ steps.set_output.outputs.should_skip }}
steps:
- id: skip_check
if: ${{ github.event_name != 'merge_group' }}
uses: fkirc/skip-duplicate-actions@v5
with:
# don't run on documentation changes
paths_ignore: '["**/README.md", "docs/**", "guidelines/**"]'
# cancel previous builds if a new commit is pushed
# but never cancel main
cancel_others: ${{ github.ref != 'refs/heads/main' }}
- id: set_output
run: |
if [ "${{ github.event_name }}" = "merge_group" ]; then
echo "should_skip=false" >> $GITHUB_OUTPUT
else
echo "should_skip=${{ steps.skip_check.outputs.should_skip }}" >> $GITHUB_OUTPUT
fi
run_san:
name: Build and run tests
runs-on: ${{ matrix.os }}
needs: pre_job
if: needs.pre_job.outputs.should_skip != 'true'
if: (github.event_name == 'merge_group' || github.event_name == 'workflow_dispatch') && needs.pre_job.outputs.should_skip != 'true'
env:
CC: clang
ASAN_OPTIONS: halt_on_error=1
@ -64,7 +74,7 @@ jobs:
- name: Run tests
shell: bash
run: |
make -j$procs test TARGETS= EXTRA_TARGETS=
make -j$procs vanilla-test TARGETS= EXTRA_TARGETS=
- name: Report errors
if: ${{ failure() }}
@ -72,7 +82,10 @@ jobs:
run: |
find tests/**/*.err -print -exec cat {} \;
- name: Run unit tests
shell: bash
run: |
make -j$procs unit-test ENABLE_LIBYOSYS=1
test-sanitizers-result:
runs-on: ubuntu-latest
needs:
- run_san
if: always() && !contains(join(needs.*.result, ','), 'failure') && !contains(join(needs.*.result, ','), 'cancelled')
steps:
- run: echo "All good"

109
.github/workflows/test-verific-cfg.yml vendored Normal file
View file

@ -0,0 +1,109 @@
name: Build various Verific configurations
on:
workflow_dispatch:
jobs:
test-verific-cfg:
if: github.repository_owner == 'YosysHQ'
runs-on: [self-hosted, linux, x64, fast]
steps:
- name: Checkout Yosys
uses: actions/checkout@v4
with:
persist-credentials: false
submodules: true
- name: Runtime environment
run: |
echo "procs=$(nproc)" >> $GITHUB_ENV
- name: verific [SV]
run: |
make config-clang
echo "ENABLE_VERIFIC := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_SYSTEMVERILOG := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_VHDL := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_HIER_TREE := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_EDIF := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_LIBERTY := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_YOSYSHQ_EXTENSIONS := 0" >> Makefile.conf
echo "ENABLE_CCACHE := 1" >> Makefile.conf
make -j$procs
- name: verific [VHDL]
run: |
make config-clang
echo "ENABLE_VERIFIC := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_SYSTEMVERILOG := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_VHDL := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_HIER_TREE := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_EDIF := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_LIBERTY := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_YOSYSHQ_EXTENSIONS := 0" >> Makefile.conf
echo "ENABLE_CCACHE := 1" >> Makefile.conf
make -j$procs
- name: verific [SV + VHDL]
run: |
make config-clang
echo "ENABLE_VERIFIC := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_SYSTEMVERILOG := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_VHDL := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_HIER_TREE := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_EDIF := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_LIBERTY := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_YOSYSHQ_EXTENSIONS := 0" >> Makefile.conf
echo "ENABLE_CCACHE := 1" >> Makefile.conf
make -j$procs
- name: verific [SV + HIER]
run: |
make config-clang
echo "ENABLE_VERIFIC := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_SYSTEMVERILOG := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_VHDL := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_HIER_TREE := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_EDIF := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_LIBERTY := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_YOSYSHQ_EXTENSIONS := 0" >> Makefile.conf
echo "ENABLE_CCACHE := 1" >> Makefile.conf
make -j$procs
- name: verific [VHDL + HIER]
run: |
make config-clang
echo "ENABLE_VERIFIC := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_SYSTEMVERILOG := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_VHDL := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_HIER_TREE := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_EDIF := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_LIBERTY := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_YOSYSHQ_EXTENSIONS := 0" >> Makefile.conf
echo "ENABLE_CCACHE := 1" >> Makefile.conf
make -j$procs
- name: verific [SV + VHDL + HIER]
run: |
make config-clang
echo "ENABLE_VERIFIC := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_SYSTEMVERILOG := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_VHDL := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_HIER_TREE := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_EDIF := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_LIBERTY := 0" >> Makefile.conf
echo "ENABLE_VERIFIC_YOSYSHQ_EXTENSIONS := 0" >> Makefile.conf
echo "ENABLE_CCACHE := 1" >> Makefile.conf
make -j$procs
- name: verific [SV + VHDL + HIER + EDIF + LIBERTY]
run: |
make config-clang
echo "ENABLE_VERIFIC := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_SYSTEMVERILOG := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_VHDL := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_HIER_TREE := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_EDIF := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_LIBERTY := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_YOSYSHQ_EXTENSIONS := 0" >> Makefile.conf
echo "ENABLE_CCACHE := 1" >> Makefile.conf
make -j$procs

View file

@ -1,22 +1,20 @@
name: Build and run tests with Verific (Linux)
on:
# always test main
push:
branches:
- main
# test PRs
pull_request:
# allow triggering tests, ignores skip check
merge_group:
#push:
# branches: [ main ]
workflow_dispatch:
jobs:
pre-job:
pre_job:
runs-on: ubuntu-latest
outputs:
should_skip: ${{ steps.skip_check.outputs.should_skip }}
should_skip: ${{ steps.set_output.outputs.should_skip }}
steps:
- id: skip_check
if: ${{ github.event_name != 'merge_group' }}
uses: fkirc/skip-duplicate-actions@v5
with:
# don't run on documentation changes
@ -25,9 +23,17 @@ jobs:
# but never cancel main
cancel_others: ${{ github.ref != 'refs/heads/main' }}
- id: set_output
run: |
if [ "${{ github.event_name }}" = "merge_group" ]; then
echo "should_skip=false" >> $GITHUB_OUTPUT
else
echo "should_skip=${{ steps.skip_check.outputs.should_skip }}" >> $GITHUB_OUTPUT
fi
test-verific:
needs: pre-job
if: ${{ needs.pre-job.outputs.should_skip != 'true' && github.repository == 'YosysHQ/Yosys' }}
needs: pre_job
if: ${{ needs.pre_job.outputs.should_skip != 'true' && github.repository_owner == 'YosysHQ' }}
runs-on: [self-hosted, linux, x64, fast]
steps:
- name: Checkout Yosys
@ -67,7 +73,7 @@ jobs:
- name: Run Yosys tests
run: |
make -j$procs test
make -j$procs vanilla-test
- name: Run Verific specific Yosys tests
run: |
@ -75,18 +81,13 @@ jobs:
cd tests/svtypes && bash run-test.sh
- name: Run SBY tests
if: ${{ github.ref == 'refs/heads/main' }}
if: ${{ github.event_name == 'merge_group' || github.event_name == 'workflow_dispatch' }}
run: |
make -C sby run_ci
- name: Run unit tests
shell: bash
run: |
make -j$procs unit-test ENABLE_LTO=1 ENABLE_LIBYOSYS=1
test-pyosys:
needs: pre-job
if: ${{ needs.pre-job.outputs.should_skip != 'true' && github.repository == 'YosysHQ/Yosys' }}
needs: pre_job
if: ${{ needs.pre_job.outputs.should_skip != 'true' && github.repository_owner == 'YosysHQ' }}
runs-on: [self-hosted, linux, x64, fast]
steps:
- name: Checkout Yosys
@ -122,3 +123,12 @@ jobs:
run: |
export PYTHONPATH=${GITHUB_WORKSPACE}/.local/usr/lib/python3/site-packages:$PYTHONPATH
python3 tests/pyosys/run_tests.py
test-verific-result:
runs-on: ubuntu-latest
needs:
- test-verific
- test-pyosys
if: always() && !contains(join(needs.*.result, ','), 'failure') && !contains(join(needs.*.result, ','), 'cancelled')
steps:
- run: echo "All good"

View file

@ -1,25 +0,0 @@
name: update-flake-lock
on:
workflow_dispatch: # allows manual triggering
schedule:
- cron: '0 0 * * 0' # runs weekly on Sunday at 00:00
jobs:
lockfile:
if: github.repository == 'YosysHQ/Yosys'
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main
- name: Update flake.lock
uses: DeterminateSystems/update-flake-lock@main
with:
token: ${{CI_CREATE_PR_TOKEN}}
pr-title: "Update flake.lock" # Title of PR to be created
pr-labels: | # Labels to be set on the PR
dependencies
automated

View file

@ -1,34 +0,0 @@
name: Bump version
on:
workflow_dispatch:
schedule:
- cron: '0 0 * * *'
jobs:
bump-version:
if: github.repository == 'YosysHQ/Yosys'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: true
persist-credentials: false
- name: Take last commit
id: log
run: echo "message=$(git log --no-merges -1 --oneline)" >> $GITHUB_OUTPUT
- name: Bump version
if: ${{ !contains(steps.log.outputs.message, 'Bump version') }}
run: |
make bumpversion
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git add Makefile
git commit -m "Bump version"
- name: Push changes # push the output folder to your repo
if: ${{ !contains(steps.log.outputs.message, 'Bump version') }}
uses: ad-m/github-push-action@master
with:
github_token: ${{ secrets.GITHUB_TOKEN }}

View file

@ -2,9 +2,58 @@
List of major changes and improvements between releases
=======================================================
Yosys 0.60 .. Yosys 0.61-dev
Yosys 0.63 .. Yosys 0.64-dev
--------------------------
Yosys 0.62 .. Yosys 0.63
--------------------------
* Various
- Added DSP inference for Gowin GW1N and GW2A.
- Added support for subtract in preadder for Xilinx arch.
- Added infrastructure to run a sat solver as a command.
* New commands and options
- Added "-ignore-unknown-cells" option to "equiv_induct"
and "equiv_simple" pass.
- Added "-force-params" option to "memory_libmap" pass.
- Added "-select-solver" option to "sat" pass.
- Added "-default_params" option to "write_verilog" pass.
- Added "-nodsp" option to "synth_gowin" pass.
Yosys 0.61 .. Yosys 0.62
--------------------------
* Various
- verific: Added "-sv2017" flag option to support System
Verilog 2017.
- verific: Added VHDL related flags to "-f" and "-F" and
support reading VHDL file from file lists.
- Updated cell libs with proper module declaration where
non standard (...) style was used.
* New commands and options
- Added "-word" option to "lut2mux" pass to enable emitting
word level cells.
- Added experimental "opt_balance_tree" pass to convert
cascaded cells into tree of cells to improve timing.
- Added "-gatesi" option to "write_blif" pass to init gates
under gates_mode in BLIF format.
- Added "-on" and "-off" options to "debug" pass for
persistent debug logging.
- Added "linux_perf" pass to control performance recording.
Yosys 0.60 .. Yosys 0.61
--------------------------
* Various
- Removed "cover" pass for coverage tracking.
- Avoid merging formal properties with "opt_merge" pass.
- Parallelize "opt_merge" pass.
* New commands and options
- Added "design_equal" pass to support fuzz-test comparison.
- Added "lut2bmux" pass to convert $lut to $bmux.
- Added "-legalize" option to "read_rtlil" pass to prevent
semantic errors.
Yosys 0.59 .. Yosys 0.60
--------------------------
* Various

View file

@ -1,70 +1,63 @@
# Introduction
# Contributing to Yosys
Thanks for thinking about contributing to the Yosys project. If this is your
Thanks for considering helping out. If this is your
first time contributing to an open source project, please take a look at the
following guide:
following guide about the basics:
https://opensource.guide/how-to-contribute/#orienting-yourself-to-a-new-project.
Information about the Yosys coding style is available on our Read the Docs:
https://yosys.readthedocs.io/en/latest/yosys_internals/extending_yosys/contributing.html.
## Asking questions
# Using the issue tracker
The [issue tracker](https://github.com/YosysHQ/yosys/issues) is used for
tracking bugs or other problems with Yosys or its documentation. It is also the
place to go for requesting new features.
When [creating a new issue](https://github.com/YosysHQ/yosys/issues/new/choose),
we have a few templates available. Please make use of these! It will make it
much easier for someone to respond and help.
### Bug reports
Before you submit an issue, please check out the [how-to guide for
`bugpoint`](https://yosys.readthedocs.io/en/latest/using_yosys/bugpoint.html).
This guide will take you through the process of using the [`bugpoint`
command](https://yosys.readthedocs.io/en/latest/cmd/bugpoint.html) in Yosys to
produce a [minimal, complete and verifiable
example](https://stackoverflow.com/help/minimal-reproducible-example) (MVCE).
Providing an MVCE with your bug report drastically increases the likelihood that
someone will be able to help resolve your issue.
# Using pull requests
If you are working on something to add to Yosys, or fix something that isn't
working quite right, make a [PR](https://github.com/YosysHQ/yosys/pulls)! An
open PR, even as a draft, tells everyone that you're working on it and they
don't have to. It can also be a useful way to solicit feedback on in-progress
changes. See below to find the best way to [ask us
questions](#asking-questions).
In general, all changes to the code are done as a PR, with [Continuous
Integration (CI)](https://github.com/YosysHQ/yosys/actions) tools that
automatically run the full suite of tests compiling and running Yosys. Please
make use of this! If you're adding a feature: add a test! Not only does it
verify that your feature is working as expected, but it can also be a handy way
for people to see how the feature is used. If you're fixing a bug: add a test!
If you can, do this first; it's okay if the test starts off failing - you
already know there is a bug. CI also helps to make sure that your changes still
work under a range of compilers, settings, and targets.
### Labels
We use [labels](https://github.com/YosysHQ/yosys/labels) to help categorise
issues and PRs. If a label seems relevant to your work, please do add it; this
also includes the labels beggining with 'status-'. The 'merge-' labels are used
by maintainers for tracking and communicating which PRs are ready and pending
merge; please do not use these labels if you are not a maintainer.
# Asking questions
If you have a question about how to use Yosys, please ask on our [Discourse forum](https://yosyshq.discourse.group/) or in our [discussions
page](https://github.com/YosysHQ/yosys/discussions).
If you have a question about how to use Yosys, please ask on our [Discourse forum](https://yosyshq.discourse.group/).
The Discourse is also a great place to ask questions about developing or
contributing to Yosys.
We have open [dev 'jour fixe' (JF) meetings](https://docs.google.com/document/d/1SapA6QAsJcsgwsdKJDgnGR2mr97pJjV4eeXg_TVJhRU/edit?usp=sharing) where developers from YosysHQ and the
community come together to discuss open issues and PRs. This is also a good
place to talk to us about how to implement larger PRs.
## Using the issue tracker
The [issue tracker](https://github.com/YosysHQ/yosys/issues) is used for
tracking bugs or other problems with Yosys or its documentation. It is also the
place to go for requesting new features.
### Bug reports
Learn more [here](https://yosyshq.readthedocs.io/projects/yosys/en/latest/yosys_internals/extending_yosys/contributing.html#reporting-bugs) about how to report bugs. We fix well-reported bugs the fastest.
## Contributing code
If you're adding complex functionality, or modifying core parts of Yosys,
we highly recommend discussing your motivation and approach
ahead of time on the [Discourse forum](https://yosyshq.discourse.group/).
### Using pull requests
If you are working on something to add to Yosys, or fix something that isn't
working quite right,
make a [pull request (PR)](https://github.com/YosysHQ/yosys/pulls).
An open PR, even as a draft, tells everyone that you're working on it and they
don't have to. It can also be a useful way to solicit feedback on in-progress
changes. See above to find the best way to [ask us questions](#asking-questions).
### Continuous integration
[Continuous Integration (CI)](https://github.com/YosysHQ/yosys/actions) tools
automatically compile Yosys and run it with the full suite of tests.
If you're a first time contributor, a maintainer has to trigger a run for you.
We test on various platforms, compilers. Sanitizer builds are only tested
on the main branch.
### Labels
We use [labels](https://github.com/YosysHQ/yosys/labels) to help categorise
issues and PRs. If a label seems relevant to your work, please do add it; this
also includes the labels beginning with 'status-'. The 'merge-' labels are used
by maintainers for tracking and communicating which PRs are ready and pending
merge; please do not use these labels if you are not a maintainer.
### Coding style
Learn more [here](https://yosys.readthedocs.io/en/latest/yosys_internals/extending_yosys/contributing.html).

View file

@ -1,6 +1,6 @@
ISC License
Copyright (C) 2012 - 2025 Claire Xenia Wolf <claire@yosyshq.com>
Copyright (C) 2012 - 2026 Claire Xenia Wolf <claire@yosyshq.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above

View file

@ -1,58 +0,0 @@
ARG IMAGE="python:3-slim-buster"
#---
FROM $IMAGE AS base
RUN apt-get update -qq \
&& DEBIAN_FRONTEND=noninteractive apt-get -y install --no-install-recommends \
ca-certificates \
clang \
lld \
curl \
libffi-dev \
libreadline-dev \
tcl-dev \
graphviz \
xdot \
&& apt-get autoclean && apt-get clean && apt-get -y autoremove \
&& update-ca-certificates \
&& rm -rf /var/lib/apt/lists
#---
FROM base AS build
RUN apt-get update -qq \
&& DEBIAN_FRONTEND=noninteractive apt-get -y install --no-install-recommends \
bison \
flex \
gawk \
gcc \
git \
iverilog \
pkg-config \
&& apt-get autoclean && apt-get clean && apt-get -y autoremove \
&& rm -rf /var/lib/apt/lists
COPY . /yosys
ENV PREFIX /opt/yosys
RUN cd /yosys \
&& make \
&& make install \
&& make test
#---
FROM base
COPY --from=build /opt/yosys /opt/yosys
ENV PATH /opt/yosys/bin:$PATH
RUN useradd -m yosys
USER yosys
CMD ["yosys"]

View file

@ -161,7 +161,19 @@ ifeq ($(OS), Haiku)
CXXFLAGS += -D_DEFAULT_SOURCE
endif
YOSYS_VER := 0.60+70
YOSYS_VER := 0.63
ifneq (, $(shell command -v git 2>/dev/null))
ifneq (, $(shell git rev-parse --git-dir 2>/dev/null))
GIT_COMMIT_COUNT := $(or $(shell git rev-list --count v$(YOSYS_VER)..HEAD 2>/dev/null),0)
ifneq ($(GIT_COMMIT_COUNT),0)
YOSYS_VER := $(YOSYS_VER)+$(GIT_COMMIT_COUNT)
endif
else
YOSYS_VER := $(YOSYS_VER)+post
endif
endif
YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1)
YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1)
YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2)
@ -185,9 +197,6 @@ endif
OBJS = kernel/version_$(GIT_REV).o
bumpversion:
sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 5bafeb7.. | wc -l`/;" Makefile
ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 ABC_USE_NAMESPACE=abc VERBOSE=$(Q)
# set ABCEXTERNAL = <abc-command> to use an external ABC instance
@ -604,6 +613,7 @@ $(eval $(call add_include_file,kernel/bitpattern.h))
$(eval $(call add_include_file,kernel/cellaigs.h))
$(eval $(call add_include_file,kernel/celledges.h))
$(eval $(call add_include_file,kernel/celltypes.h))
$(eval $(call add_include_file,kernel/newcelltypes.h))
$(eval $(call add_include_file,kernel/consteval.h))
$(eval $(call add_include_file,kernel/constids.inc))
$(eval $(call add_include_file,kernel/cost.h))
@ -638,6 +648,7 @@ $(eval $(call add_include_file,kernel/yosys_common.h))
$(eval $(call add_include_file,kernel/yw.h))
$(eval $(call add_include_file,libs/ezsat/ezsat.h))
$(eval $(call add_include_file,libs/ezsat/ezminisat.h))
$(eval $(call add_include_file,libs/ezsat/ezcmdline.h))
ifeq ($(ENABLE_ZLIB),1)
$(eval $(call add_include_file,libs/fst/fstapi.h))
endif
@ -645,8 +656,6 @@ $(eval $(call add_include_file,libs/sha1/sha1.h))
$(eval $(call add_include_file,libs/json11/json11.hpp))
$(eval $(call add_include_file,passes/fsm/fsmdata.h))
$(eval $(call add_include_file,passes/techmap/libparse.h))
$(eval $(call add_include_file,frontends/ast/ast.h))
$(eval $(call add_include_file,frontends/ast/ast_binding.h))
$(eval $(call add_include_file,frontends/blif/blifparse.h))
$(eval $(call add_include_file,backends/rtlil/rtlil_backend.h))
@ -685,6 +694,7 @@ OBJS += libs/json11/json11.o
OBJS += libs/ezsat/ezsat.o
OBJS += libs/ezsat/ezminisat.o
OBJS += libs/ezsat/ezcmdline.o
OBJS += libs/minisat/Options.o
OBJS += libs/minisat/SimpSolver.o
@ -793,9 +803,30 @@ endif
$(Q) mkdir -p $(dir $@)
$(P) $(CXX) -o $@ -c $(CPPFLAGS) $(CXXFLAGS) $<
YOSYS_REPO :=
ifneq (, $(shell command -v git 2>/dev/null))
ifneq (, $(shell git rev-parse --git-dir 2>/dev/null))
GIT_REMOTE := $(strip $(shell git config --get remote.origin.url 2>/dev/null | $(AWK) '{print tolower($$0)}'))
ifneq ($(strip $(GIT_REMOTE)),)
YOSYS_REPO := $(strip $(shell echo $(GIT_REMOTE) | $(AWK) -F '[:/]' '{gsub(/\.git$$/, "", $$NF); printf "%s/%s", $$(NF-1), $$NF}'))
endif
ifeq ($(strip $(YOSYS_REPO)),yosyshq/yosys)
YOSYS_REPO :=
endif
GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null)
ifeq ($(filter main HEAD release/v%,$(GIT_BRANCH)),)
YOSYS_REPO := $(YOSYS_REPO) at $(GIT_BRANCH)
endif
YOSYS_REPO := $(strip $(YOSYS_REPO))
endif
endif
YOSYS_GIT_STR := $(GIT_REV)$(GIT_DIRTY)
YOSYS_COMPILER := $(notdir $(CXX)) $(shell $(CXX) --version | tr ' ()' '\n' | grep '^[0-9]' | head -n1) $(filter -f% -m% -O% -DNDEBUG,$(CXXFLAGS))
YOSYS_VER_STR := Yosys $(YOSYS_VER) (git sha1 $(YOSYS_GIT_STR), $(YOSYS_COMPILER))
ifneq ($(strip $(YOSYS_REPO)),)
YOSYS_VER_STR := $(YOSYS_VER_STR) [$(YOSYS_REPO)]
endif
kernel/version_$(GIT_REV).cc: $(YOSYS_SRC)/Makefile
$(P) rm -f kernel/version_*.o kernel/version_*.d kernel/version_*.cc
@ -889,6 +920,7 @@ endif
# Tests that generate .mk with tests/gen-tests-makefile.sh
MK_TEST_DIRS =
MK_TEST_DIRS += tests/arch/analogdevices
MK_TEST_DIRS += tests/arch/anlogic
MK_TEST_DIRS += tests/arch/ecp5
MK_TEST_DIRS += tests/arch/efinix
@ -979,9 +1011,11 @@ makefile-tests/%: %/run-test.mk $(TARGETS) $(EXTRA_TARGETS)
$(MAKE) -C $* -f run-test.mk
+@echo "...passed tests in $*"
test: makefile-tests abcopt-tests seed-tests
test: vanilla-test unit-test
vanilla-test: makefile-tests abcopt-tests seed-tests
@echo ""
@echo " Passed \"make test\"."
@echo " Passed \"make vanilla-test\"."
ifeq ($(ENABLE_VERIFIC),1)
ifeq ($(YOSYS_NOVERIFIC),1)
@echo " Ran tests without verific support due to YOSYS_NOVERIFIC=1."
@ -1013,11 +1047,11 @@ ystests: $(TARGETS) $(EXTRA_TARGETS)
# Unit test
unit-test: libyosys.so
@$(MAKE) -C $(UNITESTPATH) CXX="$(CXX)" CC="$(CC)" CPPFLAGS="$(CPPFLAGS)" \
@$(MAKE) -f $(UNITESTPATH)/Makefile CXX="$(CXX)" CC="$(CC)" CPPFLAGS="$(CPPFLAGS)" \
CXXFLAGS="$(CXXFLAGS)" LINKFLAGS="$(LINKFLAGS)" LIBS="$(LIBS)" ROOTPATH="$(CURDIR)"
clean-unit-test:
@$(MAKE) -C $(UNITESTPATH) clean
@$(MAKE) -f $(UNITESTPATH)/Makefile clean
install-dev: $(PROGRAM_PREFIX)yosys-config share
$(INSTALL_SUDO) mkdir -p $(DESTDIR)$(BINDIR)

View file

@ -114,8 +114,8 @@ To build Yosys simply type 'make' in this directory.
$ sudo make install
Tests are located in the tests subdirectory and can be executed using the test
target. Note that you need gawk as well as a recent version of iverilog (i.e.
build from git). Then, execute tests via:
target. Note that you need gawk, a recent version of iverilog, and gtest.
Execute tests via:
$ make test
@ -246,6 +246,8 @@ Building the documentation
Note that there is no need to build the manual if you just want to read it.
Simply visit https://yosys.readthedocs.io/en/latest/ instead.
If you're offline, you can read the sources, replacing `.../en/latest`
with `docs/source`.
In addition to those packages listed above for building Yosys from source, the
following are used for building the website:

2
abc

@ -1 +1 @@
Subproject commit ef74590ebd78b3b707eeba56d8284faf018affa6
Subproject commit b4a657e75b16b68c514a7326642ea074f8460939

View file

@ -930,7 +930,9 @@ struct AigerBackend : public Backend {
log(" make indexes zero based, enable using map files with smt solvers.\n");
log("\n");
log(" -ywmap <filename>\n");
log(" write a map file for conversion to and from yosys witness traces.\n");
log(" write a map file for conversion to and from yosys witness traces,\n");
log(" also allows for mapping AIGER bad-state properties and invariant\n");
log(" constraints back to individual formal properties by name.\n");
log("\n");
log(" -I, -O, -B, -L\n");
log(" If the design contains no input/output/assert/flip-flop then create one\n");

View file

@ -23,7 +23,7 @@
// - zero-width operands
#include "kernel/register.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
#include "kernel/rtlil.h"
USING_YOSYS_NAMESPACE
@ -45,8 +45,22 @@ PRIVATE_NAMESPACE_BEGIN
// TODO
//#define ARITH_OPS ID($add), ID($sub), ID($neg)
#define KNOWN_OPS BITWISE_OPS, REDUCE_OPS, LOGIC_OPS, GATE_OPS, ID($pos), CMP_OPS, \
ID($pmux), ID($bmux) /*, ARITH_OPS*/
static constexpr auto known_ops = []() constexpr {
StaticCellTypes::Categories::Category c{};
for (auto id : {BITWISE_OPS})
c.set_id(id);
for (auto id : {REDUCE_OPS})
c.set_id(id);
for (auto id : {LOGIC_OPS})
c.set_id(id);
for (auto id : {GATE_OPS})
c.set_id(id);
for (auto id : {CMP_OPS})
c.set_id(id);
for (auto id : {ID($pos), ID($pmux), ID($bmux)})
c.set_id(id);
return c;
}();
template<typename Writer, typename Lit, Lit CFALSE, Lit CTRUE>
struct Index {
@ -92,7 +106,7 @@ struct Index {
int pos = index_wires(info, m);
for (auto cell : m->cells()) {
if (cell->type.in(KNOWN_OPS) || cell->type.in(ID($scopeinfo), ID($specify2), ID($specify3), ID($input_port)))
if (known_ops(cell->type) || cell->type.in(ID($scopeinfo), ID($specify2), ID($specify3), ID($input_port)))
continue;
Module *submodule = m->design->module(cell->type);
@ -104,7 +118,7 @@ struct Index {
pos += index_module(submodule);
} else {
if (allow_blackboxes) {
info.found_blackboxes.insert(cell);
info.found_blackboxes.insert(cell);
} else {
// Even if we don't allow blackboxes these might still be
// present outside of any traversed input cones, so we
@ -269,7 +283,7 @@ struct Index {
} else if (cell->type.in(ID($lt), ID($le), ID($gt), ID($ge))) {
if (cell->type.in(ID($gt), ID($ge)))
std::swap(aport, bport);
int carry = cell->type.in(ID($le), ID($ge)) ? CFALSE : CTRUE;
int carry = cell->type.in(ID($le), ID($ge)) ? CFALSE : CTRUE;
Lit a = Writer::EMPTY_LIT;
Lit b = Writer::EMPTY_LIT;
// TODO: this might not be the most economic structure; revisit at a later date
@ -579,7 +593,7 @@ struct Index {
// an output of a cell
Cell *driver = bit.wire->driverCell();
if (driver->type.in(KNOWN_OPS)) {
if (known_ops(driver->type)) {
ret = impl_op(cursor, driver, bit.wire->driverPort(), bit.offset);
} else {
Module *def = cursor.enter(*this, driver);
@ -916,15 +930,15 @@ struct XAigerWriter : AigerWriter {
std::vector<HierBit> pos;
std::vector<HierBit> pis;
// * The aiger output port sequence is COs (inputs to modeled boxes),
// inputs to opaque boxes, then module outputs. COs going first is
// required by abc.
// * proper_pos_counter counts ports which follow after COs
// * The mapping file `pseudopo` and `po` statements use indexing relative
// to the first port following COs.
// * If a module output is directly driven by an opaque box, the emission
// of the po statement in the mapping file is skipped. This is done to
// aid re-integration of the mapped result.
// * The aiger output port sequence is COs (inputs to modeled boxes),
// inputs to opaque boxes, then module outputs. COs going first is
// required by abc.
// * proper_pos_counter counts ports which follow after COs
// * The mapping file `pseudopo` and `po` statements use indexing relative
// to the first port following COs.
// * If a module output is directly driven by an opaque box, the emission
// of the po statement in the mapping file is skipped. This is done to
// aid re-integration of the mapped result.
int proper_pos_counter = 0;
pool<SigBit> driven_by_opaque_box;
@ -1331,41 +1345,50 @@ struct Aiger2Backend : Backend {
log(" perform structural hashing while writing\n");
log("\n");
log(" -flatten\n");
log(" allow descending into submodules and write a flattened view of the design\n");
log(" hierarchy starting at the selected top\n");
log("\n");
log(" allow descending into submodules and write a flattened view of the design\n");
log(" hierarchy starting at the selected top\n");
log("\n");
log("This command is able to ingest all combinational cells except for:\n");
log("\n");
pool<IdString> supported = {KNOWN_OPS};
CellTypes ct;
ct.setup_internals_eval();
log(" ");
int col = 0;
for (auto pair : ct.cell_types)
if (!supported.count(pair.first)) {
if (col + pair.first.size() + 2 > 72) {
for (size_t i = 0; i < StaticCellTypes::builder.count; i++) {
auto &cell = StaticCellTypes::builder.cells[i];
if (!cell.features.is_evaluable)
continue;
if (cell.features.is_stdcell)
continue;
if (known_ops(cell.type))
continue;
std::string name = log_id(cell.type);
if (col + name.size() + 2 > 72) {
log("\n ");
col = 0;
}
col += pair.first.size() + 2;
log("%s, ", log_id(pair.first));
col += name.size() + 2;
log("%s, ", name.c_str());
}
log("\n");
log("\n");
log("And all combinational gates except for:\n");
log("\n");
CellTypes ct2;
ct2.setup_stdcells();
log(" ");
col = 0;
for (auto pair : ct2.cell_types)
if (!supported.count(pair.first)) {
if (col + pair.first.size() + 2 > 72) {
for (size_t i = 0; i < StaticCellTypes::builder.count; i++) {
auto &cell = StaticCellTypes::builder.cells[i];
if (!cell.features.is_evaluable)
continue;
if (!cell.features.is_stdcell)
continue;
if (known_ops(cell.type))
continue;
std::string name = log_id(cell.type);
if (col + name.size() + 2 > 72) {
log("\n ");
col = 0;
}
col += pair.first.size() + 2;
log("%s, ", log_id(pair.first));
col += name.size() + 2;
log("%s, ", name.c_str());
}
log("\n");
}
@ -1423,20 +1446,20 @@ struct XAiger2Backend : Backend {
log(" perform structural hashing while writing\n");
log("\n");
log(" -flatten\n");
log(" allow descending into submodules and write a flattened view of the design\n");
log(" hierarchy starting at the selected top\n");
log("\n");
log(" -mapping_prep\n");
log(" after the file is written, prepare the module for reintegration of\n");
log(" a mapping in a subsequent command. all cells which are not blackboxed nor\n");
log(" whiteboxed are removed from the design as well as all wires which only\n");
log(" connect to removed cells\n");
log(" (conflicts with -flatten)\n");
log("\n");
log(" -map2 <file>\n");
log(" write a map2 file which 'read_xaiger2 -sc_mapping' can read to\n");
log(" reintegrate a mapping\n");
log(" (conflicts with -flatten)\n");
log(" allow descending into submodules and write a flattened view of the design\n");
log(" hierarchy starting at the selected top\n");
log("\n");
log(" -mapping_prep\n");
log(" after the file is written, prepare the module for reintegration of\n");
log(" a mapping in a subsequent command. all cells which are not blackboxed nor\n");
log(" whiteboxed are removed from the design as well as all wires which only\n");
log(" connect to removed cells\n");
log(" (conflicts with -flatten)\n");
log("\n");
log(" -map2 <file>\n");
log(" write a map2 file which 'read_xaiger2 -sc_mapping' can read to\n");
log(" reintegrate a mapping\n");
log(" (conflicts with -flatten)\n");
log("\n");
}

View file

@ -24,7 +24,7 @@
#include "kernel/rtlil.h"
#include "kernel/register.h"
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
#include "kernel/log.h"
#include <string>
@ -44,6 +44,7 @@ struct BlifDumperConfig
bool iattr_mode;
bool blackbox_mode;
bool noalias_mode;
bool gatesi_mode;
std::string buf_type, buf_in, buf_out;
std::map<RTLIL::IdString, std::pair<RTLIL::IdString, RTLIL::IdString>> unbuf_types;
@ -51,7 +52,7 @@ struct BlifDumperConfig
BlifDumperConfig() : icells_mode(false), conn_mode(false), impltf_mode(false), gates_mode(false),
cname_mode(false), iname_mode(false), param_mode(false), attr_mode(false), iattr_mode(false),
blackbox_mode(false), noalias_mode(false) { }
blackbox_mode(false), noalias_mode(false), gatesi_mode(false) { }
};
struct BlifDumper
@ -60,7 +61,7 @@ struct BlifDumper
RTLIL::Module *module;
RTLIL::Design *design;
BlifDumperConfig *config;
CellTypes ct;
NewCellTypes ct;
SigMap sigmap;
dict<SigBit, int> init_bits;
@ -118,16 +119,21 @@ struct BlifDumper
return str;
}
const std::string str_init(RTLIL::SigBit sig)
template <bool Space = true> const std::string str_init(RTLIL::SigBit sig)
{
sigmap.apply(sig);
if (init_bits.count(sig) == 0)
return " 2";
if (init_bits.count(sig) == 0) {
if constexpr (Space)
return " 2";
else
return "2";
}
string str = stringf(" %d", init_bits.at(sig));
return str;
if constexpr (Space)
return stringf(" %d", init_bits.at(sig));
else
return stringf("%d", init_bits.at(sig));
}
const char *subckt_or_gate(std::string cell_type)
@ -469,6 +475,11 @@ struct BlifDumper
f << stringf(".names %s %s\n1 1\n", str(rhs_bit), str(lhs_bit));
}
if (config->gatesi_mode) {
for (auto &&init_bit : init_bits)
f << stringf(".gateinit %s=%s\n", str(init_bit.first), str_init<false>(init_bit.first));
}
f << stringf(".end\n");
}
@ -550,6 +561,9 @@ struct BlifBackend : public Backend {
log(" -impltf\n");
log(" do not write definitions for the $true, $false and $undef wires.\n");
log("\n");
log(" -gatesi\n");
log(" write initial bit(s) with .gateinit for gates that needs to be initialized.\n");
log("\n");
}
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
{
@ -640,6 +654,10 @@ struct BlifBackend : public Backend {
config.noalias_mode = true;
continue;
}
if (args[argidx] == "-gatesi") {
config.gatesi_mode = true;
continue;
}
break;
}
extra_args(f, filename, args, argidx);

View file

@ -614,7 +614,7 @@ struct value : public expr_base<value<Bits>> {
int64_t divisor_shift = divisor.ctlz() - dividend.ctlz();
assert(divisor_shift >= 0);
divisor = divisor.shl(value<Bits>{(chunk::type) divisor_shift});
for (size_t step = 0; step <= divisor_shift; step++) {
for (size_t step = 0; step <= (uint64_t) divisor_shift; step++) {
quotient = quotient.shl(value<Bits>{1u});
if (!dividend.ucmp(divisor)) {
dividend = dividend.sub(divisor);
@ -1119,7 +1119,7 @@ struct fmt_part {
case STRING: {
buf.reserve(Bits/8);
for (int i = 0; i < Bits; i += 8) {
for (size_t i = 0; i < Bits; i += 8) {
char ch = 0;
for (int j = 0; j < 8 && i + j < int(Bits); j++)
if (val.bit(i + j))

View file

@ -23,7 +23,7 @@
#include "kernel/rtlil.h"
#include "kernel/register.h"
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
#include "kernel/log.h"
#include <string>
@ -138,7 +138,7 @@ struct EdifBackend : public Backend {
bool lsbidx = false;
std::map<RTLIL::IdString, std::map<RTLIL::IdString, int>> lib_cell_ports;
bool nogndvcc = false, gndvccy = false, keepmode = false;
CellTypes ct(design);
NewCellTypes ct(design);
EdifNames edif_names;
size_t argidx;

View file

@ -20,7 +20,7 @@
#include "kernel/rtlil.h"
#include "kernel/register.h"
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
#include "kernel/log.h"
#include <string>
@ -117,7 +117,7 @@ struct IntersynthBackend : public Backend {
std::set<std::string> conntypes_code, celltypes_code;
std::string netlists_code;
CellTypes ct(design);
NewCellTypes ct(design);
for (auto lib : libs)
ct.setup_design(lib);

View file

@ -20,7 +20,7 @@
#include "kernel/rtlil.h"
#include "kernel/register.h"
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
#include "kernel/log.h"
#include "kernel/mem.h"
#include "libs/json11/json11.hpp"
@ -32,7 +32,7 @@ PRIVATE_NAMESPACE_BEGIN
struct Smt2Worker
{
CellTypes ct;
NewCellTypes ct;
SigMap sigmap;
RTLIL::Module *module;
bool bvmode, memmode, wiresmode, verbose, statebv, statedt, forallmode;

View file

@ -735,6 +735,12 @@ def ywfile_signal(sig, step, mask=None):
output = []
def ywfile_signal_error(reason, detail=None):
msg = f"Yosys witness signal mismatch for {sig.pretty()}: {reason}"
if detail:
msg += f" ({detail})"
raise ValueError(msg)
if sig.path in smt_wires:
for wire in smt_wires[sig.path]:
width, offset = wire["width"], wire["offset"]
@ -765,6 +771,12 @@ def ywfile_signal(sig, step, mask=None):
for mem in smt_mems[sig.memory_path]:
width, size, bv = mem["width"], mem["size"], mem["statebv"]
if sig.memory_addr is not None and sig.memory_addr >= size:
ywfile_signal_error(
"memory address out of bounds",
f"address={sig.memory_addr} size={size}",
)
smt_expr = smt.net_expr(topmod, f"s{step}", mem["smtpath"])
if bv:
@ -781,18 +793,34 @@ def ywfile_signal(sig, step, mask=None):
smt_expr = "((_ extract %d %d) %s)" % (slice_high, sig.offset, smt_expr)
output.append((0, sig.width, smt_expr))
else:
ywfile_signal_error("memory not found in design")
output.sort()
output = [chunk for chunk in output if chunk[0] != chunk[1]]
if not output:
if sig.memory_path:
ywfile_signal_error("memory signal has no matching bits in design")
else:
ywfile_signal_error("signal not found in design")
pos = 0
for start, end, smt_expr in output:
assert start == pos
if start != pos:
ywfile_signal_error(
"signal width/offset mismatch",
f"expected coverage at bit {pos}",
)
pos = end
assert pos == sig.width
if pos != sig.width:
ywfile_signal_error(
"signal width/offset mismatch",
f"covered {pos} of {sig.width} bits",
)
if len(output) == 1:
return output[0][-1]

View file

@ -20,7 +20,7 @@
#include "kernel/rtlil.h"
#include "kernel/register.h"
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
#include "kernel/log.h"
#include <string>
@ -29,7 +29,7 @@ PRIVATE_NAMESPACE_BEGIN
struct SmvWorker
{
CellTypes ct;
NewCellTypes ct;
SigMap sigmap;
RTLIL::Module *module;
std::ostream &f;

View file

@ -95,7 +95,8 @@ bool VERILOG_BACKEND::id_is_verilog_escaped(const std::string &str) {
PRIVATE_NAMESPACE_BEGIN
bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, extmem, defparam, decimal, siminit, systemverilog, simple_lhs, noparallelcase;
bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, extmem, defparam, decimal, siminit, systemverilog, simple_lhs,
noparallelcase, default_params;
int auto_name_counter, auto_name_offset, auto_name_digits, extmem_counter;
dict<RTLIL::IdString, int> auto_name_map;
std::set<RTLIL::IdString> reg_wires;
@ -421,6 +422,13 @@ void dump_attributes(std::ostream &f, std::string indent, dict<RTLIL::IdString,
}
}
void dump_parameter(std::ostream &f, std::string indent, RTLIL::IdString id_string, RTLIL::Const parameter)
{
f << stringf("%sparameter %s = ", indent.c_str(), id(id_string).c_str());
dump_const(f, parameter);
f << ";\n";
}
void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire)
{
dump_attributes(f, indent, wire->attributes, "\n", /*modattr=*/false, /*regattr=*/reg_wires.count(wire->name));
@ -2143,6 +2151,9 @@ void dump_case_actions(std::ostream &f, std::string indent, RTLIL::CaseRule *cs)
bool dump_proc_switch_ifelse(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw)
{
if (sw->cases.empty())
return true;
for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) {
if ((*it)->compare.size() == 0) {
break;
@ -2435,6 +2446,10 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
f << indent + " " << "reg " << id(initial_id) << " = 0;\n";
}
if (default_params)
for (auto p : module->parameter_default_values)
dump_parameter(f, indent + " ", p.first, p.second);
// first dump input / output according to their order in module->ports
for (auto port : module->ports)
dump_wire(f, indent + " ", module->wire(port));
@ -2542,6 +2557,10 @@ struct VerilogBackend : public Backend {
log(" use 'defparam' statements instead of the Verilog-2001 syntax for\n");
log(" cell parameters.\n");
log("\n");
log(" -default_params\n");
log(" emit module parameter declarations from\n");
log(" parameter_default_values.\n");
log("\n");
log(" -blackboxes\n");
log(" usually modules with the 'blackbox' attribute are ignored. with\n");
log(" this option set only the modules with the 'blackbox' attribute\n");
@ -2579,6 +2598,7 @@ struct VerilogBackend : public Backend {
siminit = false;
simple_lhs = false;
noparallelcase = false;
default_params = false;
auto_prefix = "";
bool blackboxes = false;
@ -2639,6 +2659,10 @@ struct VerilogBackend : public Backend {
defparam = true;
continue;
}
if (arg == "-defaultparams") {
default_params = true;
continue;
}
if (arg == "-decimal") {
decimal = true;
continue;

View file

@ -17,6 +17,7 @@ coarse:
opt_clean
memory_collect
opt -noff -keepdc -fast
sort
check:
stat

View file

@ -5,8 +5,8 @@ import os
project = 'YosysHQ Yosys'
author = 'YosysHQ GmbH'
copyright ='2025 YosysHQ GmbH'
yosys_ver = "0.60"
copyright ='2026 YosysHQ GmbH'
yosys_ver = "0.63"
# select HTML theme
html_theme = 'furo-ys'

View file

@ -3,9 +3,9 @@ Symbolic model checking
.. todo:: check text context
.. note::
While it is possible to perform model checking directly in Yosys, it
.. note::
While it is possible to perform model checking directly in Yosys, it
is highly recommended to use SBY or EQY for formal hardware verification.
Symbolic Model Checking (SMC) is used to formally prove that a circuit has (or
@ -117,3 +117,32 @@ Result with fixed :file:`axis_master.v`:
Solving problem with 159144 variables and 441626 clauses..
SAT proof finished - no model found: SUCCESS!
Witness framework and per-property tracking
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When using AIGER-based formal verification flows (such as the ``abc`` engine in
SBY), Yosys provides infrastructure for tracking individual formal
properties through the verification pipeline.
The `rename -witness` pass assigns public names to all cells that participate in
the witness framework:
- Witness signal cells: `$anyconst`, `$anyseq`, `$anyinit`,
`$allconst`, `$allseq`
- Formal property cells: `$assert`, `$assume`, `$cover`, `$live`,
`$fair`, `$check`
These public names allow downstream tools to refer to individual properties by
their hierarchical path rather than by anonymous internal identifiers.
The `write_aiger -ywmap` option generates a map file for conversion to and from
Yosys witness traces, and also allows for mapping AIGER bad-state properties and
invariant constraints back to individual formal properties by name. This enables
features like per-property pass/fail reporting (e.g. ``abc pdr`` with
``--keep-going`` mode).
The `write_smt2` backend similarly uses the public witness names when emitting
SMT2 comments. Cells whose ``hdlname`` attribute contains the ``_witness_``
marker are treated as having private names for comment purposes, keeping solver
output clean.

View file

@ -355,6 +355,9 @@ from SystemVerilog:
design with `read_verilog`, all its packages are available to SystemVerilog
files being read into the same design afterwards.
- nested packages are currently not supported (i.e. calling ``import`` inside
a ``package`` .. ``endpackage`` block)
- typedefs are supported (including inside packages)
- type casts are currently not supported

View file

@ -1,57 +1,16 @@
Contributing to Yosys
=====================
.. note::
For information on making a pull request on github, refer to our
|CONTRIBUTING|_ file.
.. |CONTRIBUTING| replace:: :file:`CONTRIBUTING.md`
.. _CONTRIBUTING: https://github.com/YosysHQ/yosys/blob/main/CONTRIBUTING.md
Coding Style
------------
Formatting of code
~~~~~~~~~~~~~~~~~~
- Yosys code is using tabs for indentation. Tabs are 8 characters.
- A continuation of a statement in the following line is indented by two
additional tabs.
- Lines are as long as you want them to be. A good rule of thumb is to break
lines at about column 150.
- Opening braces can be put on the same or next line as the statement opening
the block (if, switch, for, while, do). Put the opening brace on its own line
for larger blocks, especially blocks that contains blank lines.
- Otherwise stick to the `Linux Kernel Coding Style`_.
.. _Linux Kernel Coding Style: https://www.kernel.org/doc/Documentation/process/coding-style.rst
C++ Language
~~~~~~~~~~~~
Yosys is written in C++17.
In general Yosys uses ``int`` instead of ``size_t``. To avoid compiler warnings
for implicit type casts, always use ``GetSize(foobar)`` instead of
``foobar.size()``. (``GetSize()`` is defined in :file:`kernel/yosys.h`)
Use range-based for loops whenever applicable.
Reporting bugs
--------------
- use the `bug report template`_
A good bug report includes the following information:
.. _bug report template: https://github.com/YosysHQ/yosys/issues/new?template=bug_report.yml
- short title briefly describing the issue, e.g.
Title
~~~~~
briefly describe the issue, for example:
techmap of wide mux with undefined inputs raises error during synth_xilinx
@ -64,10 +23,18 @@ Reporting bugs
Reproduction Steps
~~~~~~~~~~~~~~~~~~
- ideally a code-block (starting and ending with triple backquotes) containing
the minimized design (Verilog or RTLIL), followed by a code-block containing
the minimized yosys script OR a command line call to yosys with
code-formatting (starting and ending with single backquotes)
The reproduction steps should be a minimal, complete and verifiable
example `MVCE`_.
Providing an MVCE with your bug report drastically increases the likelihood that
someone will be able to help resolve your issue.
One way to minimize a design is to use the `bugpoint_` command.
You can learn more in the `how-to guide for bugpoint_`.
The reproduction steps are ideally a code-block (starting and ending with
triple backquotes) containing
the minimized design (Verilog or RTLIL), followed by a code-block containing
the minimized yosys script OR a command line call to yosys with
code-formatting (starting and ending with single backquotes).
.. code-block:: markdown
@ -86,9 +53,9 @@ Reproduction Steps
`yosys -p ': minimum sequence of commands;' min.v`
- alternatively can provide a single code-block which includes the minimized
design as a "here document" followed by the sequence of commands which
reproduce the error
Alternatively, you can provide a single code-block which includes the minimized
design as a "here document" followed by the sequence of commands which
reproduce the error
+ see :doc:`/using_yosys/more_scripting/load_design` for more on heredocs.
@ -101,7 +68,9 @@ Reproduction Steps
# minimum sequence of commands
```
- any environment variables or command line options should also be mentioned
Don't forget to mention:
- any important environment variables or command line options
- if the problem occurs for a range of values/designs, what is that range
- if you're using an external tool, such as ``valgrind``, to detect the issue,
what version of that tool are you using and what options are you giving it
@ -115,46 +84,58 @@ Reproduction Steps
around Yosys such as OpenLane; you should instead minimize your input and
reproduction steps to just the Yosys part.
"Expected Behaviour"
~~~~~~~~~~~~~~~~~~~~
.. _MVCE: https://stackoverflow.com/help/minimal-reproducible-example
.. _bugpoint: https://yosys.readthedocs.io/en/latest/cmd/bugpoint.html
.. _how-to guide for bugpoint: https://yosys.readthedocs.io/en/latest/using_yosys/bugpoint.html
- if you have a similar design/script that doesn't give the error, include it
here as a reference
- if the bug is that an error *should* be raised but isn't, are there any other
commands with similar error messages
"Actual Behaviour"
Expected Behaviour
~~~~~~~~~~~~~~~~~~
- any error messages go here
- any details relevant to the crash that were found with ``--trace`` or
``--debug`` flags
- if you identified the point of failure in the source code, you could mention
it here, or as a comment below
Describe what you'd expect to happen when we follow the reproduction steps
if the bug was fixed.
+ if possible, use a permalink to the source on GitHub
+ you can browse the source repository for a certain commit with the failure
If you have a similar design/script that doesn't give the error, include it
here as a reference. If the bug is that an error *should* be raised but isn't,
note if there are any other commands with similar error messages.
Actual Behaviour
~~~~~~~~~~~~~~~~
Describe what you actually see when you follow the reproduction steps.
This can include:
* any error messages
* any details relevant to the crash that were found with ``--trace`` or
``--debug`` flags
* the part of the source code that triggers the bug
* if possible, use a permalink to the source on GitHub
* you can browse the source repository for a certain commit with the failure
and open the source file, select the relevant lines (click on the line
number for the first relevant line, then while holding shift click on the
line number for the last relevant line), click on the ``...`` that appears
and select "Copy permalink"
+ should look something like
* should look something like
``https://github.com/YosysHQ/yosys/blob/<commit_hash>/path/to/file#L139-L147``
+ clicking on "Preview" should reveal a code block containing the lines of
* clicking on "Preview" should reveal a code block containing the lines of
source specified, with a link to the source file at the given commit
Additional details
Additional Details
~~~~~~~~~~~~~~~~~~
- once you have created the issue, any additional details can be added as a
comment on that issue
- could include any additional context as to what you were doing when you first
encountered the bug
- was this issue discovered through the use of a fuzzer
- if you've minimized the script, consider including the `bugpoint` script you
used, or the original script, e.g.
Anything else you think might be helpful or relevant when verifying or fixing
the bug.
Once you have created the issue, any additional details can be added as a
comment on that issue. You can include any additional context as to what you
were doing when you first encountered the bug.
If this issue discovered through the use of a fuzzer, ALWAYS declare that.
If you've minimized the script, consider including the `bugpoint` script you
used, or the original script, for example:
.. code-block:: markdown
@ -171,8 +152,226 @@ Additional details
Minimized from
`yosys -p ': original sequence of commands to produce error;' design.v`
- if you're able to, it may also help to share the original un-minimized design
+ if the design is too big for a comment, consider turning it into a `Gist`_
If possible, it may also help to share the original un-minimized design.
If the design is too big for a comment, consider turning it into a `Gist`_
.. _Gist: https://gist.github.com/
Contributing code
-----------------
Code that matters
~~~~~~~~~~~~~~~~~
If you're adding complex functionality, or modifying core parts of yosys,
we highly recommend discussing your motivation and approach
ahead of time on the `Discourse forum`_. Please, be as explicit and concrete
as possible when explaining the motivation for what you're building.
Additionally, if you do so on the forum first before you starting hacking
away at C++, you might solve your problem without writing a single line
of code!
PRs are considered for relevance, priority, and quality
based on their descriptions first, code second.
Before you build or fix something, also search for existing `issues`_.
.. _`Discourse forum`: https://yosyshq.discourse.group/
.. _`issues`: https://github.com/YosysHQ/yosys/issues
Making sense
~~~~~~~~~~~~
Given enough effort, the behavior of any code can be figured out to any
desired extent. However, the author of the code is by far in the best
position to make this as easy as possible.
Yosys is a long-standing project and has accumulated a lot of C-style code
that's not written to be read, just written to run. We improve this bit
by bit when opportunities arise, but it is what it is.
New additions are expected to be a lot cleaner.
The purpose and behavior of the code changed should be described clearly.
Your change should contain exactly what it needs to match that description.
This means:
* nothing more than that - no dead code, no undocumented features
* nothing missing - if something is partially built, that's fine,
but you have to make that clear. For example, some passes
only support some types of cells
Here are some software engineering approaches that help:
* Use abstraction to model the problem and hide details
* Maximize the usage of types (structs over loose variables),
not necessarily in an object-oriented way
* Use functions, scopes, type aliases
* In new passes, make sure the logic behind how and why it works is actually provided
in coherent comments, and that variable and type naming is consistent with the terms
you use in the description.
* The logic of the implementation should be described in mathematical
or algorithm theory terms. Correctness, termination, computational complexity.
Make it clear if you're re-implementing a classic data structure for logic synthesis
or graph traversal etc.
* There's various ways of traversing the design with use-def indices (for getting
drivers and driven signals) available in Yosys. They have advantages and sometimes
disadvantages. Prefer not re-implementing these
* Prefer references over pointers, and smart pointers over raw pointers
* Aggressively deduplicate code. Within functions, within passes,
across passes, even against existing code
* Prefer declaring things ``const``
* Prefer range-based for loops over C-style
Common mistakes
~~~~~~~~~~~~~~~
* Deleting design objects invalidates iterators. Defer deletions or hold a copy
of the list of pointers to design objects
* Deleting wires can get sketchy and is intended to be done solely by
the ``opt_clean`` pass so just don't do it
* Iterating over an entire design and checking if things are selected is more
inefficient than using the ``selected_*`` methods
* Remember to call ``fixup_ports`` at the end if you're modifying module interfaces
Testing your change
~~~~~~~~~~~~~~~~~~~
Untested code can't be maintained. Inevitable codebase-wide changes
are likely to break anything untested. Tests also help reviewers understand
the purpose of the code change in practice.
Your code needs to come with tests. If it's a feature, a test that covers
representative examples of the added behavior. If it's a bug fix, it should
reproduce the original isolated bug. But in some situations, adding a test
isn't viable. If you can't provide a test, explain this decision.
Prefer writing unit tests (:file:`tests/unit`) for isolated tests to
the internals of more serious code changes, like those to the core of yosys,
or more algorithmic ones.
The rest of the test suite is mostly based on running Yosys on various Yosys
and Tcl scripts that manually call Yosys commands.
See :doc:`/yosys_internals/extending_yosys/test_suites` for more information
about how our test suite is structured.
The basic test writing approach is checking
for the presence of some kind of object or pattern with ``-assert-count`` in
:doc:`/using_yosys/more_scripting/selections`.
It's often best to use equivalence checking with ``equiv_opt -assert``
or similar to prove that the changes done to the design by a modified pass
preserve equivalence. But some code isn't meant to preserve equivalence.
Sometimes proving equivalence takes an impractically long time for larger
inputs. Also beware, the ``equiv_`` passes are a bit quirky and might even
have incorrect results in unusual situations.
.. Changes to core parts of Yosys or passes that are included in synthesis flows
.. can change runtime and memory usage - for the better or for worse. This strongly
.. depends on the design involved. Such risky changes should then be benchmarked
.. with various designs.
.. TODO Emil benchmarking
Coding style
~~~~~~~~~~~~
Yosys is written in C++17.
In general Yosys uses ``int`` instead of ``size_t``. To avoid compiler warnings
for implicit type casts, always use ``GetSize(foobar)`` instead of
``foobar.size()``. (``GetSize()`` is defined in :file:`kernel/yosys.h`)
For auto formatting code, a :file:`.clang-format` file is present top-level.
Yosys code is using tabs for indentation. A tab is 8 characters wide,
but prefer not relying on it. A continuation of a statement
in the following line is indented by two additional tabs. Lines are
as long as you want them to be. A good rule of thumb is to break lines
at about column 150. Opening braces can be put on the same or next line
as the statement opening the block (if, switch, for, while, do).
Put the opening brace on its own line for larger blocks, especially
blocks that contains blank lines. Remove trailing whitespace on sight.
Otherwise stick to the `Linux Kernel Coding Style`_.
.. _Linux Kernel Coding Style: https://www.kernel.org/doc/Documentation/process/coding-style.rst
Git style
~~~~~~~~~
We don't have a strict commit message style.
Some style hints:
* Refactor and document existing code if you touch it,
but in separate commits from your functional changes
* Prefer smaller commits organized by good chunks. Git has a lot of features
like fixup commits, interactive rebase with autosquash
Reviewing PRs
-------------
Reviewing PRs is a totally valid form of external contributing to the project!
Who's the reviewer?
~~~~~~~~~~~~~~~~~~~
Yosys HQ is a company with the inherited mandate to make decisions on behalf
of the open source project. As such, we at HQ are collectively the maintainers.
Within HQ, we allocate reviews based on expertise with the topic at hand
as well as member time constraints.
If you're intimately acquainted with a part of the codebase, we will be happy
to defer to your experience and have you review PRs. The official way we like
is our CODEOWNERS file in the git repository. What we're looking for in code
owners is activity and trust. For activity, if you're only interested in
a yosys pass for example for the time you spend writing a thesis, it might be
better to focus on writing good tests and docs in the PRs you submit rather than
to commit to code ownership and therefore to be responsible for fixing things
and reviewing other people's PRs at various unexpected points later. If you're
prolific in some part of the codebase and not a code owner, we still value your
experience and may tag you in PRs.
As a matter of fact, the purpose of code ownership is to avoid maintainer
burnout by removing orphaned parts of the codebase. If you become a code owner
and stop being responsive, in the future, we might decide to remove such code
if convenient and costly to maintain. It's simply more respectful of the users'
time to explicitly cut something out than let it "bitrot". Larger projects like
LLVM or linux could not survive without such things, but Yosys is far smaller,
and there are expectations
.. TODO this deserves its own section elsewhere I think? But it would be distracting elsewhere
Sometimes, multiple maintainers may add review comments. This is considered
healthy collaborative even if it might create disagreement at times. If
somebody is already reviewing a PR, others, even non-maintainers are free to
leave comments with extra observations and alternate perspectives in a
collaborative spirit.
How to review
~~~~~~~~~~~~~
First, read everything above about contributing. Those are the values you
should gently enforce as a reviewer. They're ordered by importance, but
explicitly, descriptions are more important than code, long-form comments
describing the design are more important than piecemeal comments, etc.
If a PR is poorly described, incomplete, tests are broken, or if the
author is not responding, please don't feel pressured to take over their
role by reverse engineering the code or fixing things for them, unless
there are good reasons to do so.
If a PR author submits LLM outputs they haven't understood themselves,
they will not be able to implement feedback. Take this into consideration
as well. We do not ban LLM code from the codebase, we ban bad code.
Reviewers may have diverse styles of communication while reviewing - one
may do one thorough review, another may prefer a back and forth with the
basics out the way before digging into the code. Generally, PRs may have
several requests for modifications and long discussions, but often
they just are good enough to merge as-is.
The CI is required to go green for merging. New contributors need a CI
run to be triggered by a maintainer before their PRs take up computing
resources. It's a single click from the github web interface.

View file

@ -8,7 +8,44 @@ Running the included test suite
The Yosys source comes with a test suite to avoid regressions and keep
everything working as expected. Tests can be run by calling ``make test`` from
the root Yosys directory.
the root Yosys directory. By default, this runs vanilla and unit tests.
Vanilla tests
~~~~~~~~~~~~~
These make up the majority of our testing coverage.
They can be run with ``make vanilla-test`` and are based on calls to
make subcommands (``make makefile-tests``) and shell scripts
(``make seed-tests`` and ``make abcopt-tests``). Both use ``run-test.sh``
files, but make-based tests only call ``tests/gen-tests-makefile.sh``
to generate a makefile appropriate for the given directory, so only
afterwards when make is invoked do the tests actually run.
Usually their structure looks something like this:
you write a .ys file that gets automatically run,
which runs a frontend like ``read_verilog`` or ``read_rtlil`` with
a relative path or a heredoc, then runs some commands including the command
under test, and then uses :doc:`/using_yosys/more_scripting/selections`
with ``-assert-count``. Usually it's unnecessary to "register" the test anywhere
as if it's being added to an existing directory, depending
on how the ``run-test.sh`` in that directory works.
Unit tests
~~~~~~~~~~
Running the unit tests requires the following additional packages:
.. tab:: Ubuntu
.. code:: console
sudo apt-get install libgtest-dev
.. tab:: macOS
No additional requirements.
Unit tests can be run with ``make unit-test``.
Functional tests
~~~~~~~~~~~~~~~~
@ -41,23 +78,6 @@ instructions <https://github.com/Z3Prover/z3>`_.
Then, set the :makevar:`ENABLE_FUNCTIONAL_TESTS` make variable when calling
``make test`` and the functional tests will be run as well.
Unit tests
~~~~~~~~~~
Running the unit tests requires the following additional packages:
.. tab:: Ubuntu
.. code:: console
sudo apt-get install libgtest-dev
.. tab:: macOS
No additional requirements.
Unit tests can be run with ``make unit-test``.
Docs tests
~~~~~~~~~~

View file

@ -37,7 +37,7 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
#include "aigerparse.h"
YOSYS_NAMESPACE_BEGIN
@ -286,10 +286,15 @@ end_of_header:
RTLIL::IdString escaped_s = stringf("\\%s", s);
RTLIL::Wire* wire;
if (c == 'i') wire = inputs[l1];
else if (c == 'l') wire = latches[l1];
else if (c == 'o') {
if (c == 'i') {
log_assert(l1 < inputs.size());
wire = inputs[l1];
} else if (c == 'l') {
log_assert(l1 < latches.size());
wire = latches[l1];
} else if (c == 'o') {
wire = module->wire(escaped_s);
log_assert(l1 < outputs.size());
if (wire) {
// Could have been renamed by a latch
module->swap_names(wire, outputs[l1]);
@ -297,9 +302,9 @@ end_of_header:
goto next;
}
wire = outputs[l1];
}
else if (c == 'b') wire = bad_properties[l1];
else log_abort();
} else if (c == 'b') {
wire = bad_properties[l1];
} else log_abort();
module->rename(wire, escaped_s);
}
@ -652,6 +657,9 @@ void AigerReader::parse_aiger_binary()
unsigned l1, l2, l3;
std::string line;
if (M != I + L + A)
log_error("Binary AIGER input is malformed: maximum variable index M is %u, but number of inputs, latches and AND gates adds up to %u.\n", M, I + L + A);
// Parse inputs
int digits = decimal_digits(I);
for (unsigned i = 1; i <= I; ++i) {

View file

@ -403,6 +403,18 @@ struct AST_INTERNAL::ProcessGenerator
if (GetSize(syncrule->signal) != 1)
always->input_error("Found posedge/negedge event on a signal that is not 1 bit wide!\n");
addChunkActions(syncrule->actions, subst_lvalue_from, subst_lvalue_to, true);
// Automatic (nosync) variables must not become flip-flops: remove
// them from clocked sync rules so that proc_dff does not infer
// an unnecessary register for a purely combinational temporary.
syncrule->actions.erase(
std::remove_if(syncrule->actions.begin(), syncrule->actions.end(),
[](const RTLIL::SigSig &ss) {
for (auto &chunk : ss.first.chunks())
if (chunk.wire && chunk.wire->get_bool_attribute(ID::nosync))
return true;
return false;
}),
syncrule->actions.end());
proc->syncs.push_back(syncrule);
}
if (proc->syncs.empty()) {
@ -2085,8 +2097,6 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
check_unique_id(current_module, id, this, "cell");
RTLIL::Cell *cell = current_module->addCell(id, "");
set_src_attr(cell, this);
// Set attribute 'module_not_derived' which will be cleared again after the hierarchy pass
cell->set_bool_attribute(ID::module_not_derived);
for (auto it = children.begin(); it != children.end(); it++) {
auto* child = it->get();
@ -2149,6 +2159,11 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
}
log_abort();
}
// Set attribute 'module_not_derived' which will be cleared again after the hierarchy pass
if (cell->type.isPublic())
cell->set_bool_attribute(ID::module_not_derived);
for (auto &attr : attributes) {
if (attr.second->type != AST_CONSTANT)
input_error("Attribute `%s' with non-constant value.\n", attr.first);

View file

@ -269,6 +269,83 @@ static int add_dimension(AstNode *node, AstNode *rnode)
node->input_error("Unpacked array in packed struct/union member %s\n", node->str);
}
// Check if node is an unexpanded array reference (AST_IDENTIFIER -> AST_MEMORY without indexing)
static bool is_unexpanded_array_ref(AstNode *node)
{
if (node->type != AST_IDENTIFIER)
return false;
if (node->id2ast == nullptr || node->id2ast->type != AST_MEMORY)
return false;
// No indexing children = whole array reference
return node->children.empty();
}
// Check if two memories have compatible unpacked dimensions for array assignment
static bool arrays_have_compatible_dims(AstNode *mem_a, AstNode *mem_b)
{
if (mem_a->unpacked_dimensions != mem_b->unpacked_dimensions)
return false;
for (int i = 0; i < mem_a->unpacked_dimensions; i++) {
if (mem_a->dimensions[i].range_width != mem_b->dimensions[i].range_width)
return false;
}
// Also check packed dimensions (element width)
int a_width, a_size, a_bits;
int b_width, b_size, b_bits;
mem_a->meminfo(a_width, a_size, a_bits);
mem_b->meminfo(b_width, b_size, b_bits);
return a_width == b_width;
}
// Convert per-dimension element positions to declared index values.
// Position 0 is the first declared element for each unpacked dimension.
static std::vector<int> array_indices_from_position(AstNode *mem, const std::vector<int> &position)
{
int num_dims = mem->unpacked_dimensions;
log_assert(GetSize(position) == num_dims);
std::vector<int> indices(num_dims);
for (int d = 0; d < num_dims; d++) {
int low = mem->dimensions[d].range_right;
int high = low + mem->dimensions[d].range_width - 1;
indices[d] = mem->dimensions[d].range_swapped ? (low + position[d]) : (high - position[d]);
}
return indices;
}
// Generate all element positions for a multi-dimensional unpacked array and
// call callback once for each combination.
static void foreach_array_position(AstNode *mem, std::function<void(const std::vector<int>&)> callback)
{
int num_dims = mem->unpacked_dimensions;
if (num_dims == 0) {
callback({});
return;
}
std::vector<int> position(num_dims, 0);
std::vector<int> sizes(num_dims);
for (int d = 0; d < num_dims; d++)
sizes[d] = mem->dimensions[d].range_width;
// Iterate through all position combinations (rightmost dimension fastest).
while (true) {
callback(position);
int d = num_dims - 1;
while (d >= 0) {
position[d]++;
if (position[d] < sizes[d])
break;
position[d] = 0;
d--;
}
if (d < 0)
break;
}
}
static int size_packed_struct(AstNode *snode, int base_offset)
{
// Struct members will be laid out in the structure contiguously from left to right.
@ -3200,6 +3277,123 @@ skip_dynamic_range_lvalue_expansion:;
}
}
// Expand array assignment: arr_out = arr_in OR arr_out = cond ? arr_a : arr_b
// Supports multi-dimensional unpacked arrays
if ((type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE || type == AST_ASSIGN) &&
is_unexpanded_array_ref(children[0].get()))
{
AstNode *lhs = children[0].get();
AstNode *rhs = children[1].get();
AstNode *lhs_mem = lhs->id2ast;
// Case 1: Direct array assignment (b = a)
bool is_direct_assign = is_unexpanded_array_ref(rhs);
// Case 2: Ternary array assignment (out = sel ? a : b)
bool is_ternary_assign = (rhs->type == AST_TERNARY &&
is_unexpanded_array_ref(rhs->children[1].get()) &&
is_unexpanded_array_ref(rhs->children[2].get()));
if (is_direct_assign || is_ternary_assign)
{
AstNode *direct_rhs_mem = nullptr;
AstNode *true_mem = nullptr;
AstNode *false_mem = nullptr;
// Validate array compatibility
if (is_direct_assign) {
direct_rhs_mem = rhs->id2ast;
if (!arrays_have_compatible_dims(lhs_mem, direct_rhs_mem))
input_error("Array dimension mismatch in assignment\n");
} else {
true_mem = rhs->children[1]->id2ast;
false_mem = rhs->children[2]->id2ast;
if (!arrays_have_compatible_dims(lhs_mem, true_mem) ||
!arrays_have_compatible_dims(lhs_mem, false_mem))
input_error("Array dimension mismatch in ternary expression\n");
}
int num_dims = lhs_mem->unpacked_dimensions;
// Helper to add index to an identifier clone
auto add_indices_to_id = [&](std::unique_ptr<AstNode> id, const std::vector<int>& indices) {
if (num_dims == 1) {
// Single dimension: use AST_RANGE
id->children.push_back(std::make_unique<AstNode>(location, AST_RANGE,
mkconst_int(location, indices[0], true)));
} else {
// Multiple dimensions: use AST_MULTIRANGE
auto multirange = std::make_unique<AstNode>(location, AST_MULTIRANGE);
for (int idx : indices) {
multirange->children.push_back(std::make_unique<AstNode>(location, AST_RANGE,
mkconst_int(location, idx, true)));
}
id->children.push_back(std::move(multirange));
}
id->integer = num_dims;
// Reset basic_prep so multirange gets resolved during subsequent simplify passes
id->basic_prep = false;
return id;
};
// Calculate total number of elements and warn if large
int total_elements = 1;
for (int d = 0; d < num_dims; d++)
total_elements *= lhs_mem->dimensions[d].range_width;
if (total_elements > 10000)
log_warning("Expanding array assignment with %d elements at %s, this may be slow.\n",
total_elements, location.to_string().c_str());
// Collect all assignments
std::vector<std::unique_ptr<AstNode>> assignments;
foreach_array_position(lhs_mem, [&](const std::vector<int>& position) {
auto lhs_indices = array_indices_from_position(lhs_mem, position);
auto lhs_idx = add_indices_to_id(lhs->clone(), lhs_indices);
std::unique_ptr<AstNode> rhs_expr;
if (is_direct_assign) {
auto rhs_indices = array_indices_from_position(direct_rhs_mem, position);
rhs_expr = add_indices_to_id(rhs->clone(), rhs_indices);
} else {
// Ternary case
AstNode *cond = rhs->children[0].get();
AstNode *true_val = rhs->children[1].get();
AstNode *false_val = rhs->children[2].get();
auto true_indices = array_indices_from_position(true_mem, position);
auto false_indices = array_indices_from_position(false_mem, position);
auto true_idx = add_indices_to_id(true_val->clone(), true_indices);
auto false_idx = add_indices_to_id(false_val->clone(), false_indices);
rhs_expr = std::make_unique<AstNode>(location, AST_TERNARY,
cond->clone(), std::move(true_idx), std::move(false_idx));
}
auto assign = std::make_unique<AstNode>(location, type,
std::move(lhs_idx), std::move(rhs_expr));
assign->was_checked = true;
assignments.push_back(std::move(assign));
});
// For continuous assignments, add to module; for procedural, use block
if (type == AST_ASSIGN) {
// Add all but last to module
for (size_t i = 0; i + 1 < assignments.size(); i++)
current_ast_mod->children.push_back(std::move(assignments[i]));
// Last one replaces current node
newNode = std::move(assignments.back());
} else {
// Wrap in AST_BLOCK for procedural
newNode = std::make_unique<AstNode>(location, AST_BLOCK);
for (auto& assign : assignments)
newNode->children.push_back(std::move(assign));
}
goto apply_newNode;
}
}
// assignment with memory in left-hand side expression -> replace with memory write port
if (stage > 1 && (type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE) && children[0]->type == AST_IDENTIFIER &&
children[0]->id2ast && children[0]->id2ast->type == AST_MEMORY && children[0]->id2ast->children.size() >= 2 &&
@ -4690,6 +4884,7 @@ void AstNode::expand_genblock(const std::string &prefix)
switch (child->type) {
case AST_WIRE:
case AST_AUTOWIRE:
case AST_MEMORY:
case AST_STRUCT:
case AST_UNION:
@ -4718,6 +4913,93 @@ void AstNode::expand_genblock(const std::string &prefix)
}
break;
case AST_IDENTIFIER:
if (!child->str.empty() && prefix.size() > 0) {
bool is_resolved = false;
std::string identifier_str = child->str;
if (current_ast_mod != nullptr && identifier_str.compare(0, current_ast_mod->str.size(), current_ast_mod->str) == 0) {
if (identifier_str.at(current_ast_mod->str.size()) == '.') {
identifier_str = '\\' + identifier_str.substr(current_ast_mod->str.size()+1, identifier_str.size());
}
}
// search starting in the innermost scope and then stepping outward
for (size_t ppos = prefix.size() - 1; ppos; --ppos) {
if (prefix.at(ppos) != '.') continue;
std::string new_prefix = prefix.substr(0, ppos + 1);
auto attempt_resolve = [&new_prefix](const std::string &ident) -> std::string {
std::string new_name = prefix_id(new_prefix, ident);
if (current_scope.count(new_name))
return new_name;
return {};
};
// attempt to resolve the full identifier
std::string resolved = attempt_resolve(identifier_str);
if (!resolved.empty()) {
is_resolved = true;
break;
}
// attempt to resolve hierarchical prefixes within the identifier,
// as the prefix could refer to a local scope which exists but
// hasn't yet been elaborated
for (size_t spos = identifier_str.size() - 1; spos; --spos) {
if (identifier_str.at(spos) != '.') continue;
resolved = attempt_resolve(identifier_str.substr(0, spos));
if (!resolved.empty()) {
is_resolved = true;
identifier_str = resolved + identifier_str.substr(spos);
ppos = 1; // break outer loop
break;
}
}
if (current_scope.count(identifier_str) == 0) {
AstNode *current_scope_ast = (current_ast_mod == nullptr) ? current_ast : current_ast_mod;
for (auto& node : current_scope_ast->children) {
switch (node->type) {
case AST_PARAMETER:
case AST_LOCALPARAM:
case AST_WIRE:
case AST_AUTOWIRE:
case AST_GENVAR:
case AST_MEMORY:
case AST_FUNCTION:
case AST_TASK:
case AST_DPI_FUNCTION:
if (prefix_id(new_prefix, identifier_str) == node->str) {
is_resolved = true;
current_scope[node->str] = node.get();
}
break;
case AST_ENUM:
current_scope[node->str] = node.get();
for (auto& enum_node : node->children) {
log_assert(enum_node->type==AST_ENUM_ITEM);
if (prefix_id(new_prefix, identifier_str) == enum_node->str) {
is_resolved = true;
current_scope[enum_node->str] = enum_node.get();
}
}
break;
default:
break;
}
}
}
}
if ((current_scope.count(identifier_str) == 0) && is_resolved == false) {
if (current_ast_mod == nullptr) {
input_error("Identifier `%s' is implicitly declared outside of a module.\n", child->str.c_str());
} else if (flag_autowire || identifier_str == "\\$global_clock") {
auto auto_wire = std::make_unique<AstNode>(child->location, AST_AUTOWIRE);
auto_wire->str = identifier_str;
children.push_back(std::move(auto_wire));
} else {
input_error("Identifier `%s' is implicitly declared and `default_nettype is set to none.\n", identifier_str.c_str());
}
}
}
break;
default:
break;
}

View file

@ -470,6 +470,27 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool
continue;
}
if (!strcmp(cmd, ".gateinit"))
{
char *p = strtok(NULL, " \t\r\n");
if (p == NULL)
goto error;
char *n = strtok(p, "=");
char *init = strtok(NULL, "=");
if (n == NULL || init == NULL)
goto error;
if (init[0] != '0' && init[0] != '1')
goto error;
if (blif_wire(n)->attributes.find(ID::init) == blif_wire(n)->attributes.end())
blif_wire(n)->attributes.emplace(ID::init, Const(init[0] == '1' ? 1 : 0, 1));
else
blif_wire(n)->attributes[ID::init] = Const(init[0] == '1' ? 1 : 0, 1);
continue;
}
if (!strcmp(cmd, ".names"))
{
char *p;
@ -608,6 +629,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool
goto try_next_value;
}
}
log_assert(i < lutptr->size());
lutptr->set(i, !strcmp(output, "0") ? RTLIL::State::S0 : RTLIL::State::S1);
try_next_value:;
}

View file

@ -302,6 +302,9 @@ void json_import(Design *design, string &modname, JsonNode *node)
if (node->data_dict.count("attributes"))
json_parse_attr_param(module->attributes, node->data_dict.at("attributes"));
if (node->data_dict.count("parameter_default_values"))
json_parse_attr_param(module->parameter_default_values, node->data_dict.at("parameter_default_values"));
dict<int, SigBit> signal_bits;
if (node->data_dict.count("ports"))

View file

@ -40,6 +40,7 @@ struct RTLILFrontendWorker {
bool flag_nooverwrite = false;
bool flag_overwrite = false;
bool flag_lib = false;
bool flag_legalize = false;
int line_num;
std::string line_buf;
@ -322,6 +323,17 @@ struct RTLILFrontendWorker {
return val;
}
RTLIL::Wire *legalize_wire(RTLIL::IdString id)
{
int wires_size = current_module->wires_size();
if (wires_size == 0)
error("No wires found for legalization");
int hash = hash_ops<RTLIL::IdString>::hash(id).yield();
RTLIL::Wire *wire = current_module->wire_at(abs(hash % wires_size));
log("Legalizing wire `%s' to `%s'.\n", log_id(id), log_id(wire->name));
return wire;
}
RTLIL::SigSpec parse_sigspec()
{
RTLIL::SigSpec sig;
@ -339,8 +351,12 @@ struct RTLILFrontendWorker {
std::optional<RTLIL::IdString> id = try_parse_id();
if (id.has_value()) {
RTLIL::Wire *wire = current_module->wire(*id);
if (wire == nullptr)
error("Wire `%s' not found.", *id);
if (wire == nullptr) {
if (flag_legalize)
wire = legalize_wire(*id);
else
error("Wire `%s' not found.", *id);
}
sig = RTLIL::SigSpec(wire);
} else {
sig = RTLIL::SigSpec(parse_const());
@ -349,17 +365,44 @@ struct RTLILFrontendWorker {
while (try_parse_char('[')) {
int left = parse_integer();
if (left >= sig.size() || left < 0)
error("bit index %d out of range", left);
if (left >= sig.size() || left < 0) {
if (flag_legalize) {
int legalized;
if (sig.size() == 0)
legalized = 0;
else
legalized = std::max(0, std::min(left, sig.size() - 1));
log("Legalizing bit index %d to %d.\n", left, legalized);
left = legalized;
} else {
error("bit index %d out of range", left);
}
}
if (try_parse_char(':')) {
int right = parse_integer();
if (right < 0)
error("bit index %d out of range", right);
if (left < right)
error("invalid slice [%d:%d]", left, right);
sig = sig.extract(right, left-right+1);
if (right < 0) {
if (flag_legalize) {
log("Legalizing bit index %d to %d.\n", right, 0);
right = 0;
} else
error("bit index %d out of range", right);
}
if (left < right) {
if (flag_legalize) {
log("Legalizing bit index %d to %d.\n", left, right);
left = right;
} else
error("invalid slice [%d:%d]", left, right);
}
if (flag_legalize && left >= sig.size())
log("Legalizing slice %d:%d by igoring it\n", left, right);
else
sig = sig.extract(right, left - right + 1);
} else {
sig = sig.extract(left);
if (flag_legalize && left >= sig.size())
log("Legalizing slice %d by igoring it\n", left);
else
sig = sig.extract(left);
}
expect_char(']');
}
@ -476,8 +519,14 @@ struct RTLILFrontendWorker {
{
std::optional<RTLIL::IdString> id = try_parse_id();
if (id.has_value()) {
if (current_module->wire(*id) != nullptr)
error("RTLIL error: redefinition of wire %s.", *id);
if (current_module->wire(*id) != nullptr) {
if (flag_legalize) {
log("Legalizing redefinition of wire %s.\n", *id);
pool<RTLIL::Wire*> wires = {current_module->wire(*id)};
current_module->remove(wires);
} else
error("RTLIL error: redefinition of wire %s.", *id);
}
wire = current_module->addWire(std::move(*id));
break;
}
@ -528,8 +577,13 @@ struct RTLILFrontendWorker {
{
std::optional<RTLIL::IdString> id = try_parse_id();
if (id.has_value()) {
if (current_module->memories.count(*id) != 0)
error("RTLIL error: redefinition of memory %s.", *id);
if (current_module->memories.count(*id) != 0) {
if (flag_legalize) {
log("Legalizing redefinition of memory %s.\n", *id);
current_module->remove(current_module->memories.at(*id));
} else
error("RTLIL error: redefinition of memory %s.", *id);
}
memory->name = std::move(*id);
break;
}
@ -551,14 +605,36 @@ struct RTLILFrontendWorker {
expect_eol();
}
void legalize_width_parameter(RTLIL::Cell *cell, RTLIL::IdString port_name)
{
std::string width_param_name = port_name.str() + "_WIDTH";
if (cell->parameters.count(width_param_name) == 0)
return;
RTLIL::Const &param = cell->parameters.at(width_param_name);
if (param.as_int() != 0)
return;
cell->parameters[width_param_name] = RTLIL::Const(cell->getPort(port_name).size());
}
void parse_cell()
{
RTLIL::IdString cell_type = parse_id();
RTLIL::IdString cell_name = parse_id();
expect_eol();
if (current_module->cell(cell_name) != nullptr)
error("RTLIL error: redefinition of cell %s.", cell_name);
if (current_module->cell(cell_name) != nullptr) {
if (flag_legalize) {
RTLIL::IdString new_name;
int suffix = 1;
do {
new_name = RTLIL::IdString(cell_name.str() + "_" + std::to_string(suffix));
++suffix;
} while (current_module->cell(new_name) != nullptr);
log("Legalizing redefinition of cell %s by renaming to %s.\n", cell_name, new_name);
cell_name = new_name;
} else
error("RTLIL error: redefinition of cell %s.", cell_name);
}
RTLIL::Cell *cell = current_module->addCell(cell_name, cell_type);
cell->attributes = std::move(attrbuf);
@ -587,9 +663,15 @@ struct RTLILFrontendWorker {
expect_eol();
} else if (try_parse_keyword("connect")) {
RTLIL::IdString port_name = parse_id();
if (cell->hasPort(port_name))
error("RTLIL error: redefinition of cell port %s.", port_name);
if (cell->hasPort(port_name)) {
if (flag_legalize)
log("Legalizing redefinition of cell port %s.", port_name);
else
error("RTLIL error: redefinition of cell port %s.", port_name);
}
cell->setPort(std::move(port_name), parse_sigspec());
if (flag_legalize)
legalize_width_parameter(cell, port_name);
expect_eol();
} else if (try_parse_keyword("end")) {
expect_eol();
@ -606,6 +688,11 @@ struct RTLILFrontendWorker {
error("dangling attribute");
RTLIL::SigSpec s1 = parse_sigspec();
RTLIL::SigSpec s2 = parse_sigspec();
if (flag_legalize) {
int min_size = std::min(s1.size(), s2.size());
s1 = s1.extract(0, min_size);
s2 = s2.extract(0, min_size);
}
current_module->connect(std::move(s1), std::move(s2));
expect_eol();
}
@ -682,8 +769,13 @@ struct RTLILFrontendWorker {
RTLIL::IdString proc_name = parse_id();
expect_eol();
if (current_module->processes.count(proc_name) != 0)
error("RTLIL error: redefinition of process %s.", proc_name);
if (current_module->processes.count(proc_name) != 0) {
if (flag_legalize) {
log("Legalizing redefinition of process %s.\n", proc_name);
current_module->remove(current_module->processes.at(proc_name));
} else
error("RTLIL error: redefinition of process %s.", proc_name);
}
RTLIL::Process *proc = current_module->addProcess(std::move(proc_name));
proc->attributes = std::move(attrbuf);
@ -804,6 +896,11 @@ struct RTLILFrontend : public Frontend {
log(" -lib\n");
log(" only create empty blackbox modules\n");
log("\n");
log(" -legalize\n");
log(" prevent semantic errors (e.g. reference to unknown wire, redefinition of wire/cell)\n");
log(" by deterministically rewriting the input into something valid. Useful when using\n");
log(" fuzzing to generate random but valid RTLIL.\n");
log("\n");
}
void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
{
@ -828,6 +925,10 @@ struct RTLILFrontend : public Frontend {
worker.flag_lib = true;
continue;
}
if (arg == "-legalize") {
worker.flag_legalize = true;
continue;
}
break;
}
extra_args(f, filename, args, argidx);

View file

@ -3114,9 +3114,11 @@ struct VerificPass : public Pass {
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
#ifdef VERIFIC_SYSTEMVERILOG_SUPPORT
log(" verific {-vlog95|-vlog2k|-sv2005|-sv2009|-sv2012|-sv} <verilog-file>..\n");
log(" verific {-vlog95|-vlog2k|-sv2005|-sv2009|-sv2012|\n");
log(" -sv2017|-sv} <verilog-file>..\n");
log("\n");
log("Load the specified Verilog/SystemVerilog files into Verific.\n");
log("Note that -sv option will use latest supported SystemVerilog standard.\n");
log("\n");
log("All files specified in one call to this command are one compilation unit.\n");
log("Files passed to different calls to this command are treated as belonging to\n");
@ -3161,7 +3163,10 @@ struct VerificPass : public Pass {
#endif
#ifdef VERIFIC_SYSTEMVERILOG_SUPPORT
log(" verific {-f|-F} [-vlog95|-vlog2k|-sv2005|-sv2009|\n");
log(" -sv2012|-sv|-formal] <command-file>\n");
#ifdef VERIFIC_VHDL_SUPPORT
log(" -vhdl87|-vhdl93|-vhdl2k|-vhdl2008|-vhdl2019|-vhdl|\n");
#endif
log(" -sv2012|-sv2017|-sv|-formal] <command-file>\n");
log("\n");
log("Load and execute the specified command file.\n");
log("Override verilog parsing mode can be set.\n");
@ -3696,6 +3701,9 @@ struct VerificPass : public Pass {
if (GetSize(args) > argidx && (args[argidx] == "-f" || args[argidx] == "-F"))
{
unsigned verilog_mode = veri_file::UNDEFINED;
#ifdef VERIFIC_VHDL_SUPPORT
unsigned vhdl_mode = vhdl_file::UNDEFINED;
#endif
bool is_formal = false;
const char* filename = nullptr;
@ -3714,10 +3722,38 @@ struct VerificPass : public Pass {
} else if (args[argidx] == "-sv2009") {
verilog_mode = veri_file::SYSTEM_VERILOG_2009;
continue;
} else if (args[argidx] == "-sv2012" || args[argidx] == "-sv" || args[argidx] == "-formal") {
} else if (args[argidx] == "-sv2012") {
verilog_mode = veri_file::SYSTEM_VERILOG_2012;
continue;
} else if (args[argidx] == "-sv2017") {
verilog_mode = veri_file::SYSTEM_VERILOG_2017;
continue;
} else if (args[argidx] == "-sv" || args[argidx] == "-formal") {
verilog_mode = veri_file::SYSTEM_VERILOG;
if (args[argidx] == "-formal") is_formal = true;
continue;
#ifdef VERIFIC_VHDL_SUPPORT
} else if (args[argidx] == "-vhdl87") {
vhdl_mode = vhdl_file::VHDL_87;
vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_1987").c_str());
continue;
} else if (args[argidx] == "-vhdl93") {
vhdl_mode = vhdl_file::VHDL_93;
vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_1993").c_str());
continue;
} else if (args[argidx] == "-vhdl2k") {
vhdl_mode = vhdl_file::VHDL_2K;
vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_1993").c_str());
continue;
} else if (args[argidx] == "-vhdl2019") {
vhdl_mode = vhdl_file::VHDL_2019;
vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_2019").c_str());
continue;
} else if (args[argidx] == "-vhdl2008" || args[argidx] == "-vhdl") {
vhdl_mode = vhdl_file::VHDL_2008;
vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_2008").c_str());
continue;
#endif
} else if (args[argidx].compare(0, 1, "-") == 0) {
cmd_error(args, argidx, "unknown option");
goto check_error;
@ -3742,10 +3778,36 @@ struct VerificPass : public Pass {
veri_file::DefineMacro("VERIFIC");
veri_file::DefineMacro(is_formal ? "FORMAL" : "SYNTHESIS");
#ifdef VERIFIC_VHDL_SUPPORT
if (vhdl_mode == vhdl_file::UNDEFINED) {
vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_2008").c_str());
vhdl_mode = vhdl_file::VHDL_2008;
}
int i;
Array *file_names_sv = new Array(POINTER_HASH);
FOREACH_ARRAY_ITEM(file_names, i, filename) {
std::string filename_str = filename;
if ((filename_str.substr(filename_str.find_last_of(".") + 1) == "vhd") ||
(filename_str.substr(filename_str.find_last_of(".") + 1) == "vhdl")) {
if (!vhdl_file::Analyze(filename, work.c_str(), vhdl_mode)) {
verific_error_msg.clear();
log_cmd_error("Reading VHDL sources failed.\n");
}
} else {
file_names_sv->Insert(strdup(filename));
}
}
if (!veri_file::AnalyzeMultipleFiles(file_names_sv, analysis_mode, work.c_str(), veri_file::MFCU)) {
verific_error_msg.clear();
log_cmd_error("Reading Verilog/SystemVerilog sources failed.\n");
}
delete file_names_sv;
#else
if (!veri_file::AnalyzeMultipleFiles(file_names, analysis_mode, work.c_str(), veri_file::MFCU)) {
verific_error_msg.clear();
log_cmd_error("Reading Verilog/SystemVerilog sources failed.\n");
}
#endif
delete file_names;
verific_import_pending = true;
@ -3753,7 +3815,8 @@ struct VerificPass : public Pass {
}
if (GetSize(args) > argidx && (args[argidx] == "-vlog95" || args[argidx] == "-vlog2k" || args[argidx] == "-sv2005" ||
args[argidx] == "-sv2009" || args[argidx] == "-sv2012" || args[argidx] == "-sv" || args[argidx] == "-formal"))
args[argidx] == "-sv2009" || args[argidx] == "-sv2012" || args[argidx] == "-sv2017" || args[argidx] == "-sv" ||
args[argidx] == "-formal"))
{
Array file_names;
unsigned verilog_mode;
@ -3766,7 +3829,11 @@ struct VerificPass : public Pass {
verilog_mode = veri_file::SYSTEM_VERILOG_2005;
else if (args[argidx] == "-sv2009")
verilog_mode = veri_file::SYSTEM_VERILOG_2009;
else if (args[argidx] == "-sv2012" || args[argidx] == "-sv" || args[argidx] == "-formal")
else if (args[argidx] == "-sv2012")
verilog_mode = veri_file::SYSTEM_VERILOG_2012;
else if (args[argidx] == "-sv2017")
verilog_mode = veri_file::SYSTEM_VERILOG_2017;
else if (args[argidx] == "-sv" || args[argidx] == "-formal")
verilog_mode = veri_file::SYSTEM_VERILOG;
else
log_abort();

View file

@ -500,7 +500,6 @@ struct VerilogFrontend : public Frontend {
log("Parsing %s%s input from `%s' to AST representation.\n",
parse_mode.formal ? "formal " : "", parse_mode.sv ? "SystemVerilog" : "Verilog", filename.c_str());
log("verilog frontend filename %s\n", filename.c_str());
if (flag_relative_share) {
auto share_path = proc_share_dirname();
if (filename.substr(0, share_path.length()) == share_path)

View file

@ -84,7 +84,7 @@
int current_function_or_task_port_id;
std::vector<char> case_type_stack;
bool do_not_require_port_stubs;
bool current_wire_rand, current_wire_const;
bool current_wire_rand, current_wire_const, current_wire_automatic;
bool current_modport_input, current_modport_output;
bool default_nettype_wire = true;
std::istream* lexin;
@ -958,14 +958,18 @@ delay:
non_opt_delay | %empty;
io_wire_type:
{ extra->astbuf3 = std::make_unique<AstNode>(@$, AST_WIRE); extra->current_wire_rand = false; extra->current_wire_const = false; }
{ extra->astbuf3 = std::make_unique<AstNode>(@$, AST_WIRE); extra->current_wire_rand = false; extra->current_wire_const = false; extra->current_wire_automatic = false; }
wire_type_token_io wire_type_const_rand opt_wire_type_token wire_type_signedness
{ $$ = std::move(extra->astbuf3); SET_RULE_LOC(@$, @2, @$); };
non_io_wire_type:
{ extra->astbuf3 = std::make_unique<AstNode>(@$, AST_WIRE); extra->current_wire_rand = false; extra->current_wire_const = false; }
wire_type_const_rand wire_type_token wire_type_signedness
{ $$ = std::move(extra->astbuf3); SET_RULE_LOC(@$, @2, @$); };
{ extra->astbuf3 = std::make_unique<AstNode>(@$, AST_WIRE); extra->current_wire_rand = false; extra->current_wire_const = false; extra->current_wire_automatic = false; }
opt_lifetime wire_type_const_rand wire_type_token wire_type_signedness
{
if (extra->current_wire_automatic)
extra->astbuf3->set_attribute(ID::nosync, AstNode::mkconst_int(extra->astbuf3->location, 1, false));
$$ = std::move(extra->astbuf3); SET_RULE_LOC(@$, @2, @$);
};
wire_type:
io_wire_type { $$ = std::move($1); } |
@ -1253,6 +1257,10 @@ opt_automatic:
TOK_AUTOMATIC |
%empty;
opt_lifetime:
TOK_AUTOMATIC { extra->current_wire_automatic = true; } |
%empty;
task_func_args_opt:
TOK_LPAREN TOK_RPAREN | %empty | TOK_LPAREN {
extra->albuf = nullptr;

View file

@ -291,7 +291,7 @@ static RTLIL::Const const_shift_worker(const RTLIL::Const &arg1, const RTLIL::Co
if (pos < 0)
result.set(i, vacant_bits);
else if (pos >= BigInteger(GetSize(arg1)))
result.set(i, sign_ext ? arg1.back() : vacant_bits);
result.set(i, sign_ext && !arg1.empty() ? arg1.back() : vacant_bits);
else
result.set(i, arg1[pos.toInt()]);
}

View file

@ -112,6 +112,41 @@ void reduce_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
db->add_edge(cell, ID::A, i, ID::Y, 0, -1);
}
void logic_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
{
int a_width = GetSize(cell->getPort(ID::A));
int b_width = GetSize(cell->getPort(ID::B));
for (int i = 0; i < a_width; i++)
db->add_edge(cell, ID::A, i, ID::Y, 0, -1);
for (int i = 0; i < b_width; i++)
db->add_edge(cell, ID::B, i, ID::Y, 0, -1);
}
void concat_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
{
int a_width = GetSize(cell->getPort(ID::A));
int b_width = GetSize(cell->getPort(ID::B));
for (int i = 0; i < a_width; i++)
db->add_edge(cell, ID::A, i, ID::Y, i, -1);
for (int i = 0; i < b_width; i++)
db->add_edge(cell, ID::B, i, ID::Y, a_width + i, -1);
}
void slice_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
{
int offset = cell->getParam(ID::OFFSET).as_int();
int a_width = GetSize(cell->getPort(ID::A));
int y_width = GetSize(cell->getPort(ID::Y));
for (int i = 0; i < y_width; i++) {
int a_bit = offset + i;
if (a_bit >= 0 && a_bit < a_width)
db->add_edge(cell, ID::A, a_bit, ID::Y, i, -1);
}
}
void compare_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
{
int a_width = GetSize(cell->getPort(ID::A));
@ -254,7 +289,7 @@ void shift_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
int skip = 1 << (k + 1);
int base = skip -1;
if (i % skip != base && i - a_width + 2 < 1 << b_width_capped)
db->add_edge(cell, ID::B, k, ID::Y, i, -1);
db->add_edge(cell, ID::B, k, ID::Y, i, -1);
} else if (is_signed) {
if (i - a_width + 2 < 1 << b_width_capped)
db->add_edge(cell, ID::B, k, ID::Y, i, -1);
@ -388,6 +423,64 @@ void ff_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
db->add_edge(cell, ID::ARST, 0, ID::Q, k, -1);
}
void full_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
{
std::vector<RTLIL::IdString> input_ports;
std::vector<RTLIL::IdString> output_ports;
for (auto &conn : cell->connections())
{
RTLIL::IdString port = conn.first;
RTLIL::PortDir dir = cell->port_dir(port);
if (cell->input(port) || dir == RTLIL::PortDir::PD_INOUT)
input_ports.push_back(port);
if (cell->output(port) || dir == RTLIL::PortDir::PD_INOUT)
output_ports.push_back(port);
}
for (auto out_port : output_ports)
{
int out_width = GetSize(cell->getPort(out_port));
for (int out_bit = 0; out_bit < out_width; out_bit++)
{
for (auto in_port : input_ports)
{
int in_width = GetSize(cell->getPort(in_port));
for (int in_bit = 0; in_bit < in_width; in_bit++)
db->add_edge(cell, in_port, in_bit, out_port, out_bit, -1);
}
}
}
}
void bweqx_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
{
int width = GetSize(cell->getPort(ID::Y));
int a_width = GetSize(cell->getPort(ID::A));
int b_width = GetSize(cell->getPort(ID::B));
int max_width = std::min(width, std::min(a_width, b_width));
for (int i = 0; i < max_width; i++) {
db->add_edge(cell, ID::A, i, ID::Y, i, -1);
db->add_edge(cell, ID::B, i, ID::Y, i, -1);
}
}
void bwmux_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
{
int width = GetSize(cell->getPort(ID::Y));
int a_width = GetSize(cell->getPort(ID::A));
int b_width = GetSize(cell->getPort(ID::B));
int s_width = GetSize(cell->getPort(ID::S));
int max_width = std::min(width, std::min(a_width, std::min(b_width, s_width)));
for (int i = 0; i < max_width; i++) {
db->add_edge(cell, ID::A, i, ID::Y, i, -1);
db->add_edge(cell, ID::B, i, ID::Y, i, -1);
db->add_edge(cell, ID::S, i, ID::Y, i, -1);
}
}
PRIVATE_NAMESPACE_END
bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL::Cell *cell)
@ -417,6 +510,21 @@ bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL
return true;
}
if (cell->type.in(ID($logic_and), ID($logic_or))) {
logic_op(this, cell);
return true;
}
if (cell->type == ID($slice)) {
slice_op(this, cell);
return true;
}
if (cell->type == ID($concat)) {
concat_op(this, cell);
return true;
}
if (cell->type.in(ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx))) {
shift_op(this, cell);
return true;
@ -442,6 +550,16 @@ bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL
return true;
}
if (cell->type == ID($bweqx)) {
bweqx_op(this, cell);
return true;
}
if (cell->type == ID($bwmux)) {
bwmux_op(this, cell);
return true;
}
if (cell->type.in(ID($mem_v2), ID($memrd), ID($memrd_v2), ID($memwr), ID($memwr_v2), ID($meminit))) {
mem_op(this, cell);
return true;
@ -452,13 +570,24 @@ bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL
return true;
}
// FIXME: $mul $div $mod $divfloor $modfloor $slice $concat
// FIXME: $lut $sop $alu $lcu $macc $macc_v2 $fa
// FIXME: $mul $div $mod $divfloor $modfloor $pow $slice $concat $bweqx
// FIXME: $lut $sop $alu $lcu $macc $fa $logic_and $logic_or $bwmux
if (cell->type.in(ID($mul), ID($div), ID($mod), ID($divfloor), ID($modfloor), ID($pow))) {
full_op(this, cell);
return true;
}
// FIXME: $_BUF_ $_NOT_ $_AND_ $_NAND_ $_OR_ $_NOR_ $_XOR_ $_XNOR_ $_ANDNOT_ $_ORNOT_
// FIXME: $_MUX_ $_NMUX_ $_MUX4_ $_MUX8_ $_MUX16_ $_AOI3_ $_OAI3_ $_AOI4_ $_OAI4_
if (cell->type.in(ID($lut), ID($sop), ID($alu), ID($lcu), ID($macc), ID($macc_v2))) {
full_op(this, cell);
return true;
}
if (cell->type.in(
ID($_BUF_), ID($_NOT_), ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_),
ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_), ID($_MUX_), ID($_NMUX_),
ID($_MUX4_), ID($_MUX8_), ID($_MUX16_), ID($_AOI3_), ID($_OAI3_), ID($_AOI4_),
ID($_OAI4_), ID($_TBUF_))) {
full_op(this, cell);
return true;
}
// FIXME: $specify2 $specify3 $specrule ???
// FIXME: $equiv $set_tag $get_tag $overwrite_tag $original_tag
@ -468,4 +597,3 @@ bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL
return false;
}

View file

@ -21,6 +21,7 @@
#define CELLTYPES_H
#include "kernel/yosys.h"
#include "kernel/newcelltypes.h"
YOSYS_NAMESPACE_BEGIN
@ -87,22 +88,22 @@ struct CellTypes
{
setup_internals_eval();
setup_type(ID($tribuf), {ID::A, ID::EN}, {ID::Y}, true);
setup_type(ID($tribuf), {ID::A, ID::EN}, {ID::Y});
setup_type(ID($assert), {ID::A, ID::EN}, pool<RTLIL::IdString>(), true);
setup_type(ID($assume), {ID::A, ID::EN}, pool<RTLIL::IdString>(), true);
setup_type(ID($live), {ID::A, ID::EN}, pool<RTLIL::IdString>(), true);
setup_type(ID($fair), {ID::A, ID::EN}, pool<RTLIL::IdString>(), true);
setup_type(ID($cover), {ID::A, ID::EN}, pool<RTLIL::IdString>(), true);
setup_type(ID($initstate), pool<RTLIL::IdString>(), {ID::Y}, true);
setup_type(ID($anyconst), pool<RTLIL::IdString>(), {ID::Y}, true);
setup_type(ID($anyseq), pool<RTLIL::IdString>(), {ID::Y}, true);
setup_type(ID($allconst), pool<RTLIL::IdString>(), {ID::Y}, true);
setup_type(ID($allseq), pool<RTLIL::IdString>(), {ID::Y}, true);
setup_type(ID($equiv), {ID::A, ID::B}, {ID::Y}, true);
setup_type(ID($specify2), {ID::EN, ID::SRC, ID::DST}, pool<RTLIL::IdString>(), true);
setup_type(ID($specify3), {ID::EN, ID::SRC, ID::DST, ID::DAT}, pool<RTLIL::IdString>(), true);
setup_type(ID($specrule), {ID::EN_SRC, ID::EN_DST, ID::SRC, ID::DST}, pool<RTLIL::IdString>(), true);
setup_type(ID($assert), {ID::A, ID::EN}, pool<RTLIL::IdString>());
setup_type(ID($assume), {ID::A, ID::EN}, pool<RTLIL::IdString>());
setup_type(ID($live), {ID::A, ID::EN}, pool<RTLIL::IdString>());
setup_type(ID($fair), {ID::A, ID::EN}, pool<RTLIL::IdString>());
setup_type(ID($cover), {ID::A, ID::EN}, pool<RTLIL::IdString>());
setup_type(ID($initstate), pool<RTLIL::IdString>(), {ID::Y});
setup_type(ID($anyconst), pool<RTLIL::IdString>(), {ID::Y});
setup_type(ID($anyseq), pool<RTLIL::IdString>(), {ID::Y});
setup_type(ID($allconst), pool<RTLIL::IdString>(), {ID::Y});
setup_type(ID($allseq), pool<RTLIL::IdString>(), {ID::Y});
setup_type(ID($equiv), {ID::A, ID::B}, {ID::Y});
setup_type(ID($specify2), {ID::EN, ID::SRC, ID::DST}, pool<RTLIL::IdString>());
setup_type(ID($specify3), {ID::EN, ID::SRC, ID::DST, ID::DAT}, pool<RTLIL::IdString>());
setup_type(ID($specrule), {ID::EN_SRC, ID::EN_DST, ID::SRC, ID::DST}, pool<RTLIL::IdString>());
setup_type(ID($print), {ID::EN, ID::ARGS, ID::TRG}, pool<RTLIL::IdString>());
setup_type(ID($check), {ID::A, ID::EN, ID::ARGS, ID::TRG}, pool<RTLIL::IdString>());
setup_type(ID($set_tag), {ID::A, ID::SET, ID::CLR}, {ID::Y});
@ -195,7 +196,7 @@ struct CellTypes
{
setup_stdcells_eval();
setup_type(ID($_TBUF_), {ID::A, ID::E}, {ID::Y}, true);
setup_type(ID($_TBUF_), {ID::A, ID::E}, {ID::Y});
}
void setup_stdcells_eval()
@ -548,9 +549,6 @@ struct CellTypes
}
};
// initialized by yosys_setup()
extern CellTypes yosys_celltypes;
YOSYS_NAMESPACE_END
#endif

View file

@ -24,9 +24,14 @@
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
#include "kernel/macc.h"
#include "kernel/newcelltypes.h"
YOSYS_NAMESPACE_BEGIN
/**
* ConstEval provides on-demand constant propagation by traversing input cones
* with caching
*/
struct ConstEval
{
RTLIL::Module *module;
@ -40,9 +45,8 @@ struct ConstEval
ConstEval(RTLIL::Module *module, RTLIL::State defaultval = RTLIL::State::Sm) : module(module), assign_map(module), defaultval(defaultval)
{
CellTypes ct;
ct.setup_internals();
ct.setup_stdcells();
auto ct = NewCellTypes();
ct.static_cell_types = StaticCellTypes::Compat::nomem_noff;
for (auto &it : module->cells_) {
if (!ct.cell_known(it.second->type))

View file

@ -25,7 +25,7 @@
#include "kernel/rtlil.h"
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
YOSYS_NAMESPACE_BEGIN
@ -1093,10 +1093,10 @@ private:
struct DriverMap
{
CellTypes celltypes;
NewCellTypes celltypes;
DriverMap() { celltypes.setup(); }
DriverMap(Design *design) { celltypes.setup(); celltypes.setup_design(design); }
DriverMap(Design *design) { celltypes.setup(design); }
private:

View file

@ -1321,6 +1321,12 @@ public:
return i < 0 ? 0 : 1;
}
int lookup(const K &key) const
{
Hasher::hash_t hash = database.do_hash(key);
return database.do_lookup_no_rehash(key, hash);
}
void expect(const K &key, int i)
{
int j = (*this)(key);

View file

@ -90,11 +90,11 @@ int gettimeofday(struct timeval *tv, struct timezone *tz)
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&counter);
counter.QuadPart *= 1000000;
counter.QuadPart *= 1'000'000;
counter.QuadPart /= freq.QuadPart;
tv->tv_sec = long(counter.QuadPart / 1000000);
tv->tv_usec = counter.QuadPart % 1000000;
tv->tv_usec = counter.QuadPart % 1'000'000;
return 0;
}
@ -135,7 +135,7 @@ static void logv_string(std::string_view format, std::string str) {
initial_tv = tv;
if (tv.tv_usec < initial_tv.tv_usec) {
tv.tv_sec--;
tv.tv_usec += 1000000;
tv.tv_usec += 1'000'000;
}
tv.tv_sec -= initial_tv.tv_sec;
tv.tv_usec -= initial_tv.tv_usec;

View file

@ -78,7 +78,7 @@ ContentListing* ContentListing::open_option(const string &text,
}
#define MAX_LINE_LEN 80
void log_pass_str(const std::string &pass_str, std::string indent_str, bool leading_newline=false) {
void log_body_str(const std::string &pass_str, std::string indent_str, bool leading_newline=false, bool is_formatted=false) {
if (pass_str.empty())
return;
std::istringstream iss(pass_str);
@ -86,26 +86,30 @@ void log_pass_str(const std::string &pass_str, std::string indent_str, bool lead
log("\n");
for (std::string line; std::getline(iss, line);) {
log("%s", indent_str);
auto curr_len = indent_str.length();
std::istringstream lss(line);
for (std::string word; std::getline(lss, word, ' ');) {
while (word[0] == '`' && word.back() == '`')
word = word.substr(1, word.length()-2);
if (curr_len + word.length() >= MAX_LINE_LEN-1) {
curr_len = 0;
log("\n%s", indent_str);
}
if (word.length()) {
log("%s ", word);
curr_len += word.length() + 1;
if (is_formatted) {
log("%s", line);
} else {
auto curr_len = indent_str.length();
std::istringstream lss(line);
for (std::string word; std::getline(lss, word, ' ');) {
while (word[0] == '`' && word.back() == '`')
word = word.substr(1, word.length()-2);
if (curr_len + word.length() >= MAX_LINE_LEN-1) {
curr_len = 0;
log("\n%s", indent_str);
}
if (word.length()) {
log("%s ", word);
curr_len += word.length() + 1;
}
}
}
log("\n");
}
}
void log_pass_str(const std::string &pass_str, int indent=0, bool leading_newline=false) {
void log_body(const ContentListing &content, int indent=0, bool leading_newline=false) {
std::string indent_str(indent*4, ' ');
log_pass_str(pass_str, indent_str, leading_newline);
log_body_str(content.body, indent_str, leading_newline, content.type.compare("code") == 0);
}
PrettyHelp *current_help = nullptr;
@ -134,16 +138,16 @@ void PrettyHelp::log_help() const
{
for (auto &content : _root_listing) {
if (content.type.compare("usage") == 0) {
log_pass_str(content.body, 1, true);
log_body(content, 1, true);
log("\n");
} else if (content.type.compare("option") == 0) {
log_pass_str(content.body, 1);
log_body(content, 1);
for (auto text : content) {
log_pass_str(text.body, 2);
log_body(text, 2);
log("\n");
}
} else {
log_pass_str(content.body, 0);
log_body(content, 0);
log("\n");
}
}

View file

@ -23,11 +23,28 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
YOSYS_NAMESPACE_BEGIN
struct ModIndex : public RTLIL::Monitor
{
struct PointerOrderedSigBit : public RTLIL::SigBit {
PointerOrderedSigBit(SigBit s) {
wire = s.wire;
if (wire)
offset = s.offset;
else
data = s.data;
}
inline bool operator<(const RTLIL::SigBit &other) const {
if (wire == other.wire)
return wire ? (offset < other.offset) : (data < other.data);
if (wire != nullptr && other.wire != nullptr)
return wire < other.wire; // look here
return (wire != nullptr) < (other.wire != nullptr);
}
};
struct PortInfo {
RTLIL::Cell* cell;
RTLIL::IdString port;
@ -77,7 +94,7 @@ struct ModIndex : public RTLIL::Monitor
SigMap sigmap;
RTLIL::Module *module;
std::map<RTLIL::SigBit, SigBitInfo> database;
std::map<PointerOrderedSigBit, SigBitInfo> database;
int auto_reload_counter;
bool auto_reload_module;
@ -94,8 +111,11 @@ struct ModIndex : public RTLIL::Monitor
{
for (int i = 0; i < GetSize(sig); i++) {
RTLIL::SigBit bit = sigmap(sig[i]);
if (bit.wire)
if (bit.wire) {
database[bit].ports.erase(PortInfo(cell, port, i));
if (!database[bit].is_input && !database[bit].is_output && database[bit].ports.empty())
database.erase(bit);
}
}
}
@ -132,11 +152,11 @@ struct ModIndex : public RTLIL::Monitor
}
}
void check()
bool ok()
{
#ifndef NDEBUG
if (auto_reload_module)
return;
return true;
for (auto it : database)
log_assert(it.first == sigmap(it.first));
@ -156,11 +176,17 @@ struct ModIndex : public RTLIL::Monitor
else if (!(it.second == database_bak.at(it.first)))
log("ModuleIndex::check(): Different content for database[%s].\n", log_signal(it.first));
log_assert(database == database_bak);
return false;
}
return true;
#endif
}
void check()
{
log_assert(ok());
}
void notify_connect(RTLIL::Cell *cell, RTLIL::IdString port, const RTLIL::SigSpec &old_sig, const RTLIL::SigSpec &sig) override
{
log_assert(module == cell->module);
@ -332,7 +358,7 @@ struct ModWalker
RTLIL::Design *design;
RTLIL::Module *module;
CellTypes ct;
NewCellTypes ct;
SigMap sigmap;
dict<RTLIL::SigBit, pool<PortBit>> signal_drivers;

651
kernel/newcelltypes.h Normal file
View file

@ -0,0 +1,651 @@
#ifndef NEWCELLTYPES_H
#define NEWCELLTYPES_H
#include "kernel/rtlil.h"
#include "kernel/yosys.h"
YOSYS_NAMESPACE_BEGIN
/**
* This API is unstable.
* It may change or be removed in future versions and break dependent code.
*/
namespace StaticCellTypes {
// Given by last internal cell type IdString constids.inc, compilation error if too low
constexpr int MAX_CELLS = 300;
// Currently given by _MUX16_, compilation error if too low
constexpr int MAX_PORTS = 20;
struct CellTableBuilder {
struct PortList {
std::array<RTLIL::IdString, MAX_PORTS> ports{};
size_t count = 0;
constexpr PortList() = default;
constexpr PortList(std::initializer_list<RTLIL::IdString> init) {
for (auto p : init) {
ports[count++] = p;
}
}
constexpr auto begin() const { return ports.begin(); }
constexpr auto end() const { return ports.begin() + count; }
constexpr bool contains(RTLIL::IdString port) const {
for (size_t i = 0; i < count; i++) {
if (port == ports[i])
return true;
}
return false;
}
constexpr size_t size() const { return count; }
};
struct Features {
bool is_evaluable = false;
bool is_combinatorial = false;
bool is_synthesizable = false;
bool is_stdcell = false;
bool is_ff = false;
bool is_mem_noff = false;
bool is_anyinit = false;
bool is_tristate = false;
};
struct CellInfo {
RTLIL::IdString type;
PortList inputs, outputs;
Features features;
};
std::array<CellInfo, MAX_CELLS> cells{};
size_t count = 0;
constexpr void setup_type(RTLIL::IdString type, std::initializer_list<RTLIL::IdString> inputs, std::initializer_list<RTLIL::IdString> outputs, const Features& features) {
cells[count++] = {type, PortList(inputs), PortList(outputs), features};
}
constexpr void setup_internals_other()
{
Features features {};
features.is_tristate = true;
setup_type(ID($tribuf), {ID::A, ID::EN}, {ID::Y}, features);
features = {};
setup_type(ID($assert), {ID::A, ID::EN}, {}, features);
setup_type(ID($assume), {ID::A, ID::EN}, {}, features);
setup_type(ID($live), {ID::A, ID::EN}, {}, features);
setup_type(ID($fair), {ID::A, ID::EN}, {}, features);
setup_type(ID($cover), {ID::A, ID::EN}, {}, features);
setup_type(ID($initstate), {}, {ID::Y}, features);
setup_type(ID($anyconst), {}, {ID::Y}, features);
setup_type(ID($anyseq), {}, {ID::Y}, features);
setup_type(ID($allconst), {}, {ID::Y}, features);
setup_type(ID($allseq), {}, {ID::Y}, features);
setup_type(ID($equiv), {ID::A, ID::B}, {ID::Y}, features);
setup_type(ID($specify2), {ID::EN, ID::SRC, ID::DST}, {}, features);
setup_type(ID($specify3), {ID::EN, ID::SRC, ID::DST, ID::DAT}, {}, features);
setup_type(ID($specrule), {ID::EN_SRC, ID::EN_DST, ID::SRC, ID::DST}, {}, features);
setup_type(ID($print), {ID::EN, ID::ARGS, ID::TRG}, {}, features);
setup_type(ID($check), {ID::A, ID::EN, ID::ARGS, ID::TRG}, {}, features);
setup_type(ID($set_tag), {ID::A, ID::SET, ID::CLR}, {ID::Y}, features);
setup_type(ID($get_tag), {ID::A}, {ID::Y}, features);
setup_type(ID($overwrite_tag), {ID::A, ID::SET, ID::CLR}, {}, features);
setup_type(ID($original_tag), {ID::A}, {ID::Y}, features);
setup_type(ID($future_ff), {ID::A}, {ID::Y}, features);
setup_type(ID($scopeinfo), {}, {}, features);
setup_type(ID($input_port), {}, {ID::Y}, features);
setup_type(ID($connect), {ID::A, ID::B}, {}, features);
}
constexpr void setup_internals_eval()
{
Features features {};
features.is_evaluable = true;
std::initializer_list<RTLIL::IdString> unary_ops = {
ID($not), ID($pos), ID($buf), ID($neg),
ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool),
ID($logic_not), ID($slice), ID($lut), ID($sop)
};
std::initializer_list<RTLIL::IdString> binary_ops = {
ID($and), ID($or), ID($xor), ID($xnor),
ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx),
ID($lt), ID($le), ID($eq), ID($ne), ID($eqx), ID($nex), ID($ge), ID($gt),
ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($divfloor), ID($modfloor), ID($pow),
ID($logic_and), ID($logic_or), ID($concat), ID($macc),
ID($bweqx)
};
for (auto type : unary_ops)
setup_type(type, {ID::A}, {ID::Y}, features);
for (auto type : binary_ops)
setup_type(type, {ID::A, ID::B}, {ID::Y}, features);
for (auto type : {ID($mux), ID($pmux), ID($bwmux)})
setup_type(type, {ID::A, ID::B, ID::S}, {ID::Y}, features);
for (auto type : {ID($bmux), ID($demux)})
setup_type(type, {ID::A, ID::S}, {ID::Y}, features);
setup_type(ID($lcu), {ID::P, ID::G, ID::CI}, {ID::CO}, features);
setup_type(ID($alu), {ID::A, ID::B, ID::CI, ID::BI}, {ID::X, ID::Y, ID::CO}, features);
setup_type(ID($macc_v2), {ID::A, ID::B, ID::C}, {ID::Y}, features);
setup_type(ID($fa), {ID::A, ID::B, ID::C}, {ID::X, ID::Y}, features);
}
constexpr void setup_internals_ff()
{
Features features {};
features.is_ff = true;
setup_type(ID($sr), {ID::SET, ID::CLR}, {ID::Q}, features);
setup_type(ID($ff), {ID::D}, {ID::Q}, features);
setup_type(ID($dff), {ID::CLK, ID::D}, {ID::Q}, features);
setup_type(ID($dffe), {ID::CLK, ID::EN, ID::D}, {ID::Q}, features);
setup_type(ID($dffsr), {ID::CLK, ID::SET, ID::CLR, ID::D}, {ID::Q}, features);
setup_type(ID($dffsre), {ID::CLK, ID::SET, ID::CLR, ID::D, ID::EN}, {ID::Q}, features);
setup_type(ID($adff), {ID::CLK, ID::ARST, ID::D}, {ID::Q}, features);
setup_type(ID($adffe), {ID::CLK, ID::ARST, ID::D, ID::EN}, {ID::Q}, features);
setup_type(ID($aldff), {ID::CLK, ID::ALOAD, ID::AD, ID::D}, {ID::Q}, features);
setup_type(ID($aldffe), {ID::CLK, ID::ALOAD, ID::AD, ID::D, ID::EN}, {ID::Q}, features);
setup_type(ID($sdff), {ID::CLK, ID::SRST, ID::D}, {ID::Q}, features);
setup_type(ID($sdffe), {ID::CLK, ID::SRST, ID::D, ID::EN}, {ID::Q}, features);
setup_type(ID($sdffce), {ID::CLK, ID::SRST, ID::D, ID::EN}, {ID::Q}, features);
setup_type(ID($dlatch), {ID::EN, ID::D}, {ID::Q}, features);
setup_type(ID($adlatch), {ID::EN, ID::D, ID::ARST}, {ID::Q}, features);
setup_type(ID($dlatchsr), {ID::EN, ID::SET, ID::CLR, ID::D}, {ID::Q}, features);
}
constexpr void setup_internals_anyinit()
{
Features features {};
features.is_anyinit = true;
setup_type(ID($anyinit), {ID::D}, {ID::Q}, features);
}
constexpr void setup_internals_mem_noff()
{
Features features {};
features.is_mem_noff = true;
// NOT setup_internals_ff()
setup_type(ID($memrd), {ID::CLK, ID::EN, ID::ADDR}, {ID::DATA}, features);
setup_type(ID($memrd_v2), {ID::CLK, ID::EN, ID::ARST, ID::SRST, ID::ADDR}, {ID::DATA}, features);
setup_type(ID($memwr), {ID::CLK, ID::EN, ID::ADDR, ID::DATA}, {}, features);
setup_type(ID($memwr_v2), {ID::CLK, ID::EN, ID::ADDR, ID::DATA}, {}, features);
setup_type(ID($meminit), {ID::ADDR, ID::DATA}, {}, features);
setup_type(ID($meminit_v2), {ID::ADDR, ID::DATA, ID::EN}, {}, features);
setup_type(ID($mem), {ID::RD_CLK, ID::RD_EN, ID::RD_ADDR, ID::WR_CLK, ID::WR_EN, ID::WR_ADDR, ID::WR_DATA}, {ID::RD_DATA}, features);
setup_type(ID($mem_v2), {ID::RD_CLK, ID::RD_EN, ID::RD_ARST, ID::RD_SRST, ID::RD_ADDR, ID::WR_CLK, ID::WR_EN, ID::WR_ADDR, ID::WR_DATA}, {ID::RD_DATA}, features);
// What?
setup_type(ID($fsm), {ID::CLK, ID::ARST, ID::CTRL_IN}, {ID::CTRL_OUT}, features);
}
constexpr void setup_stdcells_tristate()
{
Features features {};
features.is_stdcell = true;
features.is_tristate = true;
setup_type(ID($_TBUF_), {ID::A, ID::E}, {ID::Y}, features);
}
constexpr void setup_stdcells_eval()
{
Features features {};
features.is_stdcell = true;
features.is_evaluable = true;
setup_type(ID($_BUF_), {ID::A}, {ID::Y}, features);
setup_type(ID($_NOT_), {ID::A}, {ID::Y}, features);
setup_type(ID($_AND_), {ID::A, ID::B}, {ID::Y}, features);
setup_type(ID($_NAND_), {ID::A, ID::B}, {ID::Y}, features);
setup_type(ID($_OR_), {ID::A, ID::B}, {ID::Y}, features);
setup_type(ID($_NOR_), {ID::A, ID::B}, {ID::Y}, features);
setup_type(ID($_XOR_), {ID::A, ID::B}, {ID::Y}, features);
setup_type(ID($_XNOR_), {ID::A, ID::B}, {ID::Y}, features);
setup_type(ID($_ANDNOT_), {ID::A, ID::B}, {ID::Y}, features);
setup_type(ID($_ORNOT_), {ID::A, ID::B}, {ID::Y}, features);
setup_type(ID($_MUX_), {ID::A, ID::B, ID::S}, {ID::Y}, features);
setup_type(ID($_NMUX_), {ID::A, ID::B, ID::S}, {ID::Y}, features);
setup_type(ID($_MUX4_), {ID::A, ID::B, ID::C, ID::D, ID::S, ID::T}, {ID::Y}, features);
setup_type(ID($_MUX8_), {ID::A, ID::B, ID::C, ID::D, ID::E, ID::F, ID::G, ID::H, ID::S, ID::T, ID::U}, {ID::Y}, features);
setup_type(ID($_MUX16_), {ID::A, ID::B, ID::C, ID::D, ID::E, ID::F, ID::G, ID::H, ID::I, ID::J, ID::K, ID::L, ID::M, ID::N, ID::O, ID::P, ID::S, ID::T, ID::U, ID::V}, {ID::Y}, features);
setup_type(ID($_AOI3_), {ID::A, ID::B, ID::C}, {ID::Y}, features);
setup_type(ID($_OAI3_), {ID::A, ID::B, ID::C}, {ID::Y}, features);
setup_type(ID($_AOI4_), {ID::A, ID::B, ID::C, ID::D}, {ID::Y}, features);
setup_type(ID($_OAI4_), {ID::A, ID::B, ID::C, ID::D}, {ID::Y}, features);
}
constexpr void setup_stdcells_ff() {
Features features {};
features.is_stdcell = true;
features.is_ff = true;
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// setup_type(std::string("$_SR_") + c1 + c2 + "_", {ID::S, ID::R}, {ID::Q}, features);
setup_type(ID($_SR_NN_), {ID::S, ID::R}, {ID::Q}, features);
setup_type(ID($_SR_NP_), {ID::S, ID::R}, {ID::Q}, features);
setup_type(ID($_SR_PN_), {ID::S, ID::R}, {ID::Q}, features);
setup_type(ID($_SR_PP_), {ID::S, ID::R}, {ID::Q}, features);
setup_type(ID($_FF_), {ID::D}, {ID::Q}, features);
// for (auto c1 : list_np)
// setup_type(std::string("$_DFF_") + c1 + "_", {ID::C, ID::D}, {ID::Q}, features);
setup_type(ID::$_DFF_N_, {ID::C, ID::D}, {ID::Q}, features);
setup_type(ID::$_DFF_P_, {ID::C, ID::D}, {ID::Q}, features);
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// setup_type(std::string("$_DFFE_") + c1 + c2 + "_", {ID::C, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID::$_DFFE_NN_, {ID::C, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID::$_DFFE_NP_, {ID::C, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID::$_DFFE_PN_, {ID::C, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID::$_DFFE_PP_, {ID::C, ID::D, ID::E}, {ID::Q}, features);
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// for (auto c3 : list_01)
// setup_type(std::string("$_DFF_") + c1 + c2 + c3 + "_", {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFF_NN0_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFF_NN1_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFF_NP0_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFF_NP1_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFF_PN0_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFF_PN1_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFF_PP0_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFF_PP1_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// for (auto c3 : list_01)
// for (auto c4 : list_np)
// setup_type(std::string("$_DFFE_") + c1 + c2 + c3 + c4 + "_", {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_NN0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_NN0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_NN1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_NN1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_NP0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_NP0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_NP1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_NP1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_PN0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_PN0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_PN1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_PN1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_PP0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_PP0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_PP1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFE_PP1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// setup_type(std::string("$_ALDFF_") + c1 + c2 + "_", {ID::C, ID::L, ID::AD, ID::D}, {ID::Q}, features);
setup_type(ID($_ALDFF_NN_), {ID::C, ID::L, ID::AD, ID::D}, {ID::Q}, features);
setup_type(ID($_ALDFF_NP_), {ID::C, ID::L, ID::AD, ID::D}, {ID::Q}, features);
setup_type(ID($_ALDFF_PN_), {ID::C, ID::L, ID::AD, ID::D}, {ID::Q}, features);
setup_type(ID($_ALDFF_PP_), {ID::C, ID::L, ID::AD, ID::D}, {ID::Q}, features);
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// for (auto c3 : list_np)
// setup_type(std::string("$_ALDFFE_") + c1 + c2 + c3 + "_", {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_ALDFFE_NNN_), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_ALDFFE_NNP_), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_ALDFFE_NPN_), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_ALDFFE_NPP_), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_ALDFFE_PNN_), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_ALDFFE_PNP_), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_ALDFFE_PPN_), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_ALDFFE_PPP_), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}, features);
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// for (auto c3 : list_np)
// setup_type(std::string("$_DFFSR_") + c1 + c2 + c3 + "_", {ID::C, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFFSR_NNN_), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFFSR_NNP_), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFFSR_NPN_), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFFSR_NPP_), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFFSR_PNN_), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFFSR_PNP_), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFFSR_PPN_), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DFFSR_PPP_), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}, features);
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// for (auto c3 : list_np)
// for (auto c4 : list_np)
// setup_type(std::string("$_DFFSRE_") + c1 + c2 + c3 + c4 + "_", {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_NNNN_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_NNNP_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_NNPN_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_NNPP_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_NPNN_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_NPNP_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_NPPN_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_NPPP_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_PNNN_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_PNNP_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_PNPN_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_PNPP_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_PPNN_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_PPNP_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_PPPN_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_DFFSRE_PPPP_), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}, features);
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// for (auto c3 : list_01)
// setup_type(std::string("$_SDFF_") + c1 + c2 + c3 + "_", {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_SDFF_NN0_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_SDFF_NN1_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_SDFF_NP0_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_SDFF_NP1_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_SDFF_PN0_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_SDFF_PN1_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_SDFF_PP0_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_SDFF_PP1_), {ID::C, ID::R, ID::D}, {ID::Q}, features);
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// for (auto c3 : list_01)
// for (auto c4 : list_np)
// setup_type(std::string("$_SDFFE_") + c1 + c2 + c3 + c4 + "_", {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_NN0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_NN0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_NN1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_NN1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_NP0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_NP0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_NP1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_NP1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_PN0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_PN0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_PN1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_PN1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_PP0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_PP0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_PP1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFE_PP1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// for (auto c3 : list_01)
// for (auto c4 : list_np)
// setup_type(std::string("$_SDFFCE_") + c1 + c2 + c3 + c4 + "_", {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_NN0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_NN0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_NN1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_NN1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_NP0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_NP0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_NP1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_NP1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_PN0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_PN0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_PN1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_PN1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_PP0N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_PP0P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_PP1N_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
setup_type(ID($_SDFFCE_PP1P_), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}, features);
// for (auto c1 : list_np)
// setup_type(std::string("$_DLATCH_") + c1 + "_", {ID::E, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCH_N_), {ID::E, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCH_P_), {ID::E, ID::D}, {ID::Q}, features);
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// for (auto c3 : list_01)
// setup_type(std::string("$_DLATCH_") + c1 + c2 + c3 + "_", {ID::E, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCH_NN0_), {ID::E, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCH_NN1_), {ID::E, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCH_NP0_), {ID::E, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCH_NP1_), {ID::E, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCH_PN0_), {ID::E, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCH_PN1_), {ID::E, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCH_PP0_), {ID::E, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCH_PP1_), {ID::E, ID::R, ID::D}, {ID::Q}, features);
// for (auto c1 : list_np)
// for (auto c2 : list_np)
// for (auto c3 : list_np)
// setup_type(std::string("$_DLATCHSR_") + c1 + c2 + c3 + "_", {ID::E, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCHSR_NNN_), {ID::E, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCHSR_NNP_), {ID::E, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCHSR_NPN_), {ID::E, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCHSR_NPP_), {ID::E, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCHSR_PNN_), {ID::E, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCHSR_PNP_), {ID::E, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCHSR_PPN_), {ID::E, ID::S, ID::R, ID::D}, {ID::Q}, features);
setup_type(ID($_DLATCHSR_PPP_), {ID::E, ID::S, ID::R, ID::D}, {ID::Q}, features);
}
constexpr CellTableBuilder() {
setup_internals_other();
setup_internals_eval();
setup_internals_ff();
setup_internals_anyinit();
setup_internals_mem_noff();
setup_stdcells_tristate();
setup_stdcells_eval();
setup_stdcells_ff();
}
};
constexpr CellTableBuilder builder{};
struct PortInfo {
struct PortLists {
std::array<CellTableBuilder::PortList, MAX_CELLS> data{};
constexpr CellTableBuilder::PortList operator()(IdString type) const {
return data[type.index_];
}
constexpr CellTableBuilder::PortList& operator[](size_t idx) {
return data[idx];
}
constexpr size_t size() const { return data.size(); }
};
PortLists inputs {};
PortLists outputs {};
constexpr PortInfo() {
for (size_t i = 0; i < builder.count; ++i) {
auto& cell = builder.cells[i];
size_t idx = cell.type.index_;
inputs[idx] = cell.inputs;
outputs[idx] = cell.outputs;
}
}
};
struct Categories {
struct Category {
std::array<bool, MAX_CELLS> data{};
constexpr bool operator()(IdString type) const {
size_t idx = type.index_;
if (idx >= MAX_CELLS)
return false;
return data[idx];
}
constexpr bool operator[](size_t idx) {
return data[idx];
}
constexpr void set_id(IdString type, bool val = true) {
size_t idx = type.index_;
if (idx >= MAX_CELLS)
return; // TODO should be an assert but then it's not constexpr
data[idx] = val;
}
constexpr void set(size_t idx, bool val = true) {
data[idx] = val;
}
constexpr size_t size() const { return data.size(); }
};
Category empty {};
Category is_known {};
Category is_evaluable {};
Category is_combinatorial {};
Category is_synthesizable {};
Category is_stdcell {};
Category is_ff {};
Category is_mem_noff {};
Category is_anyinit {};
Category is_tristate {};
constexpr Categories() {
for (size_t i = 0; i < builder.count; ++i) {
auto& cell = builder.cells[i];
size_t idx = cell.type.index_;
is_known.set(idx);
is_evaluable.set(idx, cell.features.is_evaluable);
is_combinatorial.set(idx, cell.features.is_combinatorial);
is_synthesizable.set(idx, cell.features.is_synthesizable);
is_stdcell.set(idx, cell.features.is_stdcell);
is_ff.set(idx, cell.features.is_ff);
is_mem_noff.set(idx, cell.features.is_mem_noff);
is_anyinit.set(idx, cell.features.is_anyinit);
is_tristate.set(idx, cell.features.is_tristate);
}
}
constexpr static Category join(Category left, Category right) {
Category c {};
for (size_t i = 0; i < MAX_CELLS; ++i) {
c.set(i, left[i] || right[i]);
}
return c;
}
constexpr static Category meet(Category left, Category right) {
Category c {};
for (size_t i = 0; i < MAX_CELLS; ++i) {
c.set(i, left[i] && right[i]);
}
return c;
}
// Sketchy! Make sure to always meet with only the known universe.
// In other words, no modus tollens allowed
constexpr static Category complement(Category arg) {
Category c {};
for (size_t i = 0; i < MAX_CELLS; ++i) {
c.set(i, !arg[i]);
}
return c;
}
};
// Pure
static constexpr PortInfo port_info;
static constexpr Categories categories;
// Legacy
namespace Compat {
static constexpr auto internals_all = Categories::meet(categories.is_known, Categories::complement(categories.is_stdcell));
static constexpr auto mem_ff = Categories::join(categories.is_ff, categories.is_mem_noff);
// old setup_internals + setup_stdcells
static constexpr auto nomem_noff = Categories::meet(categories.is_known, Categories::complement(mem_ff));
static constexpr auto internals_mem_ff = Categories::meet(internals_all, mem_ff);
// old setup_internals
static constexpr auto internals_nomem_noff = Categories::meet(internals_all, nomem_noff);
// old setup_stdcells
static constexpr auto stdcells_nomem_noff = Categories::meet(categories.is_stdcell, nomem_noff);
static constexpr auto stdcells_mem = Categories::meet(categories.is_stdcell, categories.is_mem_noff);
// old setup_internals_eval
// static constexpr auto internals_eval = Categories::meet(internals_all, categories.is_evaluable);
};
namespace {
static_assert(categories.is_evaluable(ID($and)));
static_assert(!categories.is_ff(ID($and)));
static_assert(Categories::join(categories.is_evaluable, categories.is_ff)(ID($and)));
static_assert(Categories::join(categories.is_evaluable, categories.is_ff)(ID($dffsr)));
static_assert(!Categories::join(categories.is_evaluable, categories.is_ff)(ID($anyinit)));
}
};
struct NewCellType {
RTLIL::IdString type;
pool<RTLIL::IdString> inputs, outputs;
bool is_evaluable;
bool is_combinatorial;
bool is_synthesizable;
};
struct NewCellTypes {
struct IdStringHash {
std::size_t operator()(const IdString id) const {
return static_cast<size_t>(id.hash_top().yield());
}
};
StaticCellTypes::Categories::Category static_cell_types = StaticCellTypes::categories.empty;
std::unordered_map<RTLIL::IdString, NewCellType, IdStringHash> custom_cell_types {};
NewCellTypes() {
static_cell_types = StaticCellTypes::categories.empty;
}
NewCellTypes(RTLIL::Design *design) {
static_cell_types = StaticCellTypes::categories.empty;
setup(design);
}
void setup(RTLIL::Design *design = NULL) {
if (design)
setup_design(design);
static_cell_types = StaticCellTypes::categories.is_known;
}
void setup_design(RTLIL::Design *design) {
for (auto module : design->modules())
setup_module(module);
}
void setup_module(RTLIL::Module *module) {
pool<RTLIL::IdString> inputs, outputs;
for (RTLIL::IdString wire_name : module->ports) {
RTLIL::Wire *wire = module->wire(wire_name);
if (wire->port_input)
inputs.insert(wire->name);
if (wire->port_output)
outputs.insert(wire->name);
}
setup_type(module->name, inputs, outputs);
}
void setup_type(RTLIL::IdString type, const pool<RTLIL::IdString> &inputs, const pool<RTLIL::IdString> &outputs, bool is_evaluable = false, bool is_combinatorial = false, bool is_synthesizable = false) {
NewCellType ct = {type, inputs, outputs, is_evaluable, is_combinatorial, is_synthesizable};
custom_cell_types[ct.type] = ct;
}
void clear() {
custom_cell_types.clear();
static_cell_types = StaticCellTypes::categories.empty;
}
bool cell_known(const RTLIL::IdString &type) const {
return static_cell_types(type) || custom_cell_types.count(type) != 0;
}
bool cell_output(const RTLIL::IdString &type, const RTLIL::IdString &port) const
{
if (static_cell_types(type) && StaticCellTypes::port_info.outputs(type).contains(port)) {
return true;
}
auto it = custom_cell_types.find(type);
return it != custom_cell_types.end() && it->second.outputs.count(port) != 0;
}
bool cell_input(const RTLIL::IdString &type, const RTLIL::IdString &port) const
{
if (static_cell_types(type) && StaticCellTypes::port_info.inputs(type).contains(port)) {
return true;
}
auto it = custom_cell_types.find(type);
return it != custom_cell_types.end() && it->second.inputs.count(port) != 0;
}
RTLIL::PortDir cell_port_dir(RTLIL::IdString type, RTLIL::IdString port) const
{
bool is_input, is_output;
if (static_cell_types(type)) {
is_input = StaticCellTypes::port_info.inputs(type).contains(port);
is_output = StaticCellTypes::port_info.outputs(type).contains(port);
} else {
auto it = custom_cell_types.find(type);
if (it == custom_cell_types.end())
return RTLIL::PD_UNKNOWN;
is_input = it->second.inputs.count(port);
is_output = it->second.outputs.count(port);
}
return RTLIL::PortDir(is_input + is_output * 2);
}
bool cell_evaluable(const RTLIL::IdString &type) const
{
return static_cell_types(type) && StaticCellTypes::categories.is_evaluable(type);
}
};
extern NewCellTypes yosys_celltypes;
YOSYS_NAMESPACE_END
#endif

102
kernel/pattern.h Normal file
View file

@ -0,0 +1,102 @@
#ifndef OPT_DFF_COMP_H
#define OPT_DFF_COMP_H
#include "kernel/rtlil.h"
#include <map>
#include <optional>
YOSYS_NAMESPACE_BEGIN
/**
* Pattern matching utilities for control signal analysis.
*
* A pattern_t maps control signals to required values, representing a
* product term (conjunction): {A=1, B=0} means "A AND !B".
*
* A patterns_t is a set of patterns representing a sum-of-products:
* {{A=1, B=0}, {A=0, C=1}} means "(A AND !B) OR (!A AND C)".
*
* Used for analyzing MUX tree control paths in DFF optimization.
*/
// Pattern matching for clock enable
// A pattern maps control signals to their required values for a MUX path
typedef std::map<RTLIL::SigBit, bool> pattern_t; // Set of control signals that must ALL match required vals
typedef std::set<pattern_t> patterns_t; // Alternative patterns (OR)
typedef std::pair<RTLIL::SigBit, bool> ctrl_t; // Control signal
typedef std::set<ctrl_t> ctrls_t; // Set of control signals that must ALL be active
/**
* Find if two patterns differ in exactly one variable.
* Example: {A=1,B=1} vs {A=1,B=0} returns B, allows simplification: (A&B) | (A&!B) => A
*/
inline std::optional<RTLIL::SigBit> find_complementary_pattern_var(
const pattern_t& left,
const pattern_t& right
) {
std::optional<RTLIL::SigBit> ret;
for (const auto &pt : left) {
// Left requires signal that right doesn't constrain - incompatible domains
if (right.count(pt.first) == 0)
return std::nullopt;
// Signal has same required value in both - not the complement variable
if (right.at(pt.first) == pt.second)
continue;
// Already found one differing signal, now found another - not simplifiable
if (ret)
return std::nullopt;
// First differing signal - candidate complement variable
ret = pt.first;
}
return ret;
}
/**
* Simplify a sum-of-products by merging complementary patterns: (A&B) | (A&!B) => A,
* and removing redundant patterns: A | (A&B) => A
*/
inline void simplify_patterns(patterns_t& patterns) {
auto new_patterns = patterns;
// Merge complementary patterns
bool optimized;
do {
optimized = false;
for (auto i = patterns.begin(); i != patterns.end(); i++) {
for (auto j = std::next(i, 1); j != patterns.end(); j++) {
const auto& left = (GetSize(*j) <= GetSize(*i)) ? *j : *i;
auto right = (GetSize(*i) < GetSize(*j)) ? *j : *i;
const auto complementary_var = find_complementary_pattern_var(left, right);
if (complementary_var && new_patterns.count(right)) {
new_patterns.erase(right);
right.erase(complementary_var.value());
new_patterns.insert(right);
optimized = true;
}
}
}
patterns = new_patterns;
} while(optimized);
// Remove redundant patterns
for (auto i = patterns.begin(); i != patterns.end(); ++i) {
for (auto j = std::next(i, 1); j != patterns.end(); ++j) {
const auto& left = (GetSize(*j) <= GetSize(*i)) ? *j : *i;
const auto& right = (GetSize(*i) < GetSize(*j)) ? *j : *i;
bool redundant = true;
for (const auto& pt : left)
if (right.count(pt.first) == 0 || right.at(pt.first) != pt.second)
redundant = false;
if (redundant)
new_patterns.erase(right);
}
}
patterns = std::move(new_patterns);
}
YOSYS_NAMESPACE_END
#endif

View file

@ -22,6 +22,7 @@
#include "kernel/json.h"
#include "kernel/gzip.h"
#include "kernel/log_help.h"
#include "kernel/newcelltypes.h"
#include <string.h>
#include <stdlib.h>
@ -975,16 +976,18 @@ struct HelpPass : public Pass {
json.entry("generator", yosys_maybe_version());
dict<string, vector<string>> groups;
dict<string, pair<SimHelper, CellType>> cells;
dict<string, pair<SimHelper, StaticCellTypes::CellTableBuilder::CellInfo>> cells;
// iterate over cells
bool raise_error = false;
for (auto &it : yosys_celltypes.cell_types) {
auto name = it.first.str();
for (auto it : StaticCellTypes::builder.cells) {
if (!StaticCellTypes::categories.is_known(it.type))
continue;
auto name = it.type.str();
if (cell_help_messages.contains(name)) {
auto cell_help = cell_help_messages.get(name);
groups[cell_help.group].emplace_back(name);
auto cell_pair = pair<SimHelper, CellType>(cell_help, it.second);
auto cell_pair = pair<SimHelper, StaticCellTypes::CellTableBuilder::CellInfo>(cell_help, it);
cells.emplace(name, cell_pair);
} else {
log("ERROR: Missing cell help for cell '%s'.\n", name);
@ -1025,9 +1028,9 @@ struct HelpPass : public Pass {
json.name("outputs"); json.value(outputs);
vector<string> properties;
// CellType properties
if (ct.is_evaluable) properties.push_back("is_evaluable");
if (ct.is_combinatorial) properties.push_back("is_combinatorial");
if (ct.is_synthesizable) properties.push_back("is_synthesizable");
if (ct.features.is_evaluable) properties.push_back("is_evaluable");
if (ct.features.is_combinatorial) properties.push_back("is_combinatorial");
if (ct.features.is_synthesizable) properties.push_back("is_synthesizable");
// SimHelper properties
size_t last = 0; size_t next = 0;
while ((next = ch.tags.find(", ", last)) != string::npos) {
@ -1204,7 +1207,7 @@ struct LicensePass : public Pass {
log(" | |\n");
log(" | yosys -- Yosys Open SYnthesis Suite |\n");
log(" | |\n");
log(" | Copyright (C) 2012 - 2025 Claire Xenia Wolf <claire@yosyshq.com> |\n");
log(" | Copyright (C) 2012 - 2026 Claire Xenia Wolf <claire@yosyshq.com> |\n");
log(" | |\n");
log(" | Permission to use, copy, modify, and/or distribute this software for any |\n");
log(" | purpose with or without fee is hereby granted, provided that the above |\n");

View file

@ -19,7 +19,7 @@
#include "kernel/yosys.h"
#include "kernel/macc.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
#include "kernel/binding.h"
#include "kernel/sigtools.h"
#include "frontends/verilog/verilog_frontend.h"
@ -31,6 +31,7 @@
#include <charconv>
#include <optional>
#include <string_view>
#include <sstream>
YOSYS_NAMESPACE_BEGIN
@ -185,7 +186,7 @@ struct IdStringCollector {
trace(selection_var.selected_modules);
trace(selection_var.selected_members);
}
void trace_named(const RTLIL::NamedObject named) {
void trace_named(const RTLIL::NamedObject &named) {
trace_keys(named.attributes);
trace(named.name);
}
@ -287,159 +288,17 @@ void RTLIL::OwningIdString::collect_garbage()
dict<std::string, std::string> RTLIL::constpad;
static const pool<IdString> &builtin_ff_cell_types_internal() {
static const pool<IdString> res = {
ID($sr),
ID($ff),
ID($dff),
ID($dffe),
ID($dffsr),
ID($dffsre),
ID($adff),
ID($adffe),
ID($aldff),
ID($aldffe),
ID($sdff),
ID($sdffe),
ID($sdffce),
ID($dlatch),
ID($adlatch),
ID($dlatchsr),
ID($_DFFE_NN_),
ID($_DFFE_NP_),
ID($_DFFE_PN_),
ID($_DFFE_PP_),
ID($_DFFSR_NNN_),
ID($_DFFSR_NNP_),
ID($_DFFSR_NPN_),
ID($_DFFSR_NPP_),
ID($_DFFSR_PNN_),
ID($_DFFSR_PNP_),
ID($_DFFSR_PPN_),
ID($_DFFSR_PPP_),
ID($_DFFSRE_NNNN_),
ID($_DFFSRE_NNNP_),
ID($_DFFSRE_NNPN_),
ID($_DFFSRE_NNPP_),
ID($_DFFSRE_NPNN_),
ID($_DFFSRE_NPNP_),
ID($_DFFSRE_NPPN_),
ID($_DFFSRE_NPPP_),
ID($_DFFSRE_PNNN_),
ID($_DFFSRE_PNNP_),
ID($_DFFSRE_PNPN_),
ID($_DFFSRE_PNPP_),
ID($_DFFSRE_PPNN_),
ID($_DFFSRE_PPNP_),
ID($_DFFSRE_PPPN_),
ID($_DFFSRE_PPPP_),
ID($_DFF_N_),
ID($_DFF_P_),
ID($_DFF_NN0_),
ID($_DFF_NN1_),
ID($_DFF_NP0_),
ID($_DFF_NP1_),
ID($_DFF_PN0_),
ID($_DFF_PN1_),
ID($_DFF_PP0_),
ID($_DFF_PP1_),
ID($_DFFE_NN0N_),
ID($_DFFE_NN0P_),
ID($_DFFE_NN1N_),
ID($_DFFE_NN1P_),
ID($_DFFE_NP0N_),
ID($_DFFE_NP0P_),
ID($_DFFE_NP1N_),
ID($_DFFE_NP1P_),
ID($_DFFE_PN0N_),
ID($_DFFE_PN0P_),
ID($_DFFE_PN1N_),
ID($_DFFE_PN1P_),
ID($_DFFE_PP0N_),
ID($_DFFE_PP0P_),
ID($_DFFE_PP1N_),
ID($_DFFE_PP1P_),
ID($_ALDFF_NN_),
ID($_ALDFF_NP_),
ID($_ALDFF_PN_),
ID($_ALDFF_PP_),
ID($_ALDFFE_NNN_),
ID($_ALDFFE_NNP_),
ID($_ALDFFE_NPN_),
ID($_ALDFFE_NPP_),
ID($_ALDFFE_PNN_),
ID($_ALDFFE_PNP_),
ID($_ALDFFE_PPN_),
ID($_ALDFFE_PPP_),
ID($_SDFF_NN0_),
ID($_SDFF_NN1_),
ID($_SDFF_NP0_),
ID($_SDFF_NP1_),
ID($_SDFF_PN0_),
ID($_SDFF_PN1_),
ID($_SDFF_PP0_),
ID($_SDFF_PP1_),
ID($_SDFFE_NN0N_),
ID($_SDFFE_NN0P_),
ID($_SDFFE_NN1N_),
ID($_SDFFE_NN1P_),
ID($_SDFFE_NP0N_),
ID($_SDFFE_NP0P_),
ID($_SDFFE_NP1N_),
ID($_SDFFE_NP1P_),
ID($_SDFFE_PN0N_),
ID($_SDFFE_PN0P_),
ID($_SDFFE_PN1N_),
ID($_SDFFE_PN1P_),
ID($_SDFFE_PP0N_),
ID($_SDFFE_PP0P_),
ID($_SDFFE_PP1N_),
ID($_SDFFE_PP1P_),
ID($_SDFFCE_NN0N_),
ID($_SDFFCE_NN0P_),
ID($_SDFFCE_NN1N_),
ID($_SDFFCE_NN1P_),
ID($_SDFFCE_NP0N_),
ID($_SDFFCE_NP0P_),
ID($_SDFFCE_NP1N_),
ID($_SDFFCE_NP1P_),
ID($_SDFFCE_PN0N_),
ID($_SDFFCE_PN0P_),
ID($_SDFFCE_PN1N_),
ID($_SDFFCE_PN1P_),
ID($_SDFFCE_PP0N_),
ID($_SDFFCE_PP0P_),
ID($_SDFFCE_PP1N_),
ID($_SDFFCE_PP1P_),
ID($_SR_NN_),
ID($_SR_NP_),
ID($_SR_PN_),
ID($_SR_PP_),
ID($_DLATCH_N_),
ID($_DLATCH_P_),
ID($_DLATCH_NN0_),
ID($_DLATCH_NN1_),
ID($_DLATCH_NP0_),
ID($_DLATCH_NP1_),
ID($_DLATCH_PN0_),
ID($_DLATCH_PN1_),
ID($_DLATCH_PP0_),
ID($_DLATCH_PP1_),
ID($_DLATCHSR_NNN_),
ID($_DLATCHSR_NNP_),
ID($_DLATCHSR_NPN_),
ID($_DLATCHSR_NPP_),
ID($_DLATCHSR_PNN_),
ID($_DLATCHSR_PNP_),
ID($_DLATCHSR_PPN_),
ID($_DLATCHSR_PPP_),
ID($_FF_),
};
return res;
}
const pool<IdString> &RTLIL::builtin_ff_cell_types() {
return builtin_ff_cell_types_internal();
static const pool<IdString> res = []() {
pool<IdString> r;
for (size_t i = 0; i < StaticCellTypes::builder.count; i++) {
auto &cell = StaticCellTypes::builder.cells[i];
if (cell.features.is_ff)
r.insert(cell.type);
}
return r;
}();
return res;
}
#define check(condition) log_assert(condition && "malformed Const union")
@ -1548,6 +1407,13 @@ void RTLIL::Design::pop_selection()
push_full_selection();
}
std::string RTLIL::Design::to_rtlil_str(bool only_selected) const
{
std::ostringstream f;
RTLIL_BACKEND::dump_design(f, const_cast<RTLIL::Design*>(this), only_selected);
return f.str();
}
std::vector<RTLIL::Module*> RTLIL::Design::selected_modules(RTLIL::SelectPartials partials, RTLIL::SelectBoxes boxes) const
{
bool include_partials = partials == RTLIL::SELECT_ALL;
@ -2982,6 +2848,8 @@ void RTLIL::Module::add(RTLIL::Binding *binding)
void RTLIL::Module::remove(const pool<RTLIL::Wire*> &wires)
{
log_assert(refcount_wires_ == 0);
if (wires.empty())
return;
struct DeleteWireWorker
{
@ -3039,6 +2907,13 @@ void RTLIL::Module::remove(RTLIL::Cell *cell)
}
}
void RTLIL::Module::remove(RTLIL::Memory *memory)
{
log_assert(memories.count(memory->name) != 0);
memories.erase(memory->name);
delete memory;
}
void RTLIL::Module::remove(RTLIL::Process *process)
{
log_assert(processes.count(process->name) != 0);
@ -4281,6 +4156,13 @@ RTLIL::SigSpec RTLIL::Module::FutureFF(RTLIL::IdString name, const RTLIL::SigSpe
return sig;
}
std::string RTLIL::Module::to_rtlil_str() const
{
std::ostringstream f;
RTLIL_BACKEND::dump_module(f, "", const_cast<RTLIL::Module*>(this), design, false);
return f.str();
}
RTLIL::Wire::Wire()
{
static unsigned int hashidx_count = 123456789;
@ -4308,6 +4190,13 @@ RTLIL::Wire::~Wire()
#endif
}
std::string RTLIL::Wire::to_rtlil_str() const
{
std::ostringstream f;
RTLIL_BACKEND::dump_wire(f, "", this);
return f.str();
}
#ifdef YOSYS_ENABLE_PYTHON
static std::map<unsigned int, RTLIL::Wire*> all_wires;
std::map<unsigned int, RTLIL::Wire*> *RTLIL::Wire::get_all_wires(void)
@ -4330,6 +4219,13 @@ RTLIL::Memory::Memory()
#endif
}
std::string RTLIL::Memory::to_rtlil_str() const
{
std::ostringstream f;
RTLIL_BACKEND::dump_memory(f, "", this);
return f.str();
}
RTLIL::Process::Process() : module(nullptr)
{
static unsigned int hashidx_count = 123456789;
@ -4337,6 +4233,13 @@ RTLIL::Process::Process() : module(nullptr)
hashidx_ = hashidx_count;
}
std::string RTLIL::Process::to_rtlil_str() const
{
std::ostringstream f;
RTLIL_BACKEND::dump_proc(f, "", this);
return f.str();
}
RTLIL::Cell::Cell() : module(nullptr)
{
static unsigned int hashidx_count = 123456789;
@ -4358,6 +4261,13 @@ RTLIL::Cell::~Cell()
#endif
}
std::string RTLIL::Cell::to_rtlil_str() const
{
std::ostringstream f;
RTLIL_BACKEND::dump_cell(f, "", this);
return f.str();
}
#ifdef YOSYS_ENABLE_PYTHON
static std::map<unsigned int, RTLIL::Cell*> all_cells;
std::map<unsigned int, RTLIL::Cell*> *RTLIL::Cell::get_all_cells(void)
@ -4558,7 +4468,7 @@ bool RTLIL::Cell::is_mem_cell() const
}
bool RTLIL::Cell::is_builtin_ff() const {
return builtin_ff_cell_types_internal().count(type) > 0;
return StaticCellTypes::categories.is_ff(type);
}
RTLIL::SigChunk::SigChunk(const RTLIL::SigBit &bit)

View file

@ -223,8 +223,8 @@ struct RTLIL::IdString
constexpr inline IdString() : index_(0) { }
inline IdString(const char *str) : index_(insert(std::string_view(str))) { }
constexpr inline IdString(const IdString &str) : index_(str.index_) { }
inline IdString(IdString &&str) : index_(str.index_) { str.index_ = 0; }
constexpr IdString(const IdString &str) = default;
IdString(IdString &&str) = default;
inline IdString(const std::string &str) : index_(insert(std::string_view(str))) { }
inline IdString(std::string_view str) : index_(insert(str)) { }
constexpr inline IdString(StaticId id) : index_(static_cast<short>(id)) {}
@ -737,6 +737,7 @@ template <> struct IDMacroHelper<-1> {
namespace RTLIL {
extern dict<std::string, std::string> constpad;
[[deprecated("use StaticCellTypes::categories.is_ff() instead")]]
const pool<IdString> &builtin_ff_cell_types();
static inline std::string escape_id(const std::string &str) {
@ -2031,7 +2032,10 @@ struct RTLIL::Design
// returns all selected unboxed whole modules, warning the user if any
// partially selected or boxed modules have been ignored
std::vector<RTLIL::Module*> selected_unboxed_whole_modules_warn() const { return selected_modules(SELECT_WHOLE_WARN, SB_UNBOXED_WARN); }
static std::map<unsigned int, RTLIL::Design*> *get_all_designs(void);
std::string to_rtlil_str(bool only_selected = true) const;
};
struct RTLIL::Module : public RTLIL::NamedObject
@ -2137,13 +2141,18 @@ public:
}
RTLIL::ObjRange<RTLIL::Wire*> wires() { return RTLIL::ObjRange<RTLIL::Wire*>(&wires_, &refcount_wires_); }
int wires_size() const { return wires_.size(); }
RTLIL::Wire* wire_at(int index) const { return wires_.element(index)->second; }
RTLIL::ObjRange<RTLIL::Cell*> cells() { return RTLIL::ObjRange<RTLIL::Cell*>(&cells_, &refcount_cells_); }
int cells_size() const { return cells_.size(); }
RTLIL::Cell* cell_at(int index) const { return cells_.element(index)->second; }
void add(RTLIL::Binding *binding);
// Removing wires is expensive. If you have to remove wires, remove them all at once.
void remove(const pool<RTLIL::Wire*> &wires);
void remove(RTLIL::Cell *cell);
void remove(RTLIL::Memory *memory);
void remove(RTLIL::Process *process);
void rename(RTLIL::Wire *wire, RTLIL::IdString new_name);
@ -2390,6 +2399,7 @@ public:
RTLIL::SigSpec OriginalTag (RTLIL::IdString name, const std::string &tag, const RTLIL::SigSpec &sig_a, const std::string &src = "");
RTLIL::SigSpec FutureFF (RTLIL::IdString name, const RTLIL::SigSpec &sig_e, const std::string &src = "");
std::string to_rtlil_str() const;
#ifdef YOSYS_ENABLE_PYTHON
static std::map<unsigned int, RTLIL::Module*> *get_all_modules(void);
#endif
@ -2443,6 +2453,7 @@ public:
return zero_index + start_offset;
}
std::string to_rtlil_str() const;
#ifdef YOSYS_ENABLE_PYTHON
static std::map<unsigned int, RTLIL::Wire*> *get_all_wires(void);
#endif
@ -2460,6 +2471,8 @@ struct RTLIL::Memory : public RTLIL::NamedObject
Memory();
int width, start_offset, size;
std::string to_rtlil_str() const;
#ifdef YOSYS_ENABLE_PYTHON
~Memory();
static std::map<unsigned int, RTLIL::Memory*> *get_all_memorys(void);
@ -2518,6 +2531,8 @@ public:
template<typename T> void rewrite_sigspecs(T &functor);
template<typename T> void rewrite_sigspecs2(T &functor);
std::string to_rtlil_str() const;
#ifdef YOSYS_ENABLE_PYTHON
static std::map<unsigned int, RTLIL::Cell*> *get_all_cells(void);
#endif
@ -2596,6 +2611,7 @@ public:
template<typename T> void rewrite_sigspecs(T &functor);
template<typename T> void rewrite_sigspecs2(T &functor);
RTLIL::Process *clone() const;
std::string to_rtlil_str() const;
};

View file

@ -19,6 +19,7 @@
#include "kernel/satgen.h"
#include "kernel/ff.h"
#include "kernel/yosys_common.h"
USING_YOSYS_NAMESPACE
@ -1378,7 +1379,7 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep)
return true;
}
if (cell->type == ID($scopeinfo))
if (cell->type == ID($scopeinfo) || cell->type == ID($input_port))
{
return true;
}
@ -1387,3 +1388,22 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep)
// .. and all sequential cells with asynchronous inputs
return false;
}
namespace Yosys {
void report_missing_model(bool warn_only, RTLIL::Cell* cell)
{
std::string s;
if (cell->is_builtin_ff())
s = stringf("No SAT model available for async FF cell %s (%s). Consider running `async2sync` or `clk2fflogic` first.\n", log_id(cell), log_id(cell->type));
else
s = stringf("No SAT model available for cell %s (%s).\n", log_id(cell), log_id(cell->type));
if (warn_only) {
log_formatted_warning_noprefix(s);
} else {
log_formatted_error(s);
}
}
}

View file

@ -59,6 +59,7 @@ struct SatSolver
struct ezSatPtr : public std::unique_ptr<ezSAT> {
ezSatPtr() : unique_ptr<ezSAT>(yosys_satsolver->create()) { }
explicit ezSatPtr(SatSolver *solver) : unique_ptr<ezSAT>((solver ? solver : yosys_satsolver)->create()) { }
};
struct SatGen
@ -292,6 +293,8 @@ struct SatGen
bool importCell(RTLIL::Cell *cell, int timestep = -1);
};
void report_missing_model(bool warn_only, RTLIL::Cell* cell);
YOSYS_NAMESPACE_END
#endif

View file

@ -279,7 +279,7 @@ static int tcl_get_attr(ClientData, Tcl_Interp *interp, int argc, const char *ar
ERROR("object not found")
if (string_flag) {
Tcl_SetResult(interp, (char *) obj->get_string_attribute(attr_id).c_str(), TCL_VOLATILE);
Tcl_SetObjResult(interp, Tcl_NewStringObj(obj->get_string_attribute(attr_id).c_str(), -1));
} else if (int_flag || uint_flag || sint_flag) {
if (!obj->has_attribute(attr_id))
ERROR("attribute missing (required for -int)");
@ -295,7 +295,7 @@ static int tcl_get_attr(ClientData, Tcl_Interp *interp, int argc, const char *ar
if (!obj->has_attribute(attr_id))
ERROR("attribute missing (required unless -bool or -string)")
Tcl_SetResult(interp, (char *) obj->attributes.at(attr_id).as_string().c_str(), TCL_VOLATILE);
Tcl_SetObjResult(interp, Tcl_NewStringObj(obj->attributes.at(attr_id).as_string().c_str(), -1));
}
return TCL_OK;
@ -341,7 +341,7 @@ static int tcl_has_attr(ClientData, Tcl_Interp *interp, int argc, const char *ar
if (!obj)
ERROR("object not found")
Tcl_SetResult(interp, (char *) std::to_string(obj->has_attribute(attr_id)).c_str(), TCL_VOLATILE);
Tcl_SetObjResult(interp, Tcl_NewStringObj(std::to_string(obj->has_attribute(attr_id)).c_str(), -1));
return TCL_OK;
}
@ -465,14 +465,14 @@ static int tcl_get_param(ClientData, Tcl_Interp *interp, int argc, const char *a
const RTLIL::Const &value = cell->getParam(param_id);
if (string_flag) {
Tcl_SetResult(interp, (char *) value.decode_string().c_str(), TCL_VOLATILE);
Tcl_SetObjResult(interp, Tcl_NewStringObj(value.decode_string().c_str(), -1));
} else if (int_flag || uint_flag || sint_flag) {
mp_int value_mp;
if (!const_to_mp_int(value, &value_mp, sint_flag, uint_flag))
ERROR("bignum manipulation failed");
Tcl_SetObjResult(interp, Tcl_NewBignumObj(&value_mp));
} else {
Tcl_SetResult(interp, (char *) value.as_string().c_str(), TCL_VOLATILE);
Tcl_SetObjResult(interp, Tcl_NewStringObj(value.as_string().c_str(), -1));
}
return TCL_OK;
}

View file

@ -18,8 +18,8 @@
*/
#include "kernel/yosys.h"
#include "kernel/celltypes.h"
#include "kernel/log.h"
#include "kernel/newcelltypes.h"
#ifdef YOSYS_ENABLE_READLINE
# include <readline/readline.h>
@ -92,7 +92,7 @@ const char* yosys_maybe_version() {
}
RTLIL::Design *yosys_design = NULL;
CellTypes yosys_celltypes;
NewCellTypes yosys_celltypes;
#ifdef YOSYS_ENABLE_TCL
Tcl_Interp *yosys_tcl_interp = NULL;
@ -173,7 +173,7 @@ void yosys_banner()
log("\n");
log(" /----------------------------------------------------------------------------\\\n");
log(" | yosys -- Yosys Open SYnthesis Suite |\n");
log(" | Copyright (C) 2012 - 2025 Claire Xenia Wolf <claire@yosyshq.com> |\n");
log(" | Copyright (C) 2012 - 2026 Claire Xenia Wolf <claire@yosyshq.com> |\n");
log(" | Distributed under an ISC-like license, type \"license\" to see terms |\n");
log(" \\----------------------------------------------------------------------------/\n");
log(" %s\n", yosys_maybe_version());
@ -262,7 +262,7 @@ void yosys_setup()
Pass::init_register();
yosys_design = new RTLIL::Design;
yosys_celltypes.setup();
yosys_celltypes.static_cell_types = StaticCellTypes::categories.is_known;
log_push();
}
@ -291,8 +291,6 @@ void yosys_shutdown()
log_errfile = NULL;
log_files.clear();
yosys_celltypes.clear();
#ifdef YOSYS_ENABLE_TCL
if (yosys_tcl_interp != NULL) {
if (!Tcl_InterpDeleted(yosys_tcl_interp)) {

92
libs/ezsat/ezcmdline.cc Normal file
View file

@ -0,0 +1,92 @@
#include "ezcmdline.h"
#include "../../kernel/yosys.h"
ezCmdlineSAT::ezCmdlineSAT(const std::string &cmd) : command(cmd) {}
ezCmdlineSAT::~ezCmdlineSAT() {}
bool ezCmdlineSAT::solver(const std::vector<int> &modelExpressions, std::vector<bool> &modelValues, const std::vector<int> &assumptions)
{
#if !defined(YOSYS_DISABLE_SPAWN)
const std::string tempdir_name = Yosys::make_temp_dir(Yosys::get_base_tmpdir() + "/yosys-sat-XXXXXX");
const std::string cnf_filename = Yosys::stringf("%s/problem.cnf", tempdir_name.c_str());
const std::string sat_command = Yosys::stringf("%s %s", command.c_str(), cnf_filename.c_str());
FILE *dimacs = fopen(cnf_filename.c_str(), "w");
if (dimacs == nullptr) {
Yosys::log_cmd_error("Failed to create CNF file `%s`.\n", cnf_filename.c_str());
}
std::vector<int> modelIdx;
for (auto id : modelExpressions)
modelIdx.push_back(bind(id));
std::vector<std::vector<int>> extraClauses;
for (auto id : assumptions)
extraClauses.push_back({bind(id)});
printDIMACS(dimacs, false, extraClauses);
fclose(dimacs);
bool status_sat = false;
bool status_unsat = false;
std::vector<bool> values;
auto line_callback = [&](const std::string &line) {
if (line.empty()) {
return;
}
if (line[0] == 's') {
if (line.substr(0, 5) == "s SAT") {
status_sat = true;
}
if (line.substr(0, 7) == "s UNSAT") {
status_unsat = true;
}
return;
}
if (line[0] == 'v') {
std::stringstream ss(line.substr(1));
int lit;
while (ss >> lit) {
if (lit == 0) {
return;
}
bool val = lit >= 0;
int ind = lit >= 0 ? lit - 1 : -lit - 1;
if (Yosys::GetSize(values) <= ind) {
values.resize(ind + 1);
}
values[ind] = val;
}
}
};
int return_code = Yosys::run_command(sat_command, line_callback);
if (return_code != 0 && return_code != 10 && return_code != 20) {
Yosys::log_cmd_error("Shell command failed!\n");
}
modelValues.clear();
modelValues.resize(modelIdx.size());
if (!status_sat && !status_unsat) {
solverTimeoutStatus = true;
}
if (!status_sat) {
return false;
}
for (size_t i = 0; i < modelIdx.size(); i++) {
int idx = modelIdx[i];
bool refvalue = true;
if (idx < 0)
idx = -idx, refvalue = false;
modelValues[i] = (values.at(idx - 1) == refvalue);
}
return true;
#else
Yosys::log_error("SAT solver command not available in this build!\n");
#endif
}

36
libs/ezsat/ezcmdline.h Normal file
View file

@ -0,0 +1,36 @@
/*
* ezSAT -- A simple and easy to use CNF generator for SAT solvers
*
* Copyright (C) 2013 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#ifndef EZSATCOMMAND_H
#define EZSATCOMMAND_H
#include "ezsat.h"
class ezCmdlineSAT : public ezSAT
{
private:
std::string command;
public:
ezCmdlineSAT(const std::string &cmd);
virtual ~ezCmdlineSAT();
bool solver(const std::vector<int> &modelExpressions, std::vector<bool> &modelValues, const std::vector<int> &assumptions) override;
};
#endif

View file

@ -103,7 +103,7 @@ bool ezMiniSAT::solver(const std::vector<int> &modelExpressions, std::vector<boo
{
preSolverCallback();
solverTimoutStatus = false;
solverTimeoutStatus = false;
if (0) {
contradiction:
@ -206,7 +206,7 @@ contradiction:
#if defined(HAS_ALARM)
if (solverTimeout > 0) {
if (alarmHandlerTimeout == 0)
solverTimoutStatus = true;
solverTimeoutStatus = true;
alarm(0);
sigaction(SIGALRM, &old_sig_action, NULL);
alarm(old_alarm_timeout);

View file

@ -54,7 +54,7 @@ ezSAT::ezSAT()
cnfClausesCount = 0;
solverTimeout = 0;
solverTimoutStatus = false;
solverTimeoutStatus = false;
literal("CONST_TRUE");
literal("CONST_FALSE");
@ -1222,10 +1222,15 @@ ezSATvec ezSAT::vec(const std::vector<int> &vec)
return ezSATvec(*this, vec);
}
void ezSAT::printDIMACS(FILE *f, bool verbose) const
void ezSAT::printDIMACS(FILE *f, bool verbose, const std::vector<std::vector<int>> &extraClauses) const
{
if (f == nullptr) {
fprintf(stderr, "Usage error: printDIMACS() must not be called with a null FILE pointer\n");
abort();
}
if (cnfConsumed) {
fprintf(stderr, "Usage error: printDIMACS() must not be called after cnfConsumed()!");
fprintf(stderr, "Usage error: printDIMACS() must not be called after cnfConsumed()!\n");
abort();
}
@ -1259,8 +1264,10 @@ void ezSAT::printDIMACS(FILE *f, bool verbose) const
std::vector<std::vector<int>> all_clauses;
getFullCnf(all_clauses);
assert(cnfClausesCount == int(all_clauses.size()));
for (auto c : extraClauses)
all_clauses.push_back(c);
fprintf(f, "p cnf %d %d\n", cnfVariableCount, cnfClausesCount);
fprintf(f, "p cnf %d %d\n", cnfVariableCount, (int) all_clauses.size());
int maxClauseLen = 0;
for (auto &clause : all_clauses)
maxClauseLen = std::max(int(clause.size()), maxClauseLen);

View file

@ -78,7 +78,7 @@ protected:
public:
int solverTimeout;
bool solverTimoutStatus;
bool solverTimeoutStatus;
ezSAT();
virtual ~ezSAT();
@ -153,8 +153,8 @@ public:
solverTimeout = newTimeoutSeconds;
}
bool getSolverTimoutStatus() {
return solverTimoutStatus;
bool getSolverTimeoutStatus() {
return solverTimeoutStatus;
}
// manage CNF (usually only accessed by SAT solvers)
@ -295,7 +295,7 @@ public:
// printing CNF and internal state
void printDIMACS(FILE *f, bool verbose = false) const;
void printDIMACS(FILE *f, bool verbose = false, const std::vector<std::vector<int>> &extraClauses = std::vector<std::vector<int>>()) const;
void printInternalState(FILE *f) const;
// more sophisticated constraints (designed to be used directly with assume(..))

View file

@ -5,6 +5,7 @@ endif
OBJS += passes/cmds/add.o
OBJS += passes/cmds/delete.o
OBJS += passes/cmds/design.o
OBJS += passes/cmds/design_equal.o
OBJS += passes/cmds/select.o
OBJS += passes/cmds/show.o
OBJS += passes/cmds/viz.o
@ -36,6 +37,7 @@ OBJS += passes/cmds/chformal.o
OBJS += passes/cmds/chtype.o
OBJS += passes/cmds/blackbox.o
OBJS += passes/cmds/ltp.o
OBJS += passes/cmds/linux_perf.o
ifeq ($(DISABLE_SPAWN),0)
OBJS += passes/cmds/bugpoint.o
endif

View file

@ -20,7 +20,7 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
#include "kernel/celledges.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
#include "kernel/utils.h"
#include "kernel/log_help.h"

367
passes/cmds/design_equal.cc Normal file
View file

@ -0,0 +1,367 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "kernel/yosys.h"
#include "kernel/rtlil.h"
YOSYS_NAMESPACE_BEGIN
class ModuleComparator
{
RTLIL::Module *mod_a;
RTLIL::Module *mod_b;
public:
ModuleComparator(RTLIL::Module *mod_a, RTLIL::Module *mod_b) : mod_a(mod_a), mod_b(mod_b) {}
template <typename... Args>
[[noreturn]] void error(FmtString<TypeIdentity<Args>...> fmt, const Args &... args)
{
formatted_error(fmt.format(args...));
}
[[noreturn]]
void formatted_error(std::string err)
{
log("Module A: %s\n", log_id(mod_a->name));
log_module(mod_a, " ");
log("Module B: %s\n", log_id(mod_b->name));
log_module(mod_b, " ");
log_cmd_error("Designs are different: %s\n", err);
}
bool compare_sigbit(const RTLIL::SigBit &a, const RTLIL::SigBit &b)
{
if (a.wire == nullptr && b.wire == nullptr)
return a.data == b.data;
if (a.wire != nullptr && b.wire != nullptr)
return a.wire->name == b.wire->name && a.offset == b.offset;
return false;
}
bool compare_sigspec(const RTLIL::SigSpec &a, const RTLIL::SigSpec &b)
{
if (a.size() != b.size()) return false;
auto it_a = a.begin(), it_b = b.begin();
for (; it_a != a.end(); ++it_a, ++it_b) {
if (!compare_sigbit(*it_a, *it_b)) return false;
}
return true;
}
std::string compare_attributes(const RTLIL::AttrObject *a, const RTLIL::AttrObject *b)
{
for (const auto &it : a->attributes) {
if (b->attributes.count(it.first) == 0)
return "missing attribute " + std::string(log_id(it.first)) + " in second design";
if (it.second != b->attributes.at(it.first))
return "attribute " + std::string(log_id(it.first)) + " mismatch: " + log_const(it.second) + " != " + log_const(b->attributes.at(it.first));
}
for (const auto &it : b->attributes)
if (a->attributes.count(it.first) == 0)
return "missing attribute " + std::string(log_id(it.first)) + " in first design";
return "";
}
std::string compare_wires(const RTLIL::Wire *a, const RTLIL::Wire *b)
{
if (a->name != b->name)
return "name mismatch: " + std::string(log_id(a->name)) + " != " + log_id(b->name);
if (a->width != b->width)
return "width mismatch: " + std::to_string(a->width) + " != " + std::to_string(b->width);
if (a->start_offset != b->start_offset)
return "start_offset mismatch: " + std::to_string(a->start_offset) + " != " + std::to_string(b->start_offset);
if (a->port_id != b->port_id)
return "port_id mismatch: " + std::to_string(a->port_id) + " != " + std::to_string(b->port_id);
if (a->port_input != b->port_input)
return "port_input mismatch: " + std::to_string(a->port_input) + " != " + std::to_string(b->port_input);
if (a->port_output != b->port_output)
return "port_output mismatch: " + std::to_string(a->port_output) + " != " + std::to_string(b->port_output);
if (a->upto != b->upto)
return "upto mismatch: " + std::to_string(a->upto) + " != " + std::to_string(b->upto);
if (a->is_signed != b->is_signed)
return "is_signed mismatch: " + std::to_string(a->is_signed) + " != " + std::to_string(b->is_signed);
if (std::string mismatch = compare_attributes(a, b); !mismatch.empty())
return mismatch;
return "";
}
void check_wires()
{
for (const auto &it : mod_a->wires_) {
if (mod_b->wires_.count(it.first) == 0)
error("Module %s missing wire %s in second design.\n", log_id(mod_a->name), log_id(it.first));
if (std::string mismatch = compare_wires(it.second, mod_b->wires_.at(it.first)); !mismatch.empty())
error("Module %s wire %s %s.\n", log_id(mod_a->name), log_id(it.first), mismatch);
}
for (const auto &it : mod_b->wires_)
if (mod_a->wires_.count(it.first) == 0)
error("Module %s missing wire %s in first design.\n", log_id(mod_b->name), log_id(it.first));
}
std::string compare_memories(const RTLIL::Memory *a, const RTLIL::Memory *b)
{
if (a->name != b->name)
return "name mismatch: " + std::string(log_id(a->name)) + " != " + log_id(b->name);
if (a->width != b->width)
return "width mismatch: " + std::to_string(a->width) + " != " + std::to_string(b->width);
if (a->start_offset != b->start_offset)
return "start_offset mismatch: " + std::to_string(a->start_offset) + " != " + std::to_string(b->start_offset);
if (a->size != b->size)
return "size mismatch: " + std::to_string(a->size) + " != " + std::to_string(b->size);
if (std::string mismatch = compare_attributes(a, b); !mismatch.empty())
return mismatch;
return "";
}
std::string compare_cells(const RTLIL::Cell *a, const RTLIL::Cell *b)
{
if (a->name != b->name)
return "name mismatch: " + std::string(log_id(a->name)) + " != " + log_id(b->name);
if (a->type != b->type)
return "type mismatch: " + std::string(log_id(a->type)) + " != " + log_id(b->type);
if (std::string mismatch = compare_attributes(a, b); !mismatch.empty())
return mismatch;
for (const auto &it : a->parameters) {
if (b->parameters.count(it.first) == 0)
return "parameter mismatch: missing parameter " + std::string(log_id(it.first)) + " in second design";
if (it.second != b->parameters.at(it.first))
return "parameter mismatch: " + std::string(log_id(it.first)) + " mismatch: " + log_const(it.second) + " != " + log_const(b->parameters.at(it.first));
}
for (const auto &it : b->parameters)
if (a->parameters.count(it.first) == 0)
return "parameter mismatch: missing parameter " + std::string(log_id(it.first)) + " in first design";
for (const auto &it : a->connections()) {
if (b->connections().count(it.first) == 0)
return "connection mismatch: missing connection " + std::string(log_id(it.first)) + " in second design";
if (!compare_sigspec(it.second, b->connections().at(it.first)))
return "connection " + std::string(log_id(it.first)) + " mismatch: " + log_signal(it.second) + " != " + log_signal(b->connections().at(it.first));
}
for (const auto &it : b->connections())
if (a->connections().count(it.first) == 0)
return "connection mismatch: missing connection " + std::string(log_id(it.first)) + " in first design";
return "";
}
void check_cells()
{
for (const auto &it : mod_a->cells_) {
if (mod_b->cells_.count(it.first) == 0)
error("Module %s missing cell %s in second design.\n", log_id(mod_a->name), log_id(it.first));
if (std::string mismatch = compare_cells(it.second, mod_b->cells_.at(it.first)); !mismatch.empty())
error("Module %s cell %s %s.\n", log_id(mod_a->name), log_id(it.first), mismatch);
}
for (const auto &it : mod_b->cells_)
if (mod_a->cells_.count(it.first) == 0)
error("Module %s missing cell %s in first design.\n", log_id(mod_b->name), log_id(it.first));
}
void check_memories()
{
for (const auto &it : mod_a->memories) {
if (mod_b->memories.count(it.first) == 0)
error("Module %s missing memory %s in second design.\n", log_id(mod_a->name), log_id(it.first));
if (std::string mismatch = compare_memories(it.second, mod_b->memories.at(it.first)); !mismatch.empty())
error("Module %s memory %s %s.\n", log_id(mod_a->name), log_id(it.first), mismatch);
}
for (const auto &it : mod_b->memories)
if (mod_a->memories.count(it.first) == 0)
error("Module %s missing memory %s in first design.\n", log_id(mod_b->name), log_id(it.first));
}
std::string compare_case_rules(const RTLIL::CaseRule *a, const RTLIL::CaseRule *b)
{
if (std::string mismatch = compare_attributes(a, b); !mismatch.empty()) return mismatch;
if (a->compare.size() != b->compare.size())
return "compare size mismatch: " + std::to_string(a->compare.size()) + " != " + std::to_string(b->compare.size());
for (size_t i = 0; i < a->compare.size(); i++)
if (!compare_sigspec(a->compare[i], b->compare[i]))
return "compare " + std::to_string(i) + " mismatch: " + log_signal(a->compare[i]) + " != " + log_signal(b->compare[i]);
if (a->actions.size() != b->actions.size())
return "actions size mismatch: " + std::to_string(a->actions.size()) + " != " + std::to_string(b->actions.size());
for (size_t i = 0; i < a->actions.size(); i++) {
if (!compare_sigspec(a->actions[i].first, b->actions[i].first))
return "action " + std::to_string(i) + " first mismatch: " + log_signal(a->actions[i].first) + " != " + log_signal(b->actions[i].first);
if (!compare_sigspec(a->actions[i].second, b->actions[i].second))
return "action " + std::to_string(i) + " second mismatch: " + log_signal(a->actions[i].second) + " != " + log_signal(b->actions[i].second);
}
if (a->switches.size() != b->switches.size())
return "switches size mismatch: " + std::to_string(a->switches.size()) + " != " + std::to_string(b->switches.size());
for (size_t i = 0; i < a->switches.size(); i++)
if (std::string mismatch = compare_switch_rules(a->switches[i], b->switches[i]); !mismatch.empty())
return "switch " + std::to_string(i) + " " + mismatch;
return "";
}
std::string compare_switch_rules(const RTLIL::SwitchRule *a, const RTLIL::SwitchRule *b)
{
if (std::string mismatch = compare_attributes(a, b); !mismatch.empty())
return mismatch;
if (!compare_sigspec(a->signal, b->signal))
return "signal mismatch: " + log_signal(a->signal) + " != " + log_signal(b->signal);
if (a->cases.size() != b->cases.size())
return "cases size mismatch: " + std::to_string(a->cases.size()) + " != " + std::to_string(b->cases.size());
for (size_t i = 0; i < a->cases.size(); i++)
if (std::string mismatch = compare_case_rules(a->cases[i], b->cases[i]); !mismatch.empty())
return "case " + std::to_string(i) + " " + mismatch;
return "";
}
std::string compare_sync_rules(const RTLIL::SyncRule *a, const RTLIL::SyncRule *b)
{
if (a->type != b->type)
return "type mismatch: " + std::to_string(a->type) + " != " + std::to_string(b->type);
if (!compare_sigspec(a->signal, b->signal))
return "signal mismatch: " + log_signal(a->signal) + " != " + log_signal(b->signal);
if (a->actions.size() != b->actions.size())
return "actions size mismatch: " + std::to_string(a->actions.size()) + " != " + std::to_string(b->actions.size());
for (size_t i = 0; i < a->actions.size(); i++) {
if (!compare_sigspec(a->actions[i].first, b->actions[i].first))
return "action " + std::to_string(i) + " first mismatch: " + log_signal(a->actions[i].first) + " != " + log_signal(b->actions[i].first);
if (!compare_sigspec(a->actions[i].second, b->actions[i].second))
return "action " + std::to_string(i) + " second mismatch: " + log_signal(a->actions[i].second) + " != " + log_signal(b->actions[i].second);
}
if (a->mem_write_actions.size() != b->mem_write_actions.size())
return "mem_write_actions size mismatch: " + std::to_string(a->mem_write_actions.size()) + " != " + std::to_string(b->mem_write_actions.size());
for (size_t i = 0; i < a->mem_write_actions.size(); i++) {
const auto &ma = a->mem_write_actions[i];
const auto &mb = b->mem_write_actions[i];
if (ma.memid != mb.memid)
return "mem_write_actions " + std::to_string(i) + " memid mismatch: " + log_id(ma.memid) + " != " + log_id(mb.memid);
if (!compare_sigspec(ma.address, mb.address))
return "mem_write_actions " + std::to_string(i) + " address mismatch: " + log_signal(ma.address) + " != " + log_signal(mb.address);
if (!compare_sigspec(ma.data, mb.data))
return "mem_write_actions " + std::to_string(i) + " data mismatch: " + log_signal(ma.data) + " != " + log_signal(mb.data);
if (!compare_sigspec(ma.enable, mb.enable))
return "mem_write_actions " + std::to_string(i) + " enable mismatch: " + log_signal(ma.enable) + " != " + log_signal(mb.enable);
if (ma.priority_mask != mb.priority_mask)
return "mem_write_actions " + std::to_string(i) + " priority_mask mismatch: " + log_const(ma.priority_mask) + " != " + log_const(mb.priority_mask);
if (std::string mismatch = compare_attributes(&ma, &mb); !mismatch.empty())
return "mem_write_actions " + std::to_string(i) + " " + mismatch;
}
return "";
}
std::string compare_processes(const RTLIL::Process *a, const RTLIL::Process *b)
{
if (a->name != b->name) return "name mismatch: " + std::string(log_id(a->name)) + " != " + log_id(b->name);
if (std::string mismatch = compare_attributes(a, b); !mismatch.empty())
return mismatch;
if (std::string mismatch = compare_case_rules(&a->root_case, &b->root_case); !mismatch.empty())
return "case rule " + mismatch;
if (a->syncs.size() != b->syncs.size())
return "sync count mismatch: " + std::to_string(a->syncs.size()) + " != " + std::to_string(b->syncs.size());
for (size_t i = 0; i < a->syncs.size(); i++)
if (std::string mismatch = compare_sync_rules(a->syncs[i], b->syncs[i]); !mismatch.empty())
return "sync " + std::to_string(i) + " " + mismatch;
return "";
}
void check_processes()
{
for (auto &it : mod_a->processes) {
if (mod_b->processes.count(it.first) == 0)
error("Module %s missing process %s in second design.\n", log_id(mod_a->name), log_id(it.first));
if (std::string mismatch = compare_processes(it.second, mod_b->processes.at(it.first)); !mismatch.empty())
error("Module %s process %s %s.\n", log_id(mod_a->name), log_id(it.first), mismatch.c_str());
}
for (auto &it : mod_b->processes)
if (mod_a->processes.count(it.first) == 0)
error("Module %s missing process %s in first design.\n", log_id(mod_b->name), log_id(it.first));
}
void check_connections()
{
const auto &conns_a = mod_a->connections();
const auto &conns_b = mod_b->connections();
if (conns_a.size() != conns_b.size()) {
error("Module %s connection count differs: %zu != %zu\n", log_id(mod_a->name), conns_a.size(), conns_b.size());
} else {
for (size_t i = 0; i < conns_a.size(); i++) {
if (!compare_sigspec(conns_a[i].first, conns_b[i].first))
error("Module %s connection %zu LHS %s != %s.\n", log_id(mod_a->name), i, log_signal(conns_a[i].first), log_signal(conns_b[i].first));
if (!compare_sigspec(conns_a[i].second, conns_b[i].second))
error("Module %s connection %zu RHS %s != %s.\n", log_id(mod_a->name), i, log_signal(conns_a[i].second), log_signal(conns_b[i].second));
}
}
}
void check()
{
if (mod_a->name != mod_b->name)
error("Modules have different names: %s != %s\n", log_id(mod_a->name), log_id(mod_b->name));
if (std::string mismatch = compare_attributes(mod_a, mod_b); !mismatch.empty())
error("Module %s %s.\n", log_id(mod_a->name), mismatch);
check_wires();
check_cells();
check_memories();
check_connections();
check_processes();
}
};
struct DesignEqualPass : public Pass {
DesignEqualPass() : Pass("design_equal", "check if two designs are the same") { }
void help() override
{
log("\n");
log(" design_equal <name>\n");
log("\n");
log("Compare the current design with the design previously saved under the given\n");
log("name. Abort with an error if the designs are different.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
if (args.size() != 2)
log_cmd_error("Missing argument.\n");
std::string check_name = args[1];
if (saved_designs.count(check_name) == 0)
log_cmd_error("No saved design '%s' found!\n", check_name.c_str());
RTLIL::Design *other = saved_designs.at(check_name);
for (auto &it : design->modules_) {
RTLIL::Module *mod = it.second;
if (!other->has(mod->name))
log_error("Second design missing module %s.\n", log_id(mod->name));
ModuleComparator cmp(mod, other->module(mod->name));
cmp.check();
}
for (auto &it : other->modules_) {
RTLIL::Module *mod = it.second;
if (!design->has(mod->name))
log_error("First design missing module %s.\n", log_id(mod->name));
}
log("Designs are identical.\n");
}
} DesignEqualPass;
YOSYS_NAMESPACE_END

View file

@ -1,5 +1,6 @@
#include "kernel/yosys.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
#include "kernel/ff.h"
USING_YOSYS_NAMESPACE
@ -123,7 +124,7 @@ struct LibertyStubber {
return;
}
if (RTLIL::builtin_ff_cell_types().count(base_name))
if (StaticCellTypes::categories.is_ff(base_name))
return liberty_flop(base, derived, f);
auto& base_type = ct.cell_types[base_name];

102
passes/cmds/linux_perf.cc Normal file
View file

@ -0,0 +1,102 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2014 Claire Xenia Wolf <claire@yosyshq.com>
* Copyright (C) 2014 Johann Glaser <Johann.Glaser@gmx.at>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "kernel/yosys.h"
#include "kernel/log_help.h"
#include <fcntl.h>
#include <stdlib.h>
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
#ifdef __linux__
#include <unistd.h>
struct LinuxPerf : public Pass {
LinuxPerf() : Pass("linux_perf", "turn linux perf recording off or on") {
internal();
}
bool formatted_help() override
{
auto *help = PrettyHelp::get_current();
auto content_root = help->get_root();
content_root->usage("linux_perf [on|off]");
content_root->paragraph(
"This pass turns Linux 'perf' profiling on or off, when it has been configured to use control FIFOs."
"YOSYS_PERF_CTL and YOSYS_PERF_ACK must point to Linux perf control FIFOs."
);
content_root->paragraph("Example shell command line:");
content_root->codeblock(
"mkfifo /tmp/perf.fifo /tmp/perf-ack.fifo\n"
"YOSYS_PERF_CTL=/tmp/perf.fifo YOSYS_PERF_ACK=/tmp/perf-ack.fifo \\\n"
" perf record --latency --delay=-1 \\\n"
" --control=fifo:/tmp/perf.fifo,/tmp/perf-ack.fifo --call-graph=dwarf ./yosys \\\n"
" -dt -p \"read_rtlil design.rtlil; linux_perf on; opt_clean; linux_perf off\"\n"
);
return true;
}
void execute(std::vector<std::string> args, RTLIL::Design *) override
{
if (args.size() > 2)
cmd_error(args, 2, "Unexpected argument.");
std::string_view ctl_msg;
if (args.size() == 2) {
if (args[1] == "on")
ctl_msg = "enable\n";
else if (args[1] == "off")
ctl_msg = "disable\n";
else
cmd_error(args, 1, "Unexpected argument.");
}
const char *ctl_fifo = std::getenv("YOSYS_PERF_CTL");
if (!ctl_fifo)
log_error("YOSYS_PERF_CTL environment variable not set.");
const char *ack_fifo = std::getenv("YOSYS_PERF_ACK");
if (!ack_fifo)
log_error("YOSYS_PERF_ACK environment variable not set.");
int ctl_fd = open(ctl_fifo, O_WRONLY);
if (ctl_fd < 0)
log_error("Failed to open YOSYS_PERF_CTL.");
int ack_fd = open(ack_fifo, O_RDONLY);
if (ack_fd < 0)
log_error("Failed to open YOSYS_PERF_ACK.");
int result = write(ctl_fd, ctl_msg.data(), ctl_msg.size());
if (result != static_cast<int>(ctl_msg.size()))
log_error("Failed to write to YOSYS_PERF_CTL.");
char buffer[64];
result = read(ack_fd, buffer, sizeof(buffer));
close(ctl_fd);
close(ack_fd);
if (result <= 0)
log_error("Failed to read from YOSYS_PERF_ACK.");
if (strcmp(buffer, "ack\n") != 0)
log_error("YOSYS_PERF_ACK did not return 'ack'.");
}
} LinuxPerf;
#endif
PRIVATE_NAMESPACE_END

View file

@ -254,18 +254,17 @@ struct RenamePass : public Pass {
log("\n");
log(" rename -enumerate [-pattern <pattern>] [selection]\n");
log("\n");
log("Assign short auto-generated names to all selected wires and cells with private\n");
log("names. The -pattern option can be used to set the pattern for the new names.\n");
log("The character %% in the pattern is replaced with a integer number. The default\n");
log("pattern is '_%%_'.\n");
log("Assigns auto-generated names to objects used in formal verification\n");
log("that do not have a public name. This applies to all formal property\n");
log("cells, $any*/$all* output wires, and their containing cells.\n");
log("\n");
log("\n");
log(" rename -witness\n");
log("\n");
log("Assigns auto-generated names to all $any*/$all* output wires and containing\n");
log("cells that do not have a public name. This ensures that, during formal\n");
log("verification, a solver-found trace can be fully specified using a public\n");
log("hierarchical names.\n");
log("Assigns auto-generated names to objects used in formal verification\n");
log("that do not have a public name. This applies to all formal property\n");
log("cells ($assert, $assume, $cover, $live, $fair, $check), $any*/$all*\n");
log("output wires, and their containing cells.\n");
log("\n");
log("\n");
log(" rename -hide [selection]\n");

View file

@ -37,7 +37,7 @@ struct ScratchpadPass : public Pass {
log("\n");
log(" scratchpad [options]\n");
log("\n");
log("This pass allows to read and modify values from the scratchpad of the current\n");
log("This pass allows reading and modifying values from the scratchpad of the current\n");
log("design. Options:\n");
log("\n");
log(" -get <identifier>\n");

View file

@ -165,7 +165,12 @@ struct SdcObjects {
if (!top)
log_error("Top module couldn't be determined. Check 'top' attribute usage");
for (auto port : top->ports) {
design_ports.push_back(std::make_pair(port.str().substr(1), top->wire(port)));
RTLIL::Wire *wire = top->wire(port);
if (!wire) {
// This should not be possible. See https://github.com/YosysHQ/yosys/pull/5594#issue-3791198573
log_error("Port %s doesn't exist", log_id(port));
}
design_ports.push_back(std::make_pair(port.str().substr(1), wire));
}
std::list<std::string> hierarchy{};
sniff_module(hierarchy, top);

View file

@ -18,7 +18,7 @@
*/
#include "kernel/yosys.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
#include "kernel/sigtools.h"
#include "kernel/log_help.h"
@ -488,7 +488,7 @@ static int parse_comma_list(std::set<RTLIL::IdString> &tokens, const std::string
}
}
static int select_op_expand(RTLIL::Design *design, RTLIL::Selection &lhs, std::vector<expand_rule_t> &rules, std::set<RTLIL::IdString> &limits, int max_objects, char mode, CellTypes &ct, bool eval_only)
static int select_op_expand(RTLIL::Design *design, RTLIL::Selection &lhs, std::vector<expand_rule_t> &rules, std::set<RTLIL::IdString> &limits, int max_objects, char mode, NewCellTypes &ct, bool eval_only)
{
int sel_objects = 0;
bool is_input, is_output;
@ -564,13 +564,13 @@ static void select_op_expand(RTLIL::Design *design, const std::string &arg, char
std::vector<expand_rule_t> rules;
std::set<RTLIL::IdString> limits;
CellTypes ct;
NewCellTypes ct;
if (mode != 'x')
ct.setup(design);
if (pos < int(arg.size()) && arg[pos] == '*') {
levels = 1000000;
levels = 1'000'000;
pos++;
} else
if (pos < int(arg.size()) && '0' <= arg[pos] && arg[pos] <= '9') {

View file

@ -561,7 +561,7 @@ struct statdata_t {
}
}
if (tech == "xilinx") {
if (tech == "xilinx" || tech == "analogdevices") {
log("\n");
log(" Estimated number of LCs: %10u\n", estimate_xilinx_lc());
}
@ -628,7 +628,7 @@ struct statdata_t {
first_line = false;
}
log("\n }\n");
if (tech == "xilinx") {
if (tech == "xilinx" || tech == "analogdevices") {
log(" \"estimated_num_lc\": %u,\n", estimate_xilinx_lc());
}
if (tech == "cmos") {
@ -710,7 +710,7 @@ struct statdata_t {
log("\n");
log(" }");
}
if (tech == "xilinx") {
if (tech == "xilinx" || tech == "analogdevices") {
log(",\n");
log(" \"estimated_num_lc\": %u", estimate_xilinx_lc());
}
@ -908,7 +908,7 @@ struct StatPass : public Pass {
log("\n");
log(" -tech <technology>\n");
log(" print area estimate for the specified technology. Currently supported\n");
log(" values for <technology>: xilinx, cmos\n");
log(" values for <technology>: xilinx, analogdevices, cmos\n");
log("\n");
log(" -width\n");
log(" annotate internal cell types with their word width.\n");
@ -968,7 +968,7 @@ struct StatPass : public Pass {
if (!json_mode)
log_header(design, "Printing statistics.\n");
if (techname != "" && techname != "xilinx" && techname != "cmos" && !json_mode)
if (techname != "" && techname != "xilinx" && techname != "analogdevices" && techname != "cmos" && !json_mode)
log_cmd_error("Unsupported technology: '%s'\n", techname);
if (json_mode) {

View file

@ -18,7 +18,7 @@
*/
#include "kernel/yosys.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
#include "kernel/sigtools.h"
#include "kernel/utils.h"
#include "kernel/log_help.h"

View file

@ -115,16 +115,45 @@ struct DebugPass : public Pass {
log("\n");
log("Execute the specified command with debug log messages enabled\n");
log("\n");
log(" debug -on\n");
log(" debug -off\n");
log("\n");
log("Enable or disable debug log messages globally\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
size_t argidx;
bool mode_on = false;
bool mode_off = false;
for (argidx = 1; argidx < args.size(); argidx++)
{
// .. parse options ..
if (args[argidx] == "-on") {
mode_on = true;
continue;
}
if (args[argidx] == "-off") {
mode_off = true;
continue;
}
break;
}
if (mode_on && mode_off)
log_cmd_error("Cannot specify both -on and -off\n");
if (mode_on) {
log_force_debug++;
return;
}
if (mode_off) {
if (log_force_debug > 0)
log_force_debug--;
return;
}
log_force_debug++;
try {

68
passes/equiv/equiv.h Normal file
View file

@ -0,0 +1,68 @@
#ifndef EQUIV_H
#define EQUIV_H
#include "kernel/log.h"
#include "kernel/yosys_common.h"
#include "kernel/sigtools.h"
#include "kernel/satgen.h"
#include "kernel/newcelltypes.h"
YOSYS_NAMESPACE_BEGIN
struct EquivBasicConfig {
bool model_undef = false;
int max_seq = 1;
bool set_assumes = false;
bool ignore_unknown_cells = false;
bool parse(const std::vector<std::string>& args, size_t& idx) {
if (args[idx] == "-undef") {
model_undef = true;
return true;
}
if (args[idx] == "-seq" && idx+1 < args.size()) {
max_seq = atoi(args[++idx].c_str());
return true;
}
if (args[idx] == "-set-assumes") {
set_assumes = true;
return true;
}
if (args[idx] == "-ignore-unknown-cells") {
ignore_unknown_cells = true;
return true;
}
return false;
}
static std::string help(const char* default_seq) {
return stringf(
" -undef\n"
" enable modelling of undef states\n"
"\n"
" -seq <N>\n"
" the max. number of time steps to be considered (default = %s)\n"
"\n"
" -set-assumes\n"
" set all assumptions provided via $assume cells\n"
"\n"
" -ignore-unknown-cells\n"
" ignore all cells that can not be matched to a SAT model\n"
, default_seq);
}
};
template<typename Config = EquivBasicConfig>
struct EquivWorker {
RTLIL::Module *module;
ezSatPtr ez;
SatGen satgen;
Config cfg;
EquivWorker(RTLIL::Module *module, const SigMap *sigmap, Config cfg) : module(module), satgen(ez.get(), sigmap), cfg(cfg) {
satgen.model_undef = cfg.model_undef;
}
};
YOSYS_NAMESPACE_END
#endif // EQUIV_H

View file

@ -18,49 +18,34 @@
*/
#include "kernel/yosys.h"
#include "kernel/satgen.h"
#include "kernel/sigtools.h"
#include "passes/equiv/equiv.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
struct EquivInductWorker
struct EquivInductWorker : public EquivWorker<>
{
Module *module;
SigMap sigmap;
vector<Cell*> cells;
pool<Cell*> workset;
ezSatPtr ez;
SatGen satgen;
int max_seq;
int success_counter;
bool set_assumes;
dict<int, int> ez_step_is_consistent;
pool<Cell*> cell_warn_cache;
SigPool undriven_signals;
EquivInductWorker(Module *module, const pool<Cell*> &unproven_equiv_cells, bool model_undef, int max_seq, bool set_assumes) : module(module), sigmap(module),
EquivInductWorker(Module *module, const pool<Cell*> &unproven_equiv_cells, EquivBasicConfig cfg) : EquivWorker<>(module, &sigmap, cfg), sigmap(module),
cells(module->selected_cells()), workset(unproven_equiv_cells),
satgen(ez.get(), &sigmap), max_seq(max_seq), success_counter(0), set_assumes(set_assumes)
{
satgen.model_undef = model_undef;
}
success_counter(0) {}
void create_timestep(int step)
{
vector<int> ez_equal_terms;
for (auto cell : cells) {
if (!satgen.importCell(cell, step) && !cell_warn_cache.count(cell)) {
if (cell->is_builtin_ff())
log_warning("No SAT model available for async FF cell %s (%s). Consider running `async2sync` or `clk2fflogic` first.\n", log_id(cell), log_id(cell->type));
else
log_warning("No SAT model available for cell %s (%s).\n", log_id(cell), log_id(cell->type));
cell_warn_cache.insert(cell);
if (!satgen.importCell(cell, step)) {
report_missing_model(cfg.ignore_unknown_cells, cell);
}
if (cell->type == ID($equiv)) {
SigBit bit_a = sigmap(cell->getPort(ID::A)).as_bit();
@ -78,7 +63,7 @@ struct EquivInductWorker
}
}
if (set_assumes) {
if (cfg.set_assumes) {
if (step == 1) {
RTLIL::SigSpec assumes_a, assumes_en;
satgen.getAssumes(assumes_a, assumes_en, step);
@ -123,7 +108,7 @@ struct EquivInductWorker
GetSize(satgen.initial_state), GetSize(undriven_signals));
}
for (int step = 1; step <= max_seq; step++)
for (int step = 1; step <= cfg.max_seq; step++)
{
ez->assume(ez_step_is_consistent[step]);
@ -146,7 +131,7 @@ struct EquivInductWorker
return;
}
log(" Proof for induction step failed. %s\n", step != max_seq ? "Extending to next time step." : "Trying to prove individual $equiv from workset.");
log(" Proof for induction step failed. %s\n", step != cfg.max_seq ? "Extending to next time step." : "Trying to prove individual $equiv from workset.");
}
workset.sort();
@ -158,12 +143,12 @@ struct EquivInductWorker
log(" Trying to prove $equiv for %s:", log_signal(sigmap(cell->getPort(ID::Y))));
int ez_a = satgen.importSigBit(bit_a, max_seq+1);
int ez_b = satgen.importSigBit(bit_b, max_seq+1);
int ez_a = satgen.importSigBit(bit_a, cfg.max_seq+1);
int ez_b = satgen.importSigBit(bit_b, cfg.max_seq+1);
int cond = ez->XOR(ez_a, ez_b);
if (satgen.model_undef)
cond = ez->AND(cond, ez->NOT(satgen.importUndefSigBit(bit_a, max_seq+1)));
cond = ez->AND(cond, ez->NOT(satgen.importUndefSigBit(bit_a, cfg.max_seq+1)));
if (!ez->solve(cond)) {
log(" success!\n");
@ -189,14 +174,7 @@ struct EquivInductPass : public Pass {
log("Only selected $equiv cells are proven and only selected cells are used to\n");
log("perform the proof.\n");
log("\n");
log(" -undef\n");
log(" enable modelling of undef states\n");
log("\n");
log(" -seq <N>\n");
log(" the max. number of time steps to be considered (default = 4)\n");
log("\n");
log(" -set-assumes\n");
log(" set all assumptions provided via $assume cells\n");
log("%s", EquivBasicConfig::help("4"));
log("\n");
log("This command is very effective in proving complex sequential circuits, when\n");
log("the internal state of the circuit quickly propagates to $equiv cells.\n");
@ -214,25 +192,15 @@ struct EquivInductPass : public Pass {
void execute(std::vector<std::string> args, Design *design) override
{
int success_counter = 0;
bool model_undef = false, set_assumes = false;
int max_seq = 4;
EquivBasicConfig cfg {};
cfg.max_seq = 4;
log_header(design, "Executing EQUIV_INDUCT pass.\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
if (args[argidx] == "-undef") {
model_undef = true;
if (cfg.parse(args, argidx))
continue;
}
if (args[argidx] == "-seq" && argidx+1 < args.size()) {
max_seq = atoi(args[++argidx].c_str());
continue;
}
if (args[argidx] == "-set-assumes") {
set_assumes = true;
continue;
}
break;
}
extra_args(args, argidx, design);
@ -253,7 +221,7 @@ struct EquivInductPass : public Pass {
continue;
}
EquivInductWorker worker(module, unproven_equiv_cells, model_undef, max_seq, set_assumes);
EquivInductWorker worker(module, unproven_equiv_cells, cfg);
worker.run();
success_counter += worker.success_counter;
}

View file

@ -17,15 +17,51 @@
*
*/
#include "kernel/log.h"
#include "kernel/yosys.h"
#include "kernel/satgen.h"
#include "passes/equiv/equiv.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
struct EquivSimpleWorker
struct EquivSimpleConfig : EquivBasicConfig {
bool verbose = false;
bool short_cones = false;
bool group = true;
bool parse(const std::vector<std::string>& args, size_t& idx) {
if (EquivBasicConfig::parse(args, idx))
return true;
if (args[idx] == "-v") {
verbose = true;
return true;
}
if (args[idx] == "-short") {
short_cones = true;
return true;
}
if (args[idx] == "-nogroup") {
group = false;
return true;
}
return false;
}
static std::string help(const char* default_seq) {
return EquivBasicConfig::help(default_seq) +
" -v\n"
" verbose output\n"
"\n"
" -short\n"
" create shorter input cones that stop at shared nodes. This yields\n"
" simpler SAT problems but sometimes fails to prove equivalence.\n"
"\n"
" -nogroup\n"
" disabling grouping of $equiv cells by output wire\n"
"\n";
}
};
struct EquivSimpleWorker : public EquivWorker<EquivSimpleConfig>
{
Module *module;
const vector<Cell*> &equiv_cells;
const vector<Cell*> &assume_cells;
struct Cone {
@ -43,27 +79,11 @@ struct EquivSimpleWorker
};
DesignModel model;
ezSatPtr ez;
SatGen satgen;
struct Config {
bool verbose = false;
bool short_cones = false;
bool model_undef = false;
bool nogroup = false;
bool set_assumes = false;
int max_seq = 1;
};
Config cfg;
pool<pair<Cell*, int>> imported_cells_cache;
EquivSimpleWorker(const vector<Cell*> &equiv_cells, const vector<Cell*> &assume_cells, DesignModel model, Config cfg) :
module(equiv_cells.front()->module), equiv_cells(equiv_cells), assume_cells(assume_cells),
model(model), satgen(ez.get(), &model.sigmap), cfg(cfg)
{
satgen.model_undef = cfg.model_undef;
}
EquivSimpleWorker(const vector<Cell*> &equiv_cells, const vector<Cell*> &assume_cells, DesignModel model, EquivSimpleConfig cfg) :
EquivWorker<EquivSimpleConfig>(equiv_cells.front()->module, &model.sigmap, cfg), equiv_cells(equiv_cells), assume_cells(assume_cells),
model(model) {}
struct ConeFinder {
DesignModel model;
@ -229,14 +249,6 @@ struct EquivSimpleWorker
return extra_problem_cells;
}
static void report_missing_model(Cell* cell)
{
if (cell->is_builtin_ff())
log_cmd_error("No SAT model available for async FF cell %s (%s). Consider running `async2sync` or `clk2fflogic` first.\n", log_id(cell), log_id(cell->type));
else
log_cmd_error("No SAT model available for cell %s (%s).\n", log_id(cell), log_id(cell->type));
}
void prepare_ezsat(int ez_context, SigBit bit_a, SigBit bit_b)
{
if (satgen.model_undef)
@ -257,7 +269,9 @@ struct EquivSimpleWorker
}
void construct_ezsat(const pool<SigBit>& input_bits, int step)
{
log("ezsat\n");
if (cfg.set_assumes) {
log("yep assume\n");
if (cfg.verbose && step == cfg.max_seq) {
RTLIL::SigSpec assumes_a, assumes_en;
satgen.getAssumes(assumes_a, assumes_en, step+1);
@ -323,7 +337,7 @@ struct EquivSimpleWorker
for (auto cell : problem_cells) {
auto key = pair<Cell*, int>(cell, step+1);
if (!imported_cells_cache.count(key) && !satgen.importCell(cell, step+1)) {
report_missing_model(cell);
report_missing_model(cfg.ignore_unknown_cells, cell);
}
imported_cells_cache.insert(key);
}
@ -414,59 +428,20 @@ struct EquivSimplePass : public Pass {
log("\n");
log("This command tries to prove $equiv cells using a simple direct SAT approach.\n");
log("\n");
log(" -v\n");
log(" verbose output\n");
log("\n");
log(" -undef\n");
log(" enable modelling of undef states\n");
log("\n");
log(" -short\n");
log(" create shorter input cones that stop at shared nodes. This yields\n");
log(" simpler SAT problems but sometimes fails to prove equivalence.\n");
log("\n");
log(" -nogroup\n");
log(" disabling grouping of $equiv cells by output wire\n");
log("\n");
log(" -seq <N>\n");
log(" the max. number of time steps to be considered (default = 1)\n");
log("\n");
log(" -set-assumes\n");
log(" set all assumptions provided via $assume cells\n");
log("%s", EquivSimpleConfig::help("1"));
log("\n");
}
void execute(std::vector<std::string> args, Design *design) override
{
EquivSimpleWorker::Config cfg = {};
EquivSimpleConfig cfg {};
int success_counter = 0;
log_header(design, "Executing EQUIV_SIMPLE pass.\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
if (args[argidx] == "-v") {
cfg.verbose = true;
if (cfg.parse(args, argidx))
continue;
}
if (args[argidx] == "-short") {
cfg.short_cones = true;
continue;
}
if (args[argidx] == "-undef") {
cfg.model_undef = true;
continue;
}
if (args[argidx] == "-nogroup") {
cfg.nogroup = true;
continue;
}
if (args[argidx] == "-seq" && argidx+1 < args.size()) {
cfg.max_seq = atoi(args[++argidx].c_str());
continue;
}
if (args[argidx] == "-set-assumes") {
cfg.set_assumes = true;
continue;
}
break;
}
extra_args(args, argidx, design);
@ -489,7 +464,7 @@ struct EquivSimplePass : public Pass {
if (cell->type == ID($equiv) && cell->getPort(ID::A) != cell->getPort(ID::B)) {
auto bit = sigmap(cell->getPort(ID::Y).as_bit());
auto bit_group = bit;
if (!cfg.nogroup && bit_group.wire)
if (cfg.group && bit_group.wire)
bit_group.offset = 0;
unproven_equiv_cells[bit_group][bit] = cell;
unproven_cells_counter++;

View file

@ -39,6 +39,7 @@ struct PassOptions {
bool no_auto_distributed;
bool no_auto_block;
bool no_auto_huge;
bool force_params;
double logic_cost_rom;
double logic_cost_ram;
};
@ -1859,7 +1860,7 @@ void MemMapping::emit_port(const MemConfig &cfg, std::vector<Cell*> &cells, cons
cell->setParam(stringf("\\PORT_%s_WR_BE_WIDTH", name), GetSize(hw_wren));
} else {
cell->setPort(stringf("\\PORT_%s_WR_EN", name), hw_wren);
if (cfg.def->byte != 0 && cfg.def->width_mode != WidthMode::Single)
if (cfg.def->byte != 0 && (cfg.def->width_mode != WidthMode::Single || opts.force_params))
cell->setParam(stringf("\\PORT_%s_WR_EN_WIDTH", name), GetSize(hw_wren));
}
}
@ -2068,8 +2069,10 @@ void MemMapping::emit(const MemConfig &cfg) {
std::vector<Cell *> cells;
for (int rd = 0; rd < cfg.repl_d; rd++) {
Cell *cell = mem.module->addCell(stringf("%s.%d.%d", mem.memid, rp, rd), cfg.def->id);
if (cfg.def->width_mode == WidthMode::Global)
if (cfg.def->width_mode == WidthMode::Global || opts.force_params)
cell->setParam(ID::WIDTH, cfg.def->dbits[cfg.base_width_log2]);
if (opts.force_params)
cell->setParam(ID::ABITS, cfg.def->abits);
if (cfg.def->widthscale) {
std::vector<State> val;
for (auto &bit: init_swz.bits[rd])
@ -2179,6 +2182,9 @@ struct MemoryLibMapPass : public Pass {
log(" Disables automatic mapping of given kind of RAMs. Manual mapping\n");
log(" (using ram_style or other attributes) is still supported.\n");
log("\n");
log(" -force-params\n");
log(" Always generate memories with WIDTH and ABITS parameters.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
@ -2188,6 +2194,7 @@ struct MemoryLibMapPass : public Pass {
opts.no_auto_distributed = false;
opts.no_auto_block = false;
opts.no_auto_huge = false;
opts.force_params = false;
opts.logic_cost_ram = 1.0;
opts.logic_cost_rom = 1.0/16.0;
log_header(design, "Executing MEMORY_LIBMAP pass (mapping memories to cells).\n");
@ -2214,6 +2221,10 @@ struct MemoryLibMapPass : public Pass {
opts.no_auto_huge = true;
continue;
}
if (args[argidx] == "-force-params") {
opts.force_params = true;
continue;
}
if (args[argidx] == "-logic-cost-rom" && argidx+1 < args.size()) {
opts.logic_cost_rom = strtod(args[++argidx].c_str(), nullptr);
continue;

View file

@ -23,6 +23,7 @@ OBJS += passes/opt/opt_lut_ins.o
OBJS += passes/opt/opt_ffinv.o
OBJS += passes/opt/pmux2shiftx.o
OBJS += passes/opt/muxpack.o
OBJS += passes/opt/opt_balance_tree.o
OBJS += passes/opt/peepopt.o
GENFILES += passes/opt/peepopt_pm.h

View file

@ -193,7 +193,6 @@ struct OptPass : public Pass {
}
design->optimize();
design->sort();
design->check();
log_header(design, "Finished fast OPT passes.%s\n", fast_mode ? "" : " (There is nothing left to do.)");

View file

@ -0,0 +1,379 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
* 2019 Eddie Hung <eddie@fpgeh.com>
* 2024 Akash Levy <akash@silimate.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
#include <deque>
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
struct OptBalanceTreeWorker {
// Module and signal map
Module *module;
SigMap sigmap;
// Counts of each cell type that are getting balanced
dict<IdString, int> cell_count;
// Check if cell is of the right type and has matching input/output widths
// Only allow cells with "natural" output widths (no truncation) to prevent
// equivalence issues when rebalancing (see YosysHQ/yosys#5605)
bool is_right_type(Cell* cell, IdString cell_type) {
if (cell->type != cell_type)
return false;
int y_width = cell->getParam(ID::Y_WIDTH).as_int();
int a_width = cell->getParam(ID::A_WIDTH).as_int();
int b_width = cell->getParam(ID::B_WIDTH).as_int();
// Calculate the "natural" output width for this operation
int natural_width;
if (cell_type == ID($add)) {
// Addition produces max(A_WIDTH, B_WIDTH) + 1 (for carry bit)
natural_width = std::max(a_width, b_width) + 1;
} else if (cell_type == ID($mul)) {
// Multiplication produces A_WIDTH + B_WIDTH
natural_width = a_width + b_width;
} else {
// Logic operations ($and/$or/$xor) produce max(A_WIDTH, B_WIDTH)
natural_width = std::max(a_width, b_width);
}
// Only allow cells where Y_WIDTH >= natural width (no truncation)
// This prevents rebalancing chains where truncation semantics matter
return y_width >= natural_width;
}
// Create a balanced binary tree from a vector of source signals
SigSpec create_balanced_tree(vector<SigSpec> &sources, IdString cell_type, Cell* cell) {
// Base case: if we have no sources, return an empty signal
if (sources.size() == 0)
return SigSpec();
// Base case: if we have only one source, return it
if (sources.size() == 1)
return sources[0];
// Base case: if we have two sources, create a single cell
if (sources.size() == 2) {
// Create a new cell of the same type
Cell* new_cell = module->addCell(NEW_ID, cell_type);
// Copy attributes from reference cell
new_cell->attributes = cell->attributes;
// Create output wire
int out_width = cell->getParam(ID::Y_WIDTH).as_int();
if (cell_type == ID($add))
out_width = max(sources[0].size(), sources[1].size()) + 1;
else if (cell_type == ID($mul))
out_width = sources[0].size() + sources[1].size();
Wire* out_wire = module->addWire(NEW_ID, out_width);
// Connect ports and fix up parameters
new_cell->setPort(ID::A, sources[0]);
new_cell->setPort(ID::B, sources[1]);
new_cell->setPort(ID::Y, out_wire);
new_cell->fixup_parameters();
new_cell->setParam(ID::A_SIGNED, cell->getParam(ID::A_SIGNED));
new_cell->setParam(ID::B_SIGNED, cell->getParam(ID::B_SIGNED));
// Update count and return output wire
cell_count[cell_type]++;
return out_wire;
}
// Recursive case: split sources into two groups and create subtrees
int mid = (sources.size() + 1) / 2;
vector<SigSpec> left_sources(sources.begin(), sources.begin() + mid);
vector<SigSpec> right_sources(sources.begin() + mid, sources.end());
SigSpec left_tree = create_balanced_tree(left_sources, cell_type, cell);
SigSpec right_tree = create_balanced_tree(right_sources, cell_type, cell);
// Create a cell to combine the two subtrees
Cell* new_cell = module->addCell(NEW_ID, cell_type);
// Copy attributes from reference cell
new_cell->attributes = cell->attributes;
// Create output wire
int out_width = cell->getParam(ID::Y_WIDTH).as_int();
if (cell_type == ID($add))
out_width = max(left_tree.size(), right_tree.size()) + 1;
else if (cell_type == ID($mul))
out_width = left_tree.size() + right_tree.size();
Wire* out_wire = module->addWire(NEW_ID, out_width);
// Connect ports and fix up parameters
new_cell->setPort(ID::A, left_tree);
new_cell->setPort(ID::B, right_tree);
new_cell->setPort(ID::Y, out_wire);
new_cell->fixup_parameters();
new_cell->setParam(ID::A_SIGNED, cell->getParam(ID::A_SIGNED));
new_cell->setParam(ID::B_SIGNED, cell->getParam(ID::B_SIGNED));
// Update count and return output wire
cell_count[cell_type]++;
return out_wire;
}
OptBalanceTreeWorker(Module *module, const vector<IdString> cell_types) : module(module), sigmap(module) {
// Do for each cell type
for (auto cell_type : cell_types) {
// Index all of the nets in the module
dict<SigSpec, Cell*> sig_to_driver;
dict<SigSpec, pool<Cell*>> sig_to_sink;
for (auto cell : module->selected_cells())
{
for (auto &conn : cell->connections())
{
if (cell->output(conn.first))
sig_to_driver[sigmap(conn.second)] = cell;
if (cell->input(conn.first))
{
SigSpec sig = sigmap(conn.second);
if (sig_to_sink.count(sig) == 0)
sig_to_sink[sig] = pool<Cell*>();
sig_to_sink[sig].insert(cell);
}
}
}
// Need to check if any wires connect to module ports
pool<SigSpec> input_port_sigs;
pool<SigSpec> output_port_sigs;
for (auto wire : module->selected_wires())
if (wire->port_input || wire->port_output) {
SigSpec sig = sigmap(wire);
for (auto bit : sig) {
if (wire->port_input)
input_port_sigs.insert(bit);
if (wire->port_output)
output_port_sigs.insert(bit);
}
}
// Actual logic starts here
pool<Cell*> consumed_cells;
for (auto cell : module->selected_cells())
{
// If consumed or not the correct type, skip
if (consumed_cells.count(cell) || !is_right_type(cell, cell_type))
continue;
// BFS, following all chains until they hit a cell of a different type
// Pick the longest one
auto y = sigmap(cell->getPort(ID::Y));
pool<Cell*> sinks;
pool<Cell*> current_loads = sig_to_sink[y];
pool<Cell*> next_loads;
while (!current_loads.empty())
{
// Find each sink and see what they are
for (auto x : current_loads)
{
// If not the correct type, don't follow any further
// (but add the originating cell to the list of sinks)
if (!is_right_type(x, cell_type))
{
sinks.insert(cell);
continue;
}
auto xy = sigmap(x->getPort(ID::Y));
// If this signal drives a port, add it to the sinks
// (even though it may not be the end of a chain)
for (auto bit : xy) {
if (output_port_sigs.count(bit) && !consumed_cells.count(x)) {
sinks.insert(x);
break;
}
}
// Search signal's fanout
auto& next = sig_to_sink[xy];
for (auto z : next)
next_loads.insert(z);
}
// If we couldn't find any downstream loads, stop.
// Create a reduction for each of the max-length chains we found
if (next_loads.empty())
{
for (auto s : current_loads)
{
// Not one of our gates? Don't follow any further
if (!is_right_type(s, cell_type))
continue;
sinks.insert(s);
}
break;
}
// Otherwise, continue down the chain
current_loads = next_loads;
next_loads.clear();
}
// We have our list of sinks, now go tree balance the chains
for (auto head_cell : sinks)
{
// Avoid duplication if we already were covered
if (consumed_cells.count(head_cell))
continue;
// Get sources of the chain
dict<SigSpec, int> sources;
dict<SigSpec, bool> signeds;
int inner_cells = 0;
std::deque<Cell*> bfs_queue = {head_cell};
while (bfs_queue.size())
{
Cell* x = bfs_queue.front();
bfs_queue.pop_front();
for (IdString port: {ID::A, ID::B}) {
auto sig = sigmap(x->getPort(port));
Cell* drv = sig_to_driver[sig];
bool drv_ok = drv && is_right_type(drv, cell_type);
for (auto bit : sig) {
if (input_port_sigs.count(bit) && !consumed_cells.count(drv)) {
drv_ok = false;
break;
}
}
if (drv_ok) {
inner_cells++;
bfs_queue.push_back(drv);
} else {
sources[sig]++;
signeds[sig] = x->getParam(port == ID::A ? ID::A_SIGNED : ID::B_SIGNED).as_bool();
}
}
}
if (inner_cells)
{
// Create a tree
log_debug(" Creating tree for %s with %d sources and %d inner cells...\n", log_id(head_cell), GetSize(sources), inner_cells);
// Build a vector of all source signals
vector<SigSpec> source_signals;
vector<bool> signed_flags;
for (auto &source : sources) {
for (int i = 0; i < source.second; i++) {
source_signals.push_back(source.first);
signed_flags.push_back(signeds[source.first]);
}
}
// If not all signed flags are the same, do not balance
if (!std::all_of(signed_flags.begin(), signed_flags.end(), [&](bool flag) { return flag == signed_flags[0]; })) {
continue;
}
// Create the balanced tree
SigSpec tree_output = create_balanced_tree(source_signals, cell_type, head_cell);
// Connect the tree output to the head cell's output
SigSpec head_output = sigmap(head_cell->getPort(ID::Y));
int connect_width = std::min(head_output.size(), tree_output.size());
module->connect(head_output.extract(0, connect_width), tree_output.extract(0, connect_width));
if (head_output.size() > tree_output.size()) {
SigBit sext_bit = head_cell->getParam(ID::A_SIGNED).as_bool() ? head_output[connect_width - 1] : State::S0;
module->connect(head_output.extract(connect_width, head_output.size() - connect_width), SigSpec(sext_bit, head_output.size() - connect_width));
}
// Mark consumed cell for removal
consumed_cells.insert(head_cell);
}
}
}
// Remove all consumed cells, which now have been replaced by trees
for (auto cell : consumed_cells)
module->remove(cell);
}
}
};
struct OptBalanceTreePass : public Pass {
OptBalanceTreePass() : Pass("opt_balance_tree", "$and/$or/$xor/$add/$mul cascades to trees") { }
void help() override {
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" opt_balance_tree [options] [selection]\n");
log("\n");
log("This pass converts cascaded chains of $and/$or/$xor/$add/$mul cells into\n");
log("trees of cells to improve timing.\n");
log("\n");
log(" -arith\n");
log(" only convert arithmetic cells.\n");
log("\n");
log(" -logic\n");
log(" only convert logic cells.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override {
log_header(design, "Executing OPT_BALANCE_TREE pass (cell cascades to trees).\n");
log_experimental("open_balance_tree");
// Handle arguments
size_t argidx;
vector<IdString> cell_types = {ID($and), ID($or), ID($xor), ID($add), ID($mul)};
for (argidx = 1; argidx < args.size(); argidx++) {
if (args[argidx] == "-arith") {
cell_types = {ID($add), ID($mul)};
continue;
}
if (args[argidx] == "-logic") {
cell_types = {ID($and), ID($or), ID($xor)};
continue;
}
break;
}
extra_args(args, argidx, design);
// Count of all cells that were packed
dict<IdString, int> cell_count;
for (auto module : design->selected_modules()) {
OptBalanceTreeWorker worker(module, cell_types);
for (auto cell : worker.cell_count) {
cell_count[cell.first] += cell.second;
}
}
// Log stats
for (auto cell_type : cell_types)
log("Converted %d %s cells into trees.\n", cell_count[cell_type], log_id(cell_type));
// Clean up
Yosys::run_pass("clean -purge");
}
} OptBalanceTreePass;
PRIVATE_NAMESPACE_END

View file

@ -21,6 +21,7 @@
#include "kernel/sigtools.h"
#include "kernel/log.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
#include "kernel/ffinit.h"
#include <stdlib.h>
#include <stdio.h>
@ -101,7 +102,10 @@ struct keep_cache_t
};
keep_cache_t keep_cache;
CellTypes ct_reg, ct_all;
static constexpr auto ct_reg = StaticCellTypes::Categories::join(
StaticCellTypes::Compat::mem_ff,
StaticCellTypes::categories.is_anyinit);
NewCellTypes ct_all;
int count_rm_cells, count_rm_wires;
void rmunused_module_cells(Module *module, bool verbose)
@ -271,6 +275,9 @@ bool compare_signals(RTLIL::SigBit &s1, RTLIL::SigBit &s2, SigPool &regs, SigPoo
return conns.check_any(s2);
}
if (w1 == w2)
return s2.offset < s1.offset;
if (w1->port_output != w2->port_output)
return w2->port_output;
@ -307,10 +314,10 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
if (!purge_mode)
for (auto &it : module->cells_) {
RTLIL::Cell *cell = it.second;
if (ct_reg.cell_known(cell->type)) {
if (ct_reg(cell->type)) {
bool clk2fflogic = cell->get_bool_attribute(ID(clk2fflogic));
for (auto &it2 : cell->connections())
if (clk2fflogic ? it2.first == ID::D : ct_reg.cell_output(cell->type, it2.first))
if (clk2fflogic ? it2.first == ID::D : ct_all.cell_output(cell->type, it2.first))
register_signals.add(it2.second);
}
for (auto &it2 : cell->connections())
@ -343,7 +350,7 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
RTLIL::Wire *wire = it.second;
for (int i = 0; i < wire->width; i++) {
RTLIL::SigBit s1 = RTLIL::SigBit(wire, i), s2 = assign_map(s1);
if (!compare_signals(s1, s2, register_signals, connected_signals, direct_wires))
if (compare_signals(s2, s1, register_signals, connected_signals, direct_wires))
assign_map.add(s1);
}
}
@ -467,8 +474,6 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
wire->attributes.erase(ID::init);
else
wire->attributes.at(ID::init) = initval;
used_signals.add(new_conn.first);
used_signals.add(new_conn.second);
module->connect(new_conn);
}
@ -516,14 +521,12 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
bool rmunused_module_init(RTLIL::Module *module, bool verbose)
{
bool did_something = false;
CellTypes fftypes;
fftypes.setup_internals_mem();
SigMap sigmap(module);
dict<SigBit, State> qbits;
for (auto cell : module->cells())
if (fftypes.cell_known(cell->type) && cell->hasPort(ID::Q))
if (StaticCellTypes::Compat::internals_mem_ff(cell->type) && cell->hasPort(ID::Q))
{
SigSpec sig = cell->getPort(ID::Q);
@ -696,10 +699,6 @@ struct OptCleanPass : public Pass {
keep_cache.reset(design, purge_mode);
ct_reg.setup_internals_mem();
ct_reg.setup_internals_anyinit();
ct_reg.setup_stdcells_mem();
ct_all.setup(design);
count_rm_cells = 0;
@ -715,11 +714,9 @@ struct OptCleanPass : public Pass {
log("Removed %d unused cells and %d unused wires.\n", count_rm_cells, count_rm_wires);
design->optimize();
design->sort();
design->check();
keep_cache.reset();
ct_reg.clear();
ct_all.clear();
log_pop();
@ -760,10 +757,6 @@ struct CleanPass : public Pass {
keep_cache.reset(design);
ct_reg.setup_internals_mem();
ct_reg.setup_internals_anyinit();
ct_reg.setup_stdcells_mem();
ct_all.setup(design);
count_rm_cells = 0;
@ -780,11 +773,9 @@ struct CleanPass : public Pass {
log("Removed %d unused cells and %d unused wires.\n", count_rm_cells, count_rm_wires);
design->optimize();
design->sort();
design->check();
keep_cache.reset();
ct_reg.clear();
ct_all.clear();
request_garbage_collection();

File diff suppressed because it is too large Load diff

View file

@ -20,6 +20,7 @@
#include "kernel/register.h"
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
#include "kernel/utils.h"
#include "kernel/log.h"
#include <stdlib.h>
@ -31,7 +32,7 @@ PRIVATE_NAMESPACE_BEGIN
bool did_something;
void replace_undriven(RTLIL::Module *module, const CellTypes &ct)
void replace_undriven(RTLIL::Module *module, const NewCellTypes &ct)
{
SigMap sigmap(module);
SigPool driven_signals;
@ -407,9 +408,6 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
}
}
CellTypes ct_memcells;
ct_memcells.setup_stdcells_mem();
if (!noclkinv)
for (auto cell : module->cells())
if (design->selected(module, cell)) {
@ -433,7 +431,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (cell->type.in(ID($dffe), ID($adffe), ID($aldffe), ID($sdffe), ID($sdffce), ID($dffsre), ID($dlatch), ID($adlatch), ID($dlatchsr)))
handle_polarity_inv(cell, ID::EN, ID::EN_POLARITY, assign_map, invert_map);
if (!ct_memcells.cell_known(cell->type))
if (!StaticCellTypes::Compat::stdcells_mem(cell->type))
continue;
handle_clkpol_celltype_swap(cell, "$_SR_N?_", "$_SR_P?_", ID::S, assign_map, invert_map);
@ -1667,7 +1665,11 @@ skip_identity:
int bit_idx;
const auto onehot = sig_a.is_onehot(&bit_idx);
if (onehot) {
// Power of two
// A is unsigned or positive
if (onehot && (!cell->parameters[ID::A_SIGNED].as_bool() || bit_idx < sig_a.size() - 1)) {
cell->parameters[ID::A_SIGNED] = 0;
// 2^B = 1<<B
if (bit_idx == 1) {
log_debug("Replacing pow cell `%s' in module `%s' with left-shift\n",
cell->name.c_str(), module->name.c_str());
@ -1679,7 +1681,6 @@ skip_identity:
log_debug("Replacing pow cell `%s' in module `%s' with multiply and left-shift\n",
cell->name.c_str(), module->name.c_str());
cell->type = ID($mul);
cell->parameters[ID::A_SIGNED] = 0;
cell->setPort(ID::A, Const(bit_idx, cell->parameters[ID::A_WIDTH].as_int()));
SigSpec y_wire = module->addWire(NEW_ID, y_size);
@ -2291,7 +2292,7 @@ struct OptExprPass : public Pass {
}
extra_args(args, argidx, design);
CellTypes ct(design);
NewCellTypes ct(design);
for (auto module : design->selected_modules())
{
log("Optimizing module %s.\n", log_id(module));

View file

@ -39,7 +39,8 @@ struct OptLutInsPass : public Pass {
log("\n");
log(" -tech <technology>\n");
log(" Instead of generic $lut cells, operate on LUT cells specific\n");
log(" to the given technology. Valid values are: xilinx, lattice, gowin.\n");
log(" to the given technology. Valid values are: xilinx, lattice,\n");
log(" gowin, analogdevices.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
@ -58,7 +59,7 @@ struct OptLutInsPass : public Pass {
}
extra_args(args, argidx, design);
if (techname != "" && techname != "xilinx" && techname != "lattice" && techname != "ecp5" && techname != "gowin")
if (techname != "" && techname != "xilinx" && techname != "lattice" && techname != "analogdevices" && techname != "gowin")
log_cmd_error("Unsupported technology: '%s'\n", techname);
for (auto module : design->selected_modules())
@ -81,7 +82,7 @@ struct OptLutInsPass : public Pass {
inputs = cell->getPort(ID::A);
output = cell->getPort(ID::Y);
lut = cell->getParam(ID::LUT);
} else if (techname == "xilinx" || techname == "gowin") {
} else if (techname == "xilinx" || techname == "gowin" || techname == "analogdevices") {
if (cell->type == ID(LUT1)) {
inputs = {
cell->getPort(ID(I0)),
@ -126,11 +127,11 @@ struct OptLutInsPass : public Pass {
continue;
}
lut = cell->getParam(ID::INIT);
if (techname == "xilinx")
if (techname == "xilinx" || techname == "analogdevices")
output = cell->getPort(ID::O);
else
output = cell->getPort(ID::F);
} else if (techname == "lattice" || techname == "ecp5") {
} else if (techname == "lattice") {
if (cell->type == ID(LUT4)) {
inputs = {
cell->getPort(ID::A),
@ -236,7 +237,7 @@ struct OptLutInsPass : public Pass {
} else {
// xilinx, gowin
cell->setParam(ID::INIT, new_lut);
if (techname == "xilinx")
if (techname == "xilinx" || techname == "analogdevices")
log_assert(GetSize(new_inputs) <= 6);
else
log_assert(GetSize(new_inputs) <= 4);

View file

@ -22,6 +22,7 @@
#include "kernel/sigtools.h"
#include "kernel/log.h"
#include "kernel/celltypes.h"
#include "kernel/threading.h"
#include "libs/sha1/sha1.h"
#include <stdlib.h>
#include <stdio.h>
@ -37,16 +38,73 @@ PRIVATE_NAMESPACE_BEGIN
template <typename T, typename U>
inline Hasher hash_pair(const T &t, const U &u) { return hash_ops<std::pair<T, U>>::hash(t, u); }
struct OptMergeWorker
// Some cell and its hash value.
struct CellHash
{
RTLIL::Design *design;
RTLIL::Module *module;
SigMap assign_map;
FfInitVals initvals;
bool mode_share_all;
// Index of a cell in the module
int cell_index;
Hasher::hash_t hash_value;
};
CellTypes ct;
int total_count;
// The algorithm:
// 1) Compute and store the hashes of all relevant cells, in parallel.
// 2) Given N = the number of threads, partition the cells into N buckets by hash value:
// bucket k contains the cells whose hash value mod N = k.
// 3) For each bucket in parallel, build a hashtable of that buckets cells (using the
// precomputed hashes) and record the duplicates found.
// 4) On the main thread, process the list of duplicates to remove cells.
// For efficiency we fuse the second step into the first step by having the parallel
// threads write the cells into buckets directly.
// To avoid synchronization overhead, we divide each bucket into N shards. Each
// thread j adds a cell to bucket k by writing to shard j of bucket k —
// no synchronization required. In the next phase, thread k builds the hashtable for
// bucket k by iterating over all shards of the bucket.
// The input to each thread in the "compute cell hashes" phase.
struct CellRange
{
int begin;
int end;
};
// The output from each thread in the "compute cell hashes" phase.
struct CellHashes
{
// Entry i contains the hashes where hash_value % bucketed_cell_hashes.size() == i
std::vector<std::vector<CellHash>> bucketed_cell_hashes;
};
// A duplicate cell that has been found.
struct DuplicateCell
{
// Remove this cell from the design
int remove_cell;
// ... and use this cell instead.
int keep_cell;
};
// The input to each thread in the "find duplicate cells" phase.
// Shards of buckets of cell hashes
struct Shards
{
std::vector<std::vector<std::vector<CellHash>>> &bucketed_cell_hashes;
};
// The output from each thread in the "find duplicate cells" phase.
struct FoundDuplicates
{
std::vector<DuplicateCell> duplicates;
};
struct OptMergeThreadWorker
{
const RTLIL::Module *module;
const SigMap &assign_map;
const FfInitVals &initvals;
const CellTypes &ct;
int workers;
bool mode_share_all;
bool mode_keepdc;
static Hasher hash_pmux_in(const SigSpec& sig_s, const SigSpec& sig_b, Hasher h)
{
@ -62,8 +120,8 @@ struct OptMergeWorker
static void sort_pmux_conn(dict<RTLIL::IdString, RTLIL::SigSpec> &conn)
{
SigSpec sig_s = conn.at(ID::S);
SigSpec sig_b = conn.at(ID::B);
const SigSpec &sig_s = conn.at(ID::S);
const SigSpec &sig_b = conn.at(ID::B);
int s_width = GetSize(sig_s);
int width = GetSize(sig_b) / s_width;
@ -144,7 +202,6 @@ struct OptMergeWorker
if (cell1->parameters != cell2->parameters)
return false;
if (cell1->connections_.size() != cell2->connections_.size())
return false;
for (const auto &it : cell1->connections_)
@ -199,7 +256,7 @@ struct OptMergeWorker
return conn1 == conn2;
}
bool has_dont_care_initval(const RTLIL::Cell *cell)
bool has_dont_care_initval(const RTLIL::Cell *cell) const
{
if (!cell->is_builtin_ff())
return false;
@ -207,36 +264,134 @@ struct OptMergeWorker
return !initvals(cell->getPort(ID::Q)).is_fully_def();
}
OptMergeWorker(RTLIL::Design *design, RTLIL::Module *module, bool mode_nomux, bool mode_share_all, bool mode_keepdc) :
design(design), module(module), mode_share_all(mode_share_all)
OptMergeThreadWorker(const RTLIL::Module *module, const FfInitVals &initvals,
const SigMap &assign_map, const CellTypes &ct, int workers,
bool mode_share_all, bool mode_keepdc) :
module(module), assign_map(assign_map), initvals(initvals), ct(ct),
workers(workers), mode_share_all(mode_share_all), mode_keepdc(mode_keepdc)
{
total_count = 0;
ct.setup_internals();
ct.setup_internals_mem();
ct.setup_stdcells();
ct.setup_stdcells_mem();
}
if (mode_nomux) {
ct.cell_types.erase(ID($mux));
ct.cell_types.erase(ID($pmux));
CellHashes compute_cell_hashes(const CellRange &cell_range) const
{
std::vector<std::vector<CellHash>> bucketed_cell_hashes(workers);
for (int cell_index = cell_range.begin; cell_index < cell_range.end; ++cell_index) {
const RTLIL::Cell *cell = module->cell_at(cell_index);
if (!module->selected(cell))
continue;
if (cell->type.in(ID($meminit), ID($meminit_v2), ID($mem), ID($mem_v2))) {
// Ignore those for performance: meminit can have an excessively large port,
// mem can have an excessively large parameter holding the init data
continue;
}
if (cell->type == ID($scopeinfo))
continue;
if (mode_keepdc && has_dont_care_initval(cell))
continue;
if (!cell->known())
continue;
if (!mode_share_all && !ct.cell_known(cell->type))
continue;
Hasher::hash_t h = hash_cell_function(cell, Hasher()).yield();
int bucket_index = h % workers;
bucketed_cell_hashes[bucket_index].push_back({cell_index, h});
}
return {std::move(bucketed_cell_hashes)};
}
ct.cell_types.erase(ID($tribuf));
ct.cell_types.erase(ID($_TBUF_));
ct.cell_types.erase(ID($anyseq));
ct.cell_types.erase(ID($anyconst));
ct.cell_types.erase(ID($allseq));
ct.cell_types.erase(ID($allconst));
ct.cell_types.erase(ID($check));
ct.cell_types.erase(ID($assert));
ct.cell_types.erase(ID($assume));
ct.cell_types.erase(ID($live));
ct.cell_types.erase(ID($cover));
FoundDuplicates find_duplicate_cells(int index, const Shards &in) const
{
// We keep a set of known cells. They're hashed with our hash_cell_function
// and compared with our compare_cell_parameters_and_connections.
struct CellHashOp {
std::size_t operator()(const CellHash &c) const {
return (std::size_t)c.hash_value;
}
};
struct CellEqualOp {
const OptMergeThreadWorker& worker;
CellEqualOp(const OptMergeThreadWorker& w) : worker(w) {}
bool operator()(const CellHash &lhs, const CellHash &rhs) const {
return worker.compare_cell_parameters_and_connections(
worker.module->cell_at(lhs.cell_index),
worker.module->cell_at(rhs.cell_index));
}
};
std::unordered_set<
CellHash,
CellHashOp,
CellEqualOp> known_cells(0, CellHashOp(), CellEqualOp(*this));
std::vector<DuplicateCell> duplicates;
for (const std::vector<std::vector<CellHash>> &buckets : in.bucketed_cell_hashes) {
// Clear out our buckets as we go. This keeps the work of deallocation
// off the main thread.
std::vector<CellHash> bucket = std::move(buckets[index]);
for (CellHash c : bucket) {
auto [cell_in_map, inserted] = known_cells.insert(c);
if (inserted)
continue;
CellHash map_c = *cell_in_map;
if (module->cell_at(c.cell_index)->has_keep_attr()) {
if (module->cell_at(map_c.cell_index)->has_keep_attr())
continue;
known_cells.erase(map_c);
known_cells.insert(c);
std::swap(c, map_c);
}
duplicates.push_back({c.cell_index, map_c.cell_index});
}
}
return {duplicates};
}
};
template <typename T>
void initialize_queues(std::vector<ConcurrentQueue<T>> &queues, int size) {
queues.reserve(size);
for (int i = 0; i < size; ++i)
queues.emplace_back(1);
}
struct OptMergeWorker
{
int total_count;
OptMergeWorker(RTLIL::Module *module, const CellTypes &ct, bool mode_share_all, bool mode_keepdc) :
total_count(0)
{
SigMap assign_map(module);
FfInitVals initvals;
initvals.set(&assign_map, module);
log("Finding identical cells in module `%s'.\n", module->name);
assign_map.set(module);
initvals.set(&assign_map, module);
// Use no more than one worker per thousand cells, rounded down, so
// we only start multithreading with at least 2000 cells.
int num_worker_threads = ThreadPool::pool_size(0, module->cells_size()/1000);
int workers = std::max(1, num_worker_threads);
// The main thread doesn't do any work, so if there is only one worker thread,
// just run everything on the main thread instead.
// This avoids creating and waiting on a thread, which is pretty high overhead
// for very small modules.
if (num_worker_threads == 1)
num_worker_threads = 0;
OptMergeThreadWorker thread_worker(module, initvals, assign_map, ct, workers, mode_share_all, mode_keepdc);
std::vector<ConcurrentQueue<CellRange>> cell_ranges_queues(num_worker_threads);
std::vector<ConcurrentQueue<CellHashes>> cell_hashes_queues(num_worker_threads);
std::vector<ConcurrentQueue<Shards>> shards_queues(num_worker_threads);
std::vector<ConcurrentQueue<FoundDuplicates>> duplicates_queues(num_worker_threads);
ThreadPool thread_pool(num_worker_threads, [&](int i) {
while (std::optional<CellRange> c = cell_ranges_queues[i].pop_front()) {
cell_hashes_queues[i].push_back(thread_worker.compute_cell_hashes(*c));
std::optional<Shards> shards = shards_queues[i].pop_front();
duplicates_queues[i].push_back(thread_worker.find_duplicate_cells(i, *shards));
}
});
bool did_something = true;
// A cell may have to go through a lot of collisions if the hash
@ -244,87 +399,99 @@ struct OptMergeWorker
// beyond the user's control.
while (did_something)
{
std::vector<RTLIL::Cell*> cells;
cells.reserve(module->cells().size());
for (auto cell : module->cells()) {
if (!design->selected(module, cell))
continue;
if (cell->type.in(ID($meminit), ID($meminit_v2), ID($mem), ID($mem_v2))) {
// Ignore those for performance: meminit can have an excessively large port,
// mem can have an excessively large parameter holding the init data
continue;
}
if (cell->type == ID($scopeinfo))
continue;
if (mode_keepdc && has_dont_care_initval(cell))
continue;
if (!cell->known())
continue;
if (!mode_share_all && !ct.cell_known(cell->type))
continue;
cells.push_back(cell);
}
int cells_size = module->cells_size();
log("Computing hashes of %d cells of `%s'.\n", cells_size, module->name);
std::vector<std::vector<std::vector<CellHash>>> sharded_bucketed_cell_hashes(workers);
did_something = false;
// We keep a set of known cells. They're hashed with our hash_cell_function
// and compared with our compare_cell_parameters_and_connections.
// Both need to capture OptMergeWorker to access initvals
struct CellPtrHash {
const OptMergeWorker& worker;
CellPtrHash(const OptMergeWorker& w) : worker(w) {}
std::size_t operator()(const Cell* c) const {
return (std::size_t)worker.hash_cell_function(c, Hasher()).yield();
}
};
struct CellPtrEqual {
const OptMergeWorker& worker;
CellPtrEqual(const OptMergeWorker& w) : worker(w) {}
bool operator()(const Cell* lhs, const Cell* rhs) const {
return worker.compare_cell_parameters_and_connections(lhs, rhs);
}
};
std::unordered_set<
RTLIL::Cell*,
CellPtrHash,
CellPtrEqual> known_cells (0, CellPtrHash(*this), CellPtrEqual(*this));
for (auto cell : cells)
int cell_index = 0;
int cells_size_mod_workers = cells_size % workers;
{
auto [cell_in_map, inserted] = known_cells.insert(cell);
if (!inserted) {
// We've failed to insert since we already have an equivalent cell
Cell* other_cell = *cell_in_map;
if (cell->has_keep_attr()) {
if (other_cell->has_keep_attr())
continue;
known_cells.erase(other_cell);
known_cells.insert(cell);
std::swap(other_cell, cell);
}
did_something = true;
log_debug(" Cell `%s' is identical to cell `%s'.\n", cell->name, other_cell->name);
for (auto &it : cell->connections()) {
if (cell->output(it.first)) {
RTLIL::SigSpec other_sig = other_cell->getPort(it.first);
log_debug(" Redirecting output %s: %s = %s\n", it.first,
log_signal(it.second), log_signal(other_sig));
Const init = initvals(other_sig);
initvals.remove_init(it.second);
initvals.remove_init(other_sig);
module->connect(RTLIL::SigSig(it.second, other_sig));
assign_map.add(it.second, other_sig);
initvals.set_init(other_sig, init);
}
}
log_debug(" Removing %s cell `%s' from module `%s'.\n", cell->type, cell->name, module->name);
module->remove(cell);
total_count++;
Multithreading multithreading;
for (int i = 0; i < workers; ++i) {
int num_cells = cells_size/workers + ((i < cells_size_mod_workers) ? 1 : 0);
CellRange c = { cell_index, cell_index + num_cells };
cell_index += num_cells;
if (num_worker_threads > 0)
cell_ranges_queues[i].push_back(c);
else
sharded_bucketed_cell_hashes[i] = std::move(thread_worker.compute_cell_hashes(c).bucketed_cell_hashes);
}
log_assert(cell_index == cells_size);
if (num_worker_threads > 0)
for (int i = 0; i < workers; ++i)
sharded_bucketed_cell_hashes[i] = std::move(cell_hashes_queues[i].pop_front()->bucketed_cell_hashes);
}
log("Finding duplicate cells in `%s'.\n", module->name);
std::vector<DuplicateCell> merged_duplicates;
{
Multithreading multithreading;
for (int i = 0; i < workers; ++i) {
Shards thread_shards = { sharded_bucketed_cell_hashes };
if (num_worker_threads > 0)
shards_queues[i].push_back(thread_shards);
else {
std::vector<DuplicateCell> d = std::move(thread_worker.find_duplicate_cells(i, thread_shards).duplicates);
merged_duplicates.insert(merged_duplicates.end(), d.begin(), d.end());
}
}
if (num_worker_threads > 0)
for (int i = 0; i < workers; ++i) {
std::vector<DuplicateCell> d = std::move(duplicates_queues[i].pop_front()->duplicates);
merged_duplicates.insert(merged_duplicates.end(), d.begin(), d.end());
}
}
std::sort(merged_duplicates.begin(), merged_duplicates.end(), [](const DuplicateCell &lhs, const DuplicateCell &rhs) {
// Sort them by the order in which duplicates would have been detected in a single-threaded
// run. The cell at which the duplicate would have been detected is the latter of the two
// cells involved.
return std::max(lhs.remove_cell, lhs.keep_cell) < std::max(rhs.remove_cell, rhs.keep_cell);
});
// Convert to cell pointers because removing cells will invalidate the indices.
std::vector<std::pair<RTLIL::Cell*, RTLIL::Cell*>> cell_ptrs;
for (DuplicateCell dup : merged_duplicates)
cell_ptrs.push_back({module->cell_at(dup.remove_cell), module->cell_at(dup.keep_cell)});
for (auto [remove_cell, keep_cell] : cell_ptrs)
{
log_debug(" Cell `%s' is identical to cell `%s'.\n", remove_cell->name, keep_cell->name);
for (auto &it : remove_cell->connections()) {
if (remove_cell->output(it.first)) {
RTLIL::SigSpec keep_sig = keep_cell->getPort(it.first);
log_debug(" Redirecting output %s: %s = %s\n", it.first,
log_signal(it.second), log_signal(keep_sig));
Const init = initvals(keep_sig);
initvals.remove_init(it.second);
initvals.remove_init(keep_sig);
module->connect(RTLIL::SigSig(it.second, keep_sig));
auto keep_sig_it = keep_sig.begin();
for (SigBit remove_sig_bit : it.second) {
assign_map.add(remove_sig_bit, *keep_sig_it);
++keep_sig_it;
}
initvals.set_init(keep_sig, init);
}
}
log_debug(" Removing %s cell `%s' from module `%s'.\n", remove_cell->type, remove_cell->name, module->name);
module->remove(remove_cell);
total_count++;
}
did_something = !merged_duplicates.empty();
}
for (ConcurrentQueue<CellRange> &q : cell_ranges_queues)
q.close();
for (ConcurrentQueue<Shards> &q : shards_queues)
q.close();
for (ConcurrentQueue<CellRange> &q : cell_ranges_queues)
q.close();
for (ConcurrentQueue<Shards> &q : shards_queues)
q.close();
log_suppressed();
}
};
@ -377,9 +544,25 @@ struct OptMergePass : public Pass {
}
extra_args(args, argidx, design);
CellTypes ct;
ct.setup_internals();
ct.setup_internals_mem();
ct.setup_stdcells();
ct.setup_stdcells_mem();
if (mode_nomux) {
ct.cell_types.erase(ID($mux));
ct.cell_types.erase(ID($pmux));
}
ct.cell_types.erase(ID($tribuf));
ct.cell_types.erase(ID($_TBUF_));
ct.cell_types.erase(ID($anyseq));
ct.cell_types.erase(ID($anyconst));
ct.cell_types.erase(ID($allseq));
ct.cell_types.erase(ID($allconst));
int total_count = 0;
for (auto module : design->selected_modules()) {
OptMergeWorker worker(design, module, mode_nomux, mode_share_all, mode_keepdc);
OptMergeWorker worker(module, ct, mode_share_all, mode_keepdc);
total_count += worker.total_count;
}

View file

@ -64,7 +64,7 @@ struct OptMuxtreeWorker
RTLIL::Module *module;
SigMap assign_map;
int removed_count;
int glob_evals_left = 10000000;
int glob_evals_left = 10'000'000;
struct bitinfo_t {
// Is bit directly used by non-mux cells or ports?

View file

@ -23,6 +23,7 @@
#include "kernel/modtools.h"
#include "kernel/utils.h"
#include "kernel/macc.h"
#include "kernel/newcelltypes.h"
#include <iterator>
USING_YOSYS_NAMESPACE
@ -38,19 +39,18 @@ struct ShareWorkerConfig
bool opt_force;
bool opt_aggressive;
bool opt_fast;
pool<RTLIL::IdString> generic_uni_ops, generic_bin_ops, generic_cbin_ops, generic_other_ops;
StaticCellTypes::Categories::Category generic_uni_ops, generic_bin_ops, generic_cbin_ops, generic_other_ops;
};
struct ShareWorker
{
const ShareWorkerConfig config;
int limit;
pool<RTLIL::IdString> generic_ops;
StaticCellTypes::Categories::Category generic_ops;
RTLIL::Design *design;
RTLIL::Module *module;
CellTypes fwd_ct, cone_ct;
ModWalker modwalker;
pool<RTLIL::Cell*> cells_to_remove;
@ -75,7 +75,7 @@ struct ShareWorker
queue_bits.insert(modwalker.signal_outputs.begin(), modwalker.signal_outputs.end());
for (auto &it : module->cells_)
if (!fwd_ct.cell_known(it.second->type)) {
if (!StaticCellTypes::Compat::internals_nomem_noff(it.second->type)) {
pool<RTLIL::SigBit> &bits = modwalker.cell_inputs[it.second];
queue_bits.insert(bits.begin(), bits.end());
}
@ -95,7 +95,7 @@ struct ShareWorker
queue_bits.insert(bits.begin(), bits.end());
visited_cells.insert(pbit.cell);
}
if (fwd_ct.cell_known(pbit.cell->type) && visited_cells.count(pbit.cell) == 0) {
if (StaticCellTypes::Compat::internals_nomem_noff(pbit.cell->type) && visited_cells.count(pbit.cell) == 0) {
pool<RTLIL::SigBit> &bits = modwalker.cell_inputs[pbit.cell];
terminal_bits.insert(bits.begin(), bits.end());
queue_bits.insert(bits.begin(), bits.end());
@ -388,7 +388,7 @@ struct ShareWorker
continue;
}
if (generic_ops.count(cell->type)) {
if (generic_ops(cell->type)) {
if (config.opt_aggressive)
shareable_cells.insert(cell);
continue;
@ -412,7 +412,7 @@ struct ShareWorker
return true;
}
if (config.generic_uni_ops.count(c1->type))
if (config.generic_uni_ops(c1->type))
{
if (!config.opt_aggressive)
{
@ -429,7 +429,7 @@ struct ShareWorker
return true;
}
if (config.generic_bin_ops.count(c1->type) || c1->type == ID($alu))
if (config.generic_bin_ops(c1->type) || c1->type == ID($alu))
{
if (!config.opt_aggressive)
{
@ -449,7 +449,7 @@ struct ShareWorker
return true;
}
if (config.generic_cbin_ops.count(c1->type))
if (config.generic_cbin_ops(c1->type))
{
if (!config.opt_aggressive)
{
@ -511,7 +511,7 @@ struct ShareWorker
{
log_assert(c1->type == c2->type);
if (config.generic_uni_ops.count(c1->type))
if (config.generic_uni_ops(c1->type))
{
if (c1->parameters.at(ID::A_SIGNED).as_bool() != c2->parameters.at(ID::A_SIGNED).as_bool())
{
@ -560,11 +560,11 @@ struct ShareWorker
return supercell;
}
if (config.generic_bin_ops.count(c1->type) || config.generic_cbin_ops.count(c1->type) || c1->type == ID($alu))
if (config.generic_bin_ops(c1->type) || config.generic_cbin_ops(c1->type) || c1->type == ID($alu))
{
bool modified_src_cells = false;
if (config.generic_cbin_ops.count(c1->type))
if (config.generic_cbin_ops(c1->type))
{
int score_unflipped = max(c1->parameters.at(ID::A_WIDTH).as_int(), c2->parameters.at(ID::A_WIDTH).as_int()) +
max(c1->parameters.at(ID::B_WIDTH).as_int(), c2->parameters.at(ID::B_WIDTH).as_int());
@ -758,7 +758,7 @@ struct ShareWorker
recursion_state.insert(cell);
for (auto c : consumer_cells)
if (fwd_ct.cell_known(c->type)) {
if (StaticCellTypes::Compat::internals_nomem_noff(c->type)) {
const pool<RTLIL::SigBit> &bits = find_forbidden_controls(c);
forbidden_controls_cache[cell].insert(bits.begin(), bits.end());
}
@ -897,7 +897,7 @@ struct ShareWorker
return activation_patterns_cache.at(cell);
}
for (auto &pbit : modwalker.signal_consumers[bit]) {
log_assert(fwd_ct.cell_known(pbit.cell->type));
log_assert(StaticCellTypes::Compat::internals_nomem_noff(pbit.cell->type));
if ((pbit.cell->type == ID($mux) || pbit.cell->type == ID($pmux)) && (pbit.port == ID::A || pbit.port == ID::B))
driven_data_muxes.insert(pbit.cell);
else
@ -1214,24 +1214,10 @@ struct ShareWorker
ShareWorker(ShareWorkerConfig config, RTLIL::Design* design) :
config(config), design(design), modwalker(design)
{
generic_ops.insert(config.generic_uni_ops.begin(), config.generic_uni_ops.end());
generic_ops.insert(config.generic_bin_ops.begin(), config.generic_bin_ops.end());
generic_ops.insert(config.generic_cbin_ops.begin(), config.generic_cbin_ops.end());
generic_ops.insert(config.generic_other_ops.begin(), config.generic_other_ops.end());
fwd_ct.setup_internals();
cone_ct.setup_internals();
cone_ct.cell_types.erase(ID($mul));
cone_ct.cell_types.erase(ID($mod));
cone_ct.cell_types.erase(ID($div));
cone_ct.cell_types.erase(ID($modfloor));
cone_ct.cell_types.erase(ID($divfloor));
cone_ct.cell_types.erase(ID($pow));
cone_ct.cell_types.erase(ID($shl));
cone_ct.cell_types.erase(ID($shr));
cone_ct.cell_types.erase(ID($sshl));
cone_ct.cell_types.erase(ID($sshr));
generic_ops = StaticCellTypes::Categories::join(generic_ops, config.generic_uni_ops);
generic_ops = StaticCellTypes::Categories::join(generic_ops, config.generic_bin_ops);
generic_ops = StaticCellTypes::Categories::join(generic_ops, config.generic_cbin_ops);
generic_ops = StaticCellTypes::Categories::join(generic_ops, config.generic_other_ops);
}
void operator()(RTLIL::Module *module) {
@ -1561,45 +1547,45 @@ struct SharePass : public Pass {
config.opt_aggressive = false;
config.opt_fast = false;
config.generic_uni_ops.insert(ID($not));
// config.generic_uni_ops.insert(ID($pos));
config.generic_uni_ops.insert(ID($neg));
config.generic_uni_ops.set_id(ID($not));
// config.generic_uni_ops.set_id(ID($pos));
config.generic_uni_ops.set_id(ID($neg));
config.generic_cbin_ops.insert(ID($and));
config.generic_cbin_ops.insert(ID($or));
config.generic_cbin_ops.insert(ID($xor));
config.generic_cbin_ops.insert(ID($xnor));
config.generic_cbin_ops.set_id(ID($and));
config.generic_cbin_ops.set_id(ID($or));
config.generic_cbin_ops.set_id(ID($xor));
config.generic_cbin_ops.set_id(ID($xnor));
config.generic_bin_ops.insert(ID($shl));
config.generic_bin_ops.insert(ID($shr));
config.generic_bin_ops.insert(ID($sshl));
config.generic_bin_ops.insert(ID($sshr));
config.generic_bin_ops.set_id(ID($shl));
config.generic_bin_ops.set_id(ID($shr));
config.generic_bin_ops.set_id(ID($sshl));
config.generic_bin_ops.set_id(ID($sshr));
config.generic_bin_ops.insert(ID($lt));
config.generic_bin_ops.insert(ID($le));
config.generic_bin_ops.insert(ID($eq));
config.generic_bin_ops.insert(ID($ne));
config.generic_bin_ops.insert(ID($eqx));
config.generic_bin_ops.insert(ID($nex));
config.generic_bin_ops.insert(ID($ge));
config.generic_bin_ops.insert(ID($gt));
config.generic_bin_ops.set_id(ID($lt));
config.generic_bin_ops.set_id(ID($le));
config.generic_bin_ops.set_id(ID($eq));
config.generic_bin_ops.set_id(ID($ne));
config.generic_bin_ops.set_id(ID($eqx));
config.generic_bin_ops.set_id(ID($nex));
config.generic_bin_ops.set_id(ID($ge));
config.generic_bin_ops.set_id(ID($gt));
config.generic_cbin_ops.insert(ID($add));
config.generic_cbin_ops.insert(ID($mul));
config.generic_cbin_ops.set_id(ID($add));
config.generic_cbin_ops.set_id(ID($mul));
config.generic_bin_ops.insert(ID($sub));
config.generic_bin_ops.insert(ID($div));
config.generic_bin_ops.insert(ID($mod));
config.generic_bin_ops.insert(ID($divfloor));
config.generic_bin_ops.insert(ID($modfloor));
// config.generic_bin_ops.insert(ID($pow));
config.generic_bin_ops.set_id(ID($sub));
config.generic_bin_ops.set_id(ID($div));
config.generic_bin_ops.set_id(ID($mod));
config.generic_bin_ops.set_id(ID($divfloor));
config.generic_bin_ops.set_id(ID($modfloor));
// config.generic_bin_ops.set_id(ID($pow));
config.generic_uni_ops.insert(ID($logic_not));
config.generic_cbin_ops.insert(ID($logic_and));
config.generic_cbin_ops.insert(ID($logic_or));
config.generic_uni_ops.set_id(ID($logic_not));
config.generic_cbin_ops.set_id(ID($logic_and));
config.generic_cbin_ops.set_id(ID($logic_or));
config.generic_other_ops.insert(ID($alu));
config.generic_other_ops.insert(ID($macc));
config.generic_other_ops.set_id(ID($alu));
config.generic_other_ops.set_id(ID($macc));
log_header(design, "Executing SHARE pass (SAT-based resource sharing).\n");

View file

@ -97,6 +97,7 @@ void proc_clean_switch(RTLIL::SwitchRule *sw, RTLIL::CaseRule *parent, bool &did
all_empty = false;
if (all_empty)
{
did_something = true;
for (auto cs : sw->cases)
delete cs;
sw->cases.clear();

View file

@ -31,6 +31,22 @@
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
static SatSolver *find_satsolver(const std::string &name)
{
for (auto solver = yosys_satsolver_list; solver != nullptr; solver = solver->next)
if (solver->name == name)
return solver;
return nullptr;
}
static std::string list_satsolvers()
{
std::string result;
for (auto solver = yosys_satsolver_list; solver != nullptr; solver = solver->next)
result += result.empty() ? solver->name : ", " + solver->name;
return result;
}
struct SatHelper
{
RTLIL::Design *design;
@ -60,8 +76,8 @@ struct SatHelper
int max_timestep, timeout;
bool gotTimeout;
SatHelper(RTLIL::Design *design, RTLIL::Module *module, bool enable_undef, bool set_def_formal) :
design(design), module(module), sigmap(module), ct(design), satgen(ez.get(), &sigmap)
SatHelper(RTLIL::Design *design, RTLIL::Module *module, SatSolver *solver, bool enable_undef, bool set_def_formal) :
design(design), module(module), sigmap(module), ct(design), ez(solver), satgen(ez.get(), &sigmap)
{
this->enable_undef = enable_undef;
satgen.model_undef = enable_undef;
@ -227,16 +243,12 @@ struct SatHelper
int import_cell_counter = 0;
for (auto cell : module->cells())
if (design->selected(module, cell)) {
// log("Import cell: %s\n", RTLIL::id2cstr(cell->name));
if (satgen.importCell(cell, timestep)) {
for (auto &p : cell->connections())
if (ct.cell_output(cell->type, p.first))
show_drivers.insert(sigmap(p.second), cell);
import_cell_counter++;
} else if (ignore_unknown_cells)
log_warning("Failed to import cell %s (type %s) to SAT database.\n", RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type));
else
log_error("Failed to import cell %s (type %s) to SAT database.\n", RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type));
} else report_missing_model(ignore_unknown_cells, cell);
}
log("Imported %d cells to SAT database.\n", import_cell_counter);
@ -441,7 +453,7 @@ struct SatHelper
log_assert(gotTimeout == false);
ez->setSolverTimeout(timeout);
bool success = ez->solve(modelExpressions, modelValues, assumptions);
if (ez->getSolverTimoutStatus())
if (ez->getSolverTimeoutStatus())
gotTimeout = true;
return success;
}
@ -451,7 +463,7 @@ struct SatHelper
log_assert(gotTimeout == false);
ez->setSolverTimeout(timeout);
bool success = ez->solve(modelExpressions, modelValues, a, b, c, d, e, f);
if (ez->getSolverTimoutStatus())
if (ez->getSolverTimeoutStatus())
gotTimeout = true;
return success;
}
@ -961,10 +973,10 @@ struct SatPass : public Pass {
log(" -show-regs, -show-public, -show-all\n");
log(" show all registers, show signals with 'public' names, show all signals\n");
log("\n");
log(" -ignore_div_by_zero\n");
log(" -ignore-div-by-zero\n");
log(" ignore all solutions that involve a division by zero\n");
log("\n");
log(" -ignore_unknown_cells\n");
log(" -ignore-unknown-cells\n");
log(" ignore all cells that can not be matched to a SAT model\n");
log("\n");
log("The following options can be used to set up a sequential problem:\n");
@ -1066,6 +1078,10 @@ struct SatPass : public Pass {
log(" -timeout <N>\n");
log(" Maximum number of seconds a single SAT instance may take.\n");
log("\n");
log(" -select-solver <name>\n");
log(" Select SAT solver implementation for this invocation.\n");
log(" If not given, uses scratchpad key 'sat.solver' if set, otherwise default.\n");
log("\n");
log(" -verify\n");
log(" Return an error and stop the synthesis script if the proof fails.\n");
log("\n");
@ -1097,8 +1113,14 @@ struct SatPass : public Pass {
log_header(design, "Executing SAT pass (solving SAT problems in the circuit).\n");
std::string solver_name = design->scratchpad_get_string("sat.solver", "");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
if (args[argidx] == "-select-solver" && argidx+1 < args.size()) {
solver_name = args[++argidx];
continue;
}
if (args[argidx] == "-all") {
loopcount = -1;
continue;
@ -1141,7 +1163,7 @@ struct SatPass : public Pass {
stepsize = max(1, atoi(args[++argidx].c_str()));
continue;
}
if (args[argidx] == "-ignore_div_by_zero") {
if (args[argidx] == "-ignore-div-by-zero" || args[argidx] == "-ignore_div_by_zero") {
ignore_div_by_zero = true;
continue;
}
@ -1316,7 +1338,7 @@ struct SatPass : public Pass {
show_all = true;
continue;
}
if (args[argidx] == "-ignore_unknown_cells") {
if (args[argidx] == "-ignore-unknown-cells" || args[argidx] == "-ignore_unknown_cells") {
ignore_unknown_cells = true;
continue;
}
@ -1336,6 +1358,14 @@ struct SatPass : public Pass {
}
extra_args(args, argidx, design);
SatSolver *solver = yosys_satsolver;
if (!solver_name.empty()) {
solver = find_satsolver(solver_name);
if (solver == nullptr)
log_cmd_error("Unknown SAT solver '%s'. Available solvers: %s\n",
solver_name, list_satsolvers());
}
RTLIL::Module *module = NULL;
for (auto mod : design->selected_modules()) {
if (module)
@ -1398,13 +1428,15 @@ struct SatPass : public Pass {
shows.push_back(wire->name.str());
}
log("Using SAT solver `%s`.\n", solver->name.c_str());
if (tempinduct)
{
if (loopcount > 0 || max_undef)
log_cmd_error("The options -max, -all, and -max_undef are not supported for temporal induction proofs!\n");
SatHelper basecase(design, module, enable_undef, set_def_formal);
SatHelper inductstep(design, module, enable_undef, set_def_formal);
SatHelper basecase(design, module, solver, enable_undef, set_def_formal);
SatHelper inductstep(design, module, solver, enable_undef, set_def_formal);
basecase.sets = sets;
basecase.set_assumes = set_assumes;
@ -1593,7 +1625,7 @@ struct SatPass : public Pass {
if (maxsteps > 0)
log_cmd_error("The options -maxsteps is only supported for temporal induction proofs!\n");
SatHelper sathelper(design, module, enable_undef, set_def_formal);
SatHelper sathelper(design, module, solver, enable_undef, set_def_formal);
sathelper.sets = sets;
sathelper.set_assumes = set_assumes;

View file

@ -20,6 +20,7 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
#include "kernel/mem.h"
#include "kernel/fstdata.h"
#include "kernel/ff.h"
@ -549,31 +550,27 @@ struct SimInstance
if (shared->debug)
log("[%s] eval %s (%s)\n", hiername(), log_id(cell), log_id(cell->type));
// Simple (A -> Y) and (A,B -> Y) cells
if (has_a && !has_c && !has_d && !has_s && has_y) {
set_state(sig_y, CellTypes::eval(cell, get_state(sig_a), get_state(sig_b)));
return;
}
bool err = false;
RTLIL::Const eval_state;
if (has_a && !has_c && !has_d && !has_s && has_y)
// Simple (A -> Y) and (A,B -> Y) cells
eval_state = CellTypes::eval(cell, get_state(sig_a), get_state(sig_b), &err);
else if (has_a && has_b && has_c && !has_d && !has_s && has_y)
// (A,B,C -> Y) cells
eval_state = CellTypes::eval(cell, get_state(sig_a), get_state(sig_b), get_state(sig_c), &err);
else if (has_a && !has_b && !has_c && !has_d && has_s && has_y)
// (A,S -> Y) cells
eval_state = CellTypes::eval(cell, get_state(sig_a), get_state(sig_s), &err);
else if (has_a && has_b && !has_c && !has_d && has_s && has_y)
// (A,B,S -> Y) cells
eval_state = CellTypes::eval(cell, get_state(sig_a), get_state(sig_b), get_state(sig_s), &err);
else
err = true;
// (A,B,C -> Y) cells
if (has_a && has_b && has_c && !has_d && !has_s && has_y) {
set_state(sig_y, CellTypes::eval(cell, get_state(sig_a), get_state(sig_b), get_state(sig_c)));
return;
}
// (A,S -> Y) cells
if (has_a && !has_b && !has_c && !has_d && has_s && has_y) {
set_state(sig_y, CellTypes::eval(cell, get_state(sig_a), get_state(sig_s)));
return;
}
// (A,B,S -> Y) cells
if (has_a && has_b && !has_c && !has_d && has_s && has_y) {
set_state(sig_y, CellTypes::eval(cell, get_state(sig_a), get_state(sig_b), get_state(sig_s)));
return;
}
log_warning("Unsupported evaluable cell type: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell));
if (err)
log_warning("Unsupported evaluable cell type: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell));
else
set_state(sig_y, eval_state);
return;
}

View file

@ -39,6 +39,7 @@ OBJS += passes/techmap/muxcover.o
OBJS += passes/techmap/aigmap.o
OBJS += passes/techmap/tribuf.o
OBJS += passes/techmap/lut2mux.o
OBJS += passes/techmap/lut2bmux.o
OBJS += passes/techmap/nlutmap.o
OBJS += passes/techmap/shregmap.o
OBJS += passes/techmap/deminout.o

View file

@ -43,7 +43,7 @@
#include "kernel/register.h"
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
#include "kernel/ffinit.h"
#include "kernel/ff.h"
#include "kernel/cost.h"
@ -143,6 +143,14 @@ struct AbcConfig
bool markgroups = false;
pool<std::string> enabled_gates;
bool cmos_cost = false;
bool is_yosys_abc() const {
#ifdef ABCEXTERNAL
return false;
#else
return exe_file == yosys_abc_executable;
#endif
}
};
struct AbcSigVal {
@ -155,7 +163,12 @@ struct AbcSigVal {
}
};
#if defined(__linux__) && !defined(YOSYS_DISABLE_SPAWN)
// REUSE_YOSYS_ABC_PROCESSES only works when ABC is built with ENABLE_READLINE.
#if defined(__linux__) && !defined(YOSYS_DISABLE_SPAWN) && defined(YOSYS_ENABLE_READLINE)
#define REUSE_YOSYS_ABC_PROCESSES
#endif
#ifdef REUSE_YOSYS_ABC_PROCESSES
struct AbcProcess
{
pid_t pid;
@ -188,10 +201,10 @@ struct AbcProcess
int status;
int ret = waitpid(pid, &status, 0);
if (ret != pid) {
log_error("waitpid(%d) failed", pid);
log_error("waitpid(%d) failed\n", pid);
}
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
log_error("ABC failed with status %X", status);
log_error("ABC failed with status %X\n", status);
}
if (from_child_pipe >= 0)
close(from_child_pipe);
@ -203,12 +216,12 @@ std::optional<AbcProcess> spawn_abc(const char* abc_exe, DeferredLogs &logs) {
// fork()s.
int to_child_pipe[2];
if (pipe2(to_child_pipe, O_CLOEXEC) != 0) {
logs.log_error("pipe failed");
logs.log_error("pipe failed\n");
return std::nullopt;
}
int from_child_pipe[2];
if (pipe2(from_child_pipe, O_CLOEXEC) != 0) {
logs.log_error("pipe failed");
logs.log_error("pipe failed\n");
return std::nullopt;
}
@ -221,39 +234,39 @@ std::optional<AbcProcess> spawn_abc(const char* abc_exe, DeferredLogs &logs) {
posix_spawn_file_actions_t file_actions;
if (posix_spawn_file_actions_init(&file_actions) != 0) {
logs.log_error("posix_spawn_file_actions_init failed");
logs.log_error("posix_spawn_file_actions_init failed\n");
return std::nullopt;
}
if (posix_spawn_file_actions_addclose(&file_actions, to_child_pipe[1]) != 0) {
logs.log_error("posix_spawn_file_actions_addclose failed");
logs.log_error("posix_spawn_file_actions_addclose failed\n");
return std::nullopt;
}
if (posix_spawn_file_actions_addclose(&file_actions, from_child_pipe[0]) != 0) {
logs.log_error("posix_spawn_file_actions_addclose failed");
logs.log_error("posix_spawn_file_actions_addclose failed\n");
return std::nullopt;
}
if (posix_spawn_file_actions_adddup2(&file_actions, to_child_pipe[0], STDIN_FILENO) != 0) {
logs.log_error("posix_spawn_file_actions_adddup2 failed");
logs.log_error("posix_spawn_file_actions_adddup2 failed\n");
return std::nullopt;
}
if (posix_spawn_file_actions_adddup2(&file_actions, from_child_pipe[1], STDOUT_FILENO) != 0) {
logs.log_error("posix_spawn_file_actions_adddup2 failed");
logs.log_error("posix_spawn_file_actions_adddup2 failed\n");
return std::nullopt;
}
if (posix_spawn_file_actions_addclose(&file_actions, to_child_pipe[0]) != 0) {
logs.log_error("posix_spawn_file_actions_addclose failed");
logs.log_error("posix_spawn_file_actions_addclose failed\n");
return std::nullopt;
}
if (posix_spawn_file_actions_addclose(&file_actions, from_child_pipe[1]) != 0) {
logs.log_error("posix_spawn_file_actions_addclose failed");
logs.log_error("posix_spawn_file_actions_addclose failed\n");
return std::nullopt;
}
char arg1[] = "-s";
char* argv[] = { strdup(abc_exe), arg1, nullptr };
if (0 != posix_spawnp(&result.pid, abc_exe, &file_actions, nullptr, argv, environ)) {
logs.log_error("posix_spawnp %s failed (errno=%s)", abc_exe, strerrorname_np(errno));
logs.log_error("posix_spawnp %s failed (errno=%s)\n", abc_exe, strerror(errno));
return std::nullopt;
}
free(argv[0]);
@ -272,7 +285,7 @@ using AbcSigMap = SigValMap<AbcSigVal>;
struct RunAbcState {
const AbcConfig &config;
std::string tempdir_name;
std::string per_run_tempdir_name;
std::vector<gate_t> signal_list;
bool did_run = false;
bool err = false;
@ -823,16 +836,23 @@ std::string fold_abc_cmd(std::string str)
return new_str;
}
std::string replace_tempdir(std::string text, std::string tempdir_name, bool show_tempdir)
std::string replace_tempdir(std::string text, std::string_view global_tempdir_name, std::string_view per_run_tempdir_name, bool show_tempdir)
{
if (show_tempdir)
return text;
while (1) {
size_t pos = text.find(tempdir_name);
size_t pos = text.find(global_tempdir_name);
if (pos == std::string::npos)
break;
text = text.substr(0, pos) + "<abc-temp-dir>" + text.substr(pos + GetSize(tempdir_name));
text = text.substr(0, pos) + "<abc-temp-dir>" + text.substr(pos + GetSize(global_tempdir_name));
}
while (1) {
size_t pos = text.find(per_run_tempdir_name);
if (pos == std::string::npos)
break;
text = text.substr(0, pos) + "<abc-temp-dir>" + text.substr(pos + GetSize(per_run_tempdir_name));
}
std::string selfdir_name = proc_self_dirname();
@ -854,11 +874,12 @@ struct abc_output_filter
bool got_cr;
int escape_seq_state;
std::string linebuf;
std::string tempdir_name;
std::string global_tempdir_name;
std::string per_run_tempdir_name;
bool show_tempdir;
abc_output_filter(RunAbcState& state, std::string tempdir_name, bool show_tempdir)
: state(state), tempdir_name(tempdir_name), show_tempdir(show_tempdir)
abc_output_filter(RunAbcState& state, std::string global_tempdir_name, std::string per_run_tempdir_name, bool show_tempdir)
: state(state), global_tempdir_name(global_tempdir_name), per_run_tempdir_name(per_run_tempdir_name), show_tempdir(show_tempdir)
{
got_cr = false;
escape_seq_state = 0;
@ -885,7 +906,7 @@ struct abc_output_filter
return;
}
if (ch == '\n') {
state.logs.log("ABC: %s\n", replace_tempdir(linebuf, tempdir_name, show_tempdir));
state.logs.log("ABC: %s\n", replace_tempdir(linebuf, global_tempdir_name, per_run_tempdir_name, show_tempdir));
got_cr = false, linebuf.clear();
return;
}
@ -986,15 +1007,15 @@ void AbcModuleState::prepare_module(RTLIL::Design *design, RTLIL::Module *module
const AbcConfig &config = run_abc.config;
if (config.cleanup)
run_abc.tempdir_name = get_base_tmpdir() + "/";
run_abc.per_run_tempdir_name = get_base_tmpdir() + "/";
else
run_abc.tempdir_name = "_tmp_";
run_abc.tempdir_name += proc_program_prefix() + "yosys-abc-XXXXXX";
run_abc.tempdir_name = make_temp_dir(run_abc.tempdir_name);
run_abc.per_run_tempdir_name = "_tmp_";
run_abc.per_run_tempdir_name += proc_program_prefix() + "yosys-abc-XXXXXX";
run_abc.per_run_tempdir_name = make_temp_dir(run_abc.per_run_tempdir_name);
log_header(design, "Extracting gate netlist of module `%s' to `%s/input.blif'..\n",
module->name.c_str(), replace_tempdir(run_abc.tempdir_name, run_abc.tempdir_name, config.show_tempdir).c_str());
module->name.c_str(), replace_tempdir(run_abc.per_run_tempdir_name, config.global_tempdir_name, run_abc.per_run_tempdir_name, config.show_tempdir).c_str());
std::string abc_script = stringf("read_blif \"%s/input.blif\"; ", run_abc.tempdir_name);
std::string abc_script = stringf("read_blif \"%s/input.blif\"; ", run_abc.per_run_tempdir_name);
if (!config.liberty_files.empty() || !config.genlib_files.empty()) {
std::string dont_use_args;
@ -1060,18 +1081,19 @@ void AbcModuleState::prepare_module(RTLIL::Design *design, RTLIL::Module *module
for (size_t pos = abc_script.find("{S}"); pos != std::string::npos; pos = abc_script.find("{S}", pos))
abc_script = abc_script.substr(0, pos) + config.lutin_shared + abc_script.substr(pos+3);
if (config.abc_dress)
abc_script += stringf("; dress \"%s/input.blif\"", run_abc.tempdir_name);
abc_script += stringf("; write_blif %s/output.blif", run_abc.tempdir_name);
abc_script += stringf("; dress \"%s/input.blif\"", run_abc.per_run_tempdir_name);
abc_script += stringf("; write_blif %s/output.blif", run_abc.per_run_tempdir_name);
abc_script = add_echos_to_abc_cmd(abc_script);
#if defined(__linux__) && !defined(YOSYS_DISABLE_SPAWN)
abc_script += "; echo; echo \"YOSYS_ABC_DONE\"\n";
#if defined(REUSE_YOSYS_ABC_PROCESSES)
if (config.is_yosys_abc())
abc_script += "; echo; echo \"YOSYS_ABC_DONE\"\n";
#endif
for (size_t i = 0; i+1 < abc_script.size(); i++)
if (abc_script[i] == ';' && abc_script[i+1] == ' ')
abc_script[i+1] = '\n';
std::string buffer = stringf("%s/abc.script", run_abc.tempdir_name);
std::string buffer = stringf("%s/abc.script", run_abc.per_run_tempdir_name);
FILE *f = fopen(buffer.c_str(), "wt");
if (f == nullptr)
log_error("Opening %s for writing failed: %s\n", buffer, strerror(errno));
@ -1127,42 +1149,86 @@ void AbcModuleState::prepare_module(RTLIL::Design *design, RTLIL::Module *module
handle_loops(assign_map, module);
}
#if defined(REUSE_YOSYS_ABC_PROCESSES)
static bool is_abc_prompt(const std::string &line, std::string &rest) {
size_t pos = 0;
while (true) {
// The prompt may not start at the start of the line, because
// ABC can output progress and maybe other data that isn't
// newline-terminated.
size_t start = line.find("abc ", pos);
if (start == std::string::npos)
return false;
pos = start + 4;
size_t digits = 0;
while (pos + digits < line.size() && line[pos + digits] >= '0' && line[pos + digits] <= '9')
++digits;
if (digits < 2)
return false;
if (line.substr(pos + digits, 2) == "> ") {
rest = line.substr(pos + digits + 2);
return true;
}
}
}
bool read_until_abc_done(abc_output_filter &filt, int fd, DeferredLogs &logs) {
std::string line;
char buf[1024];
bool seen_source_cmd = false;
bool seen_yosys_abc_done = false;
while (true) {
int ret = read(fd, buf, sizeof(buf) - 1);
if (ret < 0) {
logs.log_error("Failed to read from ABC, errno=%d", errno);
logs.log_error("Failed to read from ABC, errno=%d\n", errno);
return false;
}
if (ret == 0) {
logs.log_error("ABC exited prematurely");
logs.log_error("ABC exited prematurely\n");
return false;
}
char *start = buf;
char *end = buf + ret;
while (start < end) {
char *p = static_cast<char*>(memchr(start, '\n', end - start));
if (p == nullptr) {
break;
char *upto = p == nullptr ? end : p + 1;
line.append(start, upto - start);
start = upto;
std::string rest;
bool is_prompt = is_abc_prompt(line, rest);
if (is_prompt && seen_source_cmd) {
// This is the first prompt after we sourced the script.
// We are done here.
// We won't have seen a newline yet since ABC is waiting at the prompt.
if (!seen_yosys_abc_done)
logs.log_error("ABC script did not complete successfully\n");
return seen_yosys_abc_done;
}
line.append(start, p + 1 - start);
if (line.substr(0, 14) == "YOSYS_ABC_DONE") {
// Ignore any leftover output, there should only be a prompt perhaps
return true;
if (line.empty() || line[line.size() - 1] != '\n') {
// No newline yet, wait for more text
continue;
}
filt.next_line(line);
if (is_prompt && rest.substr(0, 7) == "source ")
seen_source_cmd = true;
if (line.substr(0, 14) == "YOSYS_ABC_DONE")
seen_yosys_abc_done = true;
line.clear();
start = p + 1;
}
line.append(start, end - start);
}
}
#endif
#if defined(REUSE_YOSYS_ABC_PROCESSES)
void RunAbcState::run(ConcurrentStack<AbcProcess> &process_pool)
#else
void RunAbcState::run(ConcurrentStack<AbcProcess> &)
#endif
{
std::string buffer = stringf("%s/input.blif", tempdir_name);
std::string buffer = stringf("%s/input.blif", per_run_tempdir_name);
FILE *f = fopen(buffer.c_str(), "wt");
if (f == nullptr) {
logs.log("Opening %s for writing failed: %s\n", buffer, strerror(errno));
@ -1285,15 +1351,19 @@ void RunAbcState::run(ConcurrentStack<AbcProcess> &process_pool)
logs.log("Extracted %d gates and %d wires to a netlist network with %d inputs and %d outputs.\n",
count_gates, GetSize(signal_list), count_input, count_output);
if (count_output > 0)
{
std::string tmp_script_name = stringf("%s/abc.script", tempdir_name);
logs.log("Running ABC script: %s\n", replace_tempdir(tmp_script_name, tempdir_name, config.show_tempdir));
if (count_output == 0) {
log("Don't call ABC as there is nothing to map.\n");
return;
}
int ret;
std::string tmp_script_name = stringf("%s/abc.script", per_run_tempdir_name);
do {
logs.log("Running ABC script: %s\n", replace_tempdir(tmp_script_name, config.global_tempdir_name, per_run_tempdir_name, config.show_tempdir));
errno = 0;
abc_output_filter filt(*this, tempdir_name, config.show_tempdir);
abc_output_filter filt(*this, config.global_tempdir_name, per_run_tempdir_name, config.show_tempdir);
#ifdef YOSYS_LINK_ABC
string temp_stdouterr_name = stringf("%s/stdouterr.txt", tempdir_name);
string temp_stdouterr_name = stringf("%s/stdouterr.txt", per_run_tempdir_name);
FILE *temp_stdouterr_w = fopen(temp_stdouterr_name.c_str(), "w");
if (temp_stdouterr_w == NULL)
log_error("ABC: cannot open a temporary file for output redirection");
@ -1318,7 +1388,7 @@ void RunAbcState::run(ConcurrentStack<AbcProcess> &process_pool)
abc_argv[2] = strdup("-f");
abc_argv[3] = strdup(tmp_script_name.c_str());
abc_argv[4] = 0;
int ret = abc::Abc_RealMain(4, abc_argv);
ret = abc::Abc_RealMain(4, abc_argv);
free(abc_argv[0]);
free(abc_argv[1]);
free(abc_argv[2]);
@ -1333,39 +1403,42 @@ void RunAbcState::run(ConcurrentStack<AbcProcess> &process_pool)
for (std::string line; std::getline(temp_stdouterr_r, line); )
filt.next_line(line + "\n");
temp_stdouterr_r.close();
#elif defined(__linux__) && !defined(YOSYS_DISABLE_SPAWN)
AbcProcess process;
if (std::optional<AbcProcess> process_opt = process_pool.try_pop_back()) {
process = std::move(process_opt.value());
} else if (std::optional<AbcProcess> process_opt = spawn_abc(config.exe_file.c_str(), logs)) {
process = std::move(process_opt.value());
} else {
return;
}
std::string cmd = stringf(
"empty\n"
"source %s\n", tmp_script_name);
int ret = write(process.to_child_pipe, cmd.c_str(), cmd.size());
if (ret != static_cast<int>(cmd.size())) {
logs.log_error("write failed");
return;
}
ret = read_until_abc_done(filt, process.from_child_pipe, logs) ? 0 : 1;
if (ret == 0) {
process_pool.push_back(std::move(process));
}
break;
#else
std::string cmd = stringf("\"%s\" -s -f %s/abc.script 2>&1", config.exe_file.c_str(), tempdir_name.c_str());
int ret = run_command(cmd, std::bind(&abc_output_filter::next_line, filt, std::placeholders::_1));
#endif
if (ret != 0) {
logs.log_error("ABC: execution of script \"%s\" failed: return code %d (errno=%d).\n", tmp_script_name, ret, errno);
return;
#if defined(REUSE_YOSYS_ABC_PROCESSES)
if (config.is_yosys_abc()) {
AbcProcess process;
if (std::optional<AbcProcess> process_opt = process_pool.try_pop_back()) {
process = std::move(process_opt.value());
} else if (std::optional<AbcProcess> process_opt = spawn_abc(config.exe_file.c_str(), logs)) {
process = std::move(process_opt.value());
} else {
return;
}
std::string cmd = stringf(
"empty\n"
"source %s\n", tmp_script_name);
ret = write(process.to_child_pipe, cmd.c_str(), cmd.size());
if (ret != static_cast<int>(cmd.size())) {
logs.log_error("write failed");
return;
}
ret = read_until_abc_done(filt, process.from_child_pipe, logs) ? 0 : 1;
if (ret == 0) {
process_pool.push_back(std::move(process));
}
break;
}
did_run = true;
#endif
std::string cmd = stringf("\"%s\" -s -f %s 2>&1", config.exe_file, tmp_script_name);
ret = run_command(cmd, std::bind(&abc_output_filter::next_line, filt, std::placeholders::_1));
#endif
} while (false);
if (ret != 0) {
logs.log_error("ABC: execution of script \"%s\" failed: return code %d (errno=%d).\n", tmp_script_name, ret, errno);
return;
}
log("Don't call ABC as there is nothing to map.\n");
did_run = true;
}
void emit_global_input_files(const AbcConfig &config)
@ -1437,7 +1510,7 @@ void AbcModuleState::extract(AbcSigMap &assign_map, RTLIL::Design *design, RTLIL
return;
}
std::string buffer = stringf("%s/%s", run_abc.tempdir_name, "output.blif");
std::string buffer = stringf("%s/%s", run_abc.per_run_tempdir_name, "output.blif");
std::ifstream ifs;
ifs.open(buffer);
if (ifs.fail())
@ -1724,7 +1797,7 @@ void AbcModuleState::finish()
if (run_abc.config.cleanup)
{
log("Removing temp directory.\n");
remove_directory(run_abc.tempdir_name);
remove_directory(run_abc.per_run_tempdir_name);
}
log_pop();
}
@ -2382,7 +2455,7 @@ struct AbcPass : public Pass {
continue;
}
CellTypes ct(design);
NewCellTypes ct(design);
std::vector<RTLIL::Cell*> all_cells = mod->selected_cells();
pool<RTLIL::Cell*> unassigned_cells(all_cells.begin(), all_cells.end());

View file

@ -219,9 +219,7 @@ struct Abc9Pass : public ScriptPass
for (argidx = 1; argidx < args.size(); argidx++) {
std::string arg = args[argidx];
if ((arg == "-exe" || arg == "-script" || arg == "-D" ||
/*arg == "-S" ||*/ arg == "-lut" || arg == "-luts" ||
/*arg == "-box" ||*/ arg == "-W" || arg == "-genlib" ||
arg == "-constr" || arg == "-dont_use" || arg == "-liberty") &&
arg == "-lut" || arg == "-luts" || arg == "-W") &&
argidx+1 < args.size()) {
if (arg == "-lut" || arg == "-luts")
lut_mode = true;

View file

@ -248,7 +248,7 @@ void abc9_module(RTLIL::Design *design, std::string script_file, std::string exe
}
abc9_script += stringf("; &ps -l; &write -n %s/output.aig", tempdir_name);
if (design->scratchpad_get_bool("abc9.verify")) {
if (design->scratchpad_get_bool("abc9.verify", true)) {
if (dff_mode)
abc9_script += "; &verify -s";
else

Some files were not shown because too many files have changed in this diff Show more