diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 2c1483345..2ed92c913 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -6,7 +6,7 @@ body:
attributes:
value: >
- Learn more [here](https://yosyshq.readthedocs.io/projects/yosys/en/latest/yosys_internals/extending_yosys/contributing.html#reporting-bugs) about how to report bugs. We fix well-reported bugs the fastest.
+ 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/).
diff --git a/.github/actions/setup-build-env/action.yml b/.github/actions/setup-build-env/action.yml
index c9dc5fc22..c1d3e2b01 100644
--- a/.github/actions/setup-build-env/action.yml
+++ b/.github/actions/setup-build-env/action.yml
@@ -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 libgtest-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
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 4bca5a8a5..74ecbe0ad 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -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
diff --git a/.github/workflows/extra-builds.yml b/.github/workflows/extra-builds.yml
index 8af99def8..7209b3645 100644
--- a/.github/workflows/extra-builds.yml
+++ b/.github/workflows/extra-builds.yml
@@ -1,23 +1,20 @@
name: Test extra build flows
on:
- # always test main
- push:
- branches:
- - main
- merge_group:
- # 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
@@ -26,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
@@ -48,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: .
@@ -65,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
@@ -111,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
@@ -126,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"
diff --git a/.github/workflows/prepare-docs.yml b/.github/workflows/prepare-docs.yml
index e3d917942..06c465c44 100644
--- a/.github/workflows/prepare-docs.yml
+++ b/.github/workflows/prepare-docs.yml
@@ -1,12 +1,18 @@
name: Build docs artifact with Verific
-on: [push, pull_request, merge_group]
+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 }}
diff --git a/.github/workflows/source-vendor.yml b/.github/workflows/source-vendor.yml
index dc9480ef6..d85b2af08 100644
--- a/.github/workflows/source-vendor.yml
+++ b/.github/workflows/source-vendor.yml
@@ -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
diff --git a/.github/workflows/test-build.yml b/.github/workflows/test-build.yml
index ebdeb15d8..5e990d0a5 100644
--- a/.github/workflows/test-build.yml
+++ b/.github/workflows/test-build.yml
@@ -1,23 +1,20 @@
name: Build and run tests
on:
- # always test main
- push:
- branches:
- - main
- merge_group:
- # 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
@@ -26,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
@@ -40,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 }}
@@ -54,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
@@ -85,7 +99,7 @@ jobs:
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
@@ -104,7 +118,7 @@ jobs:
fail-fast: false
steps:
- name: Checkout Yosys
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
with:
persist-credentials: false
@@ -116,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 }}
@@ -152,7 +166,7 @@ jobs:
os: [ubuntu-latest]
steps:
- name: Checkout Yosys
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
with:
persist-credentials: false
@@ -162,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 }}
@@ -192,7 +206,7 @@ jobs:
fail-fast: false
steps:
- name: Checkout Yosys
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
with:
persist-credentials: false
@@ -204,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 }}
@@ -226,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]
@@ -265,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"
diff --git a/.github/workflows/test-compile.yml b/.github/workflows/test-compile.yml
index 000d1c400..99e4973a7 100644
--- a/.github/workflows/test-compile.yml
+++ b/.github/workflows/test-compile.yml
@@ -1,23 +1,20 @@
name: Compiler testing
on:
- # always test main
- push:
- branches:
- - main
- merge_group:
- # 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
@@ -26,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' }}
@@ -39,22 +44,22 @@ jobs:
- ubuntu-latest
compiler:
# oldest supported
- - 'clang-10'
- - 'gcc-10'
+ - 'clang-14'
+ - 'gcc-11'
# newest, make sure to update maximum standard step to match
- - 'clang-19'
- - 'gcc-14'
+ - 'clang-22'
+ - 'gcc-15'
include:
# macOS x86
- os: macos-15-intel
- compiler: 'clang-19'
+ compiler: 'clang-22'
# macOS arm
- os: macos-latest
- compiler: 'clang-19'
+ compiler: 'clang-22'
fail-fast: false
steps:
- name: Checkout Yosys
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
with:
submodules: true
persist-credentials: false
@@ -69,6 +74,7 @@ jobs:
uses: aminya/setup-cpp@v1
with:
compiler: ${{ matrix.compiler }}
+ gcc: ${{ (matrix.os == 'ubuntu-latest' && matrix.compiler == 'clang-14') && '12' || '' }}
- name: Tool versions
shell: bash
@@ -76,17 +82,38 @@ jobs:
$CC --version
$CXX --version
- # minimum standard
- - name: Build C++17
- shell: bash
+ - name: Fix clang-14 toolchain
+ if: matrix.os == 'ubuntu-latest' && matrix.compiler == 'clang-14'
run: |
- make config-$CC_SHORT
- make -j$procs CXXSTD=c++17 compile-only
+ echo 'CXXFLAGS=--gcc-toolchain=/usr -isystem /usr/include/c++/12 -isystem /usr/include/x86_64-linux-gnu/c++/12' >> $GITHUB_ENV
- # maximum standard, only on newest compilers
+ # minimum standard
- name: Build C++20
- if: ${{ matrix.compiler == 'clang-19' || matrix.compiler == 'gcc-14' }}
shell: bash
run: |
make config-$CC_SHORT
make -j$procs CXXSTD=c++20 compile-only
+
+ # maximum standard, only on newest compilers
+ - name: Build C++26
+ if: ${{ matrix.compiler == 'clang-22' || matrix.compiler == 'gcc-15' }}
+ shell: bash
+ run: |
+ make config-$CC_SHORT
+ make -j$procs CXXSTD=c++26 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"
diff --git a/.github/workflows/test-sanitizers.yml b/.github/workflows/test-sanitizers.yml
index 7650470c3..6f3c10903 100644
--- a/.github/workflows/test-sanitizers.yml
+++ b/.github/workflows/test-sanitizers.yml
@@ -1,32 +1,41 @@
name: Check clang sanitizers
on:
- # always test main
- push:
- branches:
- - main
+ pull_request:
merge_group:
- # ignore PRs due to time needed
- # allow triggering tests, ignores skip check
+ #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
@@ -38,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
@@ -73,3 +82,18 @@ jobs:
run: |
find tests/**/*.err -print -exec cat {} \;
+ test-sanitizers-result:
+ runs-on: ubuntu-latest
+ needs:
+ - run_san
+ 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"
\ No newline at end of file
diff --git a/.github/workflows/test-verific-cfg.yml b/.github/workflows/test-verific-cfg.yml
new file mode 100644
index 000000000..232ca6bd7
--- /dev/null
+++ b/.github/workflows/test-verific-cfg.yml
@@ -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
diff --git a/.github/workflows/test-verific.yml b/.github/workflows/test-verific.yml
index cd2545cc8..bdd35428a 100644
--- a/.github/workflows/test-verific.yml
+++ b/.github/workflows/test-verific.yml
@@ -1,23 +1,20 @@
name: Build and run tests with Verific (Linux)
on:
- # always test main
- push:
- branches:
- - main
- merge_group:
- # 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
@@ -26,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
@@ -39,10 +44,18 @@ jobs:
- name: Runtime environment
run: |
echo "procs=$(nproc)" >> $GITHUB_ENV
+ mkdir -p "${GITHUB_WORKSPACE}/coverage"
+ echo "LLVM_PROFILE_FILE=${GITHUB_WORKSPACE}/coverage/coverage_%p.profraw" >> $GITHUB_ENV
+ echo "LLVM_PROFILE_FILE_BUFFER_SIZE=0" >> $GITHUB_ENV
+
+ - name: Skip generating files
+ if: ${{ github.event_name != 'merge_group' && github.event_name != 'workflow_dispatch' }}
+ run: |
+ echo "LLVM_PROFILE_FILE=/dev/null" >> $GITHUB_ENV
- name: Build Yosys
run: |
- make config-clang
+ make config-gcov
echo "ENABLE_VERIFIC := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_EDIF := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_LIBERTY := 1" >> Makefile.conf
@@ -73,16 +86,41 @@ jobs:
- 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 coverage
+ if: ${{ github.event_name == 'merge_group' || github.event_name == 'workflow_dispatch' }}
+ run: |
+ make coverage
+ make clean_coverage
+
+ - name: Push coverage
+ if: ${{ github.event_name == 'merge_group' || github.event_name == 'workflow_dispatch' }}
+ run: |
+ git clone https://x-access-token:${{ secrets.REPORTS_TOKEN }}@github.com/YosysHQ/reports.git out
+ rm -rf out/coverage/main
+ mkdir -p out/coverage/main
+ cp -r coverage_html/* out/coverage/main/
+ cd out
+ # find . -name "*.html" -type f -print0 | xargs -0 sed -i -z 's#\(
[[:space:]]*\)#\1\2#g'
+ git config user.name "yosyshq-ci"
+ git config user.email "105224853+yosyshq-ci@users.noreply.github.com"
+ git add .
+ if ! git diff --cached --quiet; then
+ git commit -m "Update coverage"
+ git push
+ else
+ echo "No changes to commit"
+ fi
+
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
@@ -118,3 +156,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"
diff --git a/.github/workflows/update-flake-lock.yml b/.github/workflows/update-flake-lock.yml
deleted file mode 100644
index b32498baf..000000000
--- a/.github/workflows/update-flake-lock.yml
+++ /dev/null
@@ -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
diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml
index 8e055f526..8139b5af5 100644
--- a/.github/workflows/wheels.yml
+++ b/.github/workflows/wheels.yml
@@ -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-*
diff --git a/.github/workflows/wheels/cibw_before_all.sh b/.github/workflows/wheels/cibw_before_all.sh
index 1aef650d7..6450ce273 100644
--- a/.github/workflows/wheels/cibw_before_all.sh
+++ b/.github/workflows/wheels/cibw_before_all.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
set -e -x
# Build-time dependencies
diff --git a/.gitignore b/.gitignore
index a8b04ac45..b39088088 100644
--- a/.gitignore
+++ b/.gitignore
@@ -65,10 +65,10 @@
/viz.js
# other
-/coverage.info
+/yosys.profdata
+/coverage
/coverage_html
-
# these really belong in global gitignore since they're not specific to this project but rather to user tool choice
# but too many people don't have a global gitignore configured:
# https://docs.github.com/en/get-started/git-basics/ignoring-files#configuring-ignored-files-for-all-repositories-on-your-computer
diff --git a/Brewfile b/Brewfile
index 917f1bdb4..2530b323c 100644
--- a/Brewfile
+++ b/Brewfile
@@ -9,6 +9,6 @@ brew "python3"
brew "uv"
brew "xdot"
brew "bash"
-brew "llvm@20"
+brew "llvm"
brew "lld"
brew "googletest"
diff --git a/CHANGELOG b/CHANGELOG
index e345a8514..01faf44c2 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -2,9 +2,52 @@
List of major changes and improvements between releases
=======================================================
-Yosys 0.62 .. Yosys 0.63-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
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 6eadbec31..849d1fb00 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -5,59 +5,11 @@ first time contributing to an open source project, please take a look at the
following guide about the basics:
https://opensource.guide/how-to-contribute/#orienting-yourself-to-a-new-project.
-## Asking questions
+Check out our [Contributing guidelines](https://yosys.readthedocs.io/en/latest/yosys_internals/extending_yosys/contributing.html) to learn the best ways to
-If you have a question about how to use Yosys, please ask on our [Discourse forum](https://yosyshq.discourse.group/).
-The Discourse is also a great place to ask questions about developing or
-contributing to Yosys.
++ get help
++ report bugs
++ contribute code
++ review code
-We have open [dev 'jour fixe' (JF) meetings](https://docs.google.com/document/d/1SapA6QAsJcsgwsdKJDgnGR2mr97pJjV4eeXg_TVJhRU/edit?usp=sharing) where developers from YosysHQ and the
-community come together to discuss open issues and PRs. This is also a good
-place to talk to us about how to implement larger PRs.
-
-## Using the issue tracker
-
-The [issue tracker](https://github.com/YosysHQ/yosys/issues) is used for
-tracking bugs or other problems with Yosys or its documentation. It is also the
-place to go for requesting new features.
-
-### Bug reports
-
-Learn more [here](https://yosyshq.readthedocs.io/projects/yosys/en/latest/yosys_internals/extending_yosys/contributing.html#reporting-bugs) about how to report bugs. We fix well-reported bugs the fastest.
-
-## Contributing code
-
-If you're adding complex functionality, or modifying core parts of Yosys,
-we highly recommend discussing your motivation and approach
-ahead of time on the [Discourse forum](https://yosyshq.discourse.group/).
-
-### Using pull requests
-
-If you are working on something to add to Yosys, or fix something that isn't
-working quite right,
-make a [pull request (PR)](https://github.com/YosysHQ/yosys/pulls).
-
-An open PR, even as a draft, tells everyone that you're working on it and they
-don't have to. It can also be a useful way to solicit feedback on in-progress
-changes. See above to find the best way to [ask us questions](#asking-questions).
-
-### Continuous integration
-
-[Continuous Integration (CI)](https://github.com/YosysHQ/yosys/actions) tools
-automatically compile Yosys and run it with the full suite of tests.
-If you're a first time contributor, a maintainer has to trigger a run for you.
-We test on various platforms, compilers. Sanitizer builds are only tested
-on the main branch.
-
-### Labels
-
-We use [labels](https://github.com/YosysHQ/yosys/labels) to help categorise
-issues and PRs. If a label seems relevant to your work, please do add it; this
-also includes the labels beginning with 'status-'. The 'merge-' labels are used
-by maintainers for tracking and communicating which PRs are ready and pending
-merge; please do not use these labels if you are not a maintainer.
-
-
-### Coding style
-
-Learn more [here](https://yosys.readthedocs.io/en/latest/yosys_internals/extending_yosys/contributing.html).
+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).
diff --git a/Makefile b/Makefile
index 0a15c2b23..6ee34070d 100644
--- a/Makefile
+++ b/Makefile
@@ -103,8 +103,8 @@ VPATH := $(YOSYS_SRC)
# Unit test
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
+export CXXSTD ?= c++20
+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,7 @@ ifeq ($(OS), Haiku)
CXXFLAGS += -D_DEFAULT_SOURCE
endif
-YOSYS_VER := 0.62
+YOSYS_VER := 0.65
ifneq (, $(shell command -v git 2>/dev/null))
ifneq (, $(shell git rev-parse --git-dir 2>/dev/null))
@@ -291,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
@@ -455,8 +456,11 @@ endif
endif
ifeq ($(ENABLE_GCOV),1)
-CXXFLAGS += --coverage
-LINKFLAGS += --coverage
+LLVM_PROFILE_FILE ?= $(realpath $(YOSYS_SRC))/coverage/coverage_%p.profraw
+export LLVM_PROFILE_FILE
+export LLVM_PROFILE_FILE_BUFFER_SIZE=0
+CXXFLAGS += -fprofile-instr-generate -fcoverage-mapping
+LINKFLAGS+= -fprofile-instr-generate
endif
ifeq ($(ENABLE_GPROF),1)
@@ -613,6 +617,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))
@@ -917,109 +922,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
-
-# 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
-
-# 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: vanilla-test unit-test
-vanilla-test: makefile-tests abcopt-tests seed-tests
- @echo ""
- @echo " Passed \"make vanilla-test\"."
-ifeq ($(ENABLE_VERIFIC),1)
-ifeq ($(YOSYS_NOVERIFIC),1)
- @echo " Ran tests without verific support due to YOSYS_NOVERIFIC=1."
-endif
-endif
- @echo ""
+.PHONY: vanilla-test
+
+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
@@ -1060,14 +971,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)/.
@@ -1185,16 +1096,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
@@ -1215,12 +1118,13 @@ mrproper: clean
coverage:
./$(PROGRAM_PREFIX)yosys -qp 'help; help -all'
- rm -rf coverage.info coverage_html
- lcov --capture -d . --no-external -o coverage.info
- genhtml coverage.info --output-directory coverage_html
+ rm -rf coverage_html
+ llvm-profdata merge -sparse coverage/coverage_*.profraw -o yosys.profdata
+ llvm-cov show ./$(PROGRAM_PREFIX)yosys -instr-profile=yosys.profdata -format=html -output-dir=coverage_html --compilation-dir=. -ignore-filename-regex='(^|.*/)libs/.*|/usr/include/.*|$(subst /,\/,$(VERIFIC_DIR))/.*'
clean_coverage:
- find . -name "*.gcda" -type f -delete
+ rm -rf coverage
+ rm -f yosys.profdata
FUNC_KERNEL := functional.cc functional.h sexpr.cc sexpr.h compute_graph.h
FUNC_INCLUDES := $(addprefix --include *,functional/* $(FUNC_KERNEL))
@@ -1242,7 +1146,7 @@ vcxsrc: $(GENFILES) $(EXTRA_TARGETS) kernel/version_$(GIT_REV).cc
rm -rf $(VCX_DIR_NAME){,.zip}
cp -f kernel/version_$(GIT_REV).cc kernel/version.cc
set -e; for f in `ls $(filter %.cc %.cpp,$(GENFILES)) $(addsuffix .cc,$(basename $(OBJS))) $(addsuffix .cpp,$(basename $(OBJS))) 2> /dev/null`; do \
- echo "Analyse: $$f" >&2; cpp -std=c++17 -MM -I. -D_YOSYS_ $$f; done | sed 's,.*:,,; s,//*,/,g; s,/[^/]*/\.\./,/,g; y, \\,\n\n,;' | grep '^[^/]' | sort -u | grep -v kernel/version_ > srcfiles.txt
+ echo "Analyse: $$f" >&2; cpp -std=c++20 -MM -I. -D_YOSYS_ $$f; done | sed 's,.*:,,; s,//*,/,g; s,/[^/]*/\.\./,/,g; y, \\,\n\n,;' | grep '^[^/]' | sort -u | grep -v kernel/version_ > srcfiles.txt
echo "libs/fst/fst_win_unistd.h" >> srcfiles.txt
echo "kernel/version.cc" >> srcfiles.txt
bash misc/create_vcxsrc.sh $(VCX_DIR_NAME) $(YOSYS_VER)
@@ -1282,7 +1186,7 @@ config-msys2-64: clean
echo "PREFIX := $(MINGW_PREFIX)" >> Makefile.conf
config-gcov: clean
- echo 'CONFIG := gcc' > Makefile.conf
+ echo 'CONFIG := clang' > Makefile.conf
echo 'ENABLE_GCOV := 1' >> Makefile.conf
echo 'ENABLE_DEBUG := 1' >> Makefile.conf
diff --git a/abc b/abc
index c18b835ef..5d51a5e42 160000
--- a/abc
+++ b/abc
@@ -1 +1 @@
-Subproject commit c18b835ef140217c84a26ba510f98f69d54dd48e
+Subproject commit 5d51a5e420f5de493d07bf61109a977248c86ffb
diff --git a/backends/aiger/aiger.cc b/backends/aiger/aiger.cc
index 95f4c19e2..1320937a0 100644
--- a/backends/aiger/aiger.cc
+++ b/backends/aiger/aiger.cc
@@ -340,7 +340,7 @@ struct AigerWriter
if (cell->type == ID($scopeinfo))
continue;
- log_error("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell));
+ log_error("Unsupported cell type: %s (%s)\n", cell->type.unescape(), cell);
}
for (auto bit : unused_bits)
@@ -349,10 +349,10 @@ struct AigerWriter
if (!undriven_bits.empty()) {
undriven_bits.sort();
for (auto bit : undriven_bits) {
- log_warning("Treating undriven bit %s.%s like $anyseq.\n", log_id(module), log_signal(bit));
+ log_warning("Treating undriven bit %s.%s like $anyseq.\n", module, log_signal(bit));
input_bits.insert(bit);
}
- log_warning("Treating a total of %d undriven bits in %s like $anyseq.\n", GetSize(undriven_bits), log_id(module));
+ log_warning("Treating a total of %d undriven bits in %s like $anyseq.\n", GetSize(undriven_bits), module);
}
init_map.sort();
@@ -635,35 +635,35 @@ struct AigerWriter
int a = aig_map.at(sig[i]);
log_assert((a & 1) == 0);
if (GetSize(wire) != 1)
- symbols[stringf("i%d", (a >> 1)-1)].push_back(stringf("%s[%d]", log_id(wire), i));
+ symbols[stringf("i%d", (a >> 1)-1)].push_back(stringf("%s[%d]", wire, i));
else
- symbols[stringf("i%d", (a >> 1)-1)].push_back(stringf("%s", log_id(wire)));
+ symbols[stringf("i%d", (a >> 1)-1)].push_back(stringf("%s", wire));
}
if (wire->port_output) {
int o = ordered_outputs.at(SigSpec(wire, i));
if (GetSize(wire) != 1)
- symbols[stringf("%c%d", miter_mode ? 'b' : 'o', o)].push_back(stringf("%s[%d]", log_id(wire), i));
+ symbols[stringf("%c%d", miter_mode ? 'b' : 'o', o)].push_back(stringf("%s[%d]", wire, i));
else
- symbols[stringf("%c%d", miter_mode ? 'b' : 'o', o)].push_back(stringf("%s", log_id(wire)));
+ symbols[stringf("%c%d", miter_mode ? 'b' : 'o', o)].push_back(stringf("%s", wire));
}
if (init_inputs.count(sig[i])) {
int a = init_inputs.at(sig[i]);
log_assert((a & 1) == 0);
if (GetSize(wire) != 1)
- symbols[stringf("i%d", (a >> 1)-1)].push_back(stringf("init:%s[%d]", log_id(wire), i));
+ symbols[stringf("i%d", (a >> 1)-1)].push_back(stringf("init:%s[%d]", wire, i));
else
- symbols[stringf("i%d", (a >> 1)-1)].push_back(stringf("init:%s", log_id(wire)));
+ symbols[stringf("i%d", (a >> 1)-1)].push_back(stringf("init:%s", wire));
}
if (ordered_latches.count(sig[i])) {
int l = ordered_latches.at(sig[i]);
const char *p = (zinit_mode && (aig_latchinit.at(l) == 1)) ? "!" : "";
if (GetSize(wire) != 1)
- symbols[stringf("l%d", l)].push_back(stringf("%s%s[%d]", p, log_id(wire), i));
+ symbols[stringf("l%d", l)].push_back(stringf("%s%s[%d]", p, wire, i));
else
- symbols[stringf("l%d", l)].push_back(stringf("%s%s", p, log_id(wire)));
+ symbols[stringf("l%d", l)].push_back(stringf("%s%s", p, wire));
}
}
}
@@ -705,30 +705,30 @@ struct AigerWriter
int index = no_startoffset ? i : (wire->start_offset+i);
if (verbose_map)
- wire_lines[a] += stringf("wire %d %d %s\n", a, index, log_id(wire));
+ wire_lines[a] += stringf("wire %d %d %s\n", a, index, wire);
if (wire->port_input) {
log_assert((a & 1) == 0);
- input_lines[a] += stringf("input %d %d %s\n", (a >> 1)-1, index, log_id(wire));
+ input_lines[a] += stringf("input %d %d %s\n", (a >> 1)-1, index, wire);
}
if (wire->port_output) {
int o = ordered_outputs.at(SigSpec(wire, i));
- output_lines[o] += stringf("output %d %d %s\n", o, index, log_id(wire));
+ output_lines[o] += stringf("output %d %d %s\n", o, index, wire);
}
if (init_inputs.count(sig[i])) {
int a = init_inputs.at(sig[i]);
log_assert((a & 1) == 0);
- init_lines[a] += stringf("init %d %d %s\n", (a >> 1)-1, index, log_id(wire));
+ init_lines[a] += stringf("init %d %d %s\n", (a >> 1)-1, index, wire);
}
if (ordered_latches.count(sig[i])) {
int l = ordered_latches.at(sig[i]);
if (zinit_mode && (aig_latchinit.at(l) == 1))
- latch_lines[l] += stringf("invlatch %d %d %s\n", l, index, log_id(wire));
+ latch_lines[l] += stringf("invlatch %d %d %s\n", l, index, wire);
else
- latch_lines[l] += stringf("latch %d %d %s\n", l, index, log_id(wire));
+ latch_lines[l] += stringf("latch %d %d %s\n", l, index, wire);
}
}
}
@@ -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 \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");
@@ -1025,12 +1027,12 @@ struct AigerBackend : public Backend {
log_error("Can't find top module in current design!\n");
if (!design->selected_whole_module(top_module))
- log_cmd_error("Can't handle partially selected module %s!\n", log_id(top_module));
+ log_cmd_error("Can't handle partially selected module %s!\n", top_module);
if (!top_module->processes.empty())
- log_error("Found unmapped processes in module %s: unmapped processes are not supported in AIGER backend!\n", log_id(top_module));
+ log_error("Found unmapped processes in module %s: unmapped processes are not supported in AIGER backend!\n", top_module);
if (!top_module->memories.empty())
- log_error("Found unmapped memories in module %s: unmapped memories are not supported in AIGER backend!\n", log_id(top_module));
+ log_error("Found unmapped memories in module %s: unmapped memories are not supported in AIGER backend!\n", top_module);
AigerWriter writer(top_module, no_sort, zinit_mode, imode, omode, bmode, lmode);
writer.write_aiger(*f, ascii_mode, miter_mode, symbols_mode);
diff --git a/backends/aiger/xaiger.cc b/backends/aiger/xaiger.cc
index 988bc558b..cc1085f96 100644
--- a/backends/aiger/xaiger.cc
+++ b/backends/aiger/xaiger.cc
@@ -268,7 +268,7 @@ struct XAigerWriter
if (ys_debug(1)) {
static pool> seen;
if (seen.emplace(inst_module->name, i.first).second) log("%s.%s[%d] abc9_arrival = %d\n",
- log_id(cell->type), log_id(i.first.name), offset, d);
+ cell->type.unescape(), i.first.name.unescape(), offset, d);
}
#endif
arrival_times[rhs[offset]] = d;
@@ -285,7 +285,7 @@ struct XAigerWriter
auto is_input = (port_wire && port_wire->port_input) || !cell_known || cell->input(c.first);
auto is_output = (port_wire && port_wire->port_output) || !cell_known || cell->output(c.first);
if (!is_input && !is_output)
- log_error("Connection '%s' on cell '%s' (type '%s') not recognised!\n", log_id(c.first), log_id(cell), log_id(cell->type));
+ log_error("Connection '%s' on cell '%s' (type '%s') not recognised!\n", c.first.unescape(), cell, cell->type.unescape());
if (is_input)
for (auto b : c.second) {
@@ -303,7 +303,7 @@ struct XAigerWriter
}
}
- //log_warning("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell));
+ //log_warning("Unsupported cell type: %s (%s)\n", cell->type.unescape(), cell);
}
dict> box_ports;
@@ -325,12 +325,12 @@ struct XAigerWriter
if (w->get_bool_attribute(ID::abc9_carry)) {
if (w->port_input) {
if (carry_in != IdString())
- log_error("Module '%s' contains more than one 'abc9_carry' input port.\n", log_id(box_module));
+ log_error("Module '%s' contains more than one 'abc9_carry' input port.\n", box_module);
carry_in = port_name;
}
if (w->port_output) {
if (carry_out != IdString())
- log_error("Module '%s' contains more than one 'abc9_carry' output port.\n", log_id(box_module));
+ log_error("Module '%s' contains more than one 'abc9_carry' output port.\n", box_module);
carry_out = port_name;
}
}
@@ -339,9 +339,9 @@ struct XAigerWriter
}
if (carry_in != IdString() && carry_out == IdString())
- log_error("Module '%s' contains an 'abc9_carry' input port but no output port.\n", log_id(box_module));
+ log_error("Module '%s' contains an 'abc9_carry' input port but no output port.\n", box_module);
if (carry_in == IdString() && carry_out != IdString())
- log_error("Module '%s' contains an 'abc9_carry' output port but no input port.\n", log_id(box_module));
+ log_error("Module '%s' contains an 'abc9_carry' output port but no input port.\n", box_module);
if (carry_in != IdString()) {
r.first->second.push_back(carry_in);
r.first->second.push_back(carry_out);
@@ -612,7 +612,7 @@ struct XAigerWriter
write_r_buffer(mergeability);
State init = init_map.at(q, State::Sx);
- log_debug("Cell '%s' (type %s) has (* init *) value '%s'.\n", log_id(cell), log_id(cell->type), log_signal(init));
+ log_debug("Cell '%s' (type %s) has (* init *) value '%s'.\n", cell, cell->type.unescape(), log_signal(init));
if (init == State::S1)
write_s_buffer(1);
else if (init == State::S0)
@@ -692,12 +692,12 @@ struct XAigerWriter
if (input_bits.count(b)) {
int a = aig_map.at(b);
log_assert((a & 1) == 0);
- input_lines[a] += stringf("input %d %d %s\n", (a >> 1)-1, wire->start_offset+i, log_id(wire));
+ input_lines[a] += stringf("input %d %d %s\n", (a >> 1)-1, wire->start_offset+i, wire);
}
if (output_bits.count(b)) {
int o = ordered_outputs.at(b);
- output_lines[o] += stringf("output %d %d %s\n", o - GetSize(co_bits), wire->start_offset+i, log_id(wire));
+ output_lines[o] += stringf("output %d %d %s\n", o - GetSize(co_bits), wire->start_offset+i, wire);
}
}
}
@@ -709,7 +709,7 @@ struct XAigerWriter
int box_count = 0;
for (auto cell : box_list)
- f << stringf("box %d %d %s\n", box_count++, 0, log_id(cell->name));
+ f << stringf("box %d %d %s\n", box_count++, 0, cell->name.unescape());
output_lines.sort();
for (auto &it : output_lines)
@@ -774,12 +774,12 @@ struct XAigerBackend : public Backend {
log_error("Can't find top module in current design!\n");
if (!design->selected_whole_module(top_module))
- log_cmd_error("Can't handle partially selected module %s!\n", log_id(top_module));
+ log_cmd_error("Can't handle partially selected module %s!\n", top_module);
if (!top_module->processes.empty())
- log_error("Found unmapped processes in module %s: unmapped processes are not supported in XAIGER backend!\n", log_id(top_module));
+ log_error("Found unmapped processes in module %s: unmapped processes are not supported in XAIGER backend!\n", top_module);
if (!top_module->memories.empty())
- log_error("Found unmapped memories in module %s: unmapped memories are not supported in XAIGER backend!\n", log_id(top_module));
+ log_error("Found unmapped memories in module %s: unmapped memories are not supported in XAIGER backend!\n", top_module);
XAigerWriter writer(top_module, dff_mode);
writer.write_aiger(*f, ascii_mode);
diff --git a/backends/aiger2/aiger.cc b/backends/aiger2/aiger.cc
index babc29826..0dceaedd6 100644
--- a/backends/aiger2/aiger.cc
+++ b/backends/aiger2/aiger.cc
@@ -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
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
@@ -115,7 +132,7 @@ struct Index {
continue;
if (!submodule || submodule->get_blackbox_attribute())
log_error("Unsupported cell type: %s (%s in %s)\n",
- log_id(cell->type), log_id(cell), log_id(m));
+ cell->type.unescape(), cell, m);
}
}
}
@@ -163,6 +180,11 @@ struct Index {
if (!strashing) {
return (static_cast(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 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", cell->type.unescape(), cell, 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 += minfo.module->name.unescape();
else
- ret += RTLIL::unescape_id(pair.second->name);
+ ret += cell->name.unescape();
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);
@@ -588,10 +636,10 @@ struct Index {
Wire *w = def->wire(portname);
if (!w)
log_error("Output port %s on instance %s of %s doesn't exist\n",
- log_id(portname), log_id(driver), log_id(def));
+ portname.unescape(), driver, def);
if (bit.offset >= w->width)
log_error("Bit position %d of output port %s on instance %s of %s is out of range (port has width %d)\n",
- bit.offset, log_id(portname), log_id(driver), log_id(def), w->width);
+ bit.offset, portname.unescape(), driver, def, w->width);
ret = visit(cursor, SigBit(w, bit.offset));
}
cursor.exit(*this);
@@ -607,11 +655,11 @@ struct Index {
IdString portname = bit.wire->name;
if (!instance->hasPort(portname))
log_error("Input port %s on instance %s of %s unconnected\n",
- log_id(portname), log_id(instance), log_id(instance->type));
+ portname.unescape(), instance, instance->type);
auto &port = instance->getPort(portname);
if (bit.offset >= port.size())
log_error("Bit %d of input port %s on instance %s of %s unconnected\n",
- bit.offset, log_id(portname), log_id(instance), log_id(instance->type));
+ bit.offset, portname.unescape(), instance, instance->type.unescape());
ret = visit(cursor, port[bit.offset]);
}
cursor.enter(*this, instance);
@@ -701,6 +749,9 @@ struct AigerWriter : Index {
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 {
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 {
// populate inputs
std::vector 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 {
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> 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 {
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 = bit.wire->name.unescape();
f->write(name.data(), name.size());
f->put('\n');
}
@@ -797,10 +853,11 @@ struct AigerWriter : Index {
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);
+ std::string name = bit.wire->name.unescape();
f->write(name.data(), name.size());
f->put('\n');
}
@@ -871,33 +928,34 @@ struct XAigerAnalysis : Index {
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 pos;
std::vector 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 driven_by_opaque_box;
@@ -990,7 +1048,7 @@ struct XAigerWriter : AigerWriter {
} else if (!is_input && !inputs) {
for (auto &bit : conn.second) {
if (!bit.wire || (bit.wire->port_input && !bit.wire->port_output))
- log_error("Bad connection %s/%s ~ %s\n", log_id(box), log_id(conn.first), log_signal(conn.second));
+ log_error("Bad connection %s/%s ~ %s\n", box, conn.first.unescape(), log_signal(conn.second));
ensure_pi(bit, cursor);
@@ -1015,9 +1073,9 @@ struct XAigerWriter : AigerWriter {
void prep_boxes(int pending_pos_num)
{
XAigerAnalysis analysis;
- log_debug("preforming analysis on '%s'\n", log_id(top));
+ log_debug("preforming analysis on '%s'\n", top);
analysis.analyze(top);
- log_debug("analysis on '%s' done\n", log_id(top));
+ log_debug("analysis on '%s' done\n", top);
// boxes which have timing data, maybe a whitebox model
std::vector> nonopaque_boxes;
@@ -1030,8 +1088,8 @@ struct XAigerWriter : AigerWriter {
for (auto box : minfo.found_blackboxes) {
log_debug(" - %s.%s (type %s): ", cursor.path(),
- RTLIL::unescape_id(box->name),
- log_id(box->type));
+ box,
+ box->type.unescape());
Module *box_module = design->module(box->type), *box_derived;
@@ -1100,7 +1158,7 @@ struct XAigerWriter : AigerWriter {
} else {
// FIXME: hierarchical path
log_warning("connection on port %s[%d] of instance %s (type %s) missing, using 1'bx\n",
- log_id(port_id), i, log_id(box), log_id(box->type));
+ port_id.unescape(), i, box, box->type.unescape());
bit = RTLIL::Sx;
}
@@ -1135,7 +1193,7 @@ struct XAigerWriter : AigerWriter {
} else {
// FIXME: hierarchical path
log_warning("connection on port %s[%d] of instance %s (type %s) missing\n",
- log_id(port_id), i, log_id(box), log_id(box->type));
+ port_id.unescape(), i, box, box->type.unescape());
pad_pi();
continue;
}
@@ -1152,7 +1210,7 @@ struct XAigerWriter : AigerWriter {
holes_wb->setPort(port_id, w);
} else {
log_error("Ambiguous port direction on %s/%s\n",
- log_id(box->type), log_id(port_id));
+ box->type.unescape(), port_id.unescape());
}
}
}
@@ -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 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 = cell.type.unescape();
+ 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 = cell.type.unescape();
+ 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 \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 \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");
}
diff --git a/backends/blif/blif.cc b/backends/blif/blif.cc
index 85db8679e..ac2e4edde 100644
--- a/backends/blif/blif.cc
+++ b/backends/blif/blif.cc
@@ -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
@@ -61,7 +61,7 @@ struct BlifDumper
RTLIL::Module *module;
RTLIL::Design *design;
BlifDumperConfig *config;
- CellTypes ct;
+ NewCellTypes ct;
SigMap sigmap;
dict init_bits;
@@ -91,7 +91,7 @@ struct BlifDumper
const std::string str(RTLIL::IdString id)
{
- std::string str = RTLIL::unescape_id(id);
+ std::string str = id.unescape();
for (size_t i = 0; i < str.size(); i++)
if (str[i] == '#' || str[i] == '=' || str[i] == '<' || str[i] == '>')
str[i] = '?';
@@ -108,7 +108,7 @@ struct BlifDumper
return config->undef_type == "-" || config->undef_type == "+" ? config->undef_out.c_str() : "$undef";
}
- std::string str = RTLIL::unescape_id(sig.wire->name);
+ std::string str = sig.wire->name.unescape();
for (size_t i = 0; i < str.size(); i++)
if (str[i] == '#' || str[i] == '=' || str[i] == '<' || str[i] == '>')
str[i] = '?';
@@ -150,7 +150,7 @@ struct BlifDumper
void dump_params(const char *command, dict ¶ms)
{
for (auto ¶m : params) {
- f << stringf("%s %s ", command, log_id(param.first));
+ f << stringf("%s %s ", command, param.first.unescape());
if (param.second.flags & RTLIL::CONST_FLAG_STRING) {
std::string str = param.second.decode_string();
f << stringf("\"");
@@ -678,9 +678,9 @@ struct BlifBackend : public Backend {
continue;
if (module->processes.size() != 0)
- log_error("Found unmapped processes in module %s: unmapped processes are not supported in BLIF backend!\n", log_id(module->name));
+ log_error("Found unmapped processes in module %s: unmapped processes are not supported in BLIF backend!\n", module->name.unescape());
if (module->memories.size() != 0)
- log_error("Found unmapped memories in module %s: unmapped memories are not supported in BLIF backend!\n", log_id(module->name));
+ log_error("Found unmapped memories in module %s: unmapped memories are not supported in BLIF backend!\n", module->name.unescape());
if (module->name == RTLIL::escape_id(top_module_name)) {
BlifDumper::dump(*f, module, design, config);
diff --git a/backends/btor/btor.cc b/backends/btor/btor.cc
index ca7cf8a7f..497ecc954 100644
--- a/backends/btor/btor.cc
+++ b/backends/btor/btor.cc
@@ -119,7 +119,7 @@ struct BtorWorker
template
string getinfo(T *obj, bool srcsym = false)
{
- string infostr = log_id(obj);
+ string infostr = obj->name.unescape();
if (!srcsym && !print_internal_names && infostr[0] == '$') return "";
if (obj->attributes.count(ID::src)) {
string src = obj->attributes.at(ID::src).decode_string().c_str();
@@ -243,12 +243,12 @@ struct BtorWorker
if (cell_recursion_guard.count(cell)) {
string cell_list;
for (auto c : cell_recursion_guard)
- cell_list += stringf("\n %s", log_id(c));
- log_error("Found topological loop while processing cell %s. Active cells:%s\n", log_id(cell), cell_list);
+ cell_list += stringf("\n %s", c);
+ log_error("Found topological loop while processing cell %s. Active cells:%s\n", cell, cell_list);
}
cell_recursion_guard.insert(cell);
- btorf_push(log_id(cell));
+ btorf_push(cell->name.unescape());
if (cell->type.in(ID($add), ID($sub), ID($mul), ID($and), ID($or), ID($xor), ID($xnor), ID($shl), ID($sshl), ID($shr), ID($sshr), ID($shift), ID($shiftx),
ID($concat), ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), ID($_XOR_), ID($_XNOR_)))
@@ -726,7 +726,7 @@ struct BtorWorker
if (symbol.empty() || (!print_internal_names && symbol[0] == '$'))
btorf("%d state %d\n", nid, sid);
else
- btorf("%d state %d %s\n", nid, sid, log_id(symbol));
+ btorf("%d state %d %s\n", nid, sid, symbol.unescape());
if (cell->get_bool_attribute(ID(clk2fflogic)))
ywmap_state(cell->getPort(ID::D)); // For a clk2fflogic FF the named signal is the D input not the Q output
@@ -804,12 +804,12 @@ struct BtorWorker
if (asyncwr && syncwr)
log_error("Memory %s.%s has mixed async/sync write ports.\n",
- log_id(module), log_id(mem->memid));
+ module, mem->memid.unescape());
for (auto &port : mem->rd_ports) {
if (port.clk_enable)
log_error("Memory %s.%s has sync read ports. Please use memory_nordff to convert them first.\n",
- log_id(module), log_id(mem->memid));
+ module, mem->memid.unescape());
}
int data_sid = get_bv_sid(mem->width);
@@ -871,7 +871,7 @@ struct BtorWorker
if (mem->memid[0] == '$')
btorf("%d state %d\n", nid, sid);
else
- btorf("%d state %d %s\n", nid, sid, log_id(mem->memid));
+ btorf("%d state %d %s\n", nid, sid, mem->memid.unescape());
ywmap_state(cell);
@@ -948,21 +948,20 @@ struct BtorWorker
if (cell->type.in(ID($dffe), ID($sdff), ID($sdffe), ID($sdffce)) || cell->type.str().substr(0, 6) == "$_SDFF" || (cell->type.str().substr(0, 6) == "$_DFFE" && cell->type.str().size() == 10)) {
log_error("Unsupported cell type %s for cell %s.%s -- please run `dffunmap` before `write_btor`.\n",
- log_id(cell->type), log_id(module), log_id(cell));
+ cell->type.unescape(), module, cell);
}
if (cell->type.in(ID($adff), ID($adffe), ID($aldff), ID($aldffe), ID($dffsr), ID($dffsre)) || cell->type.str().substr(0, 5) == "$_DFF" || cell->type.str().substr(0, 7) == "$_ALDFF") {
log_error("Unsupported cell type %s for cell %s.%s -- please run `async2sync; dffunmap` or `clk2fflogic` before `write_btor`.\n",
- log_id(cell->type), log_id(module), log_id(cell));
+ cell->type.unescape(), module, cell);
}
if (cell->type.in(ID($sr), ID($dlatch), ID($adlatch), ID($dlatchsr)) || cell->type.str().substr(0, 8) == "$_DLATCH" || cell->type.str().substr(0, 5) == "$_SR_") {
log_error("Unsupported cell type %s for cell %s.%s -- please run `clk2fflogic` before `write_btor`.\n",
- log_id(cell->type), log_id(module), log_id(cell));
+ cell->type.unescape(), module, cell);
}
log_error("Unsupported cell type %s for cell %s.%s.\n",
- log_id(cell->type), log_id(module), log_id(cell));
-
+ cell->type.unescape(), module, cell);
okay:
- btorf_pop(log_id(cell));
+ btorf_pop(cell->name.unescape());
cell_recursion_guard.erase(cell);
}
@@ -1167,7 +1166,7 @@ struct BtorWorker
f(f), sigmap(module), module(module), verbose(verbose), single_bad(single_bad), cover_mode(cover_mode), print_internal_names(print_internal_names), info_filename(info_filename)
{
if (!info_filename.empty())
- infof("name %s\n", log_id(module));
+ infof("name %s\n", module);
if (!ywmap_filename.empty())
ywmap_json.write_to_file(ywmap_filename);
@@ -1257,19 +1256,19 @@ struct BtorWorker
if (!wire->port_id || !wire->port_output)
continue;
- btorf_push(stringf("output %s", log_id(wire)));
+ btorf_push(stringf("output %s", wire));
int nid = get_sig_nid(wire);
btorf("%d output %d%s\n", next_nid++, nid, getinfo(wire));
- btorf_pop(stringf("output %s", log_id(wire)));
+ btorf_pop(stringf("output %s", wire));
}
for (auto cell : module->cells())
{
if (cell->type == ID($assume))
{
- btorf_push(log_id(cell));
+ btorf_push(cell->name.unescape());
int sid = get_bv_sid(1);
int nid_a = get_sig_nid(cell->getPort(ID::A));
@@ -1284,12 +1283,12 @@ struct BtorWorker
if (ywmap_json.active()) ywmap_assumes.emplace_back(cell);
- btorf_pop(log_id(cell));
+ btorf_pop(cell->name.unescape());
}
if (cell->type == ID($assert))
{
- btorf_push(log_id(cell));
+ btorf_push(cell->name.unescape());
int sid = get_bv_sid(1);
int nid_a = get_sig_nid(cell->getPort(ID::A));
@@ -1313,12 +1312,12 @@ struct BtorWorker
}
}
- btorf_pop(log_id(cell));
+ btorf_pop(cell->name.unescape());
}
if (cell->type == ID($cover) && cover_mode)
{
- btorf_push(log_id(cell));
+ btorf_push(cell->name.unescape());
int sid = get_bv_sid(1);
int nid_a = get_sig_nid(cell->getPort(ID::A));
@@ -1334,7 +1333,7 @@ struct BtorWorker
btorf("%d bad %d%s\n", nid, nid_en_and_a, getinfo(cell, true));
}
- btorf_pop(log_id(cell));
+ btorf_pop(cell->name.unescape());
}
}
@@ -1343,7 +1342,7 @@ struct BtorWorker
if (wire->port_id || wire->name[0] == '$')
continue;
- btorf_push(stringf("wire %s", log_id(wire)));
+ btorf_push(stringf("wire %s", wire));
int sid = get_bv_sid(GetSize(wire));
int nid = get_sig_nid(sigmap(wire));
@@ -1356,7 +1355,7 @@ struct BtorWorker
if (info_clocks.count(nid))
info_clocks[this_nid] |= info_clocks[nid];
- btorf_pop(stringf("wire %s", log_id(wire)));
+ btorf_pop(stringf("wire %s", wire));
continue;
}
@@ -1370,14 +1369,14 @@ struct BtorWorker
int nid = it.first;
Cell *cell = it.second;
- btorf_push(stringf("next %s", log_id(cell)));
+ btorf_push(stringf("next %s", cell));
SigSpec sig = sigmap(cell->getPort(ID::D));
int nid_q = get_sig_nid(sig);
int sid = get_bv_sid(GetSize(sig));
btorf("%d next %d %d %d%s\n", next_nid++, sid, nid, nid_q, getinfo(cell));
- btorf_pop(stringf("next %s", log_id(cell)));
+ btorf_pop(stringf("next %s", cell));
}
vector> mtodo;
@@ -1388,7 +1387,7 @@ struct BtorWorker
int nid = it.first;
Mem *mem = it.second;
- btorf_push(stringf("next %s", log_id(mem->memid)));
+ btorf_push(stringf("next %s", mem->memid.unescape()));
int abits = ceil_log2(mem->size);
@@ -1436,7 +1435,7 @@ struct BtorWorker
int nid2 = next_nid++;
btorf("%d next %d %d %d%s\n", nid2, sid, nid, nid_head, (mem->cell ? getinfo(mem->cell) : getinfo(mem->mem)));
- btorf_pop(stringf("next %s", log_id(mem->memid)));
+ btorf_pop(stringf("next %s", mem->memid.unescape()));
}
}
@@ -1630,7 +1629,7 @@ struct BtorBackend : public Backend {
log_cmd_error("No top module found.\n");
*f << stringf("; BTOR description generated by %s for module %s.\n",
- yosys_maybe_version(), log_id(topmod));
+ yosys_maybe_version(), topmod);
BtorWorker(*f, topmod, verbose, single_bad, cover_mode, print_internal_names, info_filename, ywmap_filename);
diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc
index 71913d2db..ac69bda27 100644
--- a/backends/cxxrtl/cxxrtl_backend.cc
+++ b/backends/cxxrtl/cxxrtl_backend.cc
@@ -251,7 +251,7 @@ CxxrtlPortType cxxrtl_port_type(RTLIL::Module *module, RTLIL::IdString port)
bool is_sync = output_wire->get_bool_attribute(ID(cxxrtl_sync));
if (is_comb && is_sync)
log_cmd_error("Port `%s.%s' is marked as both `cxxrtl_comb` and `cxxrtl_sync`.\n",
- log_id(module), log_signal(output_wire));
+ module, log_signal(output_wire));
else if (is_comb)
return CxxrtlPortType::COMB;
else if (is_sync)
@@ -851,7 +851,7 @@ struct CxxrtlWorker {
return {};
if (!(module->attributes.at(ID(cxxrtl_template)).flags & RTLIL::CONST_FLAG_STRING))
- log_cmd_error("Attribute `cxxrtl_template' of module `%s' is not a string.\n", log_id(module));
+ log_cmd_error("Attribute `cxxrtl_template' of module `%s' is not a string.\n", module);
std::vector param_names = split_by(module->get_string_attribute(ID(cxxrtl_template)), " \t");
for (const auto ¶m_name : param_names) {
@@ -861,7 +861,7 @@ struct CxxrtlWorker {
if (!isupper(param_name[0]))
log_cmd_error("Attribute `cxxrtl_template' of module `%s' includes a parameter `%s', "
"which does not start with an uppercase letter.\n",
- log_id(module), param_name.c_str());
+ module, param_name.c_str());
}
return param_names;
}
@@ -907,12 +907,12 @@ struct CxxrtlWorker {
RTLIL::IdString id_param_name = '\\' + param_name;
if (!cell->hasParam(id_param_name))
log_cmd_error("Cell `%s.%s' does not have a parameter `%s', which is required by the templated module `%s'.\n",
- log_id(cell->module), log_id(cell), param_name.c_str(), log_id(cell_module));
+ cell->module, cell, param_name.c_str(), cell_module);
RTLIL::Const param_value = cell->getParam(id_param_name);
if (((param_value.flags & ~RTLIL::CONST_FLAG_SIGNED) != 0) || param_value.as_int() < 0)
log_cmd_error("Parameter `%s' of cell `%s.%s', which is required by the templated module `%s', "
"is not a positive integer.\n",
- param_name.c_str(), log_id(cell->module), log_id(cell), log_id(cell_module));
+ param_name.c_str(), cell->module, cell, cell_module);
params += std::to_string(cell->getParam(id_param_name).as_int());
}
params += ">";
@@ -2576,7 +2576,7 @@ struct CxxrtlWorker {
}
dec_indent();
- log_debug("Debug information statistics for module `%s':\n", log_id(module));
+ log_debug("Debug information statistics for module `%s':\n", module);
log_debug(" Scopes: %zu", count_scopes);
log_debug(" Public wires: %zu, of which:\n", count_public_wires);
log_debug(" Member wires: %zu, of which:\n", count_member_wires);
@@ -2776,7 +2776,8 @@ struct CxxrtlWorker {
{
RTLIL::Module *top_module = nullptr;
std::vector modules;
- TopoSort topo_design;
+ using Order = IdString::compare_ptr_by_name;
+ TopoSort topo_design;
for (auto module : design->modules()) {
if (!design->selected_module(module))
continue;
@@ -2939,7 +2940,7 @@ struct CxxrtlWorker {
RTLIL::Const edge_attr = wire->attributes[ID(cxxrtl_edge)];
if (!(edge_attr.flags & RTLIL::CONST_FLAG_STRING) || (int)edge_attr.decode_string().size() != GetSize(wire))
log_cmd_error("Attribute `cxxrtl_edge' of port `%s.%s' is not a string with one character per bit.\n",
- log_id(module), log_signal(wire));
+ module, log_signal(wire));
std::string edges = wire->get_string_attribute(ID(cxxrtl_edge));
for (int i = 0; i < GetSize(wire); i++) {
@@ -2952,7 +2953,7 @@ struct CxxrtlWorker {
default:
log_cmd_error("Attribute `cxxrtl_edge' of port `%s.%s' contains specifiers "
"other than '-', 'p', 'n', or 'a'.\n",
- log_id(module), log_signal(wire));
+ module, log_signal(wire));
}
}
}
@@ -2977,7 +2978,7 @@ struct CxxrtlWorker {
for (auto cell : module->cells()) {
if (!cell->known())
- log_cmd_error("Unknown cell `%s'.\n", log_id(cell->type));
+ log_cmd_error("Unknown cell `%s'.\n", cell->type.unescape());
if (cell->is_mem_cell())
continue;
@@ -2986,7 +2987,7 @@ struct CxxrtlWorker {
if (cell_module &&
cell_module->get_blackbox_attribute() &&
!cell_module->get_bool_attribute(ID(cxxrtl_blackbox)))
- log_cmd_error("External blackbox cell `%s' is not marked as a CXXRTL blackbox.\n", log_id(cell->type));
+ log_cmd_error("External blackbox cell `%s' is not marked as a CXXRTL blackbox.\n", cell->type.unescape());
if (cell_module &&
cell_module->get_bool_attribute(ID(cxxrtl_blackbox)) &&
@@ -3115,9 +3116,9 @@ struct CxxrtlWorker {
}
if (!feedback_wires.empty()) {
has_feedback_arcs = true;
- log("Module `%s' contains feedback arcs through wires:\n", log_id(module));
+ log("Module `%s' contains feedback arcs through wires:\n", module);
for (auto wire : feedback_wires)
- log(" %s\n", log_id(wire));
+ log(" %s\n", wire);
}
// Conservatively assign wire types. Assignment of types BUFFERED and MEMBER is final, but assignment
@@ -3188,7 +3189,7 @@ struct CxxrtlWorker {
if (wire->name.isPublic() && !inline_public) continue;
if (flow.is_inlinable(wire, live_wires[wire])) {
if (flow.wire_comb_defs[wire].size() > 1)
- log_cmd_error("Wire %s.%s has multiple drivers!\n", log_id(module), log_id(wire));
+ log_cmd_error("Wire %s.%s has multiple drivers!\n", module, wire);
log_assert(flow.wire_comb_defs[wire].size() == 1);
FlowGraph::Node *node = *flow.wire_comb_defs[wire].begin();
switch (node->type) {
@@ -3236,9 +3237,9 @@ struct CxxrtlWorker {
buffered_comb_wires.insert(wire);
if (!buffered_comb_wires.empty()) {
has_buffered_comb_wires = true;
- log("Module `%s' contains buffered combinatorial wires:\n", log_id(module));
+ log("Module `%s' contains buffered combinatorial wires:\n", module);
for (auto wire : buffered_comb_wires)
- log(" %s\n", log_id(wire));
+ log(" %s\n", wire);
}
// Record whether eval() requires only one delta cycle in this module.
@@ -3419,7 +3420,7 @@ struct CxxrtlWorker {
if (!design->selected_whole_module(module))
if (design->selected_module(module))
- log_cmd_error("Can't handle partially selected module `%s'!\n", id2cstr(module->name));
+ log_cmd_error("Can't handle partially selected module `%s'!\n", module);
if (!design->selected_module(module))
continue;
diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h
index fbbe2373f..521cce4b0 100644
--- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h
+++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h
@@ -614,7 +614,7 @@ struct value : public expr_base> {
int64_t divisor_shift = divisor.ctlz() - dividend.ctlz();
assert(divisor_shift >= 0);
divisor = divisor.shl(value{(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{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))
diff --git a/backends/edif/edif.cc b/backends/edif/edif.cc
index 61d6ee254..9d3392e99 100644
--- a/backends/edif/edif.cc
+++ b/backends/edif/edif.cc
@@ -23,16 +23,18 @@
#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
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
-#define EDIF_DEF(_id) edif_names(RTLIL::unescape_id(_id), true)
-#define EDIF_DEFR(_id, _ren, _bl, _br) edif_names(RTLIL::unescape_id(_id), true, _ren, _bl, _br)
-#define EDIF_REF(_id) edif_names(RTLIL::unescape_id(_id), false)
+#define EDIF_DEF(_id) edif_names(_id.unescape(), true)
+#define EDIF_DEFR(_id, _ren, _bl, _br) edif_names(_id.unescape(), true, _ren, _bl, _br)
+#define EDIF_REF(_id) edif_names(_id.unescape(), false)
+#define EDIF_DEF_STR(_id) edif_names(RTLIL::unescape_id(_id), true)
+#define EDIF_REF_STR(_id) edif_names(RTLIL::unescape_id(_id), false)
struct EdifNames
{
@@ -138,7 +140,7 @@ struct EdifBackend : public Backend {
bool lsbidx = false;
std::map> lib_cell_ports;
bool nogndvcc = false, gndvccy = false, keepmode = false;
- CellTypes ct(design);
+ NewCellTypes ct(design);
EdifNames edif_names;
size_t argidx;
@@ -207,9 +209,9 @@ struct EdifBackend : public Backend {
top_module_name = module->name.str();
if (module->processes.size() != 0)
- log_error("Found unmapped processes in module %s: unmapped processes are not supported in EDIF backend!\n", log_id(module->name));
+ log_error("Found unmapped processes in module %s: unmapped processes are not supported in EDIF backend!\n", module->name.unescape());
if (module->memories.size() != 0)
- log_error("Found unmapped memories in module %s: unmapped memories are not supported in EDIF backend!\n", log_id(module->name));
+ log_error("Found unmapped memories in module %s: unmapped memories are not supported in EDIF backend!\n", module->name.unescape());
for (auto cell : module->cells())
{
@@ -227,7 +229,7 @@ struct EdifBackend : public Backend {
if (top_module_name.empty())
log_error("No module found in design!\n");
- *f << stringf("(edif %s\n", EDIF_DEF(top_module_name));
+ *f << stringf("(edif %s\n", EDIF_DEF_STR(top_module_name));
*f << stringf(" (edifVersion 2 0 0)\n");
*f << stringf(" (edifLevel 0)\n");
*f << stringf(" (keywordMap (keywordLevel 0))\n");
@@ -317,12 +319,12 @@ struct EdifBackend : public Backend {
for (auto &dep : it.second)
if (module_deps.count(dep) > 0)
goto not_ready_yet;
- // log("Next in topological sort: %s\n", log_id(it.first->name));
+ // log("Next in topological sort: %s\n", it.first->name.unescape());
sorted_modules.push_back(it.first);
not_ready_yet:;
}
if (sorted_modules_idx == sorted_modules.size())
- log_error("Cyclic dependency between modules found! Cycle includes module %s.\n", log_id(module_deps.begin()->first->name));
+ log_error("Cyclic dependency between modules found! Cycle includes module %s.\n", module_deps.begin()->first->name.unescape());
while (sorted_modules_idx < sorted_modules.size())
module_deps.erase(sorted_modules.at(sorted_modules_idx++));
}
@@ -486,7 +488,7 @@ struct EdifBackend : public Backend {
for (int i = 0; i < GetSize(sig); i++)
if (sig[i].wire == NULL && sig[i] != RTLIL::State::S0 && sig[i] != RTLIL::State::S1)
log_warning("Bit %d of cell port %s.%s.%s driven by %s will be left unconnected in EDIF output.\n",
- i, log_id(module), log_id(cell), log_id(p.first), log_signal(sig[i]));
+ i, module, cell, p.first.unescape(), log_signal(sig[i]));
else {
int member_idx = lsbidx ? i : GetSize(sig)-i-1;
auto m = design->module(cell->type);
@@ -534,7 +536,7 @@ struct EdifBackend : public Backend {
if (netname[i] == ' ' || netname[i] == '\\')
netname.erase(netname.begin() + i--);
}
- *f << stringf(" (net %s (joined\n", EDIF_DEF(netname));
+ *f << stringf(" (net %s (joined\n", EDIF_DEF_STR(netname));
for (auto &ref : it.second)
*f << stringf(" %s\n", ref.first);
if (sig.wire == NULL) {
@@ -572,7 +574,7 @@ struct EdifBackend : public Backend {
if (keepmode)
{
- *f << stringf(" (net %s (joined\n", EDIF_DEF(netname));
+ *f << stringf(" (net %s (joined\n", EDIF_DEF_STR(netname));
auto &refs = net_join_db.at(mapped_sig);
for (auto &ref : refs)
@@ -588,7 +590,7 @@ struct EdifBackend : public Backend {
}
else
{
- log_warning("Ignoring conflicting 'keep' property on net %s. Use -keep to generate the extra net nevertheless.\n", EDIF_DEF(netname));
+ log_warning("Ignoring conflicting 'keep' property on net %s. Use -keep to generate the extra net nevertheless.\n", EDIF_DEF_STR(netname));
}
}
}
@@ -599,8 +601,8 @@ struct EdifBackend : public Backend {
}
*f << stringf(" )\n");
- *f << stringf(" (design %s\n", EDIF_DEF(top_module_name));
- *f << stringf(" (cellRef %s (libraryRef DESIGN))\n", EDIF_REF(top_module_name));
+ *f << stringf(" (design %s\n", EDIF_DEF_STR(top_module_name));
+ *f << stringf(" (cellRef %s (libraryRef DESIGN))\n", EDIF_REF_STR(top_module_name));
*f << stringf(" )\n");
*f << stringf(")\n");
diff --git a/backends/firrtl/firrtl.cc b/backends/firrtl/firrtl.cc
index 577d95ad7..db5036552 100644
--- a/backends/firrtl/firrtl.cc
+++ b/backends/firrtl/firrtl.cc
@@ -82,7 +82,7 @@ const char *make_id(IdString id)
if (namecache.count(id) != 0)
return namecache.at(id).c_str();
- string new_id = log_id(id);
+ string new_id = id.unescape();
for (int i = 0; i < GetSize(new_id); i++)
{
@@ -263,7 +263,7 @@ void emit_extmodule(RTLIL::Cell *cell, RTLIL::Module *mod_instance, std::ostream
if (wire->port_input && wire->port_output)
{
- log_error("Module port %s.%s is inout!\n", log_id(mod_instance), log_id(wire));
+ log_error("Module port %s.%s is inout!\n", mod_instance, wire);
}
const std::string portDecl = stringf("%s%s %s: UInt<%d> %s\n",
@@ -559,12 +559,12 @@ struct FirrtlWorker
if (wire->attributes.count(ID::init)) {
log_warning("Initial value (%s) for (%s.%s) not supported\n",
wire->attributes.at(ID::init).as_string().c_str(),
- log_id(module), log_id(wire));
+ module, wire);
}
if (wire->port_id)
{
if (wire->port_input && wire->port_output)
- log_error("Module port %s.%s is inout!\n", log_id(module), log_id(wire));
+ log_error("Module port %s.%s is inout!\n", module, wire);
port_decls.push_back(stringf("%s%s %s: UInt<%d> %s\n", indent, wire->port_input ? "input" : "output",
wireName, wire->width, wireFileinfo.c_str()));
}
@@ -833,7 +833,7 @@ struct FirrtlWorker
primop = "shl";
int shiftAmount = b_sig.as_int();
if (shiftAmount < 0) {
- log_error("Negative power exponent - %d: %s.%s\n", shiftAmount, log_id(module), log_id(cell));
+ log_error("Negative power exponent - %d: %s.%s\n", shiftAmount, module, cell);
}
b_expr = std::to_string(shiftAmount);
firrtl_width = a_width + shiftAmount;
@@ -844,7 +844,7 @@ struct FirrtlWorker
firrtl_width = a_width + (1 << b_width) - 1;
}
} else {
- log_error("Non power 2: %s.%s\n", log_id(module), log_id(cell));
+ log_error("Non power 2: %s.%s\n", module, cell);
}
}
@@ -905,7 +905,7 @@ struct FirrtlWorker
{
bool clkpol = cell->parameters.at(ID::CLK_POLARITY).as_bool();
if (clkpol == false)
- log_error("Negative edge clock on FF %s.%s.\n", log_id(module), log_id(cell));
+ log_error("Negative edge clock on FF %s.%s.\n", module, cell);
int width = cell->parameters.at(ID::WIDTH).as_int();
string expr = make_expr(cell->getPort(ID::D));
@@ -983,7 +983,7 @@ struct FirrtlWorker
if (cell->type == ID($scopeinfo))
continue;
- log_error("Cell type not supported: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell));
+ log_error("Cell type not supported: %s (%s.%s)\n", cell->type.unescape(), module, cell);
}
for (auto &mem : memories) {
@@ -991,10 +991,10 @@ struct FirrtlWorker
Const init_data = mem.get_init_data();
if (!init_data.is_fully_undef())
- log_error("Memory with initialization data: %s.%s\n", log_id(module), log_id(mem.memid));
+ log_error("Memory with initialization data: %s.%s\n", module, mem.memid.unescape());
if (mem.start_offset != 0)
- log_error("Memory with nonzero offset: %s.%s\n", log_id(module), log_id(mem.memid));
+ log_error("Memory with nonzero offset: %s.%s\n", module, mem.memid.unescape());
for (int i = 0; i < GetSize(mem.rd_ports); i++)
{
@@ -1002,7 +1002,7 @@ struct FirrtlWorker
string port_name(stringf("%s.r%d", mem_id, i));
if (port.clk_enable)
- log_error("Clocked read port %d on memory %s.%s.\n", i, log_id(module), log_id(mem.memid));
+ log_error("Clocked read port %d on memory %s.%s.\n", i, module, mem.memid.unescape());
std::ostringstream rpe;
@@ -1023,12 +1023,12 @@ struct FirrtlWorker
string port_name(stringf("%s.w%d", mem_id, i));
if (!port.clk_enable)
- log_error("Unclocked write port %d on memory %s.%s.\n", i, log_id(module), log_id(mem.memid));
+ log_error("Unclocked write port %d on memory %s.%s.\n", i, module, mem.memid.unescape());
if (!port.clk_polarity)
- log_error("Negedge write port %d on memory %s.%s.\n", i, log_id(module), log_id(mem.memid));
+ log_error("Negedge write port %d on memory %s.%s.\n", i, module, mem.memid.unescape());
for (int i = 1; i < GetSize(port.en); i++)
if (port.en[0] != port.en[i])
- log_error("Complex write enable on port %d on memory %s.%s.\n", i, log_id(module), log_id(mem.memid));
+ log_error("Complex write enable on port %d on memory %s.%s.\n", i, module, mem.memid.unescape());
std::ostringstream wpe;
diff --git a/backends/functional/cxx.cc b/backends/functional/cxx.cc
index 7f4ad1ea7..d67bc9143 100644
--- a/backends/functional/cxx.cc
+++ b/backends/functional/cxx.cc
@@ -89,7 +89,7 @@ struct CxxStruct {
}
f.print("\n\t\ttemplate void visit(T &&fn) {{\n");
for (auto p : types) {
- f.print("\t\t\tfn(\"{}\", {});\n", RTLIL::unescape_id(p.first), scope(p.first, p.first));
+ f.print("\t\t\tfn(\"{}\", {});\n", p.first.unescape(), scope(p.first, p.first));
}
f.print("\t\t}}\n");
f.print("\t}};\n\n");
diff --git a/backends/functional/smtlib.cc b/backends/functional/smtlib.cc
index 1504c8fba..0451af4c7 100644
--- a/backends/functional/smtlib.cc
+++ b/backends/functional/smtlib.cc
@@ -80,7 +80,7 @@ public:
SmtStruct(std::string name, SmtScope &scope) : scope(scope), name(name) {}
void insert(IdString field_name, SmtSort sort) {
field_names(field_name);
- auto accessor = scope.unique_name("\\" + name + "_" + RTLIL::unescape_id(field_name));
+ auto accessor = scope.unique_name("\\" + name + "_" + field_name.unescape());
fields.emplace_back(Field{sort, accessor});
}
void write_definition(SExprWriter &w) {
@@ -99,7 +99,7 @@ public:
w.open(list(name));
for(auto field_name : field_names) {
w << fn(field_name);
- w.comment(RTLIL::unescape_id(field_name), true);
+ w.comment(field_name.unescape(), true);
}
w.close();
}
diff --git a/backends/functional/smtlib_rosette.cc b/backends/functional/smtlib_rosette.cc
index 73e1b48c6..b37f948b6 100644
--- a/backends/functional/smtlib_rosette.cc
+++ b/backends/functional/smtlib_rosette.cc
@@ -106,7 +106,7 @@ public:
w.open(list(name));
for(auto field_name : field_names) {
w << fn(field_name);
- w.comment(RTLIL::unescape_id(field_name), true);
+ w.comment(field_name.unescape(), true);
}
w.close();
}
@@ -281,7 +281,7 @@ struct SmtrModule {
w.push();
w.open(list());
w.open(list("assoc-result"));
- w << list("assoc", "\"" + RTLIL::unescape_id(input->name) + "\"", inputs_name);
+ w << list("assoc", "\"" + input->name.unescape() + "\"", inputs_name);
w.pop();
w.open(list("if", "assoc-result"));
w << list("cdr", "assoc-result");
@@ -298,7 +298,7 @@ struct SmtrModule {
w << list(*output_helper_name, outputs_name);
w.open(list("list"));
for (auto output : ir.outputs()) {
- w << list("cons", "\"" + RTLIL::unescape_id(output->name) + "\"", output_struct.access("outputs", output->name));
+ w << list("cons", "\"" + output->name.unescape() + "\"", output_struct.access("outputs", output->name));
}
w.pop();
}
diff --git a/backends/functional/test_generic.cc b/backends/functional/test_generic.cc
index c01649a0f..343fcfc0f 100644
--- a/backends/functional/test_generic.cc
+++ b/backends/functional/test_generic.cc
@@ -146,11 +146,11 @@ struct FunctionalTestGeneric : public Pass
log("Dumping module `%s'.\n", module->name);
auto fir = Functional::IR::from_module(module);
for(auto node : fir)
- std::cout << RTLIL::unescape_id(node.name()) << " = " << node.to_string([](auto n) { return RTLIL::unescape_id(n.name()); }) << "\n";
+ std::cout << node.name().unescape() << " = " << node.to_string([](auto n) { return n.name().unescape(); }) << "\n";
for(auto output : fir.all_outputs())
- std::cout << RTLIL::unescape_id(output->kind) << " " << RTLIL::unescape_id(output->name) << " = " << RTLIL::unescape_id(output->value().name()) << "\n";
+ std::cout << output->kind.unescape() << " " << output->name.unescape() << " = " << output->value().name().unescape() << "\n";
for(auto state : fir.all_states())
- std::cout << RTLIL::unescape_id(state->kind) << " " << RTLIL::unescape_id(state->name) << " = " << RTLIL::unescape_id(state->next_value().name()) << "\n";
+ std::cout << state->kind.unescape() << " " << state->name.unescape() << " = " << state->next_value().name().unescape() << "\n";
}
}
} FunctionalCxxBackend;
diff --git a/backends/intersynth/intersynth.cc b/backends/intersynth/intersynth.cc
index 78eab17da..1704ba429 100644
--- a/backends/intersynth/intersynth.cc
+++ b/backends/intersynth/intersynth.cc
@@ -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
@@ -41,7 +41,7 @@ static std::string netname(std::set &conntypes_code, std::setname);
+ return sig.as_wire()->name.unescape();
}
struct IntersynthBackend : public Backend {
@@ -117,7 +117,7 @@ struct IntersynthBackend : public Backend {
std::set conntypes_code, celltypes_code;
std::string netlists_code;
- CellTypes ct(design);
+ NewCellTypes ct(design);
for (auto lib : libs)
ct.setup_design(lib);
@@ -133,26 +133,26 @@ struct IntersynthBackend : public Backend {
if (selected && !design->selected_whole_module(module->name)) {
if (design->selected_module(module->name))
- log_cmd_error("Can't handle partially selected module %s!\n", log_id(module->name));
+ log_cmd_error("Can't handle partially selected module %s!\n", module->name.unescape());
continue;
}
- log("Generating netlist %s.\n", log_id(module->name));
+ log("Generating netlist %s.\n", module->name.unescape());
if (module->memories.size() != 0 || module->processes.size() != 0)
log_error("Can't generate a netlist for a module with unprocessed memories or processes!\n");
std::set constcells_code;
- netlists_code += stringf("# Netlist of module %s\n", log_id(module->name));
- netlists_code += stringf("netlist %s\n", log_id(module->name));
+ netlists_code += stringf("# Netlist of module %s\n", module->name.unescape());
+ netlists_code += stringf("netlist %s\n", module->name.unescape());
// Module Ports: "std::set celltypes_code" prevents duplicate top level ports
for (auto wire : module->wires()) {
if (wire->port_input || wire->port_output) {
celltypes_code.insert(stringf("celltype !%s b%d %sPORT\n" "%s %s %d %s PORT\n",
- log_id(wire->name), wire->width, wire->port_input ? "*" : "",
- wire->port_input ? "input" : "output", log_id(wire->name), wire->width, log_id(wire->name)));
- netlists_code += stringf("node %s %s PORT %s\n", log_id(wire->name), log_id(wire->name),
+ wire->name.unescape(), wire->width, wire->port_input ? "*" : "",
+ wire->port_input ? "input" : "output", wire->name.unescape(), wire->width, wire->name.unescape()));
+ netlists_code += stringf("node %s %s PORT %s\n", wire->name.unescape(), wire->name.unescape(),
netname(conntypes_code, celltypes_code, constcells_code, sigmap(wire)).c_str());
}
}
@@ -163,26 +163,26 @@ struct IntersynthBackend : public Backend {
std::string celltype_code, node_code;
if (!ct.cell_known(cell->type))
- log_error("Found unknown cell type %s in module!\n", log_id(cell->type));
+ log_error("Found unknown cell type %s in module!\n", cell->type.unescape());
- celltype_code = stringf("celltype %s", log_id(cell->type));
- node_code = stringf("node %s %s", log_id(cell->name), log_id(cell->type));
+ celltype_code = stringf("celltype %s", cell->type.unescape());
+ node_code = stringf("node %s %s", cell->name.unescape(), cell->type.unescape());
for (auto &port : cell->connections()) {
RTLIL::SigSpec sig = sigmap(port.second);
if (sig.size() != 0) {
conntypes_code.insert(stringf("conntype b%d %d 2 %d\n", sig.size(), sig.size(), sig.size()));
- celltype_code += stringf(" b%d %s%s", sig.size(), ct.cell_output(cell->type, port.first) ? "*" : "", log_id(port.first));
- node_code += stringf(" %s %s", log_id(port.first), netname(conntypes_code, celltypes_code, constcells_code, sig));
+ celltype_code += stringf(" b%d %s%s", sig.size(), ct.cell_output(cell->type, port.first) ? "*" : "", port.first.unescape());
+ node_code += stringf(" %s %s", port.first.unescape(), netname(conntypes_code, celltypes_code, constcells_code, sig));
}
}
for (auto ¶m : cell->parameters) {
- celltype_code += stringf(" cfg:%d %s", int(param.second.size()), log_id(param.first));
+ celltype_code += stringf(" cfg:%d %s", int(param.second.size()), param.first.unescape());
if (param.second.size() != 32) {
- node_code += stringf(" %s '", log_id(param.first));
+ node_code += stringf(" %s '", param.first.unescape());
for (int i = param.second.size()-1; i >= 0; i--)
node_code += param.second[i] == State::S1 ? "1" : "0";
} else
- node_code += stringf(" %s 0x%x", log_id(param.first), param.second.as_int());
+ node_code += stringf(" %s 0x%x", param.first.unescape(), param.second.as_int());
}
celltypes_code.insert(celltype_code + "\n");
diff --git a/backends/jny/jny.cc b/backends/jny/jny.cc
index ee0c0d14c..00650f5d8 100644
--- a/backends/jny/jny.cc
+++ b/backends/jny/jny.cc
@@ -91,7 +91,7 @@ struct JnyWriter
{
_cells.clear();
for (auto cell : mod->cells()) {
- const auto cell_type = escape_string(RTLIL::unescape_id(cell->type));
+ const auto cell_type = escape_string(cell->type.unescape());
if (_cells.find(cell_type) == _cells.end())
_cells.emplace(cell_type, std::vector());
@@ -214,7 +214,7 @@ struct JnyWriter
void write_cell_conn(const std::pair& sig, uint16_t indent_level = 0) {
const auto _indent = gen_indent(indent_level);
f << _indent << " {\n";
- f << _indent << " \"name\": \"" << escape_string(RTLIL::unescape_id(sig.first)) << "\",\n";
+ f << _indent << " \"name\": \"" << escape_string(sig.first.unescape()) << "\",\n";
f << _indent << " \"signals\": [\n";
write_sigspec(sig.second, indent_level + 2);
@@ -232,7 +232,7 @@ struct JnyWriter
const auto _indent = gen_indent(indent_level);
f << _indent << "{\n";
- f << stringf(" %s\"name\": \"%s\",\n", _indent, escape_string(RTLIL::unescape_id(mod->name)));
+ f << stringf(" %s\"name\": \"%s\",\n", _indent, escape_string(mod->name.unescape()));
f << _indent << " \"cell_sorts\": [\n";
bool first_sort{true};
@@ -280,7 +280,7 @@ struct JnyWriter
f << ",\n";
f << _indent << " {\n";
- f << stringf(" %s\"name\": \"%s\",\n", _indent, escape_string(RTLIL::unescape_id(con.first)));
+ f << stringf(" %s\"name\": \"%s\",\n", _indent, escape_string(con.first.unescape()));
f << _indent << " \"direction\": \"";
if (port_cell->input(con.first))
f << "i";
@@ -351,10 +351,10 @@ struct JnyWriter
f << stringf(",\n");
const auto param_val = param.second;
if (!param_val.empty()) {
- f << stringf(" %s\"%s\": ", _indent, escape_string(RTLIL::unescape_id(param.first)));
+ f << stringf(" %s\"%s\": ", _indent, escape_string(param.first.unescape()));
write_param_val(param_val);
} else {
- f << stringf(" %s\"%s\": true", _indent, escape_string(RTLIL::unescape_id(param.first)));
+ f << stringf(" %s\"%s\": true", _indent, escape_string(param.first.unescape()));
}
first_param = false;
@@ -366,7 +366,7 @@ struct JnyWriter
log_assert(cell != nullptr);
f << _indent << " {\n";
- f << stringf(" %s\"name\": \"%s\"", _indent, escape_string(RTLIL::unescape_id(cell->name)));
+ f << stringf(" %s\"name\": \"%s\"", _indent, escape_string(cell->name.unescape()));
if (_include_connections) {
f << ",\n" << _indent << " \"connections\": [\n";
diff --git a/backends/json/json.cc b/backends/json/json.cc
index b04083622..23d18fb15 100644
--- a/backends/json/json.cc
+++ b/backends/json/json.cc
@@ -76,7 +76,7 @@ struct JsonWriter
string get_name(IdString name)
{
- return get_string(RTLIL::unescape_id(name));
+ return get_string(name.unescape());
}
string get_bits(SigSpec sig)
@@ -152,7 +152,7 @@ struct JsonWriter
sigidcounter = 2;
if (module->has_processes()) {
- log_error("Module %s contains processes, which are not supported by JSON backend (run `proc` first).\n", log_id(module));
+ log_error("Module %s contains processes, which are not supported by JSON backend (run `proc` first).\n", module);
}
f << stringf(" %s: {\n", get_name(module->name));
@@ -316,13 +316,13 @@ struct JsonWriter
f << stringf(" /* %3d */ [ ", node_idx);
if (node.portbit >= 0)
f << stringf("\"%sport\", \"%s\", %d", node.inverter ? "n" : "",
- log_id(node.portname), node.portbit);
+ node.portname.unescape(), node.portbit);
else if (node.left_parent < 0 && node.right_parent < 0)
f << stringf("\"%s\"", node.inverter ? "true" : "false");
else
f << stringf("\"%s\", %d, %d", node.inverter ? "nand" : "and", node.left_parent, node.right_parent);
for (auto &op : node.outports)
- f << stringf(", \"%s\", %d", log_id(op.first), op.second);
+ f << stringf(", \"%s\", %d", op.first.unescape(), op.second);
f << stringf(" ]");
node_idx++;
}
diff --git a/backends/simplec/simplec.cc b/backends/simplec/simplec.cc
index 8ebc685f0..baf5aa006 100644
--- a/backends/simplec/simplec.cc
+++ b/backends/simplec/simplec.cc
@@ -78,7 +78,7 @@ struct HierDirtyFlags
for (Cell *cell : module->cells()) {
Module *mod = module->design->module(cell->type);
if (mod) children[cell->name] = new HierDirtyFlags(mod, cell->name, this,
- prefix + cid(cell->name) + ".", log_prefix + "." + prefix + log_id(cell->name));
+ prefix + cid(cell->name) + ".", log_prefix + "." + prefix + cell->name.unescape());
}
}
@@ -354,23 +354,23 @@ struct SimplecWorker
struct_declarations.push_back(" // Input Ports");
for (Wire *w : mod->wires())
if (w->port_input)
- struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width), cid(w->name), log_id(w)));
+ struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width), cid(w->name), w));
struct_declarations.push_back("");
struct_declarations.push_back(" // Output Ports");
for (Wire *w : mod->wires())
if (!w->port_input && w->port_output)
- struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width), cid(w->name), log_id(w)));
+ struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width), cid(w->name), w));
struct_declarations.push_back("");
struct_declarations.push_back(" // Internal Wires");
for (Wire *w : mod->wires())
if (!w->port_input && !w->port_output)
- struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width), cid(w->name), log_id(w)));
+ struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width), cid(w->name), w));
for (Cell *c : mod->cells())
if (design->module(c->type))
- struct_declarations.push_back(stringf(" struct %s_state_t %s; // %s", cid(c->type), cid(c->name), log_id(c)));
+ struct_declarations.push_back(stringf(" struct %s_state_t %s; // %s", cid(c->type), cid(c->name), c));
struct_declarations.push_back(stringf("};"));
struct_declarations.push_back("#endif");
@@ -391,7 +391,7 @@ struct SimplecWorker
log_assert(y.wire);
funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) +
- stringf(" // %s (%s)", log_id(cell), log_id(cell->type)));
+ stringf(" // %s (%s)", cell, cell->type.unescape()));
work->set_dirty(y);
return;
@@ -418,7 +418,7 @@ struct SimplecWorker
log_assert(y.wire);
funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) +
- stringf(" // %s (%s)", log_id(cell), log_id(cell->type)));
+ stringf(" // %s (%s)", cell, cell->type.unescape()));
work->set_dirty(y);
return;
@@ -441,7 +441,7 @@ struct SimplecWorker
log_assert(y.wire);
funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) +
- stringf(" // %s (%s)", log_id(cell), log_id(cell->type)));
+ stringf(" // %s (%s)", cell, cell->type.unescape()));
work->set_dirty(y);
return;
@@ -466,7 +466,7 @@ struct SimplecWorker
log_assert(y.wire);
funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) +
- stringf(" // %s (%s)", log_id(cell), log_id(cell->type)));
+ stringf(" // %s (%s)", cell, cell->type.unescape()));
work->set_dirty(y);
return;
@@ -490,13 +490,13 @@ struct SimplecWorker
log_assert(y.wire);
funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) +
- stringf(" // %s (%s)", log_id(cell), log_id(cell->type)));
+ stringf(" // %s (%s)", cell, cell->type.unescape()));
work->set_dirty(y);
return;
}
- log_error("No C model for %s available at the moment (FIXME).\n", log_id(cell->type));
+ log_error("No C model for %s available at the moment (FIXME).\n", cell->type.unescape());
}
void eval_dirty(HierDirtyFlags *work)
@@ -517,7 +517,7 @@ struct SimplecWorker
if (chunk.wire == nullptr)
continue;
if (verbose)
- log(" Propagating %s.%s[%d:%d].\n", work->log_prefix, log_id(chunk.wire), chunk.offset+chunk.width-1, chunk.offset);
+ log(" Propagating %s.%s[%d:%d].\n", work->log_prefix, chunk.wire, chunk.offset+chunk.width-1, chunk.offset);
funct_declarations.push_back(stringf(" // Updated signal in %s: %s", work->log_prefix, log_signal(chunk)));
}
@@ -539,8 +539,8 @@ struct SimplecWorker
work->parent->set_dirty(parent_bit);
if (verbose)
- log(" Propagating %s.%s[%d] -> %s.%s[%d].\n", work->log_prefix, log_id(bit.wire), bit.offset,
- work->parent->log_prefix.c_str(), log_id(parent_bit.wire), parent_bit.offset);
+ log(" Propagating %s.%s[%d] -> %s.%s[%d].\n", work->log_prefix, bit.wire, bit.offset,
+ work->parent->log_prefix.c_str(), parent_bit.wire, parent_bit.offset);
}
for (auto &port : bit2cell[work->module][bit])
@@ -556,12 +556,12 @@ struct SimplecWorker
child->set_dirty(child_bit);
if (verbose)
- log(" Propagating %s.%s[%d] -> %s.%s.%s[%d].\n", work->log_prefix, log_id(bit.wire), bit.offset,
- work->log_prefix.c_str(), log_id(std::get<0>(port)), log_id(child_bit.wire), child_bit.offset);
+ log(" Propagating %s.%s[%d] -> %s.%s.%s[%d].\n", work->log_prefix, bit.wire, bit.offset,
+ work->log_prefix.c_str(), std::get<0>(port), child_bit.wire, child_bit.offset);
} else {
if (verbose)
- log(" Marking cell %s.%s (via %s.%s[%d]).\n", work->log_prefix, log_id(std::get<0>(port)),
- work->log_prefix.c_str(), log_id(bit.wire), bit.offset);
+ log(" Marking cell %s.%s (via %s.%s[%d]).\n", work->log_prefix, std::get<0>(port),
+ work->log_prefix.c_str(), bit.wire, bit.offset);
work->set_dirty(std::get<0>(port));
}
}
@@ -576,10 +576,10 @@ struct SimplecWorker
if (cell == nullptr || topoidx.at(cell) < topoidx.at(c))
cell = c;
- string hiername = work->log_prefix + "." + log_id(cell);
+ string hiername = work->log_prefix + "." + cell->name.unescape();
if (verbose)
- log(" Evaluating %s (%s, best of %d).\n", hiername, log_id(cell->type), GetSize(work->dirty_cells));
+ log(" Evaluating %s (%s, best of %d).\n", hiername, cell->type.unescape(), GetSize(work->dirty_cells));
if (activated_cells.count(hiername))
reactivated_cells.insert(hiername);
@@ -618,8 +618,8 @@ struct SimplecWorker
if (verbose)
log(" Propagating alias %s.%s[%d] -> %s.%s[%d].\n",
- work->log_prefix.c_str(), log_id(canonical_bit.wire), canonical_bit.offset,
- work->log_prefix.c_str(), log_id(bit.wire), bit.offset);
+ work->log_prefix.c_str(), canonical_bit.wire, canonical_bit.offset,
+ work->log_prefix.c_str(), bit.wire, bit.offset);
}
work->sticky_dirty_bits.clear();
@@ -716,7 +716,7 @@ struct SimplecWorker
{
create_module_struct(mod);
- HierDirtyFlags work(mod, IdString(), nullptr, "state->", log_id(mod->name));
+ HierDirtyFlags work(mod, IdString(), nullptr, "state->", mod->name.unescape());
make_init_func(&work);
make_eval_func(&work);
diff --git a/backends/smt2/smt2.cc b/backends/smt2/smt2.cc
index d80622029..a9030e18a 100644
--- a/backends/smt2/smt2.cc
+++ b/backends/smt2/smt2.cc
@@ -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;
@@ -60,7 +60,7 @@ struct Smt2Worker
const char *get_id(IdString n)
{
if (ids.count(n) == 0) {
- std::string str = log_id(n);
+ std::string str = n.unescape();
for (int i = 0; i < GetSize(str); i++) {
if (str[i] == '\\')
str[i] = '/';
@@ -207,7 +207,7 @@ struct Smt2Worker
}
else if (is_output || !is_input)
log_error("Unsupported or unknown directionality on port %s of cell %s.%s (%s).\n",
- log_id(conn.first), log_id(module), log_id(cell), log_id(cell->type));
+ conn.first.unescape(), module, cell, cell->type.unescape());
if (cell->type.in(ID($dff), ID($_DFF_P_), ID($_DFF_N_)) && conn.first.in(ID::CLK, ID::C))
{
@@ -448,7 +448,7 @@ struct Smt2Worker
}
if (verbose)
- log("%*s-> import cell: %s\n", 2+2*GetSize(recursive_cells), "", log_id(cell));
+ log("%*s-> import cell: %s\n", 2+2*GetSize(recursive_cells), "", cell);
decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) Bool %s) ; %s\n",
get_id(module), idcounter, get_id(module), processed_expr.c_str(), log_signal(bit)));
@@ -498,7 +498,7 @@ struct Smt2Worker
processed_expr = stringf("((_ extract %d 0) %s)", GetSize(sig_y)-1, processed_expr);
if (verbose)
- log("%*s-> import cell: %s\n", 2+2*GetSize(recursive_cells), "", log_id(cell));
+ log("%*s-> import cell: %s\n", 2+2*GetSize(recursive_cells), "", cell);
if (type == 'b') {
decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) Bool %s) ; %s\n",
@@ -529,7 +529,7 @@ struct Smt2Worker
processed_expr += ch;
if (verbose)
- log("%*s-> import cell: %s\n", 2+2*GetSize(recursive_cells), "", log_id(cell));
+ log("%*s-> import cell: %s\n", 2+2*GetSize(recursive_cells), "", cell);
decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) Bool %s) ; %s\n",
get_id(module), idcounter, get_id(module), processed_expr.c_str(), log_signal(sig_y)));
@@ -541,7 +541,7 @@ struct Smt2Worker
{
if (verbose)
log("%*s=> export_cell %s (%s) [%s]\n", 2+2*GetSize(recursive_cells), "",
- log_id(cell), log_id(cell->type), exported_cells.count(cell) ? "old" : "new");
+ cell, cell->type.unescape(), exported_cells.count(cell) ? "old" : "new");
if (recursive_cells.count(cell))
log_error("Found logic loop in module %s! See cell %s.\n", get_id(module), get_id(cell));
@@ -750,7 +750,7 @@ struct Smt2Worker
get_bv(sig_b.extract(i*width, width)).c_str(), processed_expr.c_str());
if (verbose)
- log("%*s-> import cell: %s\n", 2+2*GetSize(recursive_cells), "", log_id(cell));
+ log("%*s-> import cell: %s\n", 2+2*GetSize(recursive_cells), "", cell);
RTLIL::SigSpec sig = sigmap(cell->getPort(ID::Y));
decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n",
@@ -786,9 +786,9 @@ struct Smt2Worker
has_async_wr = true;
}
if (has_async_wr && has_sync_wr)
- log_error("Memory %s.%s has mixed clocked/nonclocked write ports. This is not supported by \"write_smt2\".\n", log_id(cell), log_id(module));
+ log_error("Memory %s.%s has mixed clocked/nonclocked write ports. This is not supported by \"write_smt2\".\n", cell, module);
- decls.push_back(stringf("; yosys-smt2-memory %s %d %d %d %d %s\n", get_id(mem->memid), abits, mem->width, GetSize(mem->rd_ports), GetSize(mem->wr_ports), has_async_wr ? "async" : "sync"));
+ decls.push_back(stringf("; yosys-smt2-memory %s %d %d %d %d %s\n", mem->memid.unescape(), abits, mem->width, GetSize(mem->rd_ports), GetSize(mem->wr_ports), has_async_wr ? "async" : "sync"));
decls.push_back(witness_memory(get_id(mem->memid), cell, mem));
string memstate;
@@ -813,7 +813,7 @@ struct Smt2Worker
if (port.clk_enable)
log_error("Read port %d (%s) of memory %s.%s is clocked. This is not supported by \"write_smt2\"! "
- "Call \"memory\" with -nordff to avoid this error.\n", i, log_signal(port.data), log_id(mem->memid), log_id(module));
+ "Call \"memory\" with -nordff to avoid this error.\n", i, log_signal(port.data), mem->memid.unescape(), module);
decls.push_back(stringf("(define-fun |%s_m:R%dA %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n",
get_id(module), i, get_id(mem->memid), get_id(module), abits, addr.c_str(), log_signal(addr_sig)));
@@ -857,7 +857,7 @@ struct Smt2Worker
if (port.clk_enable)
log_error("Read port %d (%s) of memory %s.%s is clocked. This is not supported by \"write_smt2\"! "
- "Call \"memory\" with -nordff to avoid this error.\n", i, log_signal(port.data), log_id(mem->memid), log_id(module));
+ "Call \"memory\" with -nordff to avoid this error.\n", i, log_signal(port.data), mem->memid.unescape(), module);
decls.push_back(stringf("(define-fun |%s_m:R%dA %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n",
get_id(module), i, get_id(mem->memid), get_id(module), abits, addr.c_str(), log_signal(addr_sig)));
@@ -928,30 +928,30 @@ struct Smt2Worker
if (cell->type.in(ID($dffe), ID($sdff), ID($sdffe), ID($sdffce)) || cell->type.str().substr(0, 6) == "$_SDFF" || (cell->type.str().substr(0, 6) == "$_DFFE" && cell->type.str().size() == 10)) {
log_error("Unsupported cell type %s for cell %s.%s -- please run `dffunmap` before `write_smt2`.\n",
- log_id(cell->type), log_id(module), log_id(cell));
+ cell->type.unescape(), module, cell);
}
if (cell->type.in(ID($adff), ID($adffe), ID($aldff), ID($aldffe), ID($dffsr), ID($dffsre)) || cell->type.str().substr(0, 5) == "$_DFF" || cell->type.str().substr(0, 7) == "$_ALDFF") {
log_error("Unsupported cell type %s for cell %s.%s -- please run `async2sync; dffunmap` or `clk2fflogic` before `write_smt2`.\n",
- log_id(cell->type), log_id(module), log_id(cell));
+ cell->type.unescape(), module, cell);
}
if (cell->type.in(ID($sr), ID($dlatch), ID($adlatch), ID($dlatchsr)) || cell->type.str().substr(0, 8) == "$_DLATCH" || cell->type.str().substr(0, 5) == "$_SR_") {
log_error("Unsupported cell type %s for cell %s.%s -- please run `clk2fflogic` before `write_smt2`.\n",
- log_id(cell->type), log_id(module), log_id(cell));
+ cell->type, module, cell);
}
log_error("Unsupported cell type %s for cell %s.%s.\n",
- log_id(cell->type), log_id(module), log_id(cell));
+ cell->type, module, cell);
}
void verify_smtlib2_module()
{
if (!module->get_blackbox_attribute())
- log_error("Module %s with smtlib2_module attribute must also have blackbox attribute.\n", log_id(module));
+ log_error("Module %s with smtlib2_module attribute must also have blackbox attribute.\n", module);
if (module->cells().size() > 0)
- log_error("Module %s with smtlib2_module attribute must not have any cells inside it.\n", log_id(module));
+ log_error("Module %s with smtlib2_module attribute must not have any cells inside it.\n", module);
for (auto wire : module->wires())
if (!wire->port_id)
- log_error("Wire %s.%s must be input or output since module has smtlib2_module attribute.\n", log_id(module),
- log_id(wire));
+ log_error("Wire %s.%s must be input or output since module has smtlib2_module attribute.\n", module,
+ wire);
}
void run()
@@ -991,8 +991,8 @@ struct Smt2Worker
}
bool is_smtlib2_comb_expr = wire->has_attribute(ID::smtlib2_comb_expr);
if (is_smtlib2_comb_expr && !is_smtlib2_module)
- log_error("smtlib2_comb_expr is only valid in a module with the smtlib2_module attribute: wire %s.%s", log_id(module),
- log_id(wire));
+ log_error("smtlib2_comb_expr is only valid in a module with the smtlib2_module attribute: wire %s.%s", module,
+ wire);
if (wire->port_id || is_register || contains_clock || wire->get_bool_attribute(ID::keep) || (wiresmode && wire->name.isPublic())) {
RTLIL::SigSpec sig = sigmap(wire);
std::vector comments;
@@ -1023,10 +1023,10 @@ struct Smt2Worker
smtlib2_comb_expr =
"(let (\n" + smtlib2_inputs + ")\n" + wire->get_string_attribute(ID::smtlib2_comb_expr) + "\n)";
if (wire->port_input || !wire->port_output)
- log_error("smtlib2_comb_expr is only valid on output: wire %s.%s", log_id(module), log_id(wire));
+ log_error("smtlib2_comb_expr is only valid on output: wire %s.%s", module, wire);
if (!bvmode && GetSize(sig) > 1)
log_error("smtlib2_comb_expr is unsupported on multi-bit wires when -nobv is specified: wire %s.%s",
- log_id(module), log_id(wire));
+ module, wire);
comments.push_back(witness_signal("blackbox", wire->width, 0, get_id(wire), -1, wire));
}
@@ -1075,7 +1075,7 @@ struct Smt2Worker
if (wire->attributes.count(ID::init)) {
if (is_smtlib2_module)
log_error("init attribute not allowed on wires in module with smtlib2_module attribute: wire %s.%s",
- log_id(module), log_id(wire));
+ module, wire);
RTLIL::SigSpec sig = sigmap(wire);
Const val = wire->attributes.at(ID::init);
@@ -1381,7 +1381,7 @@ struct Smt2Worker
}
}
- if (verbose) log("=> finalizing SMT2 representation of %s.\n", log_id(module));
+ if (verbose) log("=> finalizing SMT2 representation of %s.\n", module);
for (auto c : hiercells) {
assert_list.push_back(stringf("(|%s_a| (|%s_h %s| state))", get_id(c->type), get_id(module), get_id(c->name)));
@@ -1867,12 +1867,12 @@ struct Smt2Backend : public Backend {
for (auto &dep : it.second)
if (module_deps.count(dep) > 0)
goto not_ready_yet;
- // log("Next in topological sort: %s\n", log_id(it.first->name));
+ // log("Next in topological sort: %s\n", it.first->name.unescape());
sorted_modules.push_back(it.first);
not_ready_yet:;
}
if (sorted_modules_idx == sorted_modules.size())
- log_error("Cyclic dependency between modules found! Cycle includes module %s.\n", log_id(module_deps.begin()->first->name));
+ log_error("Cyclic dependency between modules found! Cycle includes module %s.\n", module_deps.begin()->first->name.unescape());
while (sorted_modules_idx < sorted_modules.size())
module_deps.erase(sorted_modules.at(sorted_modules_idx++));
}
@@ -1902,7 +1902,7 @@ struct Smt2Backend : public Backend {
if (module->has_processes_warn())
continue;
- log("Creating SMT-LIBv2 representation of module %s.\n", log_id(module));
+ log("Creating SMT-LIBv2 representation of module %s.\n", module);
Smt2Worker worker(module, bvmode, memmode, wiresmode, verbose, statebv, statedt, forallmode, mod_stbv_width, mod_clk_cache);
worker.run();
diff --git a/backends/smv/smv.cc b/backends/smv/smv.cc
index a6ccbf27f..01f95ef45 100644
--- a/backends/smv/smv.cc
+++ b/backends/smv/smv.cc
@@ -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
@@ -29,7 +29,7 @@ PRIVATE_NAMESPACE_BEGIN
struct SmvWorker
{
- CellTypes ct;
+ NewCellTypes ct;
SigMap sigmap;
RTLIL::Module *module;
std::ostream &f;
@@ -217,7 +217,7 @@ struct SmvWorker
partial_assignment_wires.insert(wire);
if (wire->port_input)
- inputvars.push_back(stringf("%s : unsigned word[%d]; -- %s", cid(wire->name), wire->width, log_id(wire)));
+ inputvars.push_back(stringf("%s : unsigned word[%d]; -- %s", cid(wire->name), wire->width, wire));
if (wire->attributes.count(ID::init))
assignments.push_back(stringf("init(%s) := %s;", lvalue(wire), rvalue(wire->attributes.at(ID::init))));
@@ -579,18 +579,18 @@ struct SmvWorker
if (cell->type[0] == '$') {
if (cell->type.in(ID($dffe), ID($sdff), ID($sdffe), ID($sdffce)) || cell->type.str().substr(0, 6) == "$_SDFF" || (cell->type.str().substr(0, 6) == "$_DFFE" && cell->type.str().size() == 10)) {
log_error("Unsupported cell type %s for cell %s.%s -- please run `dffunmap` before `write_smv`.\n",
- log_id(cell->type), log_id(module), log_id(cell));
+ cell->type.unescape(), module, cell);
}
if (cell->type.in(ID($adff), ID($adffe), ID($aldff), ID($aldffe), ID($dffsr), ID($dffsre)) || cell->type.str().substr(0, 5) == "$_DFF" || cell->type.str().substr(0, 7) == "$_ALDFF") {
log_error("Unsupported cell type %s for cell %s.%s -- please run `async2sync; dffunmap` or `clk2fflogic` before `write_smv`.\n",
- log_id(cell->type), log_id(module), log_id(cell));
+ cell->type.unescape(), module, cell);
}
if (cell->type.in(ID($sr), ID($dlatch), ID($adlatch), ID($dlatchsr)) || cell->type.str().substr(0, 8) == "$_DLATCH" || cell->type.str().substr(0, 5) == "$_SR_") {
log_error("Unsupported cell type %s for cell %s.%s -- please run `clk2fflogic` before `write_smv`.\n",
- log_id(cell->type), log_id(module), log_id(cell));
+ cell->type.unescape(), module, cell);
}
log_error("Unsupported cell type %s for cell %s.%s.\n",
- log_id(cell->type), log_id(module), log_id(cell));
+ cell->type.unescape(), module, cell);
}
// f << stringf(" %s : %s;\n", cid(cell->name), cid(cell->type));
@@ -799,7 +799,7 @@ struct SmvBackend : public Backend {
*f << stringf("-- SMV description generated by %s\n", yosys_maybe_version());
- log("Creating SMV representation of module %s.\n", log_id(module));
+ log("Creating SMV representation of module %s.\n", module);
SmvWorker worker(module, verbose, *f);
worker.run();
@@ -819,7 +819,7 @@ struct SmvBackend : public Backend {
*f << stringf("-- SMV description generated by %s\n", yosys_maybe_version());
for (auto module : modules) {
- log("Creating SMV representation of module %s.\n", log_id(module));
+ log("Creating SMV representation of module %s.\n", module);
SmvWorker worker(module, verbose, *f);
worker.run();
}
diff --git a/backends/spice/spice.cc b/backends/spice/spice.cc
index 16458d647..5f14a2a66 100644
--- a/backends/spice/spice.cc
+++ b/backends/spice/spice.cc
@@ -30,7 +30,7 @@ PRIVATE_NAMESPACE_BEGIN
static string spice_id2str(IdString id)
{
static const char *escape_chars = "$\\[]()<>=";
- string s = RTLIL::unescape_id(id);
+ string s = id.unescape();
for (auto &ch : s)
if (strchr(escape_chars, ch) != nullptr) ch = '_';
@@ -82,7 +82,7 @@ static void print_spice_module(std::ostream &f, RTLIL::Module *module, RTLIL::De
if (design->module(cell->type) == nullptr)
{
log_warning("no (blackbox) module for cell type `%s' (%s.%s) found! Guessing order of ports.\n",
- log_id(cell->type), log_id(module), log_id(cell));
+ cell->type.unescape(), module, cell);
for (auto &conn : cell->connections()) {
RTLIL::SigSpec sig = sigmap(conn.second);
port_sigs.push_back(sig);
@@ -224,9 +224,9 @@ struct SpiceBackend : public Backend {
continue;
if (module->processes.size() != 0)
- log_error("Found unmapped processes in module %s: unmapped processes are not supported in SPICE backend!\n", log_id(module));
+ log_error("Found unmapped processes in module %s: unmapped processes are not supported in SPICE backend!\n", module);
if (module->memories.size() != 0)
- log_error("Found unmapped memories in module %s: unmapped memories are not supported in SPICE backend!\n", log_id(module));
+ log_error("Found unmapped memories in module %s: unmapped memories are not supported in SPICE backend!\n", module);
if (module->name == RTLIL::escape_id(top_module_name)) {
top_module = module;
diff --git a/backends/table/table.cc b/backends/table/table.cc
index 2bf64e7b1..bbb533965 100644
--- a/backends/table/table.cc
+++ b/backends/table/table.cc
@@ -77,8 +77,8 @@ struct TableBackend : public Backend {
if (wire->port_id == 0)
continue;
- *f << log_id(module) << "\t";
- *f << log_id(wire) << "\t";
+ *f << module->name.unescape() << "\t";
+ *f << wire->name.unescape() << "\t";
*f << "-" << "\t";
*f << "-" << "\t";
@@ -97,10 +97,10 @@ struct TableBackend : public Backend {
for (auto cell : module->cells())
for (auto conn : cell->connections())
{
- *f << log_id(module) << "\t";
- *f << log_id(cell) << "\t";
- *f << log_id(cell->type) << "\t";
- *f << log_id(conn.first) << "\t";
+ *f << module->name.unescape() << "\t";
+ *f << cell->name.unescape() << "\t";
+ *f << cell->type.unescape() << "\t";
+ *f << conn.first.unescape() << "\t";
if (cell->input(conn.first) && cell->output(conn.first))
*f << "inout" << "\t";
diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc
index 73ffcbf3e..473918264 100644
--- a/backends/verilog/verilog_backend.cc
+++ b/backends/verilog/verilog_backend.cc
@@ -2388,7 +2388,7 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
log_warning("Module %s contains RTLIL processes with sync rules. Such RTLIL "
"processes can't always be mapped directly to Verilog always blocks. "
"unintended changes in simulation behavior are possible! Use \"proc\" "
- "to convert processes to logic networks and registers.\n", log_id(module));
+ "to convert processes to logic networks and registers.\n", module);
f << stringf("\n");
for (auto it = module->processes.begin(); it != module->processes.end(); ++it)
@@ -2714,7 +2714,7 @@ struct VerilogBackend : public Backend {
continue;
if (selected && !design->selected_whole_module(module->name)) {
if (design->selected_module(module->name))
- log_cmd_error("Can't handle partially selected module %s!\n", log_id(module->name));
+ log_cmd_error("Can't handle partially selected module %s!\n", module->name.unescape());
continue;
}
log("Dumping module `%s'.\n", module->name);
diff --git a/docs/source/_images/Makefile b/docs/source/_images/Makefile
index 71f52c578..dc75c97c9 100644
--- a/docs/source/_images/Makefile
+++ b/docs/source/_images/Makefile
@@ -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 $(modules())
- log(" %s (%d wires, %d cells)\n", log_id(mod),
+ log(" %s (%d wires, %d cells)\n", mod,
GetSize(mod->wires()), GetSize(mod->cells()));
}
} MyPass;
@@ -28,7 +28,7 @@ struct Test1Pass : public Pass {
log_error("A module with the name absval already exists!\n");
RTLIL::Module *module = design->addModule("\\absval");
- log("Name of this module: %s\n", log_id(module));
+ log("Name of this module: %s\n", module);
RTLIL::Wire *a = module->addWire("\\a", 4);
a->port_input = true;
diff --git a/docs/source/code_examples/functional/dummy.cc b/docs/source/code_examples/functional/dummy.cc
index 42b05b339..a339bc275 100644
--- a/docs/source/code_examples/functional/dummy.cc
+++ b/docs/source/code_examples/functional/dummy.cc
@@ -24,19 +24,19 @@ struct FunctionalDummyBackend : public Backend {
// write node functions
for (auto node : ir)
- *f << " assign " << id2cstr(node.name())
+ *f << " assign " << node.name().unescape()
<< " = " << node.to_string() << "\n";
*f << "\n";
// write outputs and next state
for (auto output : ir.outputs())
- *f << " " << id2cstr(output->kind)
- << " " << id2cstr(output->name)
- << " = " << id2cstr(output->value().name()) << "\n";
+ *f << " " << output->kind.unescape()
+ << " " << output->name.unescape()
+ << " = " << output->value().name().unescape() << "\n";
for (auto state : ir.states())
- *f << " " << id2cstr(state->kind)
- << " " << id2cstr(state->name)
- << " = " << id2cstr(state->next_value().name()) << "\n";
+ *f << " " << state->kind.unescape()
+ << " " << state->name.unescape()
+ << " = " << state->next_value().name().unescape() << "\n";
}
}
} FunctionalDummyBackend;
diff --git a/docs/source/code_examples/macro_commands/synth_ice40.ys b/docs/source/code_examples/macro_commands/synth_ice40.ys
index 443b8e0b0..07d960a64 100644
--- a/docs/source/code_examples/macro_commands/synth_ice40.ys
+++ b/docs/source/code_examples/macro_commands/synth_ice40.ys
@@ -6,6 +6,7 @@ begin:
proc
flatten:
+ check
flatten
tribuf -logic
deminout
diff --git a/docs/source/code_examples/stubnets/stubnets.cc b/docs/source/code_examples/stubnets/stubnets.cc
index 566d24b18..41fb66e82 100644
--- a/docs/source/code_examples/stubnets/stubnets.cc
+++ b/docs/source/code_examples/stubnets/stubnets.cc
@@ -27,7 +27,7 @@ static void find_stub_nets(RTLIL::Design *design, RTLIL::Module *module, bool re
// count output lines for this module (needed only for summary output at the end)
int line_count = 0;
- log("Looking for stub wires in module %s:\n", RTLIL::id2cstr(module->name));
+ log("Looking for stub wires in module %s:\n", module);
// For all ports on all cells
for (auto &cell_iter : module->cells_)
@@ -74,11 +74,11 @@ static void find_stub_nets(RTLIL::Design *design, RTLIL::Module *module, bool re
// report stub bits and/or stub wires, don't report single bits
// if called with report_bits set to false.
if (GetSize(stub_bits) == GetSize(sig)) {
- log(" found stub wire: %s\n", RTLIL::id2cstr(wire->name));
+ log(" found stub wire: %s\n", wire);
} else {
if (!report_bits)
continue;
- log(" found wire with stub bits: %s [", RTLIL::id2cstr(wire->name));
+ log(" found wire with stub bits: %s [", wire);
for (int bit : stub_bits)
log("%s%d", bit == *stub_bits.begin() ? "" : ", ", bit);
log("]\n");
diff --git a/docs/source/conf.py b/docs/source/conf.py
index a7da22d97..b85c391db 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -6,7 +6,7 @@ import os
project = 'YosysHQ Yosys'
author = 'YosysHQ GmbH'
copyright ='2026 YosysHQ GmbH'
-yosys_ver = "0.62"
+yosys_ver = "0.65"
# select HTML theme
html_theme = 'furo-ys'
diff --git a/docs/source/getting_started/example_synth.rst b/docs/source/getting_started/example_synth.rst
index ccf0d252b..ebfaa542b 100644
--- a/docs/source/getting_started/example_synth.rst
+++ b/docs/source/getting_started/example_synth.rst
@@ -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.
diff --git a/docs/source/getting_started/installation.rst b/docs/source/getting_started/installation.rst
index 43b996353..2a90a8071 100644
--- a/docs/source/getting_started/installation.rst
+++ b/docs/source/getting_started/installation.rst
@@ -87,7 +87,7 @@ not regularly tested:
Build prerequisites
^^^^^^^^^^^^^^^^^^^
-A C++ compiler with C++17 support is required as well as some standard tools
+A C++ compiler with C++20 support is required as well as some standard tools
such as GNU Flex, GNU Bison (>=3.8), Make, and Python (>=3.11). Some additional
tools: readline, libffi, Tcl and zlib; are optional but enabled by default (see
:makevar:`ENABLE_*` settings in Makefile). Graphviz and Xdot are used by the
diff --git a/docs/source/using_yosys/bugpoint.rst b/docs/source/using_yosys/bugpoint.rst
index c524470af..71ec3b68d 100644
--- a/docs/source/using_yosys/bugpoint.rst
+++ b/docs/source/using_yosys/bugpoint.rst
@@ -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"
diff --git a/docs/source/using_yosys/more_scripting/model_checking.rst b/docs/source/using_yosys/more_scripting/model_checking.rst
index 799c99b6f..cbf5dc7b0 100644
--- a/docs/source/using_yosys/more_scripting/model_checking.rst
+++ b/docs/source/using_yosys/more_scripting/model_checking.rst
@@ -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.
diff --git a/docs/source/yosys_internals/extending_yosys/contributing.rst b/docs/source/yosys_internals/extending_yosys/contributing.rst
index 458d7dc36..8d90d2cbe 100644
--- a/docs/source/yosys_internals/extending_yosys/contributing.rst
+++ b/docs/source/yosys_internals/extending_yosys/contributing.rst
@@ -4,8 +4,10 @@ Contributing to Yosys
Reporting bugs
--------------
-A good bug report includes the following information:
+We fix well-reported bugs the fastest. A good bug report is an issue on the `issue tracker`_
+and includes the following information:
+.. _`issue tracker`: https://github.com/YosysHQ/yosys/issues
Title
~~~~~
@@ -27,8 +29,10 @@ The reproduction steps should be a minimal, complete and verifiable
example `MVCE`_.
Providing an MVCE with your bug report drastically increases the likelihood that
someone will be able to help resolve your issue.
-One way to minimize a design is to use the `bugpoint_` command.
-You can learn more in the `how-to guide for bugpoint_`.
+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 `.
The reproduction steps are ideally a code-block (starting and ending with
triple backquotes) containing
@@ -85,7 +89,6 @@ Don't forget to mention:
reproduction steps to just the Yosys part.
.. _MVCE: https://stackoverflow.com/help/minimal-reproducible-example
-.. _bugpoint: https://yosys.readthedocs.io/en/latest/cmd/bugpoint.html
.. _how-to guide for bugpoint: https://yosys.readthedocs.io/en/latest/using_yosys/bugpoint.html
Expected Behaviour
@@ -176,6 +179,12 @@ 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
@@ -277,7 +286,7 @@ have incorrect results in unusual situations.
Coding style
~~~~~~~~~~~~
-Yosys is written in C++17.
+Yosys is written in C++20.
In general Yosys uses ``int`` instead of ``size_t``. To avoid compiler warnings
for implicit type casts, always use ``GetSize(foobar)`` instead of
@@ -297,6 +306,26 @@ 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
~~~~~~~~~
@@ -317,10 +346,12 @@ Reviewing PRs is a totally valid form of external contributing to the project!
Who's the reviewer?
~~~~~~~~~~~~~~~~~~~
-Yosys HQ is a company with the inherited mandate to make decisions on behalf
-of the open source project. As such, we at HQ are collectively the maintainers.
-Within HQ, we allocate reviews based on expertise with the topic at hand
-as well as member time constraints.
+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
@@ -339,7 +370,8 @@ and stop being responsive, in the future, we might decide to remove such code
if convenient and costly to maintain. It's simply more respectful of the users'
time to explicitly cut something out than let it "bitrot". Larger projects like
LLVM or linux could not survive without such things, but Yosys is far smaller,
-and there are expectations
+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
@@ -375,3 +407,5 @@ 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.
diff --git a/docs/source/yosys_internals/extending_yosys/extensions.rst b/docs/source/yosys_internals/extending_yosys/extensions.rst
index 74a7d72d6..949c78586 100644
--- a/docs/source/yosys_internals/extending_yosys/extensions.rst
+++ b/docs/source/yosys_internals/extending_yosys/extensions.rst
@@ -230,8 +230,7 @@ Use ``log_error()`` to report a non-recoverable error:
.. code:: C++
if (design->modules.count(module->name) != 0)
- log_error("A module with the name %s already exists!\n",
- RTLIL::id2cstr(module->name));
+ log_error("A module with the name %s already exists!\n", module);
Use ``log_cmd_error()`` to report a recoverable error:
diff --git a/docs/source/yosys_internals/extending_yosys/functional_ir.rst b/docs/source/yosys_internals/extending_yosys/functional_ir.rst
index 4f363623e..1c4ab5281 100644
--- a/docs/source/yosys_internals/extending_yosys/functional_ir.rst
+++ b/docs/source/yosys_internals/extending_yosys/functional_ir.rst
@@ -181,7 +181,7 @@ pointer ``f`` to the output file, or stdout if none is given.
For this minimal example all we are doing is printing out each node. The
``node.name()`` method returns an ``RTLIL::IdString``, which we convert for
-printing with ``id2cstr()``. Then, to print the function of the node, we use
+printing with ``unescape()``. Then, to print the function of the node, we use
``node.to_string()`` which gives us a string of the form ``function(args)``. The
``function`` part is the result of ``Functional::IR::fn_to_string(node.fn())``;
while ``args`` is the zero or more arguments passed to the function, most
diff --git a/docs/source/yosys_internals/index.rst b/docs/source/yosys_internals/index.rst
index 483cc2bf8..217b88e36 100644
--- a/docs/source/yosys_internals/index.rst
+++ b/docs/source/yosys_internals/index.rst
@@ -25,7 +25,7 @@ wide range of real-world designs, including the `OpenRISC 1200 CPU`_, the
.. _k68 CPU: http://opencores.org/projects/k68
-Yosys is written in C++, targeting C++17 at minimum. This chapter describes some
+Yosys is written in C++, targeting C++20 at minimum. This chapter describes some
of the fundamental Yosys data structures. For the sake of simplicity the C++
type names used in the Yosys implementation are used in this chapter, even
though the chapter only explains the conceptual idea behind it and can be used
diff --git a/examples/aiger/README b/examples/aiger/README
index 4e7694e95..13dce2035 100644
--- a/examples/aiger/README
+++ b/examples/aiger/README
@@ -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
diff --git a/examples/aiger/demo.sh b/examples/aiger/demo.sh
index 8728b6722..a1217bbd2 100644
--- a/examples/aiger/demo.sh
+++ b/examples/aiger/demo.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
set -ex
yosys -p '
read_verilog -formal demo.v
diff --git a/examples/anlogic/build.sh b/examples/anlogic/build.sh
index e0f6b4cfe..9b6e8c479 100755
--- a/examples/anlogic/build.sh
+++ b/examples/anlogic/build.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
set -ex
yosys demo.ys
$TD_HOME/bin/td build.tcl
diff --git a/examples/basys3/run.sh b/examples/basys3/run.sh
index 10f059103..deb68d432 100644
--- a/examples/basys3/run.sh
+++ b/examples/basys3/run.sh
@@ -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
diff --git a/examples/cmos/testbench.sh b/examples/cmos/testbench.sh
index 061704b64..856169ab9 100644
--- a/examples/cmos/testbench.sh
+++ b/examples/cmos/testbench.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
set -ex
diff --git a/examples/cmos/testbench_digital.sh b/examples/cmos/testbench_digital.sh
index d7ab0fe1f..2e70e874c 100644
--- a/examples/cmos/testbench_digital.sh
+++ b/examples/cmos/testbench_digital.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
set -ex
diff --git a/examples/cxx-api/scopeinfo_example.cc b/examples/cxx-api/scopeinfo_example.cc
index fd5d2a781..edbd1533f 100644
--- a/examples/cxx-api/scopeinfo_example.cc
+++ b/examples/cxx-api/scopeinfo_example.cc
@@ -61,7 +61,7 @@ struct ScopeinfoExamplePass : public Pass {
if (do_wires) {
for (auto module : design->selected_modules()) {
- log("Source hierarchy for all selected wires within %s:\n", log_id(module));
+ log("Source hierarchy for all selected wires within %s:\n", module);
ModuleHdlnameIndex index(module);
index.index_scopeinfo_cells();
@@ -73,11 +73,11 @@ struct ScopeinfoExamplePass : public Pass {
auto wire_scope = index.containing_scope(wire);
if (!wire_scope.first.valid()) {
- log_warning("Couldn't find containing scope for %s in index\n", log_id(wire));
+ log_warning("Couldn't find containing scope for %s in index\n", wire);
continue;
}
- log("%s %s\n", wire_scope.first.path_str(), log_id(wire_scope.second));
+ log("%s %s\n", wire_scope.first.path_str(), wire_scope.second.unescape());
for (auto src : index.sources(wire))
log(" - %s\n", src);
}
@@ -127,9 +127,9 @@ struct ScopeinfoExamplePass : public Pass {
continue;
log("common_ancestor(%s %s%s%s, %s %s%s%s) = %s %s\n",
- log_id(module), scope_i.first.path_str().c_str(), scope_i.first.is_root() ? "" : " ", log_id(scope_i.second),
- log_id(module), scope_j.first.path_str().c_str(), scope_j.first.is_root() ? "" : " ", log_id(scope_j.second),
- log_id(module), common.path_str().c_str()
+ module, scope_i.first.path_str().c_str(), scope_i.first.is_root() ? "" : " ", scope_i.second.unescape(),
+ module, scope_j.first.path_str().c_str(), scope_j.first.is_root() ? "" : " ", scope_j.second.unescape(),
+ module, common.path_str().c_str()
);
if (++limit == 10)
diff --git a/examples/gowin/run.sh b/examples/gowin/run.sh
index cd260101e..960624464 100644
--- a/examples/gowin/run.sh
+++ b/examples/gowin/run.sh
@@ -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
diff --git a/examples/igloo2/runme.sh b/examples/igloo2/runme.sh
index a08894e0a..7349ff603 100644
--- a/examples/igloo2/runme.sh
+++ b/examples/igloo2/runme.sh
@@ -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}
diff --git a/examples/intel/DE2i-150/quartus_compile/runme_quartus b/examples/intel/DE2i-150/quartus_compile/runme_quartus
index 83aa3b609..2bc29582c 100644
--- a/examples/intel/DE2i-150/quartus_compile/runme_quartus
+++ b/examples/intel/DE2i-150/quartus_compile/runme_quartus
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
export REV="de2i"
diff --git a/examples/intel/MAX10/runme_postsynth b/examples/intel/MAX10/runme_postsynth
index f16210540..657c05fa8 100644
--- a/examples/intel/MAX10/runme_postsynth
+++ b/examples/intel/MAX10/runme_postsynth
@@ -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
diff --git a/examples/intel/asicworld_lfsr/run_cycloneiv b/examples/intel/asicworld_lfsr/run_cycloneiv
index c7498bded..077b7fbde 100755
--- a/examples/intel/asicworld_lfsr/run_cycloneiv
+++ b/examples/intel/asicworld_lfsr/run_cycloneiv
@@ -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
diff --git a/examples/intel/asicworld_lfsr/run_max10 b/examples/intel/asicworld_lfsr/run_max10
index b75d552bb..8fccb57f8 100755
--- a/examples/intel/asicworld_lfsr/run_max10
+++ b/examples/intel/asicworld_lfsr/run_max10
@@ -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
diff --git a/examples/intel/asicworld_lfsr/runme_postsynth b/examples/intel/asicworld_lfsr/runme_postsynth
index c3b26b034..ad5ca39d4 100755
--- a/examples/intel/asicworld_lfsr/runme_postsynth
+++ b/examples/intel/asicworld_lfsr/runme_postsynth
@@ -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
diff --git a/examples/intel/asicworld_lfsr/runme_presynth b/examples/intel/asicworld_lfsr/runme_presynth
index 51118bb4b..3ed6618d3 100755
--- a/examples/intel/asicworld_lfsr/runme_presynth
+++ b/examples/intel/asicworld_lfsr/runme_presynth
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
iverilog -o presynth lfsr_updown_tb.v lfsr_updown.v &&\
diff --git a/examples/mimas2/run.sh b/examples/mimas2/run.sh
index aafde78ed..b16341ece 100644
--- a/examples/mimas2/run.sh
+++ b/examples/mimas2/run.sh
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/usr/bin/env sh
set -e
yosys run_yosys.ys
edif2ngd example.edif
diff --git a/frontends/aiger/aigerparse.cc b/frontends/aiger/aigerparse.cc
index e55349aa7..b2cc613f2 100644
--- a/frontends/aiger/aigerparse.cc
+++ b/frontends/aiger/aigerparse.cc
@@ -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
@@ -224,7 +224,7 @@ AigerReader::AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString
module = new RTLIL::Module;
module->name = module_name;
if (design->module(module->name))
- log_error("Duplicate definition of module %s!\n", log_id(module->name));
+ log_error("Duplicate definition of module %s!\n", module->name.unescape());
}
void AigerReader::parse_aiger()
@@ -821,7 +821,7 @@ void AigerReader::post_process()
RTLIL::Wire* wire = inputs[variable];
log_assert(wire);
log_assert(wire->port_input);
- log_debug("Renaming input %s", log_id(wire));
+ log_debug("Renaming input %s", wire);
RTLIL::Wire *existing = nullptr;
if (index == 0) {
@@ -835,7 +835,7 @@ void AigerReader::post_process()
wire->port_input = false;
module->connect(wire, existing);
}
- log_debug(" -> %s\n", log_id(escaped_s));
+ log_debug(" -> %s\n", escaped_s.unescape());
}
else {
RTLIL::IdString indexed_name = stringf("%s[%d]", escaped_s, index);
@@ -846,7 +846,7 @@ void AigerReader::post_process()
module->connect(wire, existing);
wire->port_input = false;
}
- log_debug(" -> %s\n", log_id(indexed_name));
+ log_debug(" -> %s\n", indexed_name.unescape());
}
if (wideports && !existing) {
@@ -866,7 +866,7 @@ void AigerReader::post_process()
RTLIL::Wire* wire = outputs[variable + co_count];
log_assert(wire);
log_assert(wire->port_output);
- log_debug("Renaming output %s", log_id(wire));
+ log_debug("Renaming output %s", wire);
RTLIL::Wire *existing;
if (index == 0) {
@@ -882,7 +882,7 @@ void AigerReader::post_process()
module->connect(wire, existing);
wire = existing;
}
- log_debug(" -> %s\n", log_id(escaped_s));
+ log_debug(" -> %s\n", escaped_s.unescape());
}
else {
RTLIL::IdString indexed_name = stringf("%s[%d]", escaped_s, index);
@@ -894,7 +894,7 @@ void AigerReader::post_process()
existing->port_output = true;
module->connect(wire, existing);
}
- log_debug(" -> %s\n", log_id(indexed_name));
+ log_debug(" -> %s\n", indexed_name.unescape());
}
if (wideports && !existing) {
@@ -912,7 +912,7 @@ void AigerReader::post_process()
else if (type == "box") {
RTLIL::Cell* cell = module->cell(stringf("$box%d", variable));
if (!cell)
- log_debug("Box %d (%s) no longer exists.\n", variable, log_id(escaped_s));
+ log_debug("Box %d (%s) no longer exists.\n", variable, escaped_s.unescape());
else
module->rename(cell, escaped_s);
}
diff --git a/frontends/aiger2/xaiger.cc b/frontends/aiger2/xaiger.cc
index 510da0be8..a62c52169 100644
--- a/frontends/aiger2/xaiger.cc
+++ b/frontends/aiger2/xaiger.cc
@@ -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,13 +80,13 @@ 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)
- log_error("Module '%s' not found\n", log_id(module_name));
+ log_error("Module '%s' not found\n", module_name.unescape());
std::ifstream map_file;
map_file.open(map_filename);
@@ -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", box->type.unescape());
if (box_seq >= (int) boxes.size()) {
boxes.resize(box_seq + 1);
@@ -276,9 +276,9 @@ struct Xaiger2Frontend : public Frontend {
uint32_t nins = read_be32(*f);
for (uint32_t j = 0; j < nins; j++)
cell.ins.push_back(read_idstring(*f));
- log_debug("M: Cell %s (out %s, ins", log_id(cell.type), log_id(cell.out));
+ log_debug("M: Cell %s (out %s, ins", cell.type.unescape(), cell.out.unescape());
for (auto in : cell.ins)
- log_debug(" %s", log_id(in));
+ log_debug(" %s", in.unescape());
log_debug(")\n");
}
@@ -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",
diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc
index 984c4294c..f5a601c3a 100644
--- a/frontends/ast/ast.cc
+++ b/frontends/ast/ast.cc
@@ -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 = "|"; }
@@ -1533,7 +1544,7 @@ void AST::explode_interface_port(AstNode *module_ast, RTLIL::Module * intfmodule
for (auto w : intfmodule->wires()){
auto loc = module_ast->location;
auto wire = std::make_unique(loc, AST_WIRE, std::make_unique(loc, AST_RANGE, AstNode::mkconst_int(loc, w->width -1, true), AstNode::mkconst_int(loc, 0, true)));
- std::string origname = log_id(w->name);
+ std::string origname = w->name.unescape();
std::string newname = intfname + "." + origname;
wire->str = newname;
if (modport != NULL) {
@@ -1573,7 +1584,7 @@ bool AstModule::reprocess_if_necessary(RTLIL::Design *design)
continue;
if (design->module(modname) || design->module("$abstract" + modname)) {
log("Reprocessing module %s because instantiated module %s has become available.\n",
- log_id(name), log_id(modname));
+ name.unescape(), RTLIL::unescape_id(modname));
loadconfig();
process_and_replace_module(design, this, ast.get(), NULL);
return true;
@@ -1595,7 +1606,7 @@ void AstModule::expand_interfaces(RTLIL::Design *design, const dictwires()){
auto wire = std::make_unique(loc, AST_WIRE, std::make_unique(loc, AST_RANGE, AstNode::mkconst_int(loc, w->width -1, true), AstNode::mkconst_int(loc, 0, true)));
- std::string newname = log_id(w->name);
+ std::string newname = w->name.unescape();
newname = intfname + "." + newname;
wire->str = newname;
new_ast->children.push_back(std::move(wire));
@@ -1668,7 +1679,7 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, const dictname);
+ interf_info += intf.second->name.unescape();
has_interfaces = true;
}
@@ -1724,7 +1735,7 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, const dictset_bool_attribute(ID::is_interface);
}
else {
- log_error("No port with matching name found (%s) in %s. Stopping\n", log_id(intf.first), modname);
+ log_error("No port with matching name found (%s) in %s. Stopping\n", intf.first, modname);
}
}
diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h
index fd8ecddd7..f92b4a5b8 100644
--- a/frontends/ast/ast.h
+++ b/frontends/ast/ast.h
@@ -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,
diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc
index d9eb51a9c..718d5aa23 100644
--- a/frontends/ast/genrtlil.cc
+++ b/frontends/ast/genrtlil.cc
@@ -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 current_case_assigned_bits;
+
ProcessGenerator(std::unique_ptr 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 &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 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 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 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); }
@@ -2099,10 +2197,10 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
const auto* value = child->children[0].get();
if (value->type == AST_REALVALUE)
log_file_warning(*location.begin.filename, location.begin.line, "Replacing floating point parameter %s.%s = %f with string.\n",
- log_id(cell), log_id(paraname), value->realvalue);
+ cell, paraname.unescape(), value->realvalue);
else if (value->type != AST_CONSTANT)
input_error("Parameter %s.%s with non-constant value!\n",
- log_id(cell), log_id(paraname));
+ cell, paraname.unescape());
cell->parameters[paraname] = value->asParaConst();
continue;
}
diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc
index a5b8c77ac..95dca27d8 100644
--- a/frontends/ast/simplify.cc
+++ b/frontends/ast/simplify.cc
@@ -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 array_indices_from_position(AstNode *mem, const std::vector &position)
+{
+ int num_dims = mem->unpacked_dimensions;
+ log_assert(GetSize(position) == num_dims);
+
+ std::vector 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&)> callback)
+{
+ int num_dims = mem->unpacked_dimensions;
+ if (num_dims == 0) {
+ callback({});
+ return;
+ }
+
+ std::vector position(num_dims, 0);
+ std::vector 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.
@@ -1393,7 +1492,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin
const RTLIL::Wire *ref = module->wire(port_name);
if (ref == nullptr)
input_error("Cell instance refers to port %s which does not exist in module %s!.\n",
- log_id(port_name), log_id(module->name));
+ port_name.unescape(), module->name.unescape());
// select the argument, if present
log_assert(child->children.size() <= 1);
@@ -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:
@@ -3145,7 +3250,7 @@ skip_dynamic_range_lvalue_expansion:;
if (stage > 1 && type == AST_IDENTIFIER && id2ast != nullptr && id2ast->type == AST_MEMORY && !in_lvalue &&
children.size() == 1 && children[0]->type == AST_RANGE && children[0]->children.size() == 1) {
if (integer < (unsigned)id2ast->unpacked_dimensions)
- input_error("Insufficient number of array indices for %s.\n", log_id(str));
+ input_error("Insufficient number of array indices for %s.\n", RTLIL::unescape_id(str));
newNode = std::make_unique(location, AST_MEMRD, children[0]->children[0]->clone());
newNode->str = str;
newNode->id2ast = id2ast;
@@ -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 id, const std::vector& indices) {
+ int indexed_dims = GetSize(indices);
+ if (indexed_dims == 1) {
+ // Single dimension: use AST_RANGE
+ id->children.push_back(std::make_unique(location, AST_RANGE,
+ mkconst_int(location, indices[0], true)));
+ } else {
+ // Multiple dimensions: use AST_MULTIRANGE
+ auto multirange = std::make_unique(location, AST_MULTIRANGE);
+ for (int idx : indices) {
+ multirange->children.push_back(std::make_unique(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 id, AstNode *mem, const std::vector& 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 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& 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 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> assignments;
+ std::vector> pattern_temp_assignments;
+
+ foreach_array_position(lhs_mem, [&](const std::vector& position) {
+ auto lhs_idx = add_position_to_id(lhs->clone(), lhs_mem, position);
+
+ std::unique_ptr 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(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(location, AST_WIRE,
+ std::make_unique(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(location, AST_IDENTIFIER);
+ tmp_id->str = wire_tmp->str;
+ pattern_temp_assignments.push_back(std::make_unique(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(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(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 &&
@@ -3207,7 +3523,7 @@ skip_dynamic_range_lvalue_expansion:;
(children[0]->children.size() == 1 || children[0]->children.size() == 2) && children[0]->children[0]->type == AST_RANGE)
{
if (children[0]->integer < (unsigned)children[0]->id2ast->unpacked_dimensions)
- input_error("Insufficient number of array indices for %s.\n", log_id(str));
+ input_error("Insufficient number of array indices for %s.\n", RTLIL::unescape_id(str));
std::stringstream sstr;
sstr << "$memwr$" << children[0]->str << "$" << RTLIL::encode_filename(*location.begin.filename) << ":" << location.begin.line << "$" << (autoidx++);
@@ -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;
@@ -4955,7 +5273,7 @@ void AstNode::mem2reg_as_needed_pass1(dict> &mem2reg
AstNode *mem = id2ast;
if (integer < (unsigned)mem->unpacked_dimensions)
- input_error("Insufficient number of array indices for %s.\n", log_id(str));
+ input_error("Insufficient number of array indices for %s.\n", RTLIL::unescape_id(str));
// flag if used after blocking assignment (in same proc)
if ((proc_flags[mem] & AstNode::MEM2REG_FL_EQ1) && !(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_EQ2)) {
diff --git a/frontends/blif/blifparse.cc b/frontends/blif/blifparse.cc
index 350d7cafe..2eae64fa1 100644
--- a/frontends/blif/blifparse.cc
+++ b/frontends/blif/blifparse.cc
@@ -175,7 +175,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool
obj_attributes = &module->attributes;
obj_parameters = nullptr;
if (design->module(module->name))
- log_error("Duplicate definition of module %s in line %d!\n", log_id(module->name), line_count);
+ log_error("Duplicate definition of module %s in line %d!\n", module->name.unescape(), line_count);
design->add(module);
continue;
}
diff --git a/frontends/json/jsonparse.cc b/frontends/json/jsonparse.cc
index 803931f32..0fac902b5 100644
--- a/frontends/json/jsonparse.cc
+++ b/frontends/json/jsonparse.cc
@@ -295,7 +295,7 @@ void json_import(Design *design, string &modname, JsonNode *node)
module->name = RTLIL::escape_id(modname.c_str());
if (design->module(module->name))
- log_error("Re-definition of module %s.\n", log_id(module->name));
+ log_error("Re-definition of module %s.\n", module->name.unescape());
design->add(module);
@@ -320,22 +320,22 @@ void json_import(Design *design, string &modname, JsonNode *node)
JsonNode *port_node = ports_node->data_dict.at(ports_node->data_dict_keys[port_id-1]);
if (port_node->type != 'D')
- log_error("JSON port node '%s' is not a dictionary.\n", log_id(port_name));
+ log_error("JSON port node '%s' is not a dictionary.\n", port_name.unescape());
if (port_node->data_dict.count("direction") == 0)
- log_error("JSON port node '%s' has no direction attribute.\n", log_id(port_name));
+ log_error("JSON port node '%s' has no direction attribute.\n", port_name.unescape());
if (port_node->data_dict.count("bits") == 0)
- log_error("JSON port node '%s' has no bits attribute.\n", log_id(port_name));
+ log_error("JSON port node '%s' has no bits attribute.\n", port_name.unescape());
JsonNode *port_direction_node = port_node->data_dict.at("direction");
JsonNode *port_bits_node = port_node->data_dict.at("bits");
if (port_direction_node->type != 'S')
- log_error("JSON port node '%s' has non-string direction attribute.\n", log_id(port_name));
+ log_error("JSON port node '%s' has non-string direction attribute.\n", port_name.unescape());
if (port_bits_node->type != 'A')
- log_error("JSON port node '%s' has non-array bits attribute.\n", log_id(port_name));
+ log_error("JSON port node '%s' has non-array bits attribute.\n", port_name.unescape());
Wire *port_wire = module->wire(port_name);
@@ -370,7 +370,7 @@ void json_import(Design *design, string &modname, JsonNode *node)
port_wire->port_input = true;
port_wire->port_output = true;
} else
- log_error("JSON port node '%s' has invalid '%s' direction attribute.\n", log_id(port_name), port_direction_node->data_string);
+ log_error("JSON port node '%s' has invalid '%s' direction attribute.\n", port_name.unescape(), port_direction_node->data_string);
port_wire->port_id = port_id;
@@ -390,7 +390,7 @@ void json_import(Design *design, string &modname, JsonNode *node)
module->connect(sigbit, State::Sz);
else
log_error("JSON port node '%s' has invalid '%s' bit string value on bit %d.\n",
- log_id(port_name), bitval_node->data_string.c_str(), i);
+ port_name.unescape(), bitval_node->data_string.c_str(), i);
} else
if (bitval_node->type == 'N') {
int bitidx = bitval_node->data_number;
@@ -405,7 +405,7 @@ void json_import(Design *design, string &modname, JsonNode *node)
signal_bits[bitidx] = sigbit;
}
} else
- log_error("JSON port node '%s' has invalid bit value on bit %d.\n", log_id(port_name), i);
+ log_error("JSON port node '%s' has invalid bit value on bit %d.\n", port_name.unescape(), i);
}
}
@@ -425,15 +425,15 @@ void json_import(Design *design, string &modname, JsonNode *node)
JsonNode *net_node = net.second;
if (net_node->type != 'D')
- log_error("JSON netname node '%s' is not a dictionary.\n", log_id(net_name));
+ log_error("JSON netname node '%s' is not a dictionary.\n", net_name.unescape());
if (net_node->data_dict.count("bits") == 0)
- log_error("JSON netname node '%s' has no bits attribute.\n", log_id(net_name));
+ log_error("JSON netname node '%s' has no bits attribute.\n", net_name.unescape());
JsonNode *bits_node = net_node->data_dict.at("bits");
if (bits_node->type != 'A')
- log_error("JSON netname node '%s' has non-array bits attribute.\n", log_id(net_name));
+ log_error("JSON netname node '%s' has non-array bits attribute.\n", net_name.unescape());
Wire *wire = module->wire(net_name);
@@ -468,7 +468,7 @@ void json_import(Design *design, string &modname, JsonNode *node)
module->connect(sigbit, State::Sz);
else
log_error("JSON netname node '%s' has invalid '%s' bit string value on bit %d.\n",
- log_id(net_name), bitval_node->data_string.c_str(), i);
+ net_name.unescape(), bitval_node->data_string.c_str(), i);
} else
if (bitval_node->type == 'N') {
int bitidx = bitval_node->data_number;
@@ -479,7 +479,7 @@ void json_import(Design *design, string &modname, JsonNode *node)
signal_bits[bitidx] = sigbit;
}
} else
- log_error("JSON netname node '%s' has invalid bit value on bit %d.\n", log_id(net_name), i);
+ log_error("JSON netname node '%s' has invalid bit value on bit %d.\n", net_name.unescape(), i);
}
if (net_node->data_dict.count("attributes"))
@@ -500,27 +500,27 @@ void json_import(Design *design, string &modname, JsonNode *node)
JsonNode *cell_node = cell_node_it.second;
if (cell_node->type != 'D')
- log_error("JSON cells node '%s' is not a dictionary.\n", log_id(cell_name));
+ log_error("JSON cells node '%s' is not a dictionary.\n", cell_name.unescape());
if (cell_node->data_dict.count("type") == 0)
- log_error("JSON cells node '%s' has no type attribute.\n", log_id(cell_name));
+ log_error("JSON cells node '%s' has no type attribute.\n", cell_name.unescape());
JsonNode *type_node = cell_node->data_dict.at("type");
if (type_node->type != 'S')
- log_error("JSON cells node '%s' has a non-string type.\n", log_id(cell_name));
+ log_error("JSON cells node '%s' has a non-string type.\n", cell_name.unescape());
IdString cell_type = RTLIL::escape_id(type_node->data_string.c_str());
Cell *cell = module->addCell(cell_name, cell_type);
if (cell_node->data_dict.count("connections") == 0)
- log_error("JSON cells node '%s' has no connections attribute.\n", log_id(cell_name));
+ log_error("JSON cells node '%s' has no connections attribute.\n", cell_name.unescape());
JsonNode *connections_node = cell_node->data_dict.at("connections");
if (connections_node->type != 'D')
- log_error("JSON cells node '%s' has non-dictionary connections attribute.\n", log_id(cell_name));
+ log_error("JSON cells node '%s' has non-dictionary connections attribute.\n", cell_name.unescape());
for (auto &conn_it : connections_node->data_dict)
{
@@ -528,7 +528,7 @@ void json_import(Design *design, string &modname, JsonNode *node)
JsonNode *conn_node = conn_it.second;
if (conn_node->type != 'A')
- log_error("JSON cells node '%s' connection '%s' is not an array.\n", log_id(cell_name), log_id(conn_name));
+ log_error("JSON cells node '%s' connection '%s' is not an array.\n", cell_name.unescape(), conn_name.unescape());
SigSpec sig;
@@ -547,7 +547,7 @@ void json_import(Design *design, string &modname, JsonNode *node)
sig.append(State::Sz);
else
log_error("JSON cells node '%s' connection '%s' has invalid '%s' bit string value on bit %d.\n",
- log_id(cell_name), log_id(conn_name), bitval_node->data_string.c_str(), i);
+ cell_name.unescape(), conn_name.unescape(), bitval_node->data_string.c_str(), i);
} else
if (bitval_node->type == 'N') {
int bitidx = bitval_node->data_number;
@@ -556,7 +556,7 @@ void json_import(Design *design, string &modname, JsonNode *node)
sig.append(signal_bits.at(bitidx));
} else
log_error("JSON cells node '%s' connection '%s' has invalid bit value on bit %d.\n",
- log_id(cell_name), log_id(conn_name), i);
+ cell_name.unescape(), conn_name.unescape(), i);
}
@@ -587,20 +587,20 @@ void json_import(Design *design, string &modname, JsonNode *node)
mem->name = memory_name;
if (memory_node->type != 'D')
- log_error("JSON memory node '%s' is not a dictionary.\n", log_id(memory_name));
+ log_error("JSON memory node '%s' is not a dictionary.\n", memory_name.unescape());
if (memory_node->data_dict.count("width") == 0)
- log_error("JSON memory node '%s' has no width attribute.\n", log_id(memory_name));
+ log_error("JSON memory node '%s' has no width attribute.\n", memory_name.unescape());
JsonNode *width_node = memory_node->data_dict.at("width");
if (width_node->type != 'N')
- log_error("JSON memory node '%s' has a non-number width.\n", log_id(memory_name));
+ log_error("JSON memory node '%s' has a non-number width.\n", memory_name.unescape());
mem->width = width_node->data_number;
if (memory_node->data_dict.count("size") == 0)
- log_error("JSON memory node '%s' has no size attribute.\n", log_id(memory_name));
+ log_error("JSON memory node '%s' has no size attribute.\n", memory_name.unescape());
JsonNode *size_node = memory_node->data_dict.at("size");
if (size_node->type != 'N')
- log_error("JSON memory node '%s' has a non-number size.\n", log_id(memory_name));
+ log_error("JSON memory node '%s' has a non-number size.\n", memory_name.unescape());
mem->size = size_node->data_number;
mem->start_offset = 0;
diff --git a/frontends/liberty/liberty.cc b/frontends/liberty/liberty.cc
index 0aa1cee09..447f438a8 100644
--- a/frontends/liberty/liberty.cc
+++ b/frontends/liberty/liberty.cc
@@ -20,6 +20,7 @@
#include "passes/techmap/libparse.h"
#include "kernel/register.h"
#include "kernel/log.h"
+#include
YOSYS_NAMESPACE_BEGIN
@@ -40,14 +41,14 @@ static RTLIL::SigSpec parse_func_identifier(RTLIL::Module *module, const char *&
expr[id_len] == '_' || expr[id_len] == '[' || expr[id_len] == ']') id_len++;
if (id_len == 0)
- log_error("Expected identifier at `%s' in %s.\n", expr, RTLIL::unescape_id(module->name));
+ log_error("Expected identifier at `%s' in %s.\n", expr, module);
if (id_len == 1 && (*expr == '0' || *expr == '1'))
return *(expr++) == '0' ? RTLIL::State::S0 : RTLIL::State::S1;
std::string id = RTLIL::escape_id(std::string(expr, id_len));
if (!module->wires_.count(id))
- log_error("Can't resolve wire name %s in %s.\n", RTLIL::unescape_id(id), RTLIL::unescape_id(module->name));
+ log_error("Can't resolve wire name %s in %s.\n", RTLIL::unescape_id(id), module);
expr += id_len;
return module->wires_.at(id);
@@ -174,7 +175,7 @@ static RTLIL::SigSpec parse_func_expr(RTLIL::Module *module, const char *expr)
#endif
if (stack.size() != 1 || stack.back().type != 3)
- log_error("Parser error in function expr `%s'in %s.\n", orig_expr, RTLIL::unescape_id(module->name));
+ log_error("Parser error in function expr `%s'in %s.\n", orig_expr, module);
return stack.back().sig;
}
@@ -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 = module->name.unescape();
+ std::optional clear_preset_var1;
+ std::optional 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)
@@ -299,9 +339,9 @@ static bool create_latch(RTLIL::Module *module, const LibertyAst *node, bool fla
if (enable_sig.size() == 0 || data_sig.size() == 0) {
if (!flag_ignore_miss_data_latch)
- log_error("Latch cell %s has no data_in and/or enable attribute.\n", RTLIL::unescape_id(module->name));
+ log_error("Latch cell %s has no data_in and/or enable attribute.\n", module);
else
- log("Ignored latch cell %s with no data_in and/or enable attribute.\n", RTLIL::unescape_id(module->name));
+ log("Ignored latch cell %s with no data_in and/or enable attribute.\n", module);
return false;
}
@@ -592,9 +632,9 @@ struct LibertyFrontend : public Frontend {
{
if (!flag_ignore_miss_dir)
{
- log_error("Missing or invalid direction for pin %s on cell %s.\n", node->args.at(0), RTLIL::unescape_id(module->name));
+ log_error("Missing or invalid direction for pin %s on cell %s.\n", node->args.at(0), module);
} else {
- log("Ignoring cell %s with missing or invalid direction for pin %s.\n", RTLIL::unescape_id(module->name), node->args.at(0));
+ log("Ignoring cell %s with missing or invalid direction for pin %s.\n", module, node->args.at(0));
delete module;
goto skip_cell;
}
@@ -606,7 +646,7 @@ struct LibertyFrontend : public Frontend {
if (node->id == "bus" && node->args.size() == 1)
{
if (flag_ignore_buses) {
- log("Ignoring cell %s with a bus interface %s.\n", RTLIL::unescape_id(module->name), node->args.at(0));
+ log("Ignoring cell %s with a bus interface %s.\n", module, node->args.at(0));
delete module;
goto skip_cell;
}
@@ -623,7 +663,7 @@ struct LibertyFrontend : public Frontend {
}
if (!dir || (dir->value != "input" && dir->value != "output" && dir->value != "inout" && dir->value != "internal"))
- log_error("Missing or invalid direction for bus %s on cell %s.\n", node->args.at(0), RTLIL::unescape_id(module->name));
+ log_error("Missing or invalid direction for bus %s on cell %s.\n", node->args.at(0), module);
simple_comb_cell = false;
@@ -718,9 +758,9 @@ struct LibertyFrontend : public Frontend {
if (dir->value != "inout") { // allow inout with missing function, can be used for power pins
if (!flag_ignore_miss_func)
{
- log_error("Missing function on output %s of cell %s.\n", RTLIL::unescape_id(wire->name), RTLIL::unescape_id(module->name));
+ log_error("Missing function on output %s of cell %s.\n", wire, module);
} else {
- log("Ignoring cell %s with missing function on output %s.\n", RTLIL::unescape_id(module->name), RTLIL::unescape_id(wire->name));
+ log("Ignoring cell %s with missing function on output %s.\n", module, wire);
delete module;
goto skip_cell;
}
@@ -797,3 +837,4 @@ skip_cell:;
YOSYS_NAMESPACE_END
+
diff --git a/frontends/rpc/rpc_frontend.cc b/frontends/rpc/rpc_frontend.cc
index c21867b30..bc5ef013d 100644
--- a/frontends/rpc/rpc_frontend.cc
+++ b/frontends/rpc/rpc_frontend.cc
@@ -212,7 +212,7 @@ struct RpcModule : RTLIL::Module {
for (auto module : derived_design->modules_) {
std::string mangled_name = name_mangling[module.first.str()];
- log("Importing `%s' as `%s'.\n", log_id(module.first), log_id(mangled_name));
+ log("Importing `%s' as `%s'.\n", module.first.unescape(), mangled_name);
module.second->name = mangled_name;
module.second->design = design;
diff --git a/frontends/rtlil/rtlil_frontend.cc b/frontends/rtlil/rtlil_frontend.cc
index a1412d983..4709c76ed 100644
--- a/frontends/rtlil/rtlil_frontend.cc
+++ b/frontends/rtlil/rtlil_frontend.cc
@@ -286,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]) {
@@ -300,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);
@@ -330,7 +332,7 @@ struct RTLILFrontendWorker {
error("No wires found for legalization");
int hash = hash_ops::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));
+ log("Legalizing wire `%s' to `%s'.\n", id.unescape(), wire->name.unescape());
return wire;
}
diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc
index 6a1c81aa4..6b876c0f1 100644
--- a/frontends/verific/verific.cc
+++ b/frontends/verific/verific.cc
@@ -1392,13 +1392,13 @@ void VerificImporter::merge_past_ffs_clock(pool &candidates, SigBi
RTLIL::Cell *new_ff = module->addDff(NEW_ID, clock, sig_d, sig_q, clock_pol);
if (verific_verbose)
- log(" merging single-bit past_ffs into new %d-bit ff %s.\n", GetSize(sig_d), log_id(new_ff));
+ log(" merging single-bit past_ffs into new %d-bit ff %s.\n", GetSize(sig_d), new_ff);
for (int i = 0; i < GetSize(sig_d); i++)
for (auto old_ff : dbits_db[sig_d[i]])
{
if (verific_verbose)
- log(" replacing old ff %s on bit %d.\n", log_id(old_ff), i);
+ log(" replacing old ff %s on bit %d.\n", old_ff, i);
SigBit old_q = old_ff->getPort(ID::Q);
SigBit new_q = sig_q[i];
@@ -1492,10 +1492,10 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
design->add(module);
if (is_blackbox(nl)) {
- log("Importing blackbox module %s.\n", RTLIL::id2cstr(module->name));
+ log("Importing blackbox module %s.\n", module);
module->set_bool_attribute(ID::blackbox);
} else {
- log("Importing module %s.\n", RTLIL::id2cstr(module->name));
+ log("Importing module %s.\n", module);
}
import_attributes(module->attributes, nl, nl);
if (module->name.isPublic())
@@ -1736,7 +1736,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
RTLIL::IdString wire_name = module->uniquify(mode_names || net->IsUserDeclared() ? RTLIL::escape_id(net->Name()) : new_verific_id(net));
if (verific_verbose)
- log(" importing net %s as %s.\n", net->Name(), log_id(wire_name));
+ log(" importing net %s as %s.\n", net->Name(), wire_name.unescape());
RTLIL::Wire *wire = module->addWire(wire_name);
import_attributes(wire->attributes, net, nl, 1);
@@ -1760,7 +1760,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
RTLIL::IdString wire_name = module->uniquify(mode_names || netbus->IsUserDeclared() ? RTLIL::escape_id(netbus->Name()) : new_verific_id(netbus));
if (verific_verbose)
- log(" importing netbus %s as %s.\n", netbus->Name(), log_id(wire_name));
+ log(" importing netbus %s as %s.\n", netbus->Name(), wire_name.unescape());
RTLIL::Wire *wire = module->addWire(wire_name, netbus->Size());
wire->start_offset = min(netbus->LeftIndex(), netbus->RightIndex());
@@ -1894,7 +1894,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
RTLIL::IdString inst_name = module->uniquify(mode_names || inst->IsUserDeclared() ? RTLIL::escape_id(inst->Name()) : new_verific_id(inst));
if (verific_verbose)
- log(" importing cell %s (%s) as %s.\n", inst->Name(), inst->View()->Owner()->Name(), log_id(inst_name));
+ log(" importing cell %s (%s) as %s.\n", inst->Name(), inst->View()->Owner()->Name(), inst_name.unescape());
if (mode_verific)
goto import_verific_cells;
@@ -2258,7 +2258,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
for (auto &it : cell_port_conns) {
if (verific_verbose)
- log(" .%s(%s)\n", log_id(it.first), log_signal(it.second));
+ log(" .%s(%s)\n", it.first.unescape(), log_signal(it.second));
cell->setPort(it.first, it.second);
}
}
diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y
index 684727d5b..b394ce074 100644
--- a/frontends/verilog/verilog_parser.y
+++ b/frontends/verilog/verilog_parser.y
@@ -84,7 +84,7 @@
int current_function_or_task_port_id;
std::vector 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 range range_or_multirange non_opt_range non_opt_multirange
-%type wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list non_io_wire_type io_wire_type
+%type wire_type expr basic_expr concat_list assignment_pattern_list rvalue lvalue lvalue_concat_list non_io_wire_type io_wire_type
%type opt_label opt_sva_label tok_prim_wrapper hierarchical_id hierarchical_type_id integral_number
%type type_name
%type 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(@$, AST_WIRE); extra->current_wire_rand = false; extra->current_wire_const = false; }
+ { extra->astbuf3 = std::make_unique(@$, 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(@$, 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(@$, 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(@$, 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(@$, 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); } |
diff --git a/guidelines/GettingStarted b/guidelines/GettingStarted
index 17fe32523..ea15df93f 100644
--- a/guidelines/GettingStarted
+++ b/guidelines/GettingStarted
@@ -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.
\ No newline at end of file
+for the new code instead of trying to mimic the style of the surrounding code.
diff --git a/kernel/cellaigs.cc b/kernel/cellaigs.cc
index 0f897cd58..ce351514b 100644
--- a/kernel/cellaigs.cc
+++ b/kernel/cellaigs.cc
@@ -66,14 +66,7 @@ struct AigMaker
Cell *cell;
idict 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)
{
diff --git a/kernel/celltypes.h b/kernel/celltypes.h
index 34b013dd9..ceff0bd1a 100644
--- a/kernel/celltypes.h
+++ b/kernel/celltypes.h
@@ -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(), true);
- setup_type(ID($assume), {ID::A, ID::EN}, pool(), true);
- setup_type(ID($live), {ID::A, ID::EN}, pool(), true);
- setup_type(ID($fair), {ID::A, ID::EN}, pool(), true);
- setup_type(ID($cover), {ID::A, ID::EN}, pool(), true);
- setup_type(ID($initstate), pool(), {ID::Y}, true);
- setup_type(ID($anyconst), pool(), {ID::Y}, true);
- setup_type(ID($anyseq), pool(), {ID::Y}, true);
- setup_type(ID($allconst), pool(), {ID::Y}, true);
- setup_type(ID($allseq), pool(), {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(), true);
- setup_type(ID($specify3), {ID::EN, ID::SRC, ID::DST, ID::DAT}, pool(), true);
- setup_type(ID($specrule), {ID::EN_SRC, ID::EN_DST, ID::SRC, ID::DST}, pool(), true);
+ setup_type(ID($assert), {ID::A, ID::EN}, pool());
+ setup_type(ID($assume), {ID::A, ID::EN}, pool());
+ setup_type(ID($live), {ID::A, ID::EN}, pool());
+ setup_type(ID($fair), {ID::A, ID::EN}, pool());
+ setup_type(ID($cover), {ID::A, ID::EN}, pool());
+ setup_type(ID($initstate), pool(), {ID::Y});
+ setup_type(ID($anyconst), pool(), {ID::Y});
+ setup_type(ID($anyseq), pool(), {ID::Y});
+ setup_type(ID($allconst), pool(), {ID::Y});
+ setup_type(ID($allseq), pool(), {ID::Y});
+ setup_type(ID($equiv), {ID::A, ID::B}, {ID::Y});
+ setup_type(ID($specify2), {ID::EN, ID::SRC, ID::DST}, pool());
+ setup_type(ID($specify3), {ID::EN, ID::SRC, ID::DST, ID::DAT}, pool());
+ setup_type(ID($specrule), {ID::SRC_EN, ID::DST_EN, ID::SRC, ID::DST}, pool());
setup_type(ID($print), {ID::EN, ID::ARGS, ID::TRG}, pool());
setup_type(ID($check), {ID::A, ID::EN, ID::ARGS, ID::TRG}, pool());
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
diff --git a/kernel/consteval.h b/kernel/consteval.h
index ca04d722f..d00ae8f33 100644
--- a/kernel/consteval.h
+++ b/kernel/consteval.h
@@ -24,6 +24,7 @@
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
#include "kernel/macc.h"
+#include "kernel/newcelltypes.h"
YOSYS_NAMESPACE_BEGIN
@@ -44,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))
diff --git a/kernel/constids.inc b/kernel/constids.inc
index c99aa788d..cfd12e3fb 100644
--- a/kernel/constids.inc
+++ b/kernel/constids.inc
@@ -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)
diff --git a/kernel/cost.cc b/kernel/cost.cc
index 4942823d3..230afdeb1 100644
--- a/kernel/cost.cc
+++ b/kernel/cost.cc
@@ -210,6 +210,6 @@ unsigned int CellCosts::get(RTLIL::Cell *cell)
// TODO: $fsm
// ignored: $pow $memrd $memwr $meminit (and v2 counterparts)
- log_warning("Can't determine cost of %s cell (%d parameters).\n", log_id(cell->type), GetSize(cell->parameters));
+ log_warning("Can't determine cost of %s cell (%d parameters).\n", cell->type.unescape(), GetSize(cell->parameters));
return 1;
}
diff --git a/kernel/driver.cc b/kernel/driver.cc
index fa78bad59..73b687f80 100644
--- a/kernel/driver.cc
+++ b/kernel/driver.cc
@@ -23,6 +23,7 @@
#define CXXOPTS_VECTOR_DELIMITER '\0'
#include "libs/cxxopts/include/cxxopts.hpp"
#include
+#include
#ifdef YOSYS_ENABLE_READLINE
# include
@@ -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 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(
+ 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());
diff --git a/kernel/drivertools.cc b/kernel/drivertools.cc
index 90bfb0ee7..55616dea1 100644
--- a/kernel/drivertools.cc
+++ b/kernel/drivertools.cc
@@ -866,7 +866,7 @@ DriveSpec DriverMap::operator()(DriveSpec spec)
std::string log_signal(DriveChunkWire const &chunk)
{
- const char *id = log_id(chunk.wire->name);
+ std::string id = chunk.wire->name.unescape();
if (chunk.is_whole())
return id;
if (chunk.width == 1)
@@ -877,8 +877,8 @@ std::string log_signal(DriveChunkWire const &chunk)
std::string log_signal(DriveChunkPort const &chunk)
{
- const char *cell_id = log_id(chunk.cell->name);
- const char *port_id = log_id(chunk.port);
+ std::string cell_id = chunk.cell->name.unescape();
+ std::string port_id = chunk.port.unescape();
if (chunk.is_whole())
return stringf("%s <%s>", cell_id, port_id);
if (chunk.width == 1)
diff --git a/kernel/drivertools.h b/kernel/drivertools.h
index ba7b2aa84..28d3be91e 100644
--- a/kernel/drivertools.h
+++ b/kernel/drivertools.h
@@ -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:
diff --git a/kernel/ff.cc b/kernel/ff.cc
index 7dd5e24ac..727a9d9cb 100644
--- a/kernel/ff.cc
+++ b/kernel/ff.cc
@@ -792,7 +792,7 @@ void FfData::flip_bits(const pool &bits) {
Wire *new_q = module->addWire(NEW_ID, width);
if (has_sr && cell) {
- log_warning("Flipping D/Q/init and inserting priority fixup to legalize %s.%s [%s].\n", log_id(module->name), log_id(cell->name), log_id(cell->type));
+ log_warning("Flipping D/Q/init and inserting priority fixup to legalize %s.%s [%s].\n", module->name.unescape(), cell->name.unescape(), cell->type.unescape());
}
if (is_fine) {
diff --git a/kernel/ffinit.h b/kernel/ffinit.h
index 920fba307..8b4758f60 100644
--- a/kernel/ffinit.h
+++ b/kernel/ffinit.h
@@ -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 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));
diff --git a/kernel/fmt.cc b/kernel/fmt.cc
index 200e7e5ce..15179a75a 100644
--- a/kernel/fmt.cc
+++ b/kernel/fmt.cc
@@ -804,8 +804,10 @@ std::string Fmt::render() const
buf += 'X';
else if (has_z)
buf += 'Z';
- else
- buf += (part.hex_upper ? "0123456789ABCDEF" : "0123456789abcdef")[subvalue.as_int()];
+ else {
+ const char *digits = part.hex_upper ? "0123456789ABCDEF" : "0123456789abcdef";
+ buf += digits[subvalue.as_int()];
+ }
}
} else if (part.base == 10) {
if (part.show_base)
diff --git a/kernel/fstdata.cc b/kernel/fstdata.cc
index f0f00181c..8b724e69b 100644
--- a/kernel/fstdata.cc
+++ b/kernel/fstdata.cc
@@ -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;
diff --git a/kernel/fstdata.h b/kernel/fstdata.h
index a8ae40301..43f779cb5 100644
--- a/kernel/fstdata.h
+++ b/kernel/fstdata.h
@@ -55,7 +55,7 @@ class FstData
std::string valueOf(fstHandle signal);
fstHandle getHandle(std::string name);
dict 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 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;
diff --git a/kernel/functional.cc b/kernel/functional.cc
index 2a1bf598a..d04677332 100644
--- a/kernel/functional.cc
+++ b/kernel/functional.cc
@@ -136,7 +136,7 @@ struct PrintVisitor : DefaultVisitor {
std::string Node::to_string()
{
- return to_string([](Node n) { return RTLIL::unescape_id(n.name()); });
+ return to_string([](Node n) { return n.name().unescape(); });
}
std::string Node::to_string(std::function np)
@@ -572,7 +572,7 @@ private:
const auto &wr = mem->wr_ports[i];
if (wr.clk_enable)
log_error("Write port %zd of memory %s.%s is clocked. This is not supported by the functional backend. "
- "Call async2sync or clk2fflogic to avoid this error.\n", i, log_id(mem->module), log_id(mem->memid));
+ "Call async2sync or clk2fflogic to avoid this error.\n", i, mem->module, mem->memid.unescape());
Node en = enqueue(driver_map(DriveSpec(wr.en)));
Node addr = enqueue(driver_map(DriveSpec(wr.addr)));
Node new_data = enqueue(driver_map(DriveSpec(wr.data)));
@@ -582,12 +582,12 @@ private:
}
if (mem->rd_ports.empty())
log_error("Memory %s.%s has no read ports. This is not supported by the functional backend. "
- "Call opt_clean to remove it.", log_id(mem->module), log_id(mem->memid));
+ "Call opt_clean to remove it.", mem->module, mem->memid.unescape());
for (size_t i = 0; i < mem->rd_ports.size(); i++) {
const auto &rd = mem->rd_ports[i];
if (rd.clk_enable)
log_error("Read port %zd of memory %s.%s is clocked. This is not supported by the functional backend. "
- "Call memory_nordff to avoid this error.\n", i, log_id(mem->module), log_id(mem->memid));
+ "Call memory_nordff to avoid this error.\n", i, mem->module, mem->memid.unescape());
Node addr = enqueue(driver_map(DriveSpec(rd.addr)));
read_results.push_back(factory.memory_read(node, addr));
}
@@ -609,7 +609,7 @@ private:
FfData ff(&ff_initvals, cell);
if (!ff.has_gclk)
log_error("The design contains a %s flip-flop at %s. This is not supported by the functional backend. "
- "Call async2sync or clk2fflogic to avoid this error.\n", log_id(cell->type), log_id(cell));
+ "Call async2sync or clk2fflogic to avoid this error.\n", cell->type.unescape(), cell);
auto &state = factory.add_state(ff.name, ID($state), Sort(ff.width));
Node q_value = factory.value(state);
factory.suggest_name(q_value, ff.name);
@@ -677,7 +677,7 @@ public:
factory.update_pending(pending, node);
} else {
DriveSpec driver = driver_map(DriveSpec(wire_chunk));
- check_undriven(driver, RTLIL::unescape_id(wire_chunk.wire->name));
+ check_undriven(driver, wire_chunk.wire->name.unescape());
Node node = enqueue(driver);
factory.suggest_name(node, wire_chunk.wire->name);
factory.update_pending(pending, node);
@@ -695,7 +695,7 @@ public:
factory.update_pending(pending, node);
} else {
DriveSpec driver = driver_map(DriveSpec(port_chunk));
- check_undriven(driver, RTLIL::unescape_id(port_chunk.cell->name) + " port " + RTLIL::unescape_id(port_chunk.port));
+ check_undriven(driver, port_chunk.cell->name.unescape() + " port " + port_chunk.port.unescape());
factory.update_pending(pending, enqueue(driver));
}
} else {
@@ -744,7 +744,7 @@ void IR::topological_sort() {
log_warning("Combinational loop:\n");
for (int *i = begin; i != end; ++i) {
Node node(_graph[*i]);
- log("- %s = %s\n", RTLIL::unescape_id(node.name()), node.to_string());
+ log("- %s = %s\n", node.name().unescape(), node.to_string());
}
log("\n");
scc = true;
diff --git a/kernel/functional.h b/kernel/functional.h
index 073adf40a..3334f02c8 100644
--- a/kernel/functional.h
+++ b/kernel/functional.h
@@ -588,7 +588,7 @@ namespace Functional {
_used_names.insert(std::move(name));
}
std::string unique_name(IdString suggestion) {
- std::string str = RTLIL::unescape_id(suggestion);
+ std::string str = suggestion.unescape();
for(size_t i = 0; i < str.size(); i++)
if(!is_character_legal(str[i], i))
str[i] = substitution_character;
diff --git a/kernel/hashlib.h b/kernel/hashlib.h
index b43a68abf..937116178 100644
--- a/kernel/hashlib.h
+++ b/kernel/hashlib.h
@@ -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
-class dict {
+class HASHLIB_ATTRIBUTE_WARN_UNUSED dict {
struct entry_t
{
std::pair udata;
@@ -877,7 +883,7 @@ public:
};
template
-class pool
+class HASHLIB_ATTRIBUTE_WARN_UNUSED pool
{
template friend class idict;
@@ -1257,7 +1263,7 @@ public:
};
template
-class idict
+class HASHLIB_ATTRIBUTE_WARN_UNUSED idict
{
pool database;
@@ -1360,7 +1366,7 @@ public:
* i-prefixed methods operate on indices in parents
*/
template
-class mfp
+class HASHLIB_ATTRIBUTE_WARN_UNUSED mfp
{
idict database;
class AtomicParent {
diff --git a/kernel/io.h b/kernel/io.h
index 171f47a80..e15194e79 100644
--- a/kernel/io.h
+++ b/kernel/io.h
@@ -197,6 +197,28 @@ check_format(std::string_view fmt, int fmt_start, bool *has_escapes, FoundFormat
ensure_no_format_spec(fmt, fmt_start, has_escapes);
}
+template
+static auto has_name_member_imp(int)
+ -> decltype(static_cast(std::declval().name), std::true_type{});
+
+template
+static auto has_name_member_imp(long)
+ -> std::false_type;
+
+template
+struct has_name_member : decltype(has_name_member_imp(0)){};
+
+template
+static auto ptr_has_name_member_imp(int)
+ -> decltype(static_cast(std::declval()->name), std::true_type{});
+
+template
+static auto ptr_has_name_member_imp(long)
+ -> std::false_type;
+
+template
+struct ptr_has_name_member : decltype(ptr_has_name_member_imp(0)){};
+
// Check that the format string `fmt.substr(fmt_start)` is valid for the given type arguments.
// Fills `specs` with the FoundFormatSpecs found in the format string.
// `int_args_consumed` is the number of int arguments already consumed to satisfy the
@@ -245,7 +267,9 @@ constexpr void check_format(std::string_view fmt, int fmt_start, bool *has_escap
if constexpr (!std::is_convertible_v &&
!std::is_convertible_v &&
!std::is_convertible_v &&
- !std::is_convertible_v) {
+ !std::is_convertible_v &&
+ !has_name_member() &&
+ !ptr_has_name_member()) {
YOSYS_ABORT("Expected type convertible to char *");
}
*specs = found;
@@ -343,6 +367,16 @@ inline void format_emit_one(std::string &result, std::string_view fmt, const Fou
format_emit_idstring(result, spec, dynamic_ints, num_dynamic_ints, s);
return;
}
+ if constexpr (has_name_member()) {
+ const std::string &s = arg.name.unescape();
+ format_emit_string(result, spec, dynamic_ints, num_dynamic_ints, s);
+ return;
+ }
+ if constexpr (ptr_has_name_member()) {
+ const std::string &s = arg->name.unescape();
+ format_emit_string(result, spec, dynamic_ints, num_dynamic_ints, s);
+ return;
+ }
break;
case CONVSPEC_VOID_PTR:
if constexpr (std::is_convertible_v) {
@@ -441,7 +475,8 @@ public:
private:
std::string_view fmt;
bool has_escapes = false;
- FoundFormatSpec specs[sizeof...(Args)] = {};
+ // Making array at least size of one to make MSVC happy and strict to standards
+ FoundFormatSpec specs[sizeof...(Args) ? sizeof...(Args) : 1] = {};
};
template struct WrapType { using type = T; };
diff --git a/kernel/log.cc b/kernel/log.cc
index 018a19081..272b69589 100644
--- a/kernel/log.cc
+++ b/kernel/log.cc
@@ -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 = "\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();
@@ -576,7 +586,7 @@ void log_flush()
}
void log_dump_val_worker(RTLIL::IdString v) {
- log("%s", log_id(v));
+ log("%s", v.unescape());
}
void log_dump_val_worker(RTLIL::SigSpec v) {
@@ -604,7 +614,7 @@ std::string log_const(const RTLIL::Const &value, bool autoint)
const char *log_id(const RTLIL::IdString &str)
{
- std::string unescaped = RTLIL::unescape_id(str);
+ std::string unescaped = str.unescape();
log_id_cache.push_back(strdup(unescaped.c_str()));
return log_id_cache.back();
}
diff --git a/kernel/log.h b/kernel/log.h
index 63faf7091..d132ba1a0 100644
--- a/kernel/log.h
+++ b/kernel/log.h
@@ -206,12 +206,7 @@ template
log_formatted_cmd_error(fmt.format(args...));
}
-static inline void log_suppressed() {
- if (log_debug_suppressed && !log_make_debug) {
- log("\n", log_debug_suppressed);
- log_debug_suppressed = 0;
- }
-}
+void log_suppressed();
struct LogMakeDebugHdl {
bool status = false;
diff --git a/kernel/mem.cc b/kernel/mem.cc
index 02d12dea4..2f7f16c7a 100644
--- a/kernel/mem.cc
+++ b/kernel/mem.cc
@@ -663,15 +663,15 @@ namespace {
auto addr = cell->getPort(ID::ADDR);
auto data = cell->getPort(ID::DATA);
if (!addr.is_fully_const())
- log_error("Non-constant address %s in memory initialization %s.\n", log_signal(addr), log_id(cell));
+ log_error("Non-constant address %s in memory initialization %s.\n", log_signal(addr), cell);
if (!data.is_fully_const())
- log_error("Non-constant data %s in memory initialization %s.\n", log_signal(data), log_id(cell));
+ log_error("Non-constant data %s in memory initialization %s.\n", log_signal(data), cell);
init.addr = addr.as_const();
init.data = data.as_const();
if (cell->type == ID($meminit_v2)) {
auto en = cell->getPort(ID::EN);
if (!en.is_fully_const())
- log_error("Non-constant enable %s in memory initialization %s.\n", log_signal(en), log_id(cell));
+ log_error("Non-constant enable %s in memory initialization %s.\n", log_signal(en), cell);
init.en = en.as_const();
} else {
init.en = RTLIL::Const(State::S1, mem->width);
@@ -1022,7 +1022,7 @@ Cell *Mem::extract_rdff(int idx, FfInitVals *initvals) {
if (c)
log("Extracted %s FF from read port %d of %s.%s: %s\n", trans_use_addr ? "addr" : "data",
- idx, log_id(module), log_id(memid), log_id(c));
+ idx, module, memid.unescape(), c);
port.en = State::S1;
port.clk = State::S0;
diff --git a/kernel/modtools.h b/kernel/modtools.h
index 5cd8e3cb2..bdcb0f108 100644
--- a/kernel/modtools.h
+++ b/kernel/modtools.h
@@ -23,6 +23,7 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
+#include "kernel/newcelltypes.h"
YOSYS_NAMESPACE_BEGIN
@@ -177,8 +178,8 @@ struct ModIndex : public RTLIL::Monitor
return false;
}
- return true;
#endif
+ return true;
}
void check()
@@ -319,8 +320,8 @@ struct ModIndex : public RTLIL::Monitor
if (it.second.is_output)
log(" PRIMARY OUTPUT\n");
for (auto &port : it.second.ports)
- log(" PORT: %s.%s[%d] (%s)\n", log_id(port.cell),
- log_id(port.port), port.offset, log_id(port.cell->type));
+ log(" PORT: %s.%s[%d] (%s)\n", port.cell,
+ port.port.unescape(), port.offset, port.cell->type.unescape());
}
}
};
@@ -357,7 +358,7 @@ struct ModWalker
RTLIL::Design *design;
RTLIL::Module *module;
- CellTypes ct;
+ NewCellTypes ct;
SigMap sigmap;
dict> signal_drivers;
diff --git a/kernel/newcelltypes.h b/kernel/newcelltypes.h
new file mode 100644
index 000000000..eb42019af
--- /dev/null
+++ b/kernel/newcelltypes.h
@@ -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 ports{};
+ size_t count = 0;
+ constexpr PortList() = default;
+ constexpr PortList(std::initializer_list 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 cells{};
+ size_t count = 0;
+
+ constexpr void setup_type(RTLIL::IdString type, std::initializer_list inputs, std::initializer_list 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 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 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 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 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 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(id.hash_top().yield());
+ }
+ };
+ StaticCellTypes::Categories::Category static_cell_types = StaticCellTypes::categories.empty;
+ std::unordered_map 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 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 &inputs, const pool &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
diff --git a/kernel/register.cc b/kernel/register.cc
index abde8f47e..cba6d5f99 100644
--- a/kernel/register.cc
+++ b/kernel/register.cc
@@ -22,6 +22,7 @@
#include "kernel/json.h"
#include "kernel/gzip.h"
#include "kernel/log_help.h"
+#include "kernel/newcelltypes.h"
#include
#include
@@ -975,16 +976,18 @@ struct HelpPass : public Pass {
json.entry("generator", yosys_maybe_version());
dict> groups;
- dict> cells;
+ dict> 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(cell_help, it.second);
+ auto cell_pair = pair(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 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) {
diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc
index eef1c319d..31efff63d 100644
--- a/kernel/rtlil.cc
+++ b/kernel/rtlil.cc
@@ -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"
@@ -142,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(RTLIL::StaticId::STATIC_ID_END);
+
struct IdStringCollector {
+ IdStringCollector(std::vector &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 void trace(const T* v) {
trace(*v);
@@ -178,10 +187,6 @@ 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);
@@ -190,15 +195,6 @@ struct IdStringCollector {
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())
@@ -234,7 +230,8 @@ struct IdStringCollector {
trace(action.memid);
}
- std::unordered_set live;
+ std::vector &live_ids;
+ std::vector live_autoidx_ids;
};
int64_t RTLIL::OwningIdString::gc_ns;
@@ -243,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(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 live_ids(size - STATIC_ID_END);
+ std::vector 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 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);
@@ -268,8 +300,13 @@ void RTLIL::OwningIdString::collect_garbage()
global_free_idx_list_.push_back(i);
}
+ std::unordered_set 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;
}
@@ -288,159 +325,17 @@ void RTLIL::OwningIdString::collect_garbage()
dict RTLIL::constpad;
-static const pool &builtin_ff_cell_types_internal() {
- static const pool 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 &RTLIL::builtin_ff_cell_types() {
- return builtin_ff_cell_types_internal();
+ static const pool res = []() {
+ pool 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")
@@ -1331,7 +1226,7 @@ void RTLIL::Design::add(RTLIL::Module *module)
mon->notify_module_add(module);
if (yosys_xtrace) {
- log("#X# New Module: %s\n", log_id(module));
+ log("#X# New Module: %s\n", module);
log_backtrace("-X- ", yosys_xtrace-1);
}
}
@@ -1357,7 +1252,7 @@ RTLIL::Module *RTLIL::Design::addModule(RTLIL::IdString name)
mon->notify_module_add(module);
if (yosys_xtrace) {
- log("#X# New Module: %s\n", log_id(module));
+ log("#X# New Module: %s\n", module);
log_backtrace("-X- ", yosys_xtrace-1);
}
@@ -1435,7 +1330,7 @@ void RTLIL::Design::remove(RTLIL::Module *module)
mon->notify_module_del(module);
if (yosys_xtrace) {
- log("#X# Remove Module: %s\n", log_id(module));
+ log("#X# Remove Module: %s\n", module);
log_backtrace("-X- ", yosys_xtrace-1);
}
@@ -1466,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
}
@@ -1571,22 +1472,22 @@ std::vector RTLIL::Design::selected_modules(RTLIL::SelectPartial
switch (boxes)
{
case RTLIL::SB_UNBOXED_WARN:
- log_warning("Ignoring boxed module %s.\n", log_id(it.first));
+ log_warning("Ignoring boxed module %s.\n", it.first.unescape());
break;
case RTLIL::SB_EXCL_BB_WARN:
- log_warning("Ignoring blackbox module %s.\n", log_id(it.first));
+ log_warning("Ignoring blackbox module %s.\n", it.first.unescape());
break;
case RTLIL::SB_UNBOXED_ERR:
- log_error("Unsupported boxed module %s.\n", log_id(it.first));
+ log_error("Unsupported boxed module %s.\n", it.first.unescape());
break;
case RTLIL::SB_EXCL_BB_ERR:
- log_error("Unsupported blackbox module %s.\n", log_id(it.first));
+ log_error("Unsupported blackbox module %s.\n", it.first.unescape());
break;
case RTLIL::SB_UNBOXED_CMDERR:
- log_cmd_error("Unsupported boxed module %s.\n", log_id(it.first));
+ log_cmd_error("Unsupported boxed module %s.\n", it.first.unescape());
break;
case RTLIL::SB_EXCL_BB_CMDERR:
- log_cmd_error("Unsupported blackbox module %s.\n", log_id(it.first));
+ log_cmd_error("Unsupported blackbox module %s.\n", it.first.unescape());
break;
default:
break;
@@ -1595,13 +1496,13 @@ std::vector RTLIL::Design::selected_modules(RTLIL::SelectPartial
switch(partials)
{
case RTLIL::SELECT_WHOLE_WARN:
- log_warning("Ignoring partially selected module %s.\n", log_id(it.first));
+ log_warning("Ignoring partially selected module %s.\n", it.first.unescape());
break;
case RTLIL::SELECT_WHOLE_ERR:
- log_error("Unsupported partially selected module %s.\n", log_id(it.first));
+ log_error("Unsupported partially selected module %s.\n", it.first.unescape());
break;
case RTLIL::SELECT_WHOLE_CMDERR:
- log_cmd_error("Unsupported partially selected module %s.\n", log_id(it.first));
+ log_cmd_error("Unsupported partially selected module %s.\n", it.first.unescape());
break;
default:
break;
@@ -1678,7 +1579,7 @@ void RTLIL::Module::makeblackbox()
void RTLIL::Module::expand_interfaces(RTLIL::Design *, const dict &)
{
- log_error("Class doesn't support expand_interfaces (module: `%s')!\n", id2cstr(name));
+ log_error("Class doesn't support expand_interfaces (module: `%s')!\n", name.unescape());
}
bool RTLIL::Module::reprocess_if_necessary(RTLIL::Design *)
@@ -1690,7 +1591,7 @@ RTLIL::IdString RTLIL::Module::derive(RTLIL::Design*, const dict 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)
{
@@ -2690,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 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 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 packed_memids;
+ std::vector ports_declared(GetSize(module->ports));
+ ShardedVector 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 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 all_cases = {&it.second->root_case};
+ std::vector 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) {
@@ -2784,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()
{
}
@@ -2880,14 +2796,14 @@ bool RTLIL::Module::has_processes() const
bool RTLIL::Module::has_memories_warn() const
{
if (!memories.empty())
- log_warning("Ignoring module %s because it contains memories (run 'memory' command first).\n", log_id(this));
+ log_warning("Ignoring module %s because it contains memories (run 'memory' command first).\n", this);
return !memories.empty();
}
bool RTLIL::Module::has_processes_warn() const
{
if (!processes.empty())
- log_warning("Ignoring module %s because it contains processes (run 'proc' command first).\n", log_id(this));
+ log_warning("Ignoring module %s because it contains processes (run 'proc' command first).\n", this);
return !processes.empty();
}
@@ -3179,7 +3095,7 @@ void RTLIL::Module::connect(const RTLIL::SigSig &conn)
}
if (yosys_xtrace) {
- log("#X# Connect (SigSig) in %s: %s = %s (%d bits)\n", log_id(this), log_signal(conn.first), log_signal(conn.second), GetSize(conn.first));
+ log("#X# Connect (SigSig) in %s: %s = %s (%d bits)\n", this, log_signal(conn.first), log_signal(conn.second), GetSize(conn.first));
log_backtrace("-X- ", yosys_xtrace-1);
}
@@ -3202,7 +3118,7 @@ void RTLIL::Module::new_connections(const std::vector &new_conn)
mon->notify_connect(this, new_conn);
if (yosys_xtrace) {
- log("#X# New connections vector in %s:\n", log_id(this));
+ log("#X# New connections vector in %s:\n", this);
for (auto &conn: new_conn)
log("#X# %s = %s (%d bits)\n", log_signal(conn.first), log_signal(conn.second), GetSize(conn.first));
log_backtrace("-X- ", yosys_xtrace-1);
@@ -4610,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)
@@ -5074,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 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();
}
@@ -5125,24 +5045,27 @@ void RTLIL::SigSpec::remove2(const pool &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();
}
@@ -5158,24 +5081,27 @@ void RTLIL::SigSpec::remove2(const std::set &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();
}
@@ -5346,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 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;
}
@@ -5470,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)
{
diff --git a/kernel/rtlil.h b/kernel/rtlil.h
index fea53081e..b32f9ea76 100644
--- a/kernel/rtlil.h
+++ b/kernel/rtlil.h
@@ -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 constpad;
+ [[deprecated("use StaticCellTypes::categories.is_ff() instead")]]
const pool &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
};
diff --git a/kernel/rtlil_bufnorm.cc b/kernel/rtlil_bufnorm.cc
index 5f74b3380..19474b565 100644
--- a/kernel/rtlil_bufnorm.cc
+++ b/kernel/rtlil_bufnorm.cc
@@ -146,7 +146,7 @@ void RTLIL::Module::bufNormalize()
// already enqueued or becomes reachable when denormalizing $buf or
// $connect cells.
auto enqueue_cell_port = [&](Cell *cell, IdString port) {
- xlog("processing cell port %s.%s\n", log_id(cell), log_id(port));
+ xlog("processing cell port %s.%s\n", cell, port.unescape());
// An empty cell type means the cell got removed
if (cell->type.empty())
@@ -270,7 +270,7 @@ void RTLIL::Module::bufNormalize()
// normalized mode).
while (wire_queue_pos < GetSize(wire_queue_entries)) {
auto wire = wire_queue_entries[wire_queue_pos++];
- xlog("processing wire %s\n", log_id(wire));
+ xlog("processing wire %s\n", wire);
if (wire->driverCell_) {
Cell *cell = wire->driverCell_;
@@ -287,7 +287,7 @@ void RTLIL::Module::bufNormalize()
log_assert(connect_cell->type == ID($connect));
SigSpec const &sig_a = connect_cell->getPort(ID::A);
SigSpec const &sig_b = connect_cell->getPort(ID::B);
- xlog("found $connect cell %s: %s <-> %s\n", log_id(connect_cell), log_signal(sig_a), log_signal(sig_b));
+ xlog("found $connect cell %s: %s <-> %s\n", connect_cell, log_signal(sig_a), log_signal(sig_b));
for (auto &side : {sig_a, sig_b})
for (auto chunk : side.chunks())
if (chunk.wire)
@@ -452,7 +452,7 @@ void RTLIL::Module::bufNormalize()
}
if (wire->driverCell_ == nullptr) {
- xlog("wire %s drivers %s\n", log_id(wire), log_signal(wire_drivers));
+ xlog("wire %s drivers %s\n", wire, log_signal(wire_drivers));
addBuf(NEW_ID, wire_drivers, wire);
}
}
@@ -541,7 +541,7 @@ void RTLIL::Cell::unsetPort(RTLIL::IdString portname)
mon->notify_connect(this, conn_it->first, conn_it->second, signal);
if (yosys_xtrace) {
- log("#X# Unconnect %s.%s.%s\n", log_id(this->module), log_id(this), log_id(portname));
+ log("#X# Unconnect %s.%s.%s\n", this->module, this, portname.unescape());
log_backtrace("-X- ", yosys_xtrace-1);
}
@@ -601,7 +601,7 @@ void RTLIL::Cell::setPort(RTLIL::IdString portname, RTLIL::SigSpec signal)
mon->notify_connect(this, conn_it->first, conn_it->second, signal);
if (yosys_xtrace) {
- log("#X# Connect %s.%s.%s = %s (%d)\n", log_id(this->module), log_id(this), log_id(portname), log_signal(signal), GetSize(signal));
+ log("#X# Connect %s.%s.%s = %s (%d)\n", this->module, this, portname.unescape(), log_signal(signal), GetSize(signal));
log_backtrace("-X- ", yosys_xtrace-1);
}
diff --git a/kernel/satgen.cc b/kernel/satgen.cc
index f2c1e00c2..9fddc303e 100644
--- a/kernel/satgen.cc
+++ b/kernel/satgen.cc
@@ -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", cell, cell->type.unescape());
+ else
+ s = stringf("No SAT model available for cell %s (%s).\n", cell, cell->type.unescape());
+
+ if (warn_only) {
+ log_formatted_warning_noprefix(s);
+ } else {
+ log_formatted_error(s);
+ }
+}
+
+}
diff --git a/kernel/satgen.h b/kernel/satgen.h
index c53d20fe0..722433d62 100644
--- a/kernel/satgen.h
+++ b/kernel/satgen.h
@@ -102,7 +102,7 @@ struct SatGen
else
vec.push_back(bit == (undef_mode ? RTLIL::State::Sx : RTLIL::State::S1) ? ez->CONST_TRUE : ez->CONST_FALSE);
} else {
- std::string wire_name = RTLIL::unescape_id(bit.wire->name);
+ std::string wire_name = bit.wire->name.unescape();
std::string name = pf +
(bit.wire->width == 1 ? wire_name : stringf("%s [%d]", wire_name, bit.offset));
vec.push_back(ez->frozen_literal(name));
@@ -293,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
diff --git a/kernel/scopeinfo.cc b/kernel/scopeinfo.cc
index 59dd746b5..aac83d564 100644
--- a/kernel/scopeinfo.cc
+++ b/kernel/scopeinfo.cc
@@ -100,13 +100,13 @@ static const char *attr_prefix(ScopeinfoAttrs attrs)
bool scopeinfo_has_attribute(const RTLIL::Cell *scopeinfo, ScopeinfoAttrs attrs, RTLIL::IdString id)
{
log_assert(scopeinfo->type == ID($scopeinfo));
- return scopeinfo->has_attribute(attr_prefix(attrs) + RTLIL::unescape_id(id));
+ return scopeinfo->has_attribute(attr_prefix(attrs) + id.unescape());
}
RTLIL::Const scopeinfo_get_attribute(const RTLIL::Cell *scopeinfo, ScopeinfoAttrs attrs, RTLIL::IdString id)
{
log_assert(scopeinfo->type == ID($scopeinfo));
- auto found = scopeinfo->attributes.find(attr_prefix(attrs) + RTLIL::unescape_id(id));
+ auto found = scopeinfo->attributes.find(attr_prefix(attrs) + id.unescape());
if (found == scopeinfo->attributes.end())
return RTLIL::Const();
return found->second;
diff --git a/kernel/scopeinfo.h b/kernel/scopeinfo.h
index a3939b903..e06beb1dc 100644
--- a/kernel/scopeinfo.h
+++ b/kernel/scopeinfo.h
@@ -328,7 +328,7 @@ struct ModuleItem {
[[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(ptr); return h; }
};
-static inline void log_dump_val_worker(typename IdTree::Cursor cursor ) { log("%p %s", cursor.target, log_id(cursor.scope_name)); }
+static inline void log_dump_val_worker(typename IdTree::Cursor cursor ) { log("%p %s", cursor.target, cursor.scope_name.unescape()); }
template
static inline void log_dump_val_worker(const typename std::unique_ptr &cursor ) { log("unique %p", cursor.get()); }
diff --git a/kernel/tclapi.cc b/kernel/tclapi.cc
index ec0483a4a..9866f5c98 100644
--- a/kernel/tclapi.cc
+++ b/kernel/tclapi.cc
@@ -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;
}
diff --git a/kernel/threading.cc b/kernel/threading.cc
index dcc044c89..a334dfa4c 100644
--- a/kernel/threading.cc
+++ b/kernel/threading.cc
@@ -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,21 +45,32 @@ int ThreadPool::pool_size(int reserved_cores, int max_worker_threads)
#ifdef YOSYS_ENABLE_THREADS
int available_threads = std::min(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 b)
: body(std::move(b))
{
#ifdef YOSYS_ENABLE_THREADS
- threads.reserve(pool_size);
- for (int i = 0; i < pool_size; i++)
- threads.emplace_back([i, this]{ body(i); });
+ threads.reserve(pool_size);
+ for (int i = 0; i < pool_size; i++)
+ threads.emplace_back([i, this]{ body(i); });
#else
- log_assert(pool_size == 0);
+ (void)pool_size;
#endif
}
@@ -57,4 +82,74 @@ 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)
+#ifdef YOSYS_ENABLE_THREADS
+ : num_worker_threads_(std::max(1, pool_size) - 1)
+#else
+ : num_worker_threads_(0)
+#endif
+{
+ main_to_workers_signal.resize(num_worker_threads_, 0);
+ // Don't start the threads until we've constructed all our data members.
+ thread_pool = std::make_unique(num_worker_threads_, [this](int thread_num){
+ run_worker(thread_num);
+ });
+}
+
+ParallelDispatchThreadPool::~ParallelDispatchThreadPool()
+{
+ 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();
+}
+
+void ParallelDispatchThreadPool::run(std::function 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;
+ }
+ 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();
+}
+
+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
diff --git a/kernel/threading.h b/kernel/threading.h
index b8cd62f87..c843b23ae 100644
--- a/kernel/threading.h
+++ b/kernel/threading.h
@@ -8,12 +8,40 @@
#include "kernel/yosys_common.h"
#include "kernel/log.h"
+#include "kernel/utils.h"
#ifndef YOSYS_THREADING_H
#define YOSYS_THREADING_H
YOSYS_NAMESPACE_BEGIN
+// Redirect to no-op to avoid dependence on
+// and in single-threaded builds
+#ifdef YOSYS_ENABLE_THREADS
+using Mutex = std::mutex;
+using CondVar = std::condition_variable;
+using UniqueLock = std::unique_lock |