3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2026-06-27 11:08:48 +00:00

Merge branch 'YosysHQ:main' into main

This commit is contained in:
tangxifan 2026-05-14 16:59:08 -07:00 committed by GitHub
commit 54f8505045
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
485 changed files with 25197 additions and 5852 deletions

View file

@ -6,6 +6,8 @@ body:
attributes:
value: >
Learn more in our [Reporting bugs](https://yosyshq.readthedocs.io/projects/yosys/en/latest/yosys_internals/extending_yosys/contributing.html#reporting-bugs) docs section. 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 libgmock-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

@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
submodules: true
persist-credentials: false
@ -23,7 +23,7 @@ jobs:
get-build-deps: true
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
uses: github/codeql-action/init@v4
with:
languages: cpp
queries: security-extended,security-and-quality
@ -32,4 +32,4 @@ jobs:
run: make yosys -j6
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
uses: github/codeql-action/analyze@v4

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,20 +23,28 @@ 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
- uses: actions/checkout@v5
with:
submodules: true
persist-credentials: false
- run: sudo apt-get install libfl-dev
- name: Build
run: make vcxsrc YOSYS_COMPILER="Visual Studio" VCX_DIR_NAME=yosys-win32-vcxsrc-latest
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v7
with:
name: vcxsrc
path: yosys-win32-vcxsrc-latest.zip
@ -47,9 +53,9 @@ 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
- uses: actions/download-artifact@v8
with:
name: vcxsrc
path: .
@ -64,17 +70,17 @@ 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
- uses: actions/checkout@v5
with:
submodules: true
persist-credentials: false
- name: Build
run: |
WASI_SDK=wasi-sdk-27.0-x86_64-linux
WASI_SDK_URL=https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-27/wasi-sdk-27.0-x86_64-linux.tar.gz
WASI_SDK=wasi-sdk-33.0-x86_64-linux
WASI_SDK_URL=https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-33/wasi-sdk-33.0-x86_64-linux.tar.gz
if ! [ -d ${WASI_SDK} ]; then curl -L ${WASI_SDK_URL} | tar xzf -; fi
FLEX_VER=2.6.4
@ -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,14 +116,14 @@ 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:
os: [ubuntu-latest, macos-latest]
fail-fast: false
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
submodules: true
persist-credentials: false
@ -124,3 +131,21 @@ 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()
steps:
- name: Check results
run: |
echo "Needs results: ${{ join(needs.*.result, ',') }}"
if [[ "${{ join(needs.*.result, ',') }}" == *failure* ]] || \
[[ "${{ join(needs.*.result, ',') }}" == *cancelled* ]]; then
echo "Some jobs failed or were cancelled"
exit 1
fi
- run: echo "All good"

View file

@ -1,12 +1,18 @@
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/') }}
@ -17,16 +23,26 @@ jobs:
paths_ignore: '["**/README.md"]'
# don't cancel in case we're updating docs
cancel_others: 'false'
# only run on push *or* pull_request, not both
concurrent_skipping: ${{ env.docs_export && 'never' || 'same_content_newer'}}
# push filtering means we only want to skip duplicates for PRs
do_not_skip: '["workflow_dispatch", "merge_group", "push"]'
concurrent_skipping: 'same_content_newer'
- 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 +91,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,13 +1,18 @@
name: Create source archive with vendored dependencies
on: [push, workflow_dispatch]
on:
pull_request:
merge_group:
push:
branches: [ main ]
workflow_dispatch:
jobs:
vendor-sources:
runs-on: ubuntu-latest
steps:
- name: Checkout repository with submodules
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
submodules: 'recursive'
persist-credentials: false
@ -27,7 +32,7 @@ jobs:
gzip yosys-src-vendored.tar
- name: Store tarball artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: vendored-sources
path: yosys-src-vendored.tar.gz

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 }}
@ -53,7 +68,7 @@ jobs:
fail-fast: false
steps:
- name: Checkout Yosys
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
submodules: true
persist-credentials: false
@ -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,10 +96,10 @@ 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
uses: actions/upload-artifact@v7
with:
name: build-${{ matrix.os }}
path: build.tar
@ -102,7 +118,7 @@ jobs:
fail-fast: false
steps:
- name: Checkout Yosys
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
@ -114,7 +130,7 @@ jobs:
get-iverilog: true
- name: Download build artifact
uses: actions/download-artifact@v4
uses: actions/download-artifact@v8
with:
name: build-${{ matrix.os }}
@ -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() }}
@ -150,7 +166,7 @@ jobs:
os: [ubuntu-latest]
steps:
- name: Checkout Yosys
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
@ -160,7 +176,7 @@ jobs:
runs-on: ${{ matrix.os }}
- name: Download build artifact
uses: actions/download-artifact@v4
uses: actions/download-artifact@v8
with:
name: build-${{ matrix.os }}
@ -190,7 +206,7 @@ jobs:
fail-fast: false
steps:
- name: Checkout Yosys
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
@ -202,7 +218,7 @@ jobs:
get-docs-deps: true
- name: Download build artifact
uses: actions/download-artifact@v4
uses: actions/download-artifact@v8
with:
name: build-${{ matrix.os }}
@ -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,22 @@ 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()
steps:
- name: Check results
run: |
echo "Needs results: ${{ join(needs.*.result, ',') }}"
if [[ "${{ join(needs.*.result, ',') }}" == *failure* ]] || \
[[ "${{ join(needs.*.result, ',') }}" == *cancelled* ]]; then
echo "Some jobs failed or were cancelled"
exit 1
fi
- 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' }}
@ -53,7 +59,7 @@ jobs:
fail-fast: false
steps:
- name: Checkout Yosys
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
submodules: true
persist-credentials: false
@ -89,3 +95,19 @@ 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()
steps:
- name: Check results
run: |
echo "Needs results: ${{ join(needs.*.result, ',') }}"
if [[ "${{ join(needs.*.result, ',') }}" == *failure* ]] || \
[[ "${{ join(needs.*.result, ',') }}" == *cancelled* ]]; then
echo "Some jobs failed or were cancelled"
exit 1
fi
- 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
@ -37,7 +47,7 @@ jobs:
fail-fast: false
steps:
- name: Checkout Yosys
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
submodules: true
persist-credentials: false
@ -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,18 @@ jobs:
run: |
find tests/**/*.err -print -exec cat {} \;
- name: Run unit tests
shell: bash
test-sanitizers-result:
runs-on: ubuntu-latest
needs:
- run_san
if: always()
steps:
- name: Check results
run: |
make -j$procs unit-test ENABLE_LIBYOSYS=1
echo "Needs results: ${{ join(needs.*.result, ',') }}"
if [[ "${{ join(needs.*.result, ',') }}" == *failure* ]] || \
[[ "${{ join(needs.*.result, ',') }}" == *cancelled* ]]; then
echo "Some jobs failed or were cancelled"
exit 1
fi
- 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,26 +73,21 @@ jobs:
- name: Run Yosys tests
run: |
make -j$procs test
make -j$procs vanilla-test
- name: Run Verific specific Yosys tests
run: |
make -C tests/sva
cd tests/svtypes && bash run-test.sh
make -C tests/svtypes
- 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,20 @@ 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()
steps:
- name: Check results
run: |
echo "Needs results: ${{ join(needs.*.result, ',') }}"
if [[ "${{ join(needs.*.result, ',') }}" == *failure* ]] || \
[[ "${{ join(needs.*.result, ',') }}" == *cancelled* ]]; then
echo "Some jobs failed or were cancelled"
exit 1
fi
- 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

@ -49,7 +49,7 @@ jobs:
name: Build Wheels | ${{ matrix.os.name }} | ${{ matrix.os.archs }}
runs-on: ${{ matrix.os.runner }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
fetch-depth: 0
submodules: true
@ -108,13 +108,13 @@ jobs:
PATH="$PWD/bison/src:$PATH"
CIBW_BEFORE_BUILD: bash ./.github/workflows/wheels/cibw_before_build.sh
CIBW_TEST_COMMAND: python3 {project}/tests/pyosys/run_tests.py
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v7
with:
name: python-wheels-${{ matrix.os.runner }}
path: ./wheelhouse/*.whl
upload_wheels:
name: Upload Wheels
if: (github.repository == 'YosysHQ/Yosys') && (github.event_name == 'workflow_dispatch')
if: (github.repository == 'YosysHQ/yosys') && (github.event_name == 'workflow_dispatch')
runs-on: ubuntu-latest
# Specifying a GitHub environment is optional, but strongly encouraged
environment: pypi
@ -123,7 +123,7 @@ jobs:
id-token: write
needs: build_wheels
steps:
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v8
with:
path: "."
pattern: python-wheels-*

View file

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
set -e -x
# Build-time dependencies

View file

@ -9,6 +9,6 @@ brew "python3"
brew "uv"
brew "xdot"
brew "bash"
brew "llvm@20"
brew "llvm"
brew "lld"
brew "googletest"

View file

@ -2,9 +2,86 @@
List of major changes and improvements between releases
=======================================================
Yosys 0.60 .. Yosys 0.61-dev
Yosys 0.65 .. Yosys 0.66-dev
--------------------------
Yosys 0.64 .. Yosys 0.65
--------------------------
* New commands and options
- Added "arith_tree" pass to convert add/sub/macc chains
to carry-save adder trees.
- Removed "-force" option from "share" pass.
* Various
- read_verilog: support positional assignment patterns
for unpacked arrays.
Yosys 0.63 .. Yosys 0.64
--------------------------
* New commands and options
- Added "synth_analogdevices" pass to support synthesis
for Analog Devices FPGAs.
* Various
- Removed rarely-used options from ABC/ABC9.
- Removed "-S" option from "abc" pass.
- Removed "-fast" option from "abc9" and "abc9_exe".
- Calls to "abc -g AND -fast" to map logic to
AND-Inverter Graph form should be replaced with
"aigmap".
- The above change was made to SBY, so we recommend
updating it.
- Added hardware latch support for Gowin FPGAs.
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,15 @@
# 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.
Check out our [Contributing guidelines](https://yosys.readthedocs.io/en/latest/yosys_internals/extending_yosys/contributing.html) to learn the best ways to
# Using the issue tracker
+ get help
+ report bugs
+ contribute code
+ review code
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).
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.
If you're reading this file offline and don't have internet access, you can [read the contributing.rst file locally](docs/source/yosys_internals/extending_yosys/contributing.rst).

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"]

184
Makefile
View file

@ -104,7 +104,7 @@ VPATH := $(YOSYS_SRC)
UNITESTPATH := $(YOSYS_SRC)/tests/unit
export CXXSTD ?= c++17
CXXFLAGS := $(CXXFLAGS) -Wall -Wextra -ggdb -I. -I"$(YOSYS_SRC)" -MD -MP -D_YOSYS_ -fPIC -I$(PREFIX)/include
CXXFLAGS := $(CXXFLAGS) -Wall -Wextra -Werror=unused -ggdb -I. -I"$(YOSYS_SRC)" -MD -MP -D_YOSYS_ -fPIC -I$(PREFIX)/include
LIBS := $(LIBS) -lstdc++ -lm
PLUGIN_LINKFLAGS :=
PLUGIN_LIBS :=
@ -161,7 +161,19 @@ ifeq ($(OS), Haiku)
CXXFLAGS += -D_DEFAULT_SOURCE
endif
YOSYS_VER := 0.60+64
YOSYS_VER := 0.65
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
@ -282,18 +291,19 @@ ifeq ($(WASI_SDK),)
CXX = clang++
AR = llvm-ar
RANLIB = llvm-ranlib
WASIFLAGS := -target wasm32-wasi $(WASIFLAGS)
WASIFLAGS := -target wasm32-wasip1 $(WASIFLAGS)
else
CXX = $(WASI_SDK)/bin/clang++
AR = $(WASI_SDK)/bin/ar
RANLIB = $(WASI_SDK)/bin/ranlib
endif
CXXFLAGS := $(WASIFLAGS) -std=$(CXXSTD) $(OPT_LEVEL) -D_WASI_EMULATED_PROCESS_CLOCKS $(filter-out -fPIC,$(CXXFLAGS))
LINKFLAGS := $(WASIFLAGS) -Wl,-z,stack-size=1048576 $(filter-out -rdynamic,$(LINKFLAGS))
CXXFLAGS := $(WASIFLAGS) -std=$(CXXSTD) $(OPT_LEVEL) -D_WASI_EMULATED_PROCESS_CLOCKS -fwasm-exceptions -mllvm -wasm-use-legacy-eh=false $(filter-out -fPIC,$(CXXFLAGS))
LINKFLAGS := $(WASIFLAGS) -Wl,-z,stack-size=1048576 $(filter-out -rdynamic,$(LINKFLAGS)) -fwasm-exceptions -lunwind
LIBS := -lwasi-emulated-process-clocks $(filter-out -lrt,$(LIBS))
ABCMKARGS += AR="$(AR)" RANLIB="$(RANLIB)"
ABCMKARGS += ARCHFLAGS="$(WASIFLAGS) -D_WASI_EMULATED_PROCESS_CLOCKS -DABC_USE_STDINT_H -DABC_NO_DYNAMIC_LINKING -DABC_NO_RLIMIT"
ABCMKARGS += OPTFLAGS="-Os"
LTOFLAGS =
EXE = .wasm
DISABLE_SPAWN := 1
@ -604,6 +614,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 +649,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 +657,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 +695,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 +804,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
@ -887,107 +919,15 @@ else
ABCOPT=""
endif
# Tests that generate .mk with tests/gen-tests-makefile.sh
MK_TEST_DIRS =
MK_TEST_DIRS += tests/arch/anlogic
MK_TEST_DIRS += tests/arch/ecp5
MK_TEST_DIRS += tests/arch/efinix
MK_TEST_DIRS += tests/arch/gatemate
MK_TEST_DIRS += tests/arch/gowin
MK_TEST_DIRS += tests/arch/ice40
MK_TEST_DIRS += tests/arch/intel_alm
MK_TEST_DIRS += tests/arch/machxo2
MK_TEST_DIRS += tests/arch/microchip
MK_TEST_DIRS += tests/arch/nanoxplore
MK_TEST_DIRS += tests/arch/nexus
MK_TEST_DIRS += tests/arch/quicklogic/pp3
MK_TEST_DIRS += tests/arch/quicklogic/qlf_k6n10f
MK_TEST_DIRS += tests/arch/xilinx
MK_TEST_DIRS += tests/bugpoint
MK_TEST_DIRS += tests/opt
MK_TEST_DIRS += tests/sat
MK_TEST_DIRS += tests/sdc
MK_TEST_DIRS += tests/sim
MK_TEST_DIRS += tests/svtypes
MK_TEST_DIRS += tests/techmap
MK_TEST_DIRS += tests/various
MK_TEST_DIRS += tests/rtlil
ifeq ($(ENABLE_VERIFIC),1)
ifneq ($(YOSYS_NOVERIFIC),1)
MK_TEST_DIRS += tests/verific
endif
endif
MK_TEST_DIRS += tests/verilog
test: vanilla-test unit-test
# Tests that don't generate .mk
SH_TEST_DIRS =
SH_TEST_DIRS += tests/simple
SH_TEST_DIRS += tests/simple_abc9
SH_TEST_DIRS += tests/hana
SH_TEST_DIRS += tests/asicworld
# SH_TEST_DIRS += tests/realmath
SH_TEST_DIRS += tests/share
SH_TEST_DIRS += tests/opt_share
SH_TEST_DIRS += tests/fsm
SH_TEST_DIRS += tests/memlib
SH_TEST_DIRS += tests/bram
SH_TEST_DIRS += tests/svinterfaces
SH_TEST_DIRS += tests/xprop
SH_TEST_DIRS += tests/select
SH_TEST_DIRS += tests/peepopt
SH_TEST_DIRS += tests/proc
SH_TEST_DIRS += tests/blif
SH_TEST_DIRS += tests/arch
SH_TEST_DIRS += tests/rpc
SH_TEST_DIRS += tests/memfile
SH_TEST_DIRS += tests/fmt
SH_TEST_DIRS += tests/cxxrtl
SH_TEST_DIRS += tests/liberty
ifeq ($(ENABLE_FUNCTIONAL_TESTS),1)
SH_TEST_DIRS += tests/functional
endif
.PHONY: vanilla-test
# Tests that don't generate .mk and need special args
SH_ABC_TEST_DIRS =
SH_ABC_TEST_DIRS += tests/memories
SH_ABC_TEST_DIRS += tests/aiger
SH_ABC_TEST_DIRS += tests/alumacc
# seed-tests/ is a dummy string, not a directory
.PHONY: seed-tests
seed-tests: $(SH_TEST_DIRS:%=seed-tests/%)
.PHONY: seed-tests/%
seed-tests/%: %/run-test.sh $(TARGETS) $(EXTRA_TARGETS)
+cd $* && bash run-test.sh $(SEEDOPT)
+@echo "...passed tests in $*"
# abcopt-tests/ is a dummy string, not a directory
.PHONY: abcopt-tests
abcopt-tests: $(SH_ABC_TEST_DIRS:%=abcopt-tests/%)
abcopt-tests/%: %/run-test.sh $(TARGETS) $(EXTRA_TARGETS)
+cd $* && bash run-test.sh $(ABCOPT) $(SEEDOPT)
+@echo "...passed tests in $*"
# makefile-tests/ is a dummy string, not a directory
.PHONY: makefile-tests
makefile-tests: $(MK_TEST_DIRS:%=makefile-tests/%)
# this target actually emits .mk files
%.mk:
+cd $(dir $*) && bash run-test.sh
# this one spawns submake on each
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
@echo ""
@echo " Passed \"make test\"."
ifeq ($(ENABLE_VERIFIC),1)
ifeq ($(YOSYS_NOVERIFIC),1)
@echo " Ran tests without verific support due to YOSYS_NOVERIFIC=1."
endif
endif
@echo ""
vanilla-test: $(TARGETS) $(EXTRA_TARGETS)
@$(MAKE) -C tests vanilla-test \
$(if $(ENABLE_VERIFIC),ENABLE_VERIFIC=$(ENABLE_VERIFIC)) \
$(if $(YOSYS_NOVERIFIC),YOSYS_NOVERIFIC=$(YOSYS_NOVERIFIC)) \
SEEDOPT=$(SEEDOPT) ABCOPT=$(ABCOPT)
VALGRIND ?= valgrind --error-exitcode=1 --leak-check=full --show-reachable=yes --errors-for-leak-kinds=all
@ -1013,11 +953,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)
@ -1028,14 +968,14 @@ install-dev: $(PROGRAM_PREFIX)yosys-config share
install: $(TARGETS) $(EXTRA_TARGETS)
$(INSTALL_SUDO) mkdir -p $(DESTDIR)$(BINDIR)
$(INSTALL_SUDO) cp $(filter-out libyosys.so libyosys.a,$(TARGETS)) $(DESTDIR)$(BINDIR)
ifneq ($(filter $(PROGRAM_PREFIX)yosys,$(TARGETS)),)
if [ -n "$(STRIP)" ]; then $(INSTALL_SUDO) $(STRIP) -S $(DESTDIR)$(BINDIR)/$(PROGRAM_PREFIX)yosys; fi
ifneq ($(filter $(PROGRAM_PREFIX)yosys$(EXE),$(TARGETS)),)
if [ -n "$(STRIP)" ]; then $(INSTALL_SUDO) $(STRIP) -S $(DESTDIR)$(BINDIR)/$(PROGRAM_PREFIX)yosys$(EXE); fi
endif
ifneq ($(filter $(PROGRAM_PREFIX)yosys-abc,$(TARGETS)),)
if [ -n "$(STRIP)" ]; then $(INSTALL_SUDO) $(STRIP) $(DESTDIR)$(BINDIR)/$(PROGRAM_PREFIX)yosys-abc; fi
ifneq ($(filter $(PROGRAM_PREFIX)yosys-abc$(EXE),$(TARGETS)),)
if [ -n "$(STRIP)" ]; then $(INSTALL_SUDO) $(STRIP) $(DESTDIR)$(BINDIR)/$(PROGRAM_PREFIX)yosys-abc$(EXE); fi
endif
ifneq ($(filter $(PROGRAM_PREFIX)yosys-filterlib,$(TARGETS)),)
if [ -n "$(STRIP)" ]; then $(INSTALL_SUDO) $(STRIP) $(DESTDIR)$(BINDIR)/$(PROGRAM_PREFIX)yosys-filterlib; fi
ifneq ($(filter $(PROGRAM_PREFIX)yosys-filterlib$(EXE),$(TARGETS)),)
if [ -n "$(STRIP)" ]; then $(INSTALL_SUDO) $(STRIP) $(DESTDIR)$(BINDIR)/$(PROGRAM_PREFIX)yosys-filterlib$(EXE); fi
endif
$(INSTALL_SUDO) mkdir -p $(DESTDIR)$(DATDIR)
$(INSTALL_SUDO) cp -r share/. $(DESTDIR)$(DATDIR)/.
@ -1153,16 +1093,8 @@ clean: clean-py clean-unit-test
rm -f $(OBJS) $(GENFILES) $(TARGETS) $(EXTRA_TARGETS) $(EXTRA_OBJS)
rm -f kernel/version_*.o kernel/version_*.cc
rm -f libs/*/*.d frontends/*/*.d passes/*/*.d backends/*/*.d kernel/*.d techlibs/*/*.d
rm -rf tests/asicworld/*.out tests/asicworld/*.log
rm -rf tests/hana/*.out tests/hana/*.log
rm -rf tests/simple/*.out tests/simple/*.log
rm -rf tests/memories/*.out tests/memories/*.log tests/memories/*.dmp
rm -rf tests/sat/*.log tests/techmap/*.log tests/various/*.log
rm -rf tests/bram/temp tests/fsm/temp tests/realmath/temp tests/share/temp tests/smv/temp tests/various/temp
rm -rf vloghtb/Makefile vloghtb/refdat vloghtb/rtl vloghtb/scripts vloghtb/spec vloghtb/check_yosys vloghtb/vloghammer_tb.tar.bz2 vloghtb/temp vloghtb/log_test_*
rm -f tests/svinterfaces/*.log_stdout tests/svinterfaces/*.log_stderr tests/svinterfaces/dut_result.txt tests/svinterfaces/reference_result.txt tests/svinterfaces/a.out tests/svinterfaces/*_syn.v tests/svinterfaces/*.diff
rm -f tests/tools/cmp_tbdata
rm -f $(addsuffix /run-test.mk,$(MK_TEST_DIRS))
-$(MAKE) -C $(YOSYS_SRC)/tests clean
-$(MAKE) -C $(YOSYS_SRC)/docs clean
rm -rf docs/util/__pycache__
rm -f libyosys.so

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 9182a8048d0bc86b39a6cb6b0488a7e1d10b2607
Subproject commit 5d51a5e420f5de493d07bf61109a977248c86ffb

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

@ -21,9 +21,12 @@
// - gracefully handling inout ports (an error message probably)
// - undriven wires
// - zero-width operands
// - decide how to unify this with cellaigs
// - break up Index into something smaller
// - (C++20) remove snprintf-into-std::ostream weirdness
#include "kernel/register.h"
#include "kernel/celltypes.h"
#include "kernel/newcelltypes.h"
#include "kernel/rtlil.h"
USING_YOSYS_NAMESPACE
@ -45,8 +48,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 +109,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 +121,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
@ -163,6 +180,11 @@ struct Index {
if (!strashing) {
return (static_cast<Writer*>(this))->emit_gate(a, b);
} else {
// AigMaker::node2index
// In XAIGER, the ordering of inputs is used to distinguish between AND
// and XOR gates. AND gates have their first input literal be larger
// than their second, and vice-versa for XORs.
if (a < b) std::swap(a, b);
auto pair = std::make_pair(a, b);
@ -183,7 +205,9 @@ struct Index {
Lit OR(Lit a, Lit b)
{
return NOT(AND(NOT(a), NOT(b)));
Lit not_a = NOT(a);
Lit not_b = NOT(b);
return NOT(AND(not_a, not_b));
}
Lit MUX(Lit a, Lit b, Lit s)
@ -197,17 +221,24 @@ struct Index {
return b;
}
return OR(AND(a, NOT(s)), AND(b, s));
Lit not_s = NOT(s);
Lit a_active = AND(a, not_s);
Lit b_active = AND(b, s);
return OR(a_active, b_active);
}
Lit XOR(Lit a, Lit b)
{
return OR(AND(a, NOT(b)), AND(NOT(a), b));
Lit not_a = NOT(a);
Lit not_b = NOT(b);
Lit a_and_not_b = AND(a, not_b);
Lit not_a_and_b = AND(not_a, b);
return OR(a_and_not_b, not_a_and_b);
}
Lit XNOR(Lit a, Lit b)
{
return NOT(OR(AND(a, NOT(b)), AND(NOT(a), b)));
return NOT(XOR(a, b));
}
Lit CARRY(Lit a, Lit b, Lit c)
@ -219,7 +250,10 @@ struct Index {
return AND(a, b);
}
}
return OR(AND(a, b), AND(c, OR(a, b)));
Lit a_or_b = OR(a, b);
Lit a_or_b_and_c = AND(c, a_or_b);
Lit a_and_b = AND(a, b);
return OR(a_and_b, a_or_b_and_c);
}
Lit REDUCE(std::vector<Lit> lits, bool op_xor=false)
@ -269,7 +303,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
@ -367,7 +401,7 @@ struct Index {
} else if (cell->type.in(ID($xor), ID($_XOR_))) {
return XOR(a, b);
} else if (cell->type.in(ID($xnor), ID($_XNOR_))) {
return NOT(XOR(a, b));
return XNOR(a, b);
} else if (cell->type.in(ID($_ANDNOT_))) {
return AND(a, NOT(b));
} else if (cell->type.in(ID($_ORNOT_))) {
@ -387,7 +421,9 @@ struct Index {
if (oport == ID::Y) {
return XOR(ab, c);
} else /* oport == ID::X */ {
return OR(AND(a, b), AND(c, ab));
Lit a_and_b = AND(a, b);
Lit c_and_ab = AND(c, ab);
return OR(a_and_b, c_and_ab);
}
} else if (cell->type.in(ID($_AOI3_), ID($_OAI3_), ID($_AOI4_), ID($_OAI4_))) {
Lit c, d;
@ -398,10 +434,15 @@ struct Index {
else
d = cell->type == ID($_AOI3_) ? CTRUE : CFALSE;
if (/* aoi */ cell->type.in(ID($_AOI3_), ID($_AOI4_)))
return NOT(OR(AND(a, b), AND(c, d)));
else
return NOT(AND(OR(a, b), OR(c, d)));
if (/* aoi */ cell->type.in(ID($_AOI3_), ID($_AOI4_))) {
Lit a_and_b = AND(a, b);
Lit c_and_d = AND(c, d);
return NOT(OR(a_and_b, c_and_d));
} else {
Lit a_or_b = OR(a, b);
Lit c_or_d = OR(c, d);
return NOT(AND(a_or_b, c_or_d));
}
} else {
log_abort();
}
@ -422,7 +463,11 @@ struct Index {
sels.push_back(NOT(s));
}
return OR(AND(REDUCE(sels), a), NOT(REDUCE(bar)));
Lit reduce_sels = REDUCE(sels);
Lit reduce_sels_and_a = AND(reduce_sels, a);
Lit reduce_bar = NOT(REDUCE(bar));
return OR(reduce_sels_and_a, reduce_bar);
} else if (cell->type == ID($bmux)) {
SigSpec aport = cell->getPort(ID::A);
SigSpec sport = cell->getPort(ID::S);
@ -492,7 +537,7 @@ struct Index {
Design *design = index.design;
auto &minfo = leaf_minfo(index);
if (!minfo.suboffsets.count(cell))
log_error("Reached unsupport cell %s (%s in %s)\n", log_id(cell->type), log_id(cell), log_id(cell->module));
log_error("Reached unsupported cell %s (%s in %s)\n", log_id(cell->type), log_id(cell), log_id(cell->module));
Module *def = design->module(cell->type);
log_assert(def);
levels.push_back(Level(index.modules.at(def), cell));
@ -511,13 +556,13 @@ struct Index {
{
std::string ret;
bool first = true;
for (auto pair : levels) {
for (auto [minfo, cell] : levels) {
if (!first)
ret += ".";
if (!pair.second)
ret += RTLIL::unescape_id(pair.first.module->name);
if (!cell)
ret += RTLIL::unescape_id(minfo.module->name);
else
ret += RTLIL::unescape_id(pair.second->name);
ret += RTLIL::unescape_id(cell->name);
first = false;
}
return ret;
@ -526,8 +571,8 @@ struct Index {
int hash() const
{
int hash = 0;
for (auto pair : levels)
hash += (uintptr_t) pair.second;
for (auto [_, cell] : levels)
hash += (uintptr_t) cell;
return hash;
}
@ -536,9 +581,12 @@ struct Index {
if (levels.size() != other.levels.size())
return false;
for (int i = 0; i < levels.size(); i++)
if (levels[i].second != other.levels[i].second)
for (int i = 0; i < levels.size(); i++) {
auto* cell = levels[i].second;
auto* other_cell = other.levels[i].second;
if (cell != other_cell)
return false;
}
return true;
}
@ -579,7 +627,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);
@ -701,6 +749,9 @@ struct AigerWriter : Index<AigerWriter, unsigned int, 0, 1> {
nands++;
lit_counter += 2;
// In XAIGER, the ordering of inputs is used to distinguish between AND
// and XOR gates. AND gates have their first input literal be larger
// than their second, and vice-versa for XORs.
if (a < b) std::swap(a, b);
encode(out - a);
encode(a - b);
@ -717,7 +768,7 @@ struct AigerWriter : Index<AigerWriter, unsigned int, 0, 1> {
log_assert(lit_counter == (Lit) (ninputs + nlatches + nands) * 2 + 2);
char buf[128];
snprintf(buf, sizeof(buf) - 1, "aig %08d %08d %08d %08d %08d\n",
snprintf(buf, sizeof(buf), "aig %08d %08d %08d %08d %08d\n",
ninputs + nlatches + nands, ninputs, nlatches, noutputs, nands);
f->write(buf, strlen(buf));
}
@ -730,15 +781,16 @@ struct AigerWriter : Index<AigerWriter, unsigned int, 0, 1> {
// populate inputs
std::vector<SigBit> inputs;
for (auto id : top->ports) {
Wire *w = top->wire(id);
log_assert(w);
if (w->port_input && !w->port_output)
for (int i = 0; i < w->width; i++) {
pi_literal(SigBit(w, i)) = lit_counter;
inputs.push_back(SigBit(w, i));
lit_counter += 2;
ninputs++;
}
Wire *w = top->wire(id);
log_assert(w);
if (w->port_input && !w->port_output)
for (int i = 0; i < w->width; i++) {
auto bit = SigBit(w, i);
pi_literal(bit) = lit_counter;
inputs.push_back(bit);
lit_counter += 2;
ninputs++;
}
}
this->f = f;
@ -746,35 +798,38 @@ struct AigerWriter : Index<AigerWriter, unsigned int, 0, 1> {
write_header();
// insert padding where output literals will go (once known)
for (auto id : top->ports) {
Wire *w = top->wire(id);
log_assert(w);
if (w->port_output) {
for (auto bit : SigSpec(w)) {
(void) bit;
char buf[16];
snprintf(buf, sizeof(buf) - 1, "%08d\n", 0);
f->write(buf, strlen(buf));
noutputs++;
Wire *w = top->wire(id);
log_assert(w);
if (w->port_output) {
for (auto bit : SigSpec(w)) {
(void) bit;
char buf[16];
snprintf(buf, sizeof(buf), "%08d\n", 0);
f->write(buf, strlen(buf));
noutputs++;
}
}
}
}
auto data_start = f->tellp();
// now the guts
std::vector<std::pair<SigBit, int>> outputs;
for (auto w : top->wires())
if (w->port_output) {
for (auto bit : SigSpec(w))
outputs.push_back({bit, eval_po(bit)});
}
if (w->port_output) {
for (auto bit : SigSpec(w))
// Each call to eval_po eventually reaches emit_gate and
// encode which writes to f.
outputs.push_back({bit, eval_po(bit)});
}
auto data_end = f->tellp();
// revisit header and the list of outputs
f->seekp(file_start);
write_header();
for (auto pair : outputs) {
for (auto [_, po] : outputs) {
char buf[16];
snprintf(buf, sizeof(buf) - 1, "%08d\n", pair.second);
snprintf(buf, sizeof(buf), "%08d\n", po);
f->write(buf, strlen(buf));
}
// double check we arrived at the same offset for the
@ -783,12 +838,13 @@ struct AigerWriter : Index<AigerWriter, unsigned int, 0, 1> {
f->seekp(data_end);
int i = 0;
for (auto pair : outputs) {
if (SigSpec(pair.first).is_wire()) {
for (auto [bit, _] : outputs) {
if (SigSpec(bit).is_wire()) {
// primary output symbol
char buf[32];
snprintf(buf, sizeof(buf) - 1, "o%d ", i);
snprintf(buf, sizeof(buf), "o%d ", i);
f->write(buf, strlen(buf));
std::string name = RTLIL::unescape_id(pair.first.wire->name);
std::string name = RTLIL::unescape_id(bit.wire->name);
f->write(name.data(), name.size());
f->put('\n');
}
@ -797,8 +853,9 @@ struct AigerWriter : Index<AigerWriter, unsigned int, 0, 1> {
i = 0;
for (auto bit : inputs) {
if (SigSpec(bit).is_wire()) {
// primary input symbol
char buf[32];
snprintf(buf, sizeof(buf) - 1, "i%d ", i);
snprintf(buf, sizeof(buf), "i%d ", i);
f->write(buf, strlen(buf));
std::string name = RTLIL::unescape_id(bit.wire->name);
f->write(name.data(), name.size());
@ -871,33 +928,34 @@ struct XAigerAnalysis : Index<XAigerAnalysis, int, 0, 0> {
Wire *w = top->wire(id);
log_assert(w);
if (w->port_input && !w->port_output)
for (int i = 0; i < w->width; i++)
pi_literal(SigBit(w, i)) = 0;
for (int i = 0; i < w->width; i++)
pi_literal(SigBit(w, i)) = 0;
}
HierCursor cursor;
for (auto box : top_minfo->found_blackboxes) {
Module *def = design->module(box->type);
if (!(def && def->has_attribute(ID::abc9_box_id)))
for (auto &conn : box->connections_)
if (box->port_dir(conn.first) != RTLIL::PD_INPUT)
for (auto bit : conn.second)
pi_literal(bit, &cursor) = 0;
for (auto &conn : box->connections_)
if (box->port_dir(conn.first) != RTLIL::PD_INPUT)
for (auto bit : conn.second)
pi_literal(bit, &cursor) = 0;
}
for (auto w : top->wires())
if (w->port_output) {
for (auto bit : SigSpec(w))
(void) eval_po(bit);
for (auto w : top->wires()) {
if (w->port_output) {
for (auto bit : SigSpec(w))
(void) eval_po(bit);
}
}
for (auto box : top_minfo->found_blackboxes) {
Module *def = design->module(box->type);
if (!(def && def->has_attribute(ID::abc9_box_id)))
for (auto &conn : box->connections_)
if (box->port_dir(conn.first) == RTLIL::PD_INPUT)
for (auto bit : conn.second)
(void) eval_po(bit);
for (auto &conn : box->connections_)
if (box->port_dir(conn.first) == RTLIL::PD_INPUT)
for (auto bit : conn.second)
(void) eval_po(bit);
}
}
};
@ -916,15 +974,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;
@ -1202,29 +1260,29 @@ struct XAigerWriter : AigerWriter {
reset_counters();
for (auto w : top->wires())
if (w->port_input && !w->port_output)
for (int i = 0; i < w->width; i++)
ensure_pi(SigBit(w, i));
if (w->port_input && !w->port_output)
for (int i = 0; i < w->width; i++)
ensure_pi(SigBit(w, i));
int proper_po_num = 0;
for (auto w : top->wires())
if (w->port_output)
proper_po_num += w->width;
if (w->port_output)
proper_po_num += w->width;
prep_boxes(proper_po_num);
for (auto w : top->wires())
if (w->port_output)
for (int i = 0; i < w->width; i++) {
// When a module output is directly driven by an opaque box, we
// don't emit it to the mapping file to aid re-integration, but we
// do emit a proper PO.
if (map_file.is_open() && !driven_by_opaque_box.count(SigBit(w, i))) {
map_file << "po " << proper_pos_counter << " " << i
<< " " << w->name.c_str() << "\n";
}
proper_pos_counter++;
pos.push_back(std::make_pair(SigBit(w, i), HierCursor{}));
}
if (w->port_output)
for (int i = 0; i < w->width; i++) {
// When a module output is directly driven by an opaque box, we
// don't emit it to the mapping file to aid re-integration, but we
// do emit a proper PO.
if (map_file.is_open() && !driven_by_opaque_box.count(SigBit(w, i))) {
map_file << "po " << proper_pos_counter << " " << i
<< " " << w->name.c_str() << "\n";
}
proper_pos_counter++;
pos.push_back(std::make_pair(SigBit(w, i), HierCursor{}));
}
this->f = f;
// start with the header
@ -1234,7 +1292,7 @@ struct XAigerWriter : AigerWriter {
// insert padding where output literals will go (once known)
for (auto _ : pos) {
char buf[16];
snprintf(buf, sizeof(buf) - 1, "%08d\n", 0);
snprintf(buf, sizeof(buf), "%08d\n", 0);
f->write(buf, strlen(buf));
}
auto data_start = f->tellp();
@ -1251,35 +1309,36 @@ struct XAigerWriter : AigerWriter {
write_header();
for (auto lit : outlits) {
char buf[16];
snprintf(buf, sizeof(buf) - 1, "%08d\n", lit);
snprintf(buf, sizeof(buf), "%08d\n", lit);
f->write(buf, strlen(buf));
}
// double check we arrived at the same offset for the
// main data section
log_assert(data_start == f->tellp());
// extensions
// XAIGER extensions
f->seekp(0, std::ios::end);
f->put('c');
f->put('c'); // 'c': comment (marks beginning of extensions)
// insert empty 'r' and 's' sections (abc crashes if we provide 'a' without those)
f->put('r');
write_be32(*f, 4);
write_be32(*f, 0);
f->put('s');
write_be32(*f, 4);
write_be32(*f, 0);
f->put('r'); // 'r': register classes
write_be32(*f, 4); // length in bytes
write_be32(*f, 0); // no register classes
f->put('h');
f->put('s'); // 's': register initial values
write_be32(*f, 4); // length in bytes
write_be32(*f, 0); // no register initial values
f->put('h'); // 'h': hierarchy information
// TODO: get rid of std::string copy
std::string h_buffer_str = h_buffer.str();
write_be32(*f, h_buffer_str.size());
f->write(h_buffer_str.data(), h_buffer_str.size());
write_be32(*f, h_buffer_str.size()); // length in bytes
f->write(h_buffer_str.data(), h_buffer_str.size()); // data
#if 1
f->put('a');
write_be32(*f, 0); // size to be filled later
f->put('a'); // 'a': additional AIG (used for holes)
write_be32(*f, 0); // length in bytes (to be filled later)
auto holes_aiger_start = f->tellp();
{
AigerWriter holes_writer;
@ -1291,7 +1350,7 @@ struct XAigerWriter : AigerWriter {
auto holes_aiger_size = f->tellp() - holes_aiger_start;
f->seekp(holes_aiger_start, std::ios::beg);
f->seekp(-4, std::ios::cur);
write_be32(*f, holes_aiger_size);
write_be32(*f, holes_aiger_size); // length in bytes
#endif
f->seekp(0, std::ios::end);
@ -1331,41 +1390,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 +1491,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

@ -2776,7 +2776,8 @@ struct CxxrtlWorker {
{
RTLIL::Module *top_module = nullptr;
std::vector<RTLIL::Module*> modules;
TopoSort<RTLIL::Module*> topo_design;
using Order = IdString::compare_ptr_by_name<RTLIL::NamedObject>;
TopoSort<RTLIL::Module*, Order> topo_design;
for (auto module : design->modules()) {
if (!design->selected_module(module))
continue;

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

@ -32,8 +32,9 @@ FORCE:
TEX_FILES := $(shell find . -name *.tex)
all_tex: $(TEX_FILES:.tex=.pdf) $(TEX_FILES:.tex=.svg)
# limit output size to US letter (same as latexpdf output) to avoid oversize error
%.pdf: %.dot
$(FAKETIME) dot -Tpdf -o $@ $<
$(FAKETIME) dot -Tpdf -Gsize="8.5,11" -o $@ $<
%.pdf: %.tex
cd $(@D) && $(FAKETIME) pdflatex $(<F) --interaction=nonstopmode

View file

@ -154,6 +154,10 @@ to the following Verilog code template, where ``RST_EDGE`` is ``posedge`` if
``RST_LVL`` if ``1``, ``negedge`` otherwise, and ``SET_EDGE`` is ``posedge`` if
``SET_LVL`` if ``1``, ``negedge`` otherwise.
When both set and reset are active, the state and output is undefined. The Verilog
code model does not correspond to this due to limitations
of synthesizable Verilog.
.. code-block:: verilog
:force:
@ -187,6 +191,10 @@ types relate to the following Verilog code template, where ``RST_EDGE`` is
``posedge`` if ``RST_LVL`` if ``1``, ``negedge`` otherwise, and ``SET_EDGE`` is
``posedge`` if ``SET_LVL`` if ``1``, ``negedge`` otherwise.
When both set and reset are active, the state and output is undefined. The Verilog
code model does not correspond to this due to limitations
of synthesizable Verilog.
.. code-block:: verilog
:force:

View file

@ -78,6 +78,7 @@ D-type flip-flops with asynchronous set and reset are represented by `$dffsr`
cells. As the `$dff` cells they have ``CLK``, ``D`` and ``Q`` ports. In addition
they also have multi-bit ``SET`` and ``CLR`` input ports and the corresponding
polarity parameters, like `$sr` cells.
When both set and reset are active, the state and output is undefined.
D-type flip-flops with enable are represented by `$dffe`, `$adffe`, `$aldffe`,
`$dffsre`, `$sdffe`, and `$sdffce` cells, which are enhanced variants of `$dff`,

View file

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

View file

@ -6,6 +6,7 @@ begin:
proc
flatten:
check
flatten
tribuf -logic
deminout

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.65"
# select HTML theme
html_theme = 'furo-ys'

View file

@ -291,7 +291,10 @@ In `synth_ice40` we get these:
:name: synth_flatten
:caption: ``flatten`` section
First off is `flatten`. Flattening the design like this can allow for
We start by runnning `check`. This doesn't affect the design, but it can
identify a few obvious problems which will cause errors later. Calling it here
lets us fail faster rather than wasting time on something we know is impossible.
Next up is `flatten`. Flattening the design like this can allow for
optimizations between modules which would otherwise be missed. Let's run
:yoscrypt:`flatten;;` on our design.
@ -363,17 +366,14 @@ In the iCE40 flow, we start with the following commands:
:caption: ``coarse`` section (part 1)
:name: synth_coarse1
We've already come across `opt_expr`, and `opt_clean` is the same as `clean` but
with more verbose output. The `check` pass identifies a few obvious problems
which will cause errors later. Calling it here lets us fail faster rather than
wasting time on something we know is impossible.
Next up is :yoscrypt:`opt -nodffe -nosdff` performing a set of simple
optimizations on the design. This command also ensures that only a specific
subset of FF types are included, in preparation for the next command:
:cmd:title:`fsm`. Both `opt` and `fsm` are macro commands which are explored in
more detail in :doc:`/using_yosys/synthesis/opt` and
:doc:`/using_yosys/synthesis/fsm` respectively.
We've already come across `opt_expr` and `check`, and `opt_clean` is the same as
`clean` but with more verbose output. Next up is :yoscrypt:`opt -nodffe
-nosdff` performing a set of simple optimizations on the design. This command
also ensures that only a specific subset of FF types are included, in
preparation for the next command: :cmd:title:`fsm`. Both `opt` and `fsm` are
macro commands which are explored in more detail in
:doc:`/using_yosys/synthesis/opt` and :doc:`/using_yosys/synthesis/fsm`
respectively.
Up until now, the data path for ``rdata`` has remained the same since
:ref:`rdata_flat`. However the next call to `opt` does cause a change.

View file

@ -293,7 +293,7 @@ is still valid.
:caption: Example test.sh for C-Reduce
:name: egtest
#!/bin/bash
#!/usr/bin/env bash
verilator --lint-only test.v &&/
yosys -p 'logger -expect error "unsupported" 1; read_verilog test.v'
@ -333,7 +333,7 @@ an input argument: ``sv-bugpoint outDir/ test.sh test.v``
.. code-block:: bash
:caption: Example test.sh for sv-bugpoint
#!/bin/bash
#!/usr/bin/env bash
verilator --lint-only $1 &&/
yosys -p "logger -expect error \"unsupported\" 1; read_verilog $1"

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,18 @@
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`_
We fix well-reported bugs the fastest. A good bug report is an issue on the `issue tracker`_
and includes the following information:
.. _bug report template: https://github.com/YosysHQ/yosys/issues/new?template=bug_report.yml
.. _`issue tracker`: https://github.com/YosysHQ/yosys/issues
- 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 +25,20 @@ 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.
Make sure that your report input is free of any problems as reported by the
`check` command.
One way to minimize a design is to use the `bugpoint` command.
You can learn more in the :doc:`how-to guide for bugpoint </using_yosys/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 +57,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 +72,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 +88,57 @@ 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
.. _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 +155,257 @@ 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`_.
We have open `developer 'jour fixe' (Dev JF) meetings`_
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.
.. _`developer 'jour fixe' (Dev JF) meetings`: https://docs.google.com/document/d/1SapA6QAsJcsgwsdKJDgnGR2mr97pJjV4eeXg_TVJhRU/edit?usp=sharing
.. _`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
Pull requests (PRs)
~~~~~~~~~~~~~~~~~~~
If you are working on something to add to Yosys, or fix something that isn't
working quite right,
make a `pull request (PR)`_.
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.
We use `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.
.. _`pull request (PR)`: https://github.com/YosysHQ/yosys/pulls
.. _`labels`: https://github.com/YosysHQ/yosys/labels
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?
~~~~~~~~~~~~~~~~~~~
YosysHQ GmbH is a company with a mandate to make decisions for the good
of YosysHQ open source software. It was co-founded by Claire Xenia Wolf,
the original author of Yosys.
Within it, we allocate reviews based on expertise with the topic at hand
as well as member time constraints. However, decisions including reviews
are also contributed by people external to the company.
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 implicit expectations of stability we aim to
respect within reason.
.. 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.
We test on various platforms and compilers. Sanitizer builds are only
tested on the main branch.

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

@ -14,7 +14,7 @@ in the PATH. E.g. extract the release to /usr/local/libexec/super_prove
and then create a /usr/local/bin/super_prove file with the following
contents (and "chmod +x" that file):
#!/bin/bash
#!/usr/bin/env bash
exec /usr/local/libexec/super_prove/bin/super_prove.sh "$@"
The "demo.sh" script also expects the "z3" SMT2 solver in the PATH for

View file

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
set -ex
yosys -p '
read_verilog -formal demo.v

View file

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
set -ex
yosys demo.ys
$TD_HOME/bin/td build.tcl

View file

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
yosys run_yosys.ys
vivado -nolog -nojournal -mode batch -source run_vivado.tcl
vivado -nolog -nojournal -mode batch -source run_prog.tcl

View file

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
set -ex

View file

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
set -ex

View file

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
set -ex
yosys -p "synth_gowin -top demo -vout demo_syn.v" demo.v
$GOWIN_HOME/bin/gowin -d demo_syn.v -cst demo.cst -sdc demo.sdc -p GW1NR-9-QFN88-6 -pn GW1NR-LV9QN88C6/I5 -cfg device.cfg -bit -tr -ph -timing -gpa -rpt -warning_all

View file

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
set -ex
yosys -p 'synth_sf2 -top example -edif netlist.edn -vlog netlist.vm' example.v
export LM_LICENSE_FILE=${LM_LICENSE_FILE:-1702@localhost}

View file

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
export REV="de2i"

View file

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
iverilog -D POST_IMPL -o verif_post -s tb_top tb_top.v top.vqm $(yosys-config --datdir/altera_intel/max10/cells_comb_max10.v)
vvp -N verif_post

View file

@ -1,2 +1,2 @@
#!/bin/env bash
#!/usr/bin/env bash
yosys -p "synth_intel -family cycloneiv -top lfsr_updown -vqm top.vqm" lfsr_updown.v

View file

@ -1,2 +1,2 @@
#!/bin/env bash
#!/usr/bin/env bash
yosys -p "synth_intel -family max10 -top lfsr_updown -vqm top.vqm" lfsr_updown.v

View file

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
iverilog -D POST_IMPL -o verif_post -s tb lfsr_updown_tb.v top.vqm $(yosys-config --datdir/altera_intel/max10/cells_comb_max10.v)
vvp -N verif_post

View file

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
iverilog -o presynth lfsr_updown_tb.v lfsr_updown.v &&\

View file

@ -1,4 +1,4 @@
#!/bin/sh
#!/usr/bin/env sh
set -e
yosys run_yosys.ys
edif2ngd example.edif

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

@ -24,7 +24,7 @@ PRIVATE_NAMESPACE_BEGIN
uint32_t read_be32(std::istream &f) {
return ((uint32_t) f.get() << 24) |
((uint32_t) f.get() << 16) |
((uint32_t) f.get() << 16) |
((uint32_t) f.get() << 8) | (uint32_t) f.get();
}
@ -80,9 +80,9 @@ struct Xaiger2Frontend : public Frontend {
extra_args(f, filename, args, argidx, true);
if (map_filename.empty())
log_error("A '-map2' argument required\n");
log_error("A '-map2' argument is required\n");
if (module_name.empty())
log_error("A '-module_name' argument required\n");
log_error("A '-module_name' argument is required\n");
Module *module = design->module(module_name);
if (!module)
@ -128,10 +128,10 @@ struct Xaiger2Frontend : public Frontend {
int woffset;
std::string name;
if (!(map_file >> pi_idx >> woffset >> name))
log_error("Bad map file (1)\n");
log_error("Bad map file: couldn't read 'pi' line\n");
int lit = (2 * pi_idx) + 2;
if (lit < 0 || lit >= (int) bits.size())
log_error("Bad map file (2)\n");
log_error("Bad map file: primary input literal out of range\n");
Wire *w = module->wire(name);
if (!w || woffset < 0 || woffset >= w->width)
log_error("Map file references non-existent signal bit %s[%d]\n",
@ -141,9 +141,9 @@ struct Xaiger2Frontend : public Frontend {
int box_seq;
std::string name;
if (!(map_file >> box_seq >> name))
log_error("Bad map file (20)\n");
log_error("Bad map file: couldn't read 'box' line\n");
if (box_seq < 0)
log_error("Bad map file (21)\n");
log_error("Bad map file: box out of range\n");
Cell *box = module->cell(RTLIL::escape_id(name));
if (!box)
@ -158,7 +158,7 @@ struct Xaiger2Frontend : public Frontend {
}
if (!def)
log_error("Bad map file (22)\n");
log_error("Bad map file: no module found for box type '%s'\n", log_id(box->type));
if (box_seq >= (int) boxes.size()) {
boxes.resize(box_seq + 1);
@ -403,15 +403,15 @@ struct Xaiger2Frontend : public Frontend {
int woffset;
std::string name;
if (!(map_file >> po_idx >> woffset >> name))
log_error("Bad map file (3)\n");
log_error("Bad map file: couldn't read 'po' line\n");
po_idx += co_counter;
if (po_idx < 0 || po_idx >= (int) outputs.size())
log_error("Bad map file (4)\n");
log_error("Bad map file: primary output index out of range\n");
int lit = outputs[po_idx];
if (lit < 0 || lit >= (int) bits.size())
log_error("Bad map file (5)\n");
log_error("Bad map file: primary output literal out of range\n");
if (bits[lit] == RTLIL::Sm)
log_error("Bad map file (6)\n");
log_error("Bad map file: primary output literal is a marker\n");
Wire *w = module->wire(name);
if (!w || woffset < 0 || woffset >= w->width)
log_error("Map file references non-existent signal bit %s[%d]\n",
@ -423,15 +423,15 @@ struct Xaiger2Frontend : public Frontend {
std::string box_name;
std::string box_port;
if (!(map_file >> po_idx >> poffset >> box_name >> box_port))
log_error("Bad map file (7)\n");
log_error("Bad map file: couldn't read 'pseudopo' line\n");
po_idx += co_counter;
if (po_idx < 0 || po_idx >= (int) outputs.size())
log_error("Bad map file (8)\n");
log_error("Bad map file: pseudo primary output index out of range\n");
int lit = outputs[po_idx];
if (lit < 0 || lit >= (int) bits.size())
log_error("Bad map file (9)\n");
log_error("Bad map file: pseudo primary output literal out of range\n");
if (bits[lit] == RTLIL::Sm)
log_error("Bad map file (10)\n");
log_error("Bad map file: pseudo primary output literal is a marker\n");
Cell *cell = module->cell(box_name);
if (!cell || !cell->hasPort(box_port))
log_error("Map file references non-existent box port %s/%s\n",

View file

@ -100,6 +100,7 @@ std::string AST::type2str(AstNodeType type)
X(AST_CAST_SIZE)
X(AST_CONCAT)
X(AST_REPLICATE)
X(AST_ASSIGN_PATTERN)
X(AST_BIT_NOT)
X(AST_BIT_AND)
X(AST_BIT_OR)
@ -696,6 +697,16 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const
fprintf(f, "}}");
break;
case AST_ASSIGN_PATTERN:
fprintf(f, "'{");
for (int i = 0; i < GetSize(children); i++) {
if (i != 0)
fprintf(f, ", ");
children[i]->dumpVlog(f, "");
}
fprintf(f, "}");
break;
if (0) { case AST_BIT_NOT: txt = "~"; }
if (0) { case AST_REDUCE_AND: txt = "&"; }
if (0) { case AST_REDUCE_OR: txt = "|"; }

View file

@ -78,6 +78,7 @@ namespace AST
AST_CAST_SIZE,
AST_CONCAT,
AST_REPLICATE,
AST_ASSIGN_PATTERN,
AST_BIT_NOT,
AST_BIT_AND,
AST_BIT_OR,

View file

@ -342,6 +342,9 @@ struct AST_INTERNAL::ProcessGenerator
// The most recently assigned $print or $check cell \PRIORITY.
int last_effect_priority;
// Track which signals have been assigned in current_case to avoid unnecessary removeSignalFromCaseTree calls
pool<RTLIL::SigBit> current_case_assigned_bits;
ProcessGenerator(std::unique_ptr<AstNode> a, RTLIL::SigSpec initSyncSignalsArg = RTLIL::SigSpec()) : always(std::move(a)), initSyncSignals(initSyncSignalsArg), last_effect_priority(0)
{
// rewrite lookahead references
@ -403,6 +406,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()) {
@ -418,6 +433,10 @@ struct AST_INTERNAL::ProcessGenerator
subst_rvalue_map = subst_lvalue_from.to_sigbit_dict(RTLIL::SigSpec(RTLIL::State::Sx, GetSize(subst_lvalue_from)));
} else {
addChunkActions(current_case->actions, subst_lvalue_to, subst_lvalue_from);
// Track initial assignments
for (auto &bit : subst_lvalue_to)
if (bit.wire != NULL)
current_case_assigned_bits.insert(bit);
}
// process the AST
@ -545,14 +564,42 @@ struct AST_INTERNAL::ProcessGenerator
// e.g. when the last statement in the code "a = 23; if (b) a = 42; a = 0;" is processed this
// function is called to clean up the first two assignments as they are overwritten by
// the third assignment.
void removeSignalFromCaseTree(const RTLIL::SigSpec &pattern, RTLIL::CaseRule *cs)
void removeSignalFromCaseTree(const pool<RTLIL::SigBit> &pattern_bits, RTLIL::CaseRule *cs)
{
for (auto it = cs->actions.begin(); it != cs->actions.end(); it++)
it->first.remove2(pattern, &it->second);
// Optimization: check actions in reverse order and stop early if we've found all pattern bits
pool<RTLIL::SigBit> remaining_bits = pattern_bits;
for (auto it = cs->actions.rbegin(); it != cs->actions.rend(); ++it) {
bool has_pattern = false;
for (auto &bit : it->first) {
if (bit.wire != NULL && remaining_bits.count(bit)) {
has_pattern = true;
remaining_bits.erase(bit);
}
}
if (has_pattern) {
it->first.remove2(pattern_bits, &it->second);
}
// Early exit if we've processed all bits in pattern
if (remaining_bits.empty())
break;
}
for (auto it = cs->switches.begin(); it != cs->switches.end(); it++)
for (auto it2 = (*it)->cases.begin(); it2 != (*it)->cases.end(); it2++)
removeSignalFromCaseTree(pattern, *it2);
removeSignalFromCaseTree(pattern_bits, *it2);
}
void removeSignalFromCaseTree(const RTLIL::SigSpec &pattern, RTLIL::CaseRule *cs)
{
pool<RTLIL::SigBit> pattern_bits;
pattern_bits.reserve(pattern.size());
for (auto &bit : pattern)
if (bit.wire != NULL)
pattern_bits.insert(bit);
removeSignalFromCaseTree(pattern_bits, cs);
}
// add an assignment (aka "action") but split it up in chunks. this way huge assignments
@ -611,7 +658,23 @@ struct AST_INTERNAL::ProcessGenerator
subst_rvalue_map.set(unmapped_lvalue[i], rvalue[i]);
}
removeSignalFromCaseTree(lvalue, current_case);
// Check if any bits in lvalue have been assigned before in current_case
bool has_overlap = false;
for (auto &bit : lvalue) {
if (bit.wire != NULL && current_case_assigned_bits.count(bit)) {
has_overlap = true;
break;
}
}
if (has_overlap)
removeSignalFromCaseTree(lvalue, current_case);
// Track newly assigned bits
for (auto &bit : lvalue)
if (bit.wire != NULL)
current_case_assigned_bits.insert(bit);
remove_unwanted_lvalue_bits(lvalue, rvalue);
current_case->actions.push_back(RTLIL::SigSig(lvalue, rvalue));
}
@ -658,9 +721,15 @@ struct AST_INTERNAL::ProcessGenerator
RTLIL::CaseRule *backup_case = current_case;
current_case = new RTLIL::CaseRule;
pool<RTLIL::SigBit> backup_assigned_bits = std::move(current_case_assigned_bits);
current_case_assigned_bits.clear();
set_src_attr(current_case, child.get());
last_generated_case = current_case;
addChunkActions(current_case->actions, this_case_eq_ltemp, this_case_eq_rvalue);
// Track temp assignments
for (auto &bit : this_case_eq_ltemp)
if (bit.wire != NULL)
current_case_assigned_bits.insert(bit);
for (auto& node : child->children) {
if (node->type == AST_DEFAULT)
default_case = current_case;
@ -674,6 +743,7 @@ struct AST_INTERNAL::ProcessGenerator
else
log_assert(current_case->compare.size() == 0);
current_case = backup_case;
current_case_assigned_bits = std::move(backup_assigned_bits);
subst_lvalue_map.restore();
subst_rvalue_map.restore();
@ -702,8 +772,24 @@ struct AST_INTERNAL::ProcessGenerator
subst_rvalue_map.set(this_case_eq_lvalue[i], this_case_eq_ltemp[i]);
this_case_eq_lvalue.replace(subst_lvalue_map.stdmap());
removeSignalFromCaseTree(this_case_eq_lvalue, current_case);
// Check if any bits in lvalue have been assigned before in current_case
bool has_overlap = false;
for (auto &bit : this_case_eq_lvalue) {
if (bit.wire != NULL && current_case_assigned_bits.count(bit)) {
has_overlap = true;
break;
}
}
if (has_overlap)
removeSignalFromCaseTree(this_case_eq_lvalue, current_case);
addChunkActions(current_case->actions, this_case_eq_lvalue, this_case_eq_ltemp);
// Track newly assigned bits
for (auto &bit : this_case_eq_lvalue)
if (bit.wire != NULL)
current_case_assigned_bits.insert(bit);
}
break;
@ -712,7 +798,7 @@ struct AST_INTERNAL::ProcessGenerator
break;
case AST_ASSIGN:
ast->input_error("Found continous assignment in always/initial block!\n");
ast->input_error("Found continuous assignment in always/initial block!\n");
break;
case AST_PARAMETER:
@ -1126,6 +1212,15 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
sign_hint = false;
break;
case AST_ASSIGN_PATTERN:
for (auto& child : children) {
sub_width_hint = 0;
sub_sign_hint = true;
child->detectSignWidthWorker(sub_width_hint, sub_sign_hint);
}
sign_hint = false;
break;
case AST_NEG:
case AST_BIT_NOT:
case AST_POS:
@ -1738,6 +1833,9 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
return sig;
}
case AST_ASSIGN_PATTERN:
input_error("Assignment pattern is only supported for whole unpacked array assignments.\n");
// generate cells for unary operations: $not, $pos, $neg
if (0) { case AST_BIT_NOT: type_name = ID($not); }
if (0) { case AST_POS: type_name = ID($pos); }
@ -2085,8 +2183,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 +2245,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

@ -91,6 +91,11 @@ void AstNode::fixup_hierarchy_flags(bool force_descend)
children[0]->set_in_param_flag(true, force_descend);
break;
case AST_ASSIGN_PATTERN:
for (auto& child : children)
child->set_in_param_flag(in_param, force_descend);
break;
case AST_GENFOR:
case AST_FOR:
for (auto& child : children) {
@ -269,6 +274,100 @@ 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;
}
// Check if mem_b matches mem_a's unpacked dimensions starting at first_dim.
static bool arrays_have_compatible_dims_from(AstNode *mem_a, int first_dim, AstNode *mem_b)
{
if (mem_b->unpacked_dimensions != mem_a->unpacked_dimensions - first_dim)
return false;
for (int i = 0; i < mem_b->unpacked_dimensions; i++) {
if (mem_a->dimensions[first_dim + 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.
@ -1653,6 +1752,12 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin
children_are_self_determined = true;
break;
case AST_ASSIGN_PATTERN:
// Assignment pattern elements are context-determined by the target element type.
// Keep child width context intact until whole-array assignment expansion creates scalar assignments.
detect_width_simple = true;
break;
case AST_NEG:
case AST_BIT_NOT:
case AST_POS:
@ -3200,6 +3305,217 @@ skip_dynamic_range_lvalue_expansion:;
}
}
// Expand array assignment: arr_out = arr_in OR arr_out = cond ? arr_a : arr_b OR arr_out = '{a, 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()));
// Case 3: Positional assignment pattern (out = '{a, b})
bool is_pattern_assign = rhs->type == AST_ASSIGN_PATTERN;
if (is_direct_assign || is_ternary_assign || is_pattern_assign)
{
AstNode *direct_rhs_mem = nullptr;
AstNode *true_mem = nullptr;
AstNode *false_mem = nullptr;
int num_dims = lhs_mem->unpacked_dimensions;
int total_elements = 1;
for (int d = 0; d < num_dims; d++)
total_elements *= lhs_mem->dimensions[d].range_width;
int element_width, mem_size, addr_bits;
lhs_mem->meminfo(element_width, mem_size, addr_bits);
bool pattern_is_flat = false;
// Helper to add indices to an array identifier clone.
auto add_indices_to_id = [&](std::unique_ptr<AstNode> id, const std::vector<int>& indices) {
int indexed_dims = GetSize(indices);
if (indexed_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 = indexed_dims;
// Reset basic_prep so multirange gets resolved during subsequent simplify passes
id->basic_prep = false;
return id;
};
auto add_position_to_id = [&](std::unique_ptr<AstNode> id, AstNode *mem, const std::vector<int>& position) {
return add_indices_to_id(std::move(id), array_indices_from_position(mem, position));
};
// Validate nested assignment pattern shape against unpacked dimensions.
std::function<void(AstNode*, int)> validate_pattern_shape = [&](AstNode *pattern, int dim) {
log_assert(pattern->type == AST_ASSIGN_PATTERN);
int expected = lhs_mem->dimensions[dim].range_width;
if (GetSize(pattern->children) != expected)
input_error("Assignment pattern element count mismatch at dimension %d: got %d, expected %d\n",
dim + 1, GetSize(pattern->children), expected);
if (dim + 1 == num_dims)
return;
for (auto& child : pattern->children) {
if (child->type == AST_ASSIGN_PATTERN) {
validate_pattern_shape(child.get(), dim + 1);
} else if (is_unexpanded_array_ref(child.get()) &&
arrays_have_compatible_dims_from(lhs_mem, dim + 1, child->id2ast)) {
continue;
} else {
input_error("Nested assignment pattern or compatible array expression required for dimension %d\n", dim + 2);
}
}
};
// Select the assignment pattern element for an unpacked array position.
auto pattern_element_at_position = [&](const std::vector<int>& position, int flat_index) {
if (pattern_is_flat)
return rhs->children[flat_index]->clone();
AstNode *pattern = rhs;
for (int d = 0; d < num_dims; d++) {
log_assert(pattern->type == AST_ASSIGN_PATTERN);
AstNode *element = pattern->children[position[d]].get();
if (d + 1 == num_dims)
return element->clone();
if (element->type == AST_ASSIGN_PATTERN) {
pattern = element;
} else {
std::vector<int> subposition(position.begin() + d + 1, position.end());
return add_position_to_id(element->clone(), element->id2ast, subposition);
}
}
log_abort();
};
// 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 if (is_ternary_assign) {
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");
} else {
if (num_dims > 1 && GetSize(rhs->children) == lhs_mem->dimensions[0].range_width) {
validate_pattern_shape(rhs, 0);
} else if (num_dims == 1 && GetSize(rhs->children) == total_elements) {
pattern_is_flat = true;
} else {
if (num_dims > 1 && GetSize(rhs->children) == lhs_mem->dimensions[0].range_width)
validate_pattern_shape(rhs, 0);
int expected = num_dims > 1 ? lhs_mem->dimensions[0].range_width : total_elements;
input_error("Assignment pattern element count mismatch: got %d, expected %d\n", GetSize(rhs->children), expected);
}
}
// Warn if array assignment expansion is large.
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;
std::vector<std::unique_ptr<AstNode>> pattern_temp_assignments;
foreach_array_position(lhs_mem, [&](const std::vector<int>& position) {
auto lhs_idx = add_position_to_id(lhs->clone(), lhs_mem, position);
std::unique_ptr<AstNode> rhs_expr;
if (is_direct_assign) {
rhs_expr = add_position_to_id(rhs->clone(), direct_rhs_mem, position);
} else if (is_ternary_assign) {
// Ternary case
AstNode *cond = rhs->children[0].get();
AstNode *true_val = rhs->children[1].get();
AstNode *false_val = rhs->children[2].get();
auto true_idx = add_position_to_id(true_val->clone(), true_mem, position);
auto false_idx = add_position_to_id(false_val->clone(), false_mem, position);
rhs_expr = std::make_unique<AstNode>(location, AST_TERNARY,
cond->clone(), std::move(true_idx), std::move(false_idx));
} else {
auto pattern_rhs = pattern_element_at_position(position, GetSize(assignments));
if (type == AST_ASSIGN_EQ) {
auto wire_tmp_owned = std::make_unique<AstNode>(location, AST_WIRE,
std::make_unique<AstNode>(location, AST_RANGE,
mkconst_int(location, element_width - 1, true),
mkconst_int(location, 0, true)));
auto wire_tmp = wire_tmp_owned.get();
wire_tmp->str = stringf("$assignpattern$%s:%d$%d",
RTLIL::encode_filename(*location.begin.filename), location.begin.line, autoidx++);
current_scope[wire_tmp->str] = wire_tmp;
current_ast_mod->children.push_back(std::move(wire_tmp_owned));
wire_tmp->set_attribute(ID::nosync, AstNode::mkconst_int(location, 1, false));
while (wire_tmp->simplify(true, 1, -1, false)) { }
wire_tmp->is_logic = true;
wire_tmp->is_signed = lhs_mem->is_signed;
auto tmp_id = std::make_unique<AstNode>(location, AST_IDENTIFIER);
tmp_id->str = wire_tmp->str;
pattern_temp_assignments.push_back(std::make_unique<AstNode>(location, AST_ASSIGN_EQ,
tmp_id->clone(), std::move(pattern_rhs)));
rhs_expr = std::move(tmp_id);
} else {
rhs_expr = std::move(pattern_rhs);
}
}
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 : pattern_temp_assignments)
newNode->children.push_back(std::move(assign));
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 &&
@ -4458,6 +4774,8 @@ replace_fcall_later:;
tmp_bits.insert(tmp_bits.end(), children.at(1)->bits.begin(), children.at(1)->bits.end());
newNode = children.at(1)->is_string ? mkconst_str(location, tmp_bits) : mkconst_bits(location, tmp_bits, false);
break;
case AST_ASSIGN_PATTERN:
goto not_const;
default:
not_const:
break;
@ -4690,6 +5008,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 +5037,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

@ -20,6 +20,7 @@
#include "passes/techmap/libparse.h"
#include "kernel/register.h"
#include "kernel/log.h"
#include <array>
YOSYS_NAMESPACE_BEGIN
@ -210,7 +211,10 @@ static void create_ff(RTLIL::Module *module, const LibertyAst *node)
auto [iq_sig, iqn_sig] = find_latch_ff_wires(module, node);
RTLIL::SigSpec clk_sig, data_sig, clear_sig, preset_sig;
bool clk_polarity = true, clear_polarity = true, preset_polarity = true;
const std::string name = RTLIL::unescape_id(module->name);
std::optional<char> clear_preset_var1;
std::optional<char> clear_preset_var2;
for (auto child : node->children) {
if (child->id == "clocked_on")
clk_sig = parse_func_expr(module, child->value.c_str());
@ -220,10 +224,18 @@ static void create_ff(RTLIL::Module *module, const LibertyAst *node)
clear_sig = parse_func_expr(module, child->value.c_str());
if (child->id == "preset")
preset_sig = parse_func_expr(module, child->value.c_str());
for (auto& [id, var] : {pair{"clear_preset_var1", &clear_preset_var1}, {"clear_preset_var2", &clear_preset_var2}})
if (child->id == id) {
if (child->value.size() != 1)
log_error("Unexpected length of clear_preset_var* value %s in FF cell %s\n", child->value, name);
*var = child->value[0];
}
}
if (clk_sig.size() == 0 || data_sig.size() == 0)
log_error("FF cell %s has no next_state and/or clocked_on attribute.\n", RTLIL::unescape_id(module->name));
log_error("FF cell %s has no next_state and/or clocked_on attribute.\n", name);
for (bool rerun_invert_rollback = true; rerun_invert_rollback;)
{
@ -248,36 +260,64 @@ static void create_ff(RTLIL::Module *module, const LibertyAst *node)
}
}
RTLIL::Cell *cell = module->addCell(NEW_ID, ID($_NOT_));
cell->setPort(ID::A, iq_sig);
cell->setPort(ID::Y, iqn_sig);
for (auto& [out_sig, cp_var, neg] : {tuple{iq_sig, clear_preset_var1, false}, {iqn_sig, clear_preset_var2, true}}) {
SigSpec q_sig = out_sig;
if (neg) {
q_sig = module->addWire(NEW_ID, out_sig.as_wire());
module->addNotGate(NEW_ID, q_sig, out_sig);
}
cell = module->addCell(NEW_ID, "");
cell->setPort(ID::D, data_sig);
cell->setPort(ID::Q, iq_sig);
cell->setPort(ID::C, clk_sig);
RTLIL::Cell* cell = module->addCell(NEW_ID, "");
cell->setPort(ID::D, data_sig);
cell->setPort(ID::Q, q_sig);
cell->setPort(ID::C, clk_sig);
if (clear_sig.size() == 0 && preset_sig.size() == 0) {
cell->type = stringf("$_DFF_%c_", clk_polarity ? 'P' : 'N');
if (clear_sig.size() == 0 && preset_sig.size() == 0) {
cell->type = stringf("$_DFF_%c_", clk_polarity ? 'P' : 'N');
}
if (clear_sig.size() == 1 && preset_sig.size() == 0) {
cell->type = stringf("$_DFF_%c%c0_", clk_polarity ? 'P' : 'N', clear_polarity ? 'P' : 'N');
cell->setPort(ID::R, clear_sig);
}
if (clear_sig.size() == 0 && preset_sig.size() == 1) {
cell->type = stringf("$_DFF_%c%c1_", clk_polarity ? 'P' : 'N', preset_polarity ? 'P' : 'N');
cell->setPort(ID::R, preset_sig);
}
if (clear_sig.size() == 1 && preset_sig.size() == 1) {
cell->type = stringf("$_DFFSR_%c%c%c_", clk_polarity ? 'P' : 'N', preset_polarity ? 'P' : 'N', clear_polarity ? 'P' : 'N');
SigBit s_sig = preset_sig;
SigBit r_sig = clear_sig;
if (cp_var && *cp_var != 'X') {
// Either set or reset dominates
bool set_dominates;
if (*cp_var == 'L') {
set_dominates = neg;
} else if (*cp_var == 'H') {
set_dominates = !neg;
} else {
log_error("FF cell %s has unsupported clear&preset behavior \'%c\'.\n", name, *cp_var);
}
log_debug("cell %s variable %d cp_var %c set dominates? %d\n", name, (int)neg + 1, *cp_var, set_dominates);
// S&R priority is well-defined now
if (set_dominates) {
r_sig = module->AndnotGate(NEW_ID, r_sig, s_sig);
} else {
s_sig = module->AndnotGate(NEW_ID, s_sig, r_sig);
}
} else {
log_debug("cell %s variable %d undef c&p behavior\n", name, (int)neg + 1);
}
cell->setPort(ID::S, s_sig);
cell->setPort(ID::R, r_sig);
}
log_assert(!cell->type.empty());
}
if (clear_sig.size() == 1 && preset_sig.size() == 0) {
cell->type = stringf("$_DFF_%c%c0_", clk_polarity ? 'P' : 'N', clear_polarity ? 'P' : 'N');
cell->setPort(ID::R, clear_sig);
}
if (clear_sig.size() == 0 && preset_sig.size() == 1) {
cell->type = stringf("$_DFF_%c%c1_", clk_polarity ? 'P' : 'N', preset_polarity ? 'P' : 'N');
cell->setPort(ID::R, preset_sig);
}
if (clear_sig.size() == 1 && preset_sig.size() == 1) {
cell->type = stringf("$_DFFSR_%c%c%c_", clk_polarity ? 'P' : 'N', preset_polarity ? 'P' : 'N', clear_polarity ? 'P' : 'N');
cell->setPort(ID::S, preset_sig);
cell->setPort(ID::R, clear_sig);
}
log_assert(!cell->type.empty());
}
static bool create_latch(RTLIL::Module *module, const LibertyAst *node, bool flag_ignore_miss_data_latch)
@ -797,3 +837,4 @@ skip_cell:;
YOSYS_NAMESPACE_END

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;
@ -285,6 +286,7 @@ struct RTLILFrontendWorker {
if (width > MAX_CONST_WIDTH)
error("Constant width %lld out of range before `%s`.", width, error_token());
bits.reserve(width);
int start_idx = idx;
while (true) {
RTLIL::State bit;
switch (line[idx]) {
@ -299,8 +301,9 @@ struct RTLILFrontendWorker {
bits.push_back(bit);
++idx;
}
done:
std::reverse(bits.begin(), bits.end());
done:
if (start_idx < idx)
std::reverse(bits.begin(), bits.end());
if (GetSize(bits) > width)
bits.resize(width);
@ -322,6 +325,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 +353,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 +367,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 +521,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 +579,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 +607,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 +665,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 +690,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 +771,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 +898,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 +927,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;
@ -546,7 +546,7 @@
%token TOK_z "'z'"
%type <ast_t> range range_or_multirange non_opt_range non_opt_multirange
%type <ast_t> wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list non_io_wire_type io_wire_type
%type <ast_t> wire_type expr basic_expr concat_list assignment_pattern_list rvalue lvalue lvalue_concat_list non_io_wire_type io_wire_type
%type <string_t> opt_label opt_sva_label tok_prim_wrapper hierarchical_id hierarchical_type_id integral_number
%type <string_t> type_name
%type <ast_t> opt_enum_init enum_type struct_type enum_struct_type func_return_type typedef_base_type
@ -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;
@ -3341,6 +3349,11 @@ basic_expr:
TOK_LCURL concat_list TOK_RCURL {
$$ = std::move($2);
} |
OP_CAST TOK_LCURL assignment_pattern_list optional_comma TOK_RCURL {
if (!mode->sv)
err_at_loc(@1, "Assignment patterns are only supported in SystemVerilog mode.");
$$ = std::move($3);
} |
TOK_LCURL expr TOK_LCURL concat_list TOK_RCURL TOK_RCURL {
$$ = std::make_unique<AstNode>(@$, AST_REPLICATE, std::move($2), std::move($4));
} |
@ -3572,6 +3585,16 @@ concat_list:
$$->children.push_back(std::move($1));
};
assignment_pattern_list:
expr {
$$ = std::make_unique<AstNode>(@$, AST_ASSIGN_PATTERN);
$$->children.push_back(std::move($1));
} |
assignment_pattern_list TOK_COMMA expr {
$$ = std::move($1);
$$->children.push_back(std::move($3));
};
integral_number:
TOK_CONSTVAL { $$ = std::move($1); } |
TOK_UNBASED_UNSIZED_CONSTVAL { $$ = std::move($1); } |

View file

@ -5,7 +5,7 @@ Getting Started
Outline of a Yosys command
--------------------------
Here is a the C++ code for a "hello_world" Yosys command (hello.cc):
Here is the C++ code for a "hello_world" Yosys command (hello.cc):
#include "kernel/yosys.h"
@ -85,7 +85,7 @@ the declarations for the following types in kernel/rtlil.h:
The module is a container with connected cells and wires
in it. The design is a container with modules in it.
All this types are also available without the RTLIL:: prefix in the Yosys
All these types are also available without the RTLIL:: prefix in the Yosys
namespace.
4. SigMap and other Helper Classes
@ -204,4 +204,4 @@ Notes on the existing codebase
For historical reasons not all parts of Yosys adhere to the current coding
style. When adding code to existing parts of the system, adhere to this guide
for the new code instead of trying to mimic the style of the surrounding code.
for the new code instead of trying to mimic the style of the surrounding code.

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

@ -66,14 +66,7 @@ struct AigMaker
Cell *cell;
idict<AigNode> aig_indices;
int the_true_node;
int the_false_node;
AigMaker(Aig *aig, Cell *cell) : aig(aig), cell(cell)
{
the_true_node = -1;
the_false_node = -1;
}
AigMaker(Aig *aig, Cell *cell) : aig(aig), cell(cell) {}
int node2index(const AigNode &node)
{

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::SRC_EN, ID::DST_EN, 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

@ -459,9 +459,7 @@ X(EDGE_POL)
X(EFX_ADD)
X(EN)
X(ENPOL)
X(EN_DST)
X(EN_POLARITY)
X(EN_SRC)
X(EQN)
X(F)
X(FDCE)
@ -835,6 +833,7 @@ X(abcgroup)
X(acc_fir)
X(acc_fir_i)
X(add_carry)
X(aiger2_zbuf)
X(allconst)
X(allseq)
X(always_comb)

View file

@ -23,6 +23,7 @@
#define CXXOPTS_VECTOR_DELIMITER '\0'
#include "libs/cxxopts/include/cxxopts.hpp"
#include <iostream>
#include <chrono>
#ifdef YOSYS_ENABLE_READLINE
# include <readline/readline.h>
@ -143,19 +144,6 @@ int yosys_history_offset = 0;
std::string yosys_history_file;
#endif
#if defined(__wasm)
extern "C" {
// FIXME: WASI does not currently support exceptions.
void* __cxa_allocate_exception(size_t thrown_size) throw() {
return malloc(thrown_size);
}
bool __cxa_uncaught_exception() throw();
void __cxa_throw(void* thrown_exception, struct std::type_info * tinfo, void (*dest)(void*)) {
std::terminate();
}
}
#endif
void yosys_atexit()
{
#if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE)
@ -195,6 +183,7 @@ namespace Yosys {
int main(int argc, char **argv)
{
auto wall_clock_start = std::chrono::steady_clock::now();
std::string frontend_command = "auto";
std::string backend_command = "auto";
std::vector<std::string> vlog_defines;
@ -675,6 +664,7 @@ int main(int argc, char **argv)
#ifdef _WIN32
log("End of script. Logfile hash: %s\n", hash);
(void)wall_clock_start;
#else
std::string meminfo;
std::string stats_divider = ", ";
@ -700,8 +690,11 @@ int main(int argc, char **argv)
meminfo = stringf(", MEM: %.2f MB peak",
ru_buffer.ru_maxrss / (1024.0 * 1024.0));
#endif
log("End of script. Logfile hash: %s%sCPU: user %.2fs system %.2fs%s\n", hash,
stats_divider.c_str(), ru_buffer.ru_utime.tv_sec + 1e-6 * ru_buffer.ru_utime.tv_usec,
double wall_seconds = std::chrono::duration<double>(
std::chrono::steady_clock::now() - wall_clock_start).count();
log("End of script. Logfile hash: %s%stime: %.2fs, user: %.2fs, system: %.2fs%s\n", hash,
stats_divider.c_str(), wall_seconds, ru_buffer.ru_utime.tv_sec + 1e-6 * ru_buffer.ru_utime.tv_usec,
ru_buffer.ru_stime.tv_sec + 1e-6 * ru_buffer.ru_stime.tv_usec, meminfo.c_str());
#endif
log("%s\n", yosys_maybe_version());

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

@ -22,6 +22,7 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
#include "kernel/threading.h"
YOSYS_NAMESPACE_BEGIN
@ -35,34 +36,55 @@ struct FfInitVals
sigmap = sigmap_;
initbits.clear();
for (auto wire : module->wires())
if (wire->attributes.count(ID::init))
process_wire(wire);
}
void process_wire(RTLIL::Wire *wire)
{
SigSpec wirebits = (*sigmap)(wire);
Const initval = wire->attributes.at(ID::init);
for (int i = 0; i < GetSize(wirebits) && i < GetSize(initval); i++)
{
if (wire->attributes.count(ID::init) == 0)
SigBit bit = wirebits[i];
State val = initval[i];
if (val != State::S0 && val != State::S1 && bit.wire != nullptr)
continue;
SigSpec wirebits = (*sigmap)(wire);
Const initval = wire->attributes.at(ID::init);
for (int i = 0; i < GetSize(wirebits) && i < GetSize(initval); i++)
{
SigBit bit = wirebits[i];
State val = initval[i];
if (val != State::S0 && val != State::S1 && bit.wire != nullptr)
continue;
if (initbits.count(bit)) {
if (initbits.at(bit).first != val)
log_error("Conflicting init values for signal %s (%s = %s != %s).\n",
log_signal(bit), log_signal(SigBit(wire, i)),
log_signal(val), log_signal(initbits.at(bit).first));
continue;
}
initbits[bit] = std::make_pair(val,SigBit(wire,i));
if (initbits.count(bit)) {
if (initbits.at(bit).first != val)
log_error("Conflicting init values for signal %s (%s = %s != %s).\n",
log_signal(bit), log_signal(SigBit(wire, i)),
log_signal(val), log_signal(initbits.at(bit).first));
continue;
}
initbits[bit] = std::make_pair(val,SigBit(wire,i));
}
}
void set_parallel(const SigMapView *sigmap_, ParallelDispatchThreadPool &thread_pool, RTLIL::Module *module)
{
sigmap = sigmap_;
initbits.clear();
const RTLIL::Module *const_module = module;
ParallelDispatchThreadPool::Subpool subpool(thread_pool, ThreadPool::work_pool_size(0, module->wires_size(), 1000));
ShardedVector<RTLIL::Wire*> init_wires(subpool);
subpool.run([const_module, &init_wires](const ParallelDispatchThreadPool::RunCtx &ctx) {
for (int i : ctx.item_range(const_module->wires_size())) {
RTLIL::Wire *wire = const_module->wire_at(i);
if (wire->attributes.count(ID::init))
init_wires.insert(ctx, wire);
}
});
for (RTLIL::Wire *wire : init_wires)
process_wire(wire);
}
RTLIL::State operator()(RTLIL::SigBit bit) const
{
auto it = initbits.find((*sigmap)(bit));

View file

@ -45,8 +45,7 @@ FstData::FstData(std::string filename) : ctx(nullptr)
ctx = (fstReaderContext *)fstReaderOpen(filename.c_str());
if (!ctx)
log_error("Error opening '%s' as FST file\n", filename);
int scale = (int)fstReaderGetTimescale(ctx);
timescale = pow(10.0, scale);
scale = (int)fstReaderGetTimescale(ctx);
timescale_str = "";
int unit = 0;
int zeros = 0;

View file

@ -55,7 +55,7 @@ class FstData
std::string valueOf(fstHandle signal);
fstHandle getHandle(std::string name);
dict<int,fstHandle> getMemoryHandles(std::string name);
double getTimescale() { return timescale; }
int getScale() { return scale; }
const char *getTimescaleString() { return timescale_str.c_str(); }
private:
void extractVarNames();
@ -69,7 +69,7 @@ private:
uint64_t last_time;
std::map<fstHandle, std::string> past_data;
uint64_t past_time;
double timescale;
int scale; // exponent of 10, e.g. -6 = us, -9 = ns
std::string timescale_str;
uint64_t start_time;
uint64_t end_time;

View file

@ -55,6 +55,12 @@ namespace hashlib {
* instead of pointers.
*/
#if defined(__GNUC__) || defined(__clang__)
# define HASHLIB_ATTRIBUTE_WARN_UNUSED __attribute__((warn_unused))
#else
# define HASHLIB_ATTRIBUTE_WARN_UNUSED
#endif
const int hashtable_size_trigger = 2;
const int hashtable_size_factor = 3;
@ -402,7 +408,7 @@ private:
};
template<typename K, typename T, typename OPS>
class dict {
class HASHLIB_ATTRIBUTE_WARN_UNUSED dict {
struct entry_t
{
std::pair<K, T> udata;
@ -877,7 +883,7 @@ public:
};
template<typename K, typename OPS>
class pool
class HASHLIB_ATTRIBUTE_WARN_UNUSED pool
{
template<typename, int, typename> friend class idict;
@ -1257,7 +1263,7 @@ public:
};
template<typename K, int offset, typename OPS>
class idict
class HASHLIB_ATTRIBUTE_WARN_UNUSED idict
{
pool<K, OPS> database;
@ -1321,6 +1327,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);
@ -1354,7 +1366,7 @@ public:
* i-prefixed methods operate on indices in parents
*/
template<typename K, typename OPS>
class mfp
class HASHLIB_ATTRIBUTE_WARN_UNUSED mfp
{
idict<K, 0, OPS> database;
class AtomicParent {

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;
@ -324,6 +324,14 @@ void log_formatted_file_info(std::string_view filename, int lineno, std::string
log("%s:%d: Info: %s", filename, lineno, str);
}
void log_suppressed() {
if (log_debug_suppressed && !log_make_debug) {
constexpr const char* format = "<suppressed ~%d debug messages>\n";
logv_string(format, stringf(format, log_debug_suppressed));
log_debug_suppressed = 0;
}
}
[[noreturn]]
static void log_error_with_prefix(std::string_view prefix, std::string str)
{
@ -345,7 +353,9 @@ static void log_error_with_prefix(std::string_view prefix, std::string str)
}
log_last_error = std::move(str);
log("%s%s", prefix, log_last_error);
std::string message(prefix);
message += log_last_error;
logv_string("%s%s", message);
log_flush();
log_make_debug = bak_log_make_debug;
@ -355,7 +365,7 @@ static void log_error_with_prefix(std::string_view prefix, std::string str)
item.current_count++;
for (auto &[_, item] : log_expect_prefix_error)
if (std::regex_search(string(prefix) + string(log_last_error), item.pattern))
if (std::regex_search(message, item.pattern))
item.current_count++;
log_check_expected();

View file

@ -206,12 +206,7 @@ template <typename... Args>
log_formatted_cmd_error(fmt.format(args...));
}
static inline void log_suppressed() {
if (log_debug_suppressed && !log_make_debug) {
log("<suppressed ~%d debug messages>\n", log_debug_suppressed);
log_debug_suppressed = 0;
}
}
void log_suppressed();
struct LogMakeDebugHdl {
bool status = false;

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,9 +176,15 @@ 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;
}
#endif
return true;
}
void check()
{
log_assert(ok());
}
void notify_connect(RTLIL::Cell *cell, RTLIL::IdString port, const RTLIL::SigSpec &old_sig, const RTLIL::SigSpec &sig) override
@ -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::SRC_EN, ID::DST_EN, 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,9 +19,10 @@
#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 "kernel/threading.h"
#include "frontends/verilog/verilog_frontend.h"
#include "frontends/verilog/preproc.h"
#include "backends/rtlil/rtlil_backend.h"
@ -31,6 +32,7 @@
#include <charconv>
#include <optional>
#include <string_view>
#include <sstream>
YOSYS_NAMESPACE_BEGIN
@ -141,9 +143,17 @@ static constexpr bool check_well_known_id_order()
// and in sorted ascii order, as required by the ID macro.
static_assert(check_well_known_id_order());
constexpr int STATIC_ID_END = static_cast<int>(RTLIL::StaticId::STATIC_ID_END);
struct IdStringCollector {
IdStringCollector(std::vector<MonotonicFlag> &live_ids)
: live_ids(live_ids) {}
void trace(IdString id) {
live.insert(id.index_);
if (id.index_ >= STATIC_ID_END)
live_ids[id.index_ - STATIC_ID_END].set();
else if (id.index_ < 0)
live_autoidx_ids.push_back(id.index_);
}
template <typename T> void trace(const T* v) {
trace(*v);
@ -177,27 +187,14 @@ struct IdStringCollector {
trace(element);
}
void trace(const RTLIL::Design &design) {
trace_values(design.modules_);
trace(design.selection_vars);
}
void trace(const RTLIL::Selection &selection_var) {
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);
}
void trace(const RTLIL::Module &module) {
trace_named(module);
trace_values(module.wires_);
trace_values(module.cells_);
trace(module.avail_parameters);
trace_keys(module.parameter_default_values);
trace_values(module.memories);
trace_values(module.processes);
}
void trace(const RTLIL::Wire &wire) {
trace_named(wire);
if (wire.known_driver())
@ -233,7 +230,8 @@ struct IdStringCollector {
trace(action.memid);
}
std::unordered_set<int> live;
std::vector<MonotonicFlag> &live_ids;
std::vector<int> live_autoidx_ids;
};
int64_t RTLIL::OwningIdString::gc_ns;
@ -242,20 +240,55 @@ int RTLIL::OwningIdString::gc_count;
void RTLIL::OwningIdString::collect_garbage()
{
int64_t start = PerformanceTimer::query();
IdStringCollector collector;
for (auto &[idx, design] : *RTLIL::Design::get_all_designs()) {
collector.trace(*design);
}
int size = GetSize(global_id_storage_);
for (int i = static_cast<int>(StaticId::STATIC_ID_END); i < size; ++i) {
RTLIL::IdString::Storage &storage = global_id_storage_.at(i);
if (storage.buf == nullptr)
continue;
if (collector.live.find(i) != collector.live.end())
continue;
if (global_refcount_storage_.find(i) != global_refcount_storage_.end())
continue;
int pool_size = 0;
for (auto &[idx, design] : *RTLIL::Design::get_all_designs())
for (RTLIL::Module *module : design->modules())
pool_size = std::max(pool_size, ThreadPool::work_pool_size(0, module->cells_size(), 1000));
ParallelDispatchThreadPool thread_pool(pool_size);
int size = GetSize(global_id_storage_);
std::vector<MonotonicFlag> live_ids(size - STATIC_ID_END);
std::vector<IdStringCollector> collectors;
int num_threads = thread_pool.num_threads();
collectors.reserve(num_threads);
for (int i = 0; i < num_threads; ++i)
collectors.emplace_back(live_ids);
for (auto &[idx, design] : *RTLIL::Design::get_all_designs()) {
for (RTLIL::Module *module : design->modules()) {
collectors[0].trace_named(*module);
ParallelDispatchThreadPool::Subpool subpool(thread_pool, ThreadPool::work_pool_size(0, module->cells_size(), 1000));
subpool.run([&collectors, module](const ParallelDispatchThreadPool::RunCtx &ctx) {
for (int i : ctx.item_range(module->cells_size()))
collectors[ctx.thread_num].trace(module->cell_at(i));
for (int i : ctx.item_range(module->wires_size()))
collectors[ctx.thread_num].trace(module->wire_at(i));
});
collectors[0].trace(module->avail_parameters);
collectors[0].trace_keys(module->parameter_default_values);
collectors[0].trace_values(module->memories);
collectors[0].trace_values(module->processes);
}
collectors[0].trace(design->selection_vars);
}
ShardedVector<int> free_ids(thread_pool);
thread_pool.run([&live_ids, size, &free_ids](const ParallelDispatchThreadPool::RunCtx &ctx) {
for (int i : ctx.item_range(size - STATIC_ID_END)) {
int index = i + STATIC_ID_END;
RTLIL::IdString::Storage &storage = global_id_storage_.at(index);
if (storage.buf == nullptr)
continue;
if (live_ids[i].load())
continue;
if (global_refcount_storage_.find(index) != global_refcount_storage_.end())
continue;
free_ids.insert(ctx, index);
}
});
for (int i : free_ids) {
RTLIL::IdString::Storage &storage = global_id_storage_.at(i);
if (yosys_xtrace) {
log("#X# Removed IdString '%s' with index %d.\n", storage.buf, i);
log_backtrace("-X- ", yosys_xtrace-1);
@ -267,8 +300,13 @@ void RTLIL::OwningIdString::collect_garbage()
global_free_idx_list_.push_back(i);
}
std::unordered_set<int> live_autoidx_ids;
for (IdStringCollector &collector : collectors)
for (int id : collector.live_autoidx_ids)
live_autoidx_ids.insert(id);
for (auto it = global_autoidx_id_storage_.begin(); it != global_autoidx_id_storage_.end();) {
if (collector.live.find(it->first) != collector.live.end()) {
if (live_autoidx_ids.find(it->first) != live_autoidx_ids.end()) {
++it;
continue;
}
@ -287,159 +325,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")
@ -1465,15 +1361,21 @@ void RTLIL::Design::sort_modules()
modules_.sort(sort_by_id_str());
}
void check_module(RTLIL::Module *module, ParallelDispatchThreadPool &thread_pool);
void RTLIL::Design::check()
{
#ifndef NDEBUG
log_assert(!selection_stack.empty());
int pool_size = 0;
for (auto &it : modules_)
pool_size = std::max(pool_size, ThreadPool::work_pool_size(0, it.second->cells_size(), 1000));
ParallelDispatchThreadPool thread_pool(pool_size);
for (auto &it : modules_) {
log_assert(this == it.second->design);
log_assert(it.first == it.second->name);
log_assert(!it.first.empty());
it.second->check();
check_module(it.second, thread_pool);
}
#endif
}
@ -1548,6 +1450,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;
@ -1702,11 +1611,11 @@ size_t RTLIL::Module::count_id(RTLIL::IdString id)
namespace {
struct InternalCellChecker
{
RTLIL::Module *module;
const RTLIL::Module *module;
RTLIL::Cell *cell;
pool<RTLIL::IdString> expected_params, expected_ports;
InternalCellChecker(RTLIL::Module *module, RTLIL::Cell *cell) : module(module), cell(cell) { }
InternalCellChecker(const RTLIL::Module *module, RTLIL::Cell *cell) : module(module), cell(cell) { }
void error(int linenr)
{
@ -2682,88 +2591,96 @@ void RTLIL::Module::sort()
it.second->attributes.sort(sort_by_id_str());
}
void RTLIL::Module::check()
void check_module(RTLIL::Module *module, ParallelDispatchThreadPool &thread_pool)
{
#ifndef NDEBUG
std::vector<bool> ports_declared;
for (auto &it : wires_) {
log_assert(this == it.second->module);
log_assert(it.first == it.second->name);
log_assert(!it.first.empty());
log_assert(it.second->width >= 0);
log_assert(it.second->port_id >= 0);
for (auto &it2 : it.second->attributes)
log_assert(!it2.first.empty());
if (it.second->port_id) {
log_assert(GetSize(ports) >= it.second->port_id);
log_assert(ports.at(it.second->port_id-1) == it.first);
log_assert(it.second->port_input || it.second->port_output);
if (GetSize(ports_declared) < it.second->port_id)
ports_declared.resize(it.second->port_id);
log_assert(ports_declared[it.second->port_id-1] == false);
ports_declared[it.second->port_id-1] = true;
} else
log_assert(!it.second->port_input && !it.second->port_output);
}
for (auto port_declared : ports_declared)
log_assert(port_declared == true);
log_assert(GetSize(ports) == GetSize(ports_declared));
ParallelDispatchThreadPool::Subpool subpool(thread_pool, ThreadPool::work_pool_size(0, module->cells_size(), 1000));
const RTLIL::Module *const_module = module;
for (auto &it : memories) {
pool<std::string> memory_strings;
for (auto &it : module->memories) {
log_assert(it.first == it.second->name);
log_assert(!it.first.empty());
log_assert(it.second->width >= 0);
log_assert(it.second->size >= 0);
for (auto &it2 : it.second->attributes)
log_assert(!it2.first.empty());
memory_strings.insert(it.second->name.str());
}
pool<IdString> packed_memids;
std::vector<MonotonicFlag> ports_declared(GetSize(module->ports));
ShardedVector<std::string> memids(subpool);
subpool.run([const_module, &ports_declared, &memory_strings, &memids](const ParallelDispatchThreadPool::RunCtx &ctx) {
for (int i : ctx.item_range(const_module->cells_size())) {
auto it = *const_module->cells_.element(i);
log_assert(const_module == it.second->module);
log_assert(it.first == it.second->name);
log_assert(!it.first.empty());
log_assert(!it.second->type.empty());
for (auto &it2 : it.second->connections()) {
log_assert(!it2.first.empty());
it2.second.check(const_module);
}
for (auto &it2 : it.second->attributes)
log_assert(!it2.first.empty());
for (auto &it2 : it.second->parameters)
log_assert(!it2.first.empty());
InternalCellChecker checker(const_module, it.second);
checker.check();
if (it.second->has_memid()) {
log_assert(memory_strings.count(it.second->parameters.at(ID::MEMID).decode_string()));
} else if (it.second->is_mem_cell()) {
std::string memid = it.second->parameters.at(ID::MEMID).decode_string();
log_assert(!memory_strings.count(memid));
memids.insert(ctx, std::move(memid));
}
auto cell_mod = const_module->design->module(it.first);
if (cell_mod != nullptr) {
// assertion check below to make sure that there are no
// cases where a cell has a blackbox attribute since
// that is deprecated
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
log_assert(!it.second->get_blackbox_attribute());
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
}
}
for (auto &it : cells_) {
log_assert(this == it.second->module);
log_assert(it.first == it.second->name);
log_assert(!it.first.empty());
log_assert(!it.second->type.empty());
for (auto &it2 : it.second->connections()) {
log_assert(!it2.first.empty());
it2.second.check(this);
for (int i : ctx.item_range(const_module->wires_size())) {
auto it = *const_module->wires_.element(i);
log_assert(const_module == it.second->module);
log_assert(it.first == it.second->name);
log_assert(!it.first.empty());
log_assert(it.second->width >= 0);
log_assert(it.second->port_id >= 0);
for (auto &it2 : it.second->attributes)
log_assert(!it2.first.empty());
if (it.second->port_id) {
log_assert(GetSize(const_module->ports) >= it.second->port_id);
log_assert(const_module->ports.at(it.second->port_id-1) == it.first);
log_assert(it.second->port_input || it.second->port_output);
log_assert(it.second->port_id <= GetSize(ports_declared));
bool previously_declared = ports_declared[it.second->port_id-1].set_and_return_old();
log_assert(previously_declared == false);
} else
log_assert(!it.second->port_input && !it.second->port_output);
}
for (auto &it2 : it.second->attributes)
log_assert(!it2.first.empty());
for (auto &it2 : it.second->parameters)
log_assert(!it2.first.empty());
InternalCellChecker checker(this, it.second);
checker.check();
if (it.second->has_memid()) {
log_assert(memories.count(it.second->parameters.at(ID::MEMID).decode_string()));
} else if (it.second->is_mem_cell()) {
IdString memid = it.second->parameters.at(ID::MEMID).decode_string();
log_assert(!memories.count(memid));
log_assert(!packed_memids.count(memid));
packed_memids.insert(memid);
}
auto cell_mod = design->module(it.first);
if (cell_mod != nullptr) {
// assertion check below to make sure that there are no
// cases where a cell has a blackbox attribute since
// that is deprecated
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
log_assert(!it.second->get_blackbox_attribute());
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
}
}
});
for (const MonotonicFlag &port_declared : ports_declared)
log_assert(port_declared.load() == true);
pool<std::string> memids_pool;
for (std::string &memid : memids)
log_assert(memids_pool.insert(memid).second);
for (auto &it : processes) {
for (auto &it : module->processes) {
log_assert(it.first == it.second->name);
log_assert(!it.first.empty());
log_assert(it.second->root_case.compare.empty());
std::vector<CaseRule*> all_cases = {&it.second->root_case};
std::vector<RTLIL::CaseRule*> all_cases = {&it.second->root_case};
for (size_t i = 0; i < all_cases.size(); i++) {
for (auto &switch_it : all_cases[i]->switches) {
for (auto &case_it : switch_it->cases) {
@ -2776,34 +2693,41 @@ void RTLIL::Module::check()
}
for (auto &sync_it : it.second->syncs) {
switch (sync_it->type) {
case SyncType::ST0:
case SyncType::ST1:
case SyncType::STp:
case SyncType::STn:
case SyncType::STe:
case RTLIL::SyncType::ST0:
case RTLIL::SyncType::ST1:
case RTLIL::SyncType::STp:
case RTLIL::SyncType::STn:
case RTLIL::SyncType::STe:
log_assert(!sync_it->signal.empty());
break;
case SyncType::STa:
case SyncType::STg:
case SyncType::STi:
case RTLIL::SyncType::STa:
case RTLIL::SyncType::STg:
case RTLIL::SyncType::STi:
log_assert(sync_it->signal.empty());
break;
}
}
}
for (auto &it : connections_) {
for (auto &it : module->connections_) {
log_assert(it.first.size() == it.second.size());
log_assert(!it.first.has_const());
it.first.check(this);
it.second.check(this);
it.first.check(module);
it.second.check(module);
}
for (auto &it : attributes)
for (auto &it : module->attributes)
log_assert(!it.first.empty());
#endif
}
void RTLIL::Module::check()
{
int pool_size = ThreadPool::work_pool_size(0, cells_size(), 1000);
ParallelDispatchThreadPool thread_pool(pool_size);
check_module(this, thread_pool);
}
void RTLIL::Module::optimize()
{
}
@ -2982,6 +2906,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 +2965,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 +4214,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 +4248,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 +4277,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 +4291,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 +4319,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 +4526,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)
@ -5022,31 +4990,35 @@ void RTLIL::SigSpec::remove2(const RTLIL::SigSpec &pattern, RTLIL::SigSpec *othe
other->unpack();
}
bool modified = false;
bool other_modified = false;
for (int i = GetSize(bits_) - 1; i >= 0; i--)
{
if (bits_[i].wire == NULL) continue;
// Convert pattern to pool for O(1) lookup, avoiding O(n*m) chunk iteration
pool<SigBit> pattern_bits;
pattern_bits.reserve(pattern.size());
for (auto &bit : pattern)
if (bit.wire != NULL)
pattern_bits.insert(bit);
for (auto &pattern_chunk : pattern.chunks())
if (bits_[i].wire == pattern_chunk.wire &&
bits_[i].offset >= pattern_chunk.offset &&
bits_[i].offset < pattern_chunk.offset + pattern_chunk.width) {
modified = true;
bits_.erase(bits_.begin() + i);
if (other != NULL) {
other_modified = true;
other->bits_.erase(other->bits_.begin() + i);
}
break;
// Compact in-place to avoid O(n^2) erase operations
size_t write_idx = 0;
for (size_t read_idx = 0; read_idx < bits_.size(); read_idx++)
{
if (!(bits_[read_idx].wire != NULL && pattern_bits.count(bits_[read_idx]))) {
if (write_idx != read_idx) {
bits_[write_idx] = bits_[read_idx];
if (other != NULL)
other->bits_[write_idx] = other->bits_[read_idx];
}
write_idx++;
}
}
bool modified = (write_idx < bits_.size());
if (modified) {
bits_.resize(write_idx);
hash_.clear();
try_repack();
}
if (other_modified) {
if (other != NULL && modified) {
other->bits_.resize(write_idx);
other->hash_.clear();
other->try_repack();
}
@ -5073,24 +5045,27 @@ void RTLIL::SigSpec::remove2(const pool<RTLIL::SigBit> &pattern, RTLIL::SigSpec
other->unpack();
}
bool modified = false;
bool other_modified = false;
for (int i = GetSize(bits_) - 1; i >= 0; i--) {
if (bits_[i].wire != NULL && pattern.count(bits_[i])) {
modified = true;
bits_.erase(bits_.begin() + i);
if (other != NULL) {
other_modified = true;
other->bits_.erase(other->bits_.begin() + i);
// Avoid O(n^2) complexity by compacting in-place
size_t write_idx = 0;
for (size_t read_idx = 0; read_idx < bits_.size(); read_idx++) {
if (!(bits_[read_idx].wire != NULL && pattern.count(bits_[read_idx]))) {
if (write_idx != read_idx) {
bits_[write_idx] = bits_[read_idx];
if (other != NULL)
other->bits_[write_idx] = other->bits_[read_idx];
}
write_idx++;
}
}
bool modified = (write_idx < bits_.size());
if (modified) {
bits_.resize(write_idx);
hash_.clear();
try_repack();
}
if (other_modified) {
if (other != NULL && modified) {
other->bits_.resize(write_idx);
other->hash_.clear();
other->try_repack();
}
@ -5106,24 +5081,27 @@ void RTLIL::SigSpec::remove2(const std::set<RTLIL::SigBit> &pattern, RTLIL::SigS
other->unpack();
}
bool modified = false;
bool other_modified = false;
for (int i = GetSize(bits_) - 1; i >= 0; i--) {
if (bits_[i].wire != NULL && pattern.count(bits_[i])) {
modified = true;
bits_.erase(bits_.begin() + i);
if (other != NULL) {
other_modified = true;
other->bits_.erase(other->bits_.begin() + i);
// Avoid O(n^2) complexity by compacting in-place
size_t write_idx = 0;
for (size_t read_idx = 0; read_idx < bits_.size(); read_idx++) {
if (!(bits_[read_idx].wire != NULL && pattern.count(bits_[read_idx]))) {
if (write_idx != read_idx) {
bits_[write_idx] = bits_[read_idx];
if (other != NULL)
other->bits_[write_idx] = other->bits_[read_idx];
}
write_idx++;
}
}
bool modified = (write_idx < bits_.size());
if (modified) {
bits_.resize(write_idx);
hash_.clear();
try_repack();
}
if (other_modified) {
if (other != NULL && modified) {
other->bits_.resize(write_idx);
other->hash_.clear();
other->try_repack();
}
@ -5294,26 +5272,32 @@ RTLIL::SigSpec RTLIL::SigSpec::extract(int offset, int length) const
log_assert(length >= 0);
log_assert(offset + length <= size());
SigSpec extracted;
Chunks cs = chunks();
auto it = cs.begin();
for (; offset; offset -= it->width, ++it) {
if (offset < it->width) {
int chunk_length = min(it->width - offset, length);
extracted.append(it->extract(offset, chunk_length));
length -= chunk_length;
++it;
break;
}
}
for (; length; length -= it->width, ++it) {
if (length >= it->width) {
extracted.append(*it);
std::vector<SigBit> extracted;
SigBit first;
bool is_packing = true;
for (int i = offset; i < offset + length; i++) {
bool was_packing_before = is_packing;
SigBit bit = (*this)[i];
if (i == offset) {
first = bit;
if (!bit.wire)
is_packing = false;
} else {
extracted.append(it->extract(0, length));
break;
if (bit.wire != first.wire)
is_packing = false;
if (bit.wire)
if (bit.offset != first.offset + (i - offset))
is_packing = false;
}
if (was_packing_before && !is_packing)
for (int j = offset; j < i; j++)
extracted.push_back((*this)[j]);
if (!is_packing)
extracted.push_back((*this)[i]);
}
if (is_packing)
return SigChunk(first.wire, first.offset, length);
return extracted;
}
@ -5418,7 +5402,7 @@ RTLIL::SigSpec RTLIL::SigSpec::repeat(int num) const
}
#ifndef NDEBUG
void RTLIL::SigSpec::check(Module *mod) const
void RTLIL::SigSpec::check(const Module *mod) const
{
if (rep_ == CHUNK)
{

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)) {}
@ -275,6 +275,17 @@ struct RTLIL::IdString
*out += std::to_string(-index_);
}
std::string unescape() const {
if (index_ < 0) {
// Must start with "$auto$" so no unescaping required.
return str();
}
std::string_view str = global_id_storage_.at(index_).str_view();
if (str.size() < 2 || str[0] != '\\' || str[1] == '$' || str[1] == '\\' || (str[1] >= '0' && str[1] <= '9'))
return std::string(str);
return std::string(str.substr(1));
}
class Substrings {
std::string_view first_;
int suffix_number;
@ -737,6 +748,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) {
@ -758,7 +770,7 @@ namespace RTLIL {
}
static inline std::string unescape_id(RTLIL::IdString str) {
return unescape_id(str.str());
return str.unescape();
}
static inline const char *id2cstr(RTLIL::IdString str) {
@ -1388,6 +1400,8 @@ struct RTLIL::SigSpecConstIterator
struct RTLIL::SigSpec
{
private:
friend class SigSpecRepTest;
FRIEND_TEST(SigSpecRepTest, Extract);
enum Representation : char {
CHUNK,
BITS,
@ -1748,9 +1762,9 @@ public:
}
#ifndef NDEBUG
void check(Module *mod = nullptr) const;
void check(const Module *mod = nullptr) const;
#else
void check(Module *mod = nullptr) const { (void)mod; }
void check(const Module *mod = nullptr) const { (void)mod; }
#endif
};
@ -2031,7 +2045,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 +2154,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 +2412,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 +2466,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 +2484,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 +2544,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 +2624,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

@ -17,6 +17,20 @@ static int get_max_threads()
return max_threads;
}
static int init_work_units_per_thread_override()
{
const char *v = getenv("YOSYS_WORK_UNITS_PER_THREAD");
if (v == nullptr)
return 0;
return atoi(v);
}
static int get_work_units_per_thread_override()
{
static int work_units_per_thread = init_work_units_per_thread_override();
return work_units_per_thread;
}
void DeferredLogs::flush()
{
for (auto &m : logs)
@ -31,12 +45,23 @@ int ThreadPool::pool_size(int reserved_cores, int max_worker_threads)
#ifdef YOSYS_ENABLE_THREADS
int available_threads = std::min<int>(std::thread::hardware_concurrency(), get_max_threads());
int num_threads = std::min(available_threads - reserved_cores, max_worker_threads);
return std::max(0, num_threads);
return std::max(0, num_threads);
#else
return 0;
(void)reserved_cores;
(void)max_worker_threads;
(void)get_max_threads();
return 0;
#endif
}
int ThreadPool::work_pool_size(int reserved_cores, int work_units, int work_units_per_thread)
{
int work_units_per_thread_override = get_work_units_per_thread_override();
if (work_units_per_thread_override > 0)
work_units_per_thread = work_units_per_thread_override;
return pool_size(reserved_cores, work_units / work_units_per_thread);
}
ThreadPool::ThreadPool(int pool_size, std::function<void(int)> b)
: body(std::move(b))
{
@ -57,4 +82,76 @@ ThreadPool::~ThreadPool()
#endif
}
IntRange item_range_for_worker(int num_items, int thread_num, int num_threads)
{
if (num_threads <= 1) {
return {0, num_items};
}
int items_per_thread = num_items / num_threads;
int extra_items = num_items % num_threads;
// The first `extra_items` threads get one extra item.
int start = thread_num * items_per_thread + std::min(thread_num, extra_items);
int end = (thread_num + 1) * items_per_thread + std::min(thread_num + 1, extra_items);
return {start, end};
}
ParallelDispatchThreadPool::ParallelDispatchThreadPool(int pool_size)
: num_worker_threads_(std::max(1, pool_size) - 1)
{
#ifdef YOSYS_ENABLE_THREADS
main_to_workers_signal.resize(num_worker_threads_, 0);
#endif
// Don't start the threads until we've constructed all our data members.
thread_pool = std::make_unique<ThreadPool>(num_worker_threads_, [this](int thread_num){
run_worker(thread_num);
});
}
ParallelDispatchThreadPool::~ParallelDispatchThreadPool()
{
#ifdef YOSYS_ENABLE_THREADS
if (num_worker_threads_ == 0)
return;
current_work = nullptr;
num_active_worker_threads_.store(num_worker_threads_, std::memory_order_relaxed);
signal_workers_start();
wait_for_workers_done();
#endif
}
void ParallelDispatchThreadPool::run(std::function<void(const RunCtx &)> work, int max_threads)
{
Multithreading multithreading;
int num_active_worker_threads = num_threads(max_threads) - 1;
if (num_active_worker_threads == 0) {
work({{0}, 1});
return;
}
#ifdef YOSYS_ENABLE_THREADS
num_active_worker_threads_.store(num_active_worker_threads, std::memory_order_relaxed);
current_work = &work;
signal_workers_start();
work({{0}, num_active_worker_threads + 1});
wait_for_workers_done();
#endif
}
void ParallelDispatchThreadPool::run_worker(int thread_num)
{
#ifdef YOSYS_ENABLE_THREADS
while (true)
{
worker_wait_for_start(thread_num);
if (current_work == nullptr)
break;
int num_active_worker_threads = num_active_worker_threads_.load(std::memory_order_relaxed);
(*current_work)({{thread_num + 1}, num_active_worker_threads + 1});
signal_worker_done();
}
signal_worker_done();
#else
(void)current_work;
#endif
}
YOSYS_NAMESPACE_END

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