3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2026-01-19 16:53:20 +00:00

Merge remote-tracking branch 'origin/main' into icells_not_derived

This commit is contained in:
Gus Smith 2026-01-18 16:10:53 -08:00
commit 629b3023fd
1402 changed files with 116560 additions and 39840 deletions

View file

@ -10,3 +10,11 @@ insert_final_newline = true
indent_style = space
indent_size = 2
trim_trailing_whitespace = false
[*.rst]
indent_style = space
indent_size = 3
[*.yml]
indent_style = space
indent_size = 2

View file

@ -1 +1 @@
$Format:%h$
$Format:%H$

View file

@ -6,15 +6,14 @@ body:
attributes:
value: >
If you have a general question, please ask it in the [Discussions](https://github.com/YosysHQ/yosys/discussions) area
or join our [IRC Channel](https://web.libera.chat/#yosys) or [Community Slack](https://join.slack.com/t/yosyshq/shared_invite/zt-1aopkns2q-EiQ97BeQDt_pwvE41sGSuA).
If you have a general question, please ask it on the [Discourse forum](https://yosyshq.discourse.group/).
If you have a feature request, please fill out the appropriate issue form, this form is for bugs and/or regressions.
Please contact [YosysHQ GmbH](https://www.yosyshq.com/) if you need
commercial support for Yosys.
commercial support or work done for Yosys.
- type: input
id: yosys_version
@ -34,6 +33,7 @@ body:
- macOS
- Windows
- BSD
- WebAssembly
multiple: true
validations:
required: true
@ -42,7 +42,7 @@ body:
attributes:
value: >
When providing steps to reproduce the issue, please ensure that the issue
is reproducible in the current git master of Yosys. Also ensure to
is reproducible in the current git main of Yosys. Also ensure to
provide all necessary source files needed.

View file

@ -1,11 +1,8 @@
contact_links:
- name: Discussions
url: https://github.com/YosysHQ/yosys/discussions
about: "Have a question? Ask it on our discussions page!"
- name: Community Slack
url: https://join.slack.com/t/yosyshq/shared_invite/zt-1aopkns2q-EiQ97BeQDt_pwvE41sGSuA
about: "Yosys Community Slack"
- name: Discourse
url: https://yosyshq.discourse.group
about: "Have a question? Ask it on our Discourse group!"
- name: IRC Channel
url: https://web.libera.chat/#yosys
about: "#yosys on irc.libera.chat"

54
.github/ISSUE_TEMPLATE/docs_report.yml vendored Normal file
View file

@ -0,0 +1,54 @@
name: Documentation Report
description: Report a problem with the Yosys documentation
labels: ["pending-verification"]
body:
- type: markdown
attributes:
value: >
If you have a general question, please ask it on the [Discourse forum](https://yosyshq.discourse.group/).
If you have found a bug in Yosys, or in building the documentation,
please fill out the Bug Report issue form, this form is for problems
with the live documentation on [Read the
Docs](https://yosyshq.readthedocs.io/projects/yosys/). Please only
report problems that appear on the latest version of the documentation.
Please contact [YosysHQ GmbH](https://www.yosyshq.com/) if you need
commercial support for Yosys.
- type: input
id: docs_url
attributes:
label: Link to page
description: "Please provide a link to the page where the problem was found."
placeholder: "e.g. https://yosyshq.readthedocs.io/projects/yosys/"
validations:
required: true
- type: input
id: build_number
attributes:
label: Build number
description: "If possible, please provide the latest build number from https://readthedocs.org/projects/yosys/builds/."
placeholder: "e.g. Build #24078236"
validations:
required: false
- type: textarea
id: problem
attributes:
label: Issue
description: "Please describe what is incorrect, invalid, or missing."
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected
description: "If applicable, please describe what should appear instead."
validations:
required: false

View file

@ -1,18 +1,17 @@
name: Feature Request
description: "Submit a feature request for Yosys"
description: "Submit a feature request for Yosys"
labels: ["feature-request"]
body:
- type: markdown
attributes:
value: >
If you have a general question, please ask it in the [Discussions](https://github.com/YosysHQ/yosys/discussions) area
or join our [IRC Channel](https://web.libera.chat/#yosys) or [Community Slack](https://join.slack.com/t/yosyshq/shared_invite/zt-1aopkns2q-EiQ97BeQDt_pwvE41sGSuA).
If you have a general question, please ask it on the [Discourse forum](https://yosyshq.discourse.group/).
If you have a bug report, please fill out the appropriate issue form, this form is for feature requests.
Please contact [YosysHQ GmbH](https://www.yosyshq.com/) if you need
commercial support or work done for Yosys.

9
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View file

@ -0,0 +1,9 @@
_If your work is part of a larger effort, please discuss your general plans on [Discourse](https://yosyshq.discourse.group/) first to align your vision with maintainers._
_What are the reasons/motivation for this change?_
_Explain how this is achieved._
_Make sure your change comes with tests. If not possible, share how a reviewer might evaluate it._
_These template prompts can be deleted when you're done responding to them._

View file

@ -0,0 +1,91 @@
name: Build environment setup
description: Configure build env for Yosys builds
inputs:
runs-on:
required: true
type: string
get-build-deps:
description: 'Install Yosys build dependencies'
default: false
required: false
type: boolean
get-docs-deps:
description: 'Install Yosys docs dependencies'
default: false
required: false
type: boolean
get-test-deps:
description: 'Install Yosys test dependencies'
default: false
required: false
type: boolean
get-iverilog:
description: 'Install iverilog'
default: false
required: false
type: boolean
runs:
using: composite
steps:
# if updating common/build/docs dependencies, make sure to update README.md
# and docs/source/getting_started/installation.rst to match.
- name: Linux common dependencies
if: runner.os == 'Linux'
uses: awalsh128/cache-apt-pkgs-action@v1.6.0
with:
packages: gawk git make python3
version: ${{ inputs.runs-on }}-commonys
- name: Linux build dependencies
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
version: ${{ inputs.runs-on }}-buildys
- name: Linux docs dependencies
if: runner.os == 'Linux' && inputs.get-docs-deps == 'true'
uses: awalsh128/cache-apt-pkgs-action@v1.6.0
with:
packages: graphviz xdot
version: ${{ inputs.runs-on }}-docsys
# if updating test dependencies, make sure to update
# docs/source/yosys_internals/extending_yosys/test_suites.rst to match.
- name: Linux test dependencies
if: runner.os == 'Linux' && inputs.get-test-deps == 'true'
uses: awalsh128/cache-apt-pkgs-action@v1.6.0
with:
packages: libgtest-dev
version: ${{ inputs.runs-on }}-testys
- name: Install macOS Dependencies
if: runner.os == 'macOS'
shell: bash
run: |
brew bundle
- name: Linux runtime environment
if: runner.os == 'Linux'
shell: bash
run: |
echo "${{ github.workspace }}/.local/bin" >> $GITHUB_PATH
echo "procs=$(nproc)" >> $GITHUB_ENV
- name: macOS runtime environment
if: runner.os == 'macOS'
shell: bash
run: |
echo "${{ github.workspace }}/.local/bin" >> $GITHUB_PATH
echo "$(brew --prefix llvm@20)/bin" >> $GITHUB_PATH
echo "$(brew --prefix bison)/bin" >> $GITHUB_PATH
echo "$(brew --prefix flex)/bin" >> $GITHUB_PATH
echo "procs=$(sysctl -n hw.ncpu)" >> $GITHUB_ENV
- name: Setup iverilog
if: inputs.get-iverilog == 'true'
uses: ./.github/actions/setup-iverilog
with:
runs-on: ${{ inputs.runs-on }}

View file

@ -0,0 +1,70 @@
name: iverilog setup
description: Cached build and install of iverilog
inputs:
runs-on:
required: true
type: string
runs:
using: composite
steps:
- name: iverilog Linux deps
if: steps.restore-iverilog.outputs.cache-hit != 'true' && runner.os == 'Linux'
uses: awalsh128/cache-apt-pkgs-action@v1.6.0
with:
packages: autoconf gperf make gcc g++ bison flex libbz2-dev
version: ${{ inputs.runs-on }}-iverilog
- name: iverilog macOS deps
if: steps.restore-iverilog.outputs.cache-hit != 'true' && runner.os == 'macOS'
shell: bash
run: |
brew install autoconf
- name: Get iverilog
id: get-iverilog
shell: bash
run: |
git clone https://github.com/steveicarus/iverilog.git
cd iverilog
echo "IVERILOG_GIT=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
- name: Get vcd2fst
shell: bash
run: |
git clone https://github.com/mmicko/libwave.git
mkdir -p ${{ github.workspace }}/.local/
cd libwave
cmake . -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/.local
make -j$procs
make install
- uses: actions/cache/restore@v4
id: restore-iverilog
with:
path: .local/
key: ${{ inputs.runs-on }}-${{ steps.get-iverilog.outputs.IVERILOG_GIT }}
- name: Build iverilog
if: steps.restore-iverilog.outputs.cache-hit != 'true'
shell: bash
run: |
mkdir -p ${{ github.workspace }}/.local/
cd iverilog
autoconf
CC=gcc CXX=g++ ./configure --prefix=${{ github.workspace }}/.local
make -j$procs
make install
- name: Check iverilog
shell: bash
run: |
iverilog -V
- uses: actions/cache/save@v4
id: save-iverilog
if: steps.restore-iverilog.outputs.cache-hit != 'true'
with:
path: .local/
key: ${{ steps.restore-iverilog.outputs.cache-primary-key }}

View file

@ -10,11 +10,17 @@ jobs:
name: Analyze
runs-on: ubuntu-latest
steps:
- name: Install deps
run: sudo apt-get install bison flex libreadline-dev tcl-dev libffi-dev
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: true
persist-credentials: false
- name: Setup environment
uses: ./.github/actions/setup-build-env
with:
runs-on: ubuntu-latest
get-build-deps: true
- name: Initialize CodeQL
uses: github/codeql-action/init@v3

View file

@ -1,18 +0,0 @@
name: Emscripten Build
on: [push, pull_request]
jobs:
emcc:
runs-on: ubuntu-latest
steps:
- uses: mymindstorm/setup-emsdk@v14
- uses: actions/checkout@v4
- name: Build
run: |
make config-emcc
make YOSYS_VER=latest
- uses: actions/upload-artifact@v4
with:
name: yosysjs
path: yosysjs-latest.zip

127
.github/workflows/extra-builds.yml vendored Normal file
View file

@ -0,0 +1,127 @@
name: Test extra build flows
on:
# always test main
push:
branches:
- main
merge_group:
# test PRs
pull_request:
# allow triggering tests, ignores skip check
workflow_dispatch:
jobs:
pre_job:
runs-on: ubuntu-latest
outputs:
should_skip: ${{ steps.skip_check.outputs.should_skip }}
steps:
- id: skip_check
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' }}
vs-prep:
name: Prepare Visual Studio build
runs-on: ubuntu-latest
needs: [pre_job]
if: needs.pre_job.outputs.should_skip != 'true'
steps:
- uses: actions/checkout@v4
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
with:
name: vcxsrc
path: yosys-win32-vcxsrc-latest.zip
vs-build:
name: Visual Studio build
runs-on: windows-latest
needs: [vs-prep, pre_job]
if: needs.pre_job.outputs.should_skip != 'true'
steps:
- uses: actions/download-artifact@v4
with:
name: vcxsrc
path: .
- name: unzip
run: unzip yosys-win32-vcxsrc-latest.zip
- name: setup-msbuild
uses: microsoft/setup-msbuild@v2
- name: MSBuild
working-directory: yosys-win32-vcxsrc-latest
run: msbuild YosysVS.sln /p:PlatformToolset=v142 /p:Configuration=Release /p:WindowsTargetPlatformVersion=10.0.26100.0
wasi-build:
name: WASI build
needs: pre_job
if: needs.pre_job.outputs.should_skip != 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
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
if ! [ -d ${WASI_SDK} ]; then curl -L ${WASI_SDK_URL} | tar xzf -; fi
FLEX_VER=2.6.4
FLEX=flex-${FLEX_VER}
FLEX_URL=https://github.com/westes/flex/releases/download/v${FLEX_VER}/${FLEX}.tar.gz
if ! [ -d ${FLEX} ]; then curl -L ${FLEX_URL} | tar xzf -; fi
mkdir -p flex-build
(cd flex-build &&
../${FLEX}/configure --prefix=$(pwd)/../flex-prefix &&
make &&
make install)
mkdir -p build
cat > build/Makefile.conf <<END
export PATH := $(pwd)/${WASI_SDK}/bin:$(pwd)/flex-prefix/bin:${PATH}
WASI_SYSROOT := $(pwd)/${WASI_SDK}/share/wasi-sysroot
CONFIG := wasi
PREFIX := /
ENABLE_TCL := 0
ENABLE_READLINE := 0
ENABLE_PLUGINS := 0
ENABLE_ZLIB := 0
CXXFLAGS += -I$(pwd)/flex-prefix/include
END
make -C build -f ../Makefile CXX=clang -j$(nproc)
nix-build:
name: "Build nix flake"
needs: pre_job
if: 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
with:
submodules: true
persist-credentials: false
- uses: cachix/install-nix-action@v31
with:
install_url: https://releases.nixos.org/nix/nix-2.30.0/install
- run: nix build .?submodules=1 -L

83
.github/workflows/prepare-docs.yml vendored Normal file
View file

@ -0,0 +1,83 @@
name: Build docs artifact with Verific
on: [push, pull_request, merge_group]
jobs:
check_docs_rebuild:
runs-on: ubuntu-latest
outputs:
skip_check: ${{ steps.skip_check.outputs.should_skip }}
docs_export: ${{ steps.docs_var.outputs.docs_export }}
env:
docs_export: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/docs-preview') || startsWith(github.ref, 'refs/tags/') }}
steps:
- id: skip_check
uses: fkirc/skip-duplicate-actions@v5
with:
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'}}
- id: docs_var
run: echo "docs_export=${docs_export}" >> $GITHUB_OUTPUT
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' }}
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: Build Yosys
run: |
make config-clang
echo "ENABLE_VERIFIC := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_EDIF := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_LIBERTY := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_YOSYSHQ_EXTENSIONS := 1" >> Makefile.conf
echo "ENABLE_CCACHE := 1" >> Makefile.conf
echo "ENABLE_HELP_SOURCE := 1" >> Makefile.conf
make -j$procs
- name: Prepare docs
shell: bash
run:
make docs/prep -j$procs TARGETS= EXTRA_TARGETS=
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: cmd-ref-${{ github.sha }}
path: |
docs/source/generated
docs/source/_images
docs/source/code_examples
- name: Install doc prereqs
shell: bash
run: |
make docs/reqs
- name: Test build docs
shell: bash
run: |
make -C docs html -j$procs TARGETS= EXTRA_TARGETS=
- name: Trigger RTDs build
if: ${{ needs.check_docs_rebuild.outputs.docs_export == 'true' }}
uses: dfm/rtds-action@v1.1.0
with:
webhook_url: ${{ secrets.RTDS_WEBHOOK_URL }}
webhook_token: ${{ secrets.RTDS_WEBHOOK_TOKEN }}
commit_ref: ${{ github.ref }}

34
.github/workflows/source-vendor.yml vendored Normal file
View file

@ -0,0 +1,34 @@
name: Create source archive with vendored dependencies
on: [push, workflow_dispatch]
jobs:
vendor-sources:
runs-on: ubuntu-latest
steps:
- name: Checkout repository with submodules
uses: actions/checkout@v4
with:
submodules: 'recursive'
persist-credentials: false
- name: Create clean tarball
run: |
git archive --format=tar HEAD -o yosys-src-vendored.tar
git submodule foreach '
git archive --format=tar --prefix="${sm_path}/" HEAD --output=${toplevel}/vendor-${name}.tar
'
# 2008 bug https://lists.gnu.org/archive/html/bug-tar/2008-08/msg00002.html
for file in vendor-*.tar; do
tar --concatenate --file=yosys-src-vendored.tar "$file"
done
gzip yosys-src-vendored.tar
- name: Store tarball artifact
uses: actions/upload-artifact@v4
with:
name: vendored-sources
path: yosys-src-vendored.tar.gz
retention-days: 1

266
.github/workflows/test-build.yml vendored Normal file
View file

@ -0,0 +1,266 @@
name: Build and run tests
on:
# always test main
push:
branches:
- main
merge_group:
# test PRs
pull_request:
# allow triggering tests, ignores skip check
workflow_dispatch:
jobs:
pre_job:
runs-on: ubuntu-latest
outputs:
should_skip: ${{ steps.skip_check.outputs.should_skip }}
steps:
- id: skip_check
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' }}
pre_docs_job:
runs-on: ubuntu-latest
outputs:
should_skip: ${{ steps.skip_check.outputs.should_skip }}
steps:
- id: skip_check
uses: fkirc/skip-duplicate-actions@v5
with:
# don't run on readme changes
paths_ignore: '["**/README.md"]'
# cancel previous builds if a new commit is pushed
# but never cancel main
cancel_others: ${{ github.ref != 'refs/heads/main' }}
build-yosys:
name: Reusable build
runs-on: ${{ matrix.os }}
# pre_job is a subset of pre_docs_job, so we can always build for pre_docs_job
needs: pre_docs_job
if: needs.pre_docs_job.outputs.should_skip != 'true'
env:
CC: clang
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
fail-fast: false
steps:
- name: Checkout Yosys
uses: actions/checkout@v4
with:
submodules: true
persist-credentials: false
- name: Setup environment
uses: ./.github/actions/setup-build-env
with:
runs-on: ${{ matrix.os }}
get-build-deps: true
- name: Build
shell: bash
run: |
mkdir build
cd build
make -f ../Makefile config-$CC
make -f ../Makefile -j$procs
- name: Log yosys-config output
run: |
./yosys-config || true
- name: Compress build
shell: bash
run: |
cd build
tar -cvf ../build.tar share/ yosys yosys-*
- name: Store build artifact
uses: actions/upload-artifact@v4
with:
name: build-${{ matrix.os }}
path: build.tar
retention-days: 1
test-yosys:
name: Run tests
runs-on: ${{ matrix.os }}
needs: [build-yosys, pre_job]
if: needs.pre_job.outputs.should_skip != 'true'
env:
CC: clang
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
fail-fast: false
steps:
- name: Checkout Yosys
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Setup environment
uses: ./.github/actions/setup-build-env
with:
runs-on: ${{ matrix.os }}
get-test-deps: true
get-iverilog: true
- name: Download build artifact
uses: actions/download-artifact@v4
with:
name: build-${{ matrix.os }}
- name: Uncompress build
shell: bash
run:
tar -xvf build.tar
- name: Log yosys-config output
run: |
./yosys-config || true
- name: Run tests
shell: bash
run: |
make -j$procs test TARGETS= EXTRA_TARGETS= CONFIG=$CC
- name: Report errors
if: ${{ failure() }}
shell: bash
run: |
find tests/**/*.err -print -exec cat {} \;
test-cells:
name: Run test_cell
runs-on: ${{ matrix.os }}
needs: [build-yosys, pre_job]
if: needs.pre_job.outputs.should_skip != 'true'
env:
CC: clang
strategy:
matrix:
os: [ubuntu-latest]
steps:
- name: Checkout Yosys
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Setup environment
uses: ./.github/actions/setup-build-env
with:
runs-on: ${{ matrix.os }}
- name: Download build artifact
uses: actions/download-artifact@v4
with:
name: build-${{ matrix.os }}
- name: Uncompress build
shell: bash
run:
tar -xvf build.tar
- name: test_cell
shell: bash
run: |
./yosys -p 'test_cell -n 20 -s 1 all'
./yosys -p 'test_cell -n 20 -s 1 -nosat -aigmap $pow $pmux'
./yosys -p 'test_cell -n 20 -s 1 -nosat -aigmap $eqx $nex $bweqx'
./yosys -p 'test_cell -n 20 -s 1 -aigmap $buf'
test-docs:
name: Run docs tests
runs-on: ${{ matrix.os }}
needs: [build-yosys, pre_docs_job]
if: needs.pre_docs_job.outputs.should_skip != 'true'
env:
CC: clang
strategy:
matrix:
os: [ubuntu-latest]
fail-fast: false
steps:
- name: Checkout Yosys
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Setup environment
uses: ./.github/actions/setup-build-env
with:
runs-on: ${{ matrix.os }}
get-build-deps: true
get-docs-deps: true
- name: Download build artifact
uses: actions/download-artifact@v4
with:
name: build-${{ matrix.os }}
- name: Uncompress build
shell: bash
run:
tar -xvf build.tar
- name: Log yosys-config output
run: |
./yosys-config || true
- name: Run tests
shell: bash
run: |
make -C docs test -j$procs
test-docs-build:
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' }}
strategy:
matrix:
docs-target: [html, latexpdf]
fail-fast: false
steps:
- name: Checkout Yosys
uses: actions/checkout@v4
with:
submodules: true
persist-credentials: false
- name: Runtime environment
run: |
echo "procs=$(nproc)" >> $GITHUB_ENV
- name: Build Yosys
run: |
make config-clang
echo "ENABLE_CCACHE := 1" >> Makefile.conf
echo "ENABLE_HELP_SOURCE := 1" >> Makefile.conf
make -j$procs
- name: Install doc prereqs
shell: bash
run: |
make docs/reqs
- name: Build docs
shell: bash
run: |
make docs DOC_TARGET=${{ matrix.docs-target }} -j$procs
- name: Store docs build artifact
uses: actions/upload-artifact@v4
with:
name: docs-build-${{ matrix.docs-target }}
path: docs/build/
retention-days: 7

92
.github/workflows/test-compile.yml vendored Normal file
View file

@ -0,0 +1,92 @@
name: Compiler testing
on:
# always test main
push:
branches:
- main
merge_group:
# test PRs
pull_request:
# allow triggering tests, ignores skip check
workflow_dispatch:
jobs:
pre_job:
runs-on: ubuntu-latest
outputs:
should_skip: ${{ steps.skip_check.outputs.should_skip }}
steps:
- id: skip_check
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' }}
test-compile:
runs-on: ${{ matrix.os }}
needs: pre_job
if: 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' }}
strategy:
matrix:
os:
- ubuntu-latest
compiler:
# oldest supported
- 'clang-10'
- 'gcc-10'
# newest, make sure to update maximum standard step to match
- 'clang-19'
- 'gcc-14'
include:
# macOS x86
- os: macos-15-intel
compiler: 'clang-19'
# macOS arm
- os: macos-latest
compiler: 'clang-19'
fail-fast: false
steps:
- name: Checkout Yosys
uses: actions/checkout@v4
with:
submodules: true
persist-credentials: false
- name: Setup environment
uses: ./.github/actions/setup-build-env
with:
runs-on: ${{ matrix.os }}
get-build-deps: true
- name: Setup Cpp
uses: aminya/setup-cpp@v1
with:
compiler: ${{ matrix.compiler }}
- name: Tool versions
shell: bash
run: |
$CC --version
$CXX --version
# minimum standard
- name: Build C++17
shell: bash
run: |
make config-$CC_SHORT
make -j$procs CXXSTD=c++17 compile-only
# maximum standard, only on newest compilers
- 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

View file

@ -1,123 +0,0 @@
name: Build and run tests (Linux)
on: [push, pull_request]
jobs:
test-linux:
runs-on: ${{ matrix.os.id }}
strategy:
matrix:
os:
- { id: ubuntu-20.04, name: focal }
compiler:
- 'clang-12'
- 'gcc-11'
cpp_std:
- 'c++11'
- 'c++14'
- 'c++17'
- 'c++20'
include:
# Limit the older compilers to C++11 mode
- os: { id: ubuntu-20.04, name: focal }
compiler: 'clang-11'
cpp_std: 'c++11'
- os: { id: ubuntu-20.04, name: focal }
compiler: 'gcc-10'
cpp_std: 'c++11'
fail-fast: false
steps:
- name: Install Dependencies
shell: bash
run: |
sudo apt-get update
sudo apt-get install gperf build-essential bison flex libreadline-dev gawk tcl-dev libffi-dev git graphviz xdot pkg-config python python3 libboost-system-dev libboost-python-dev libboost-filesystem-dev zlib1g-dev
- name: Setup GCC
if: startsWith(matrix.compiler, 'gcc')
shell: bash
run: |
CXX=${CC/#gcc/g++}
sudo apt-add-repository ppa:ubuntu-toolchain-r/test
sudo apt-get update
sudo apt-get install $CC $CXX
echo "CC=$CC" >> $GITHUB_ENV
echo "CXX=$CXX" >> $GITHUB_ENV
echo "CXXFLAGS=-Wp,-D_GLIBCXX_ASSERTIONS" >> $GITHUB_ENV
env:
CC: ${{ matrix.compiler }}
- name: Setup Clang
if: startsWith(matrix.compiler, 'clang')
shell: bash
run: |
wget https://apt.llvm.org/llvm-snapshot.gpg.key
sudo apt-key add llvm-snapshot.gpg.key
rm llvm-snapshot.gpg.key
sudo apt-add-repository "deb https://apt.llvm.org/${{ matrix.os.name }}/ llvm-toolchain-${{ matrix.os.name }} main"
sudo apt-get update
CXX=${CC/#clang/clang++}
sudo apt-get install $CC $CXX
echo "CC=$CC" >> $GITHUB_ENV
echo "CXX=$CXX" >> $GITHUB_ENV
env:
CC: ${{ matrix.compiler }}
- name: Runtime environment
shell: bash
env:
WORKSPACE: ${{ github.workspace }}
run: |
echo "GITHUB_WORKSPACE=`pwd`" >> $GITHUB_ENV
echo "$GITHUB_WORKSPACE/.local/bin" >> $GITHUB_PATH
echo "procs=$(nproc)" >> $GITHUB_ENV
- name: Tool versions
shell: bash
run: |
$CC --version
$CXX --version
- name: Checkout Yosys
uses: actions/checkout@v4
- name: Get iverilog
shell: bash
run: |
git clone https://github.com/steveicarus/iverilog.git
cd iverilog
echo "IVERILOG_GIT=$(git rev-parse HEAD)" >> $GITHUB_ENV
- name: Cache iverilog
id: cache-iverilog
uses: actions/cache@v4
with:
path: .local/
key: ${{ matrix.os.id }}-${{ env.IVERILOG_GIT }}
- name: Build iverilog
if: steps.cache-iverilog.outputs.cache-hit != 'true'
shell: bash
run: |
mkdir -p $GITHUB_WORKSPACE/.local/
cd iverilog
autoconf
CC=gcc CXX=g++ ./configure --prefix=$GITHUB_WORKSPACE/.local
make -j${{ env.procs }}
make install
- name: Build yosys
shell: bash
run: |
make config-${CC%%-*}
make -j${{ env.procs }} CXXSTD=${{ matrix.cpp_std }} CC=$CC CXX=$CC LD=$CC
- name: Run tests
if: (matrix.cpp_std == 'c++11') && (matrix.compiler == 'gcc-11')
shell: bash
run: |
make -j${{ env.procs }} test CXXSTD=${{ matrix.cpp_std }} CC=$CC CXX=$CC LD=$CC
- name: Log yosys-config output
run: |
./yosys-config || true

View file

@ -1,75 +0,0 @@
name: Build and run tests (macOS)
on: [push, pull_request]
jobs:
test-macos:
runs-on: ${{ matrix.os.id }}
strategy:
matrix:
os:
- { id: macos-13, name: 'Ventura' }
cpp_std:
- 'c++11'
- 'c++17'
fail-fast: false
steps:
- name: Install Dependencies
run: |
brew install bison flex gawk libffi pkg-config bash
- name: Runtime environment
shell: bash
env:
WORKSPACE: ${{ github.workspace }}
run: |
echo "GITHUB_WORKSPACE=`pwd`" >> $GITHUB_ENV
echo "$GITHUB_WORKSPACE/.local/bin" >> $GITHUB_PATH
echo "$(brew --prefix bison)/bin" >> $GITHUB_PATH
echo "$(brew --prefix flex)/bin" >> $GITHUB_PATH
echo "procs=$(sysctl -n hw.ncpu)" >> $GITHUB_ENV
- name: Tool versions
shell: bash
run: |
cc --version
- name: Checkout Yosys
uses: actions/checkout@v4
- name: Get iverilog
shell: bash
run: |
git clone https://github.com/steveicarus/iverilog.git
cd iverilog
echo "IVERILOG_GIT=$(git rev-parse HEAD)" >> $GITHUB_ENV
- name: Cache iverilog
id: cache-iverilog
uses: actions/cache@v4
with:
path: .local/
key: ${{ matrix.os.id }}-${{ env.IVERILOG_GIT }}
- name: Build iverilog
if: steps.cache-iverilog.outputs.cache-hit != 'true'
shell: bash
run: |
mkdir -p $GITHUB_WORKSPACE/.local/
cd iverilog
autoconf
CC=gcc CXX=g++ ./configure --prefix=$GITHUB_WORKSPACE/.local/
make -j${{ env.procs }}
make install
- name: Build yosys
shell: bash
run: |
make config-clang
make -j${{ env.procs }} CXXSTD=${{ matrix.cpp_std }} CC=cc CXX=cc LD=cc
- name: Run tests
if: matrix.cpp_std == 'c++11'
shell: bash
run: |
make -j${{ env.procs }} test CXXSTD=${{ matrix.cpp_std }} CC=cc CXX=cc LD=cc

79
.github/workflows/test-sanitizers.yml vendored Normal file
View file

@ -0,0 +1,79 @@
name: Check clang sanitizers
on:
# always test main
push:
branches:
- main
merge_group:
# ignore PRs due to time needed
# allow triggering tests, ignores skip check
workflow_dispatch:
jobs:
pre_job:
runs-on: ubuntu-latest
outputs:
should_skip: ${{ steps.skip_check.outputs.should_skip }}
steps:
- id: skip_check
uses: fkirc/skip-duplicate-actions@v5
with:
# don't run on documentation changes
paths_ignore: '["**/README.md", "docs/**", "guidelines/**"]'
run_san:
name: Build and run tests
runs-on: ${{ matrix.os }}
needs: pre_job
if: needs.pre_job.outputs.should_skip != 'true'
env:
CC: clang
ASAN_OPTIONS: halt_on_error=1
UBSAN_OPTIONS: halt_on_error=1
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
sanitizer: ['undefined,address']
fail-fast: false
steps:
- name: Checkout Yosys
uses: actions/checkout@v4
with:
submodules: true
persist-credentials: false
- name: Setup environment
uses: ./.github/actions/setup-build-env
with:
runs-on: ${{ matrix.os }}
get-build-deps: true
get-test-deps: true
get-iverilog: true
- name: Build
shell: bash
run: |
make config-$CC
echo 'SANITIZER = ${{ matrix.sanitizer }}' >> Makefile.conf
make -j$procs
- name: Log yosys-config output
run: |
./yosys-config || true
- name: Run tests
shell: bash
run: |
make -j$procs test TARGETS= EXTRA_TARGETS=
- name: Report errors
if: ${{ failure() }}
shell: bash
run: |
find tests/**/*.err -print -exec cat {} \;
- name: Run unit tests
shell: bash
run: |
make -j$procs unit-test ENABLE_LIBYOSYS=1

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

@ -0,0 +1,125 @@
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
workflow_dispatch:
jobs:
pre-job:
runs-on: ubuntu-latest
outputs:
should_skip: ${{ steps.skip_check.outputs.should_skip }}
steps:
- id: skip_check
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' }}
test-verific:
needs: pre-job
if: ${{ needs.pre-job.outputs.should_skip != 'true' && github.repository == 'YosysHQ/Yosys' }}
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: Build Yosys
run: |
make config-clang
echo "ENABLE_VERIFIC := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_EDIF := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_LIBERTY := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_YOSYSHQ_EXTENSIONS := 1" >> Makefile.conf
echo "ENABLE_CCACHE := 1" >> Makefile.conf
echo "ENABLE_FUNCTIONAL_TESTS := 1" >> Makefile.conf
make -j$procs ENABLE_LTO=1
- name: Install Yosys
run: |
make install DESTDIR=${GITHUB_WORKSPACE}/.local PREFIX=
- name: Checkout SBY
uses: actions/checkout@v4
with:
repository: 'YosysHQ/sby'
path: 'sby'
persist-credentials: false
- name: Build SBY
run: |
make -C sby install DESTDIR=${GITHUB_WORKSPACE}/.local PREFIX=
- name: Run Yosys tests
run: |
make -j$procs test
- name: Run Verific specific Yosys tests
run: |
make -C tests/sva
cd tests/svtypes && bash run-test.sh
- name: Run SBY tests
if: ${{ github.ref == 'refs/heads/main' }}
run: |
make -C sby run_ci
- name: Run unit tests
shell: bash
run: |
make -j$procs unit-test ENABLE_LTO=1 ENABLE_LIBYOSYS=1
test-pyosys:
needs: pre-job
if: ${{ needs.pre-job.outputs.should_skip != 'true' && github.repository == 'YosysHQ/Yosys' }}
runs-on: [self-hosted, linux, x64, fast]
steps:
- name: Checkout Yosys
uses: actions/checkout@v4
with:
persist-credentials: false
submodules: true
- name: Install UV
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
- name: Runtime environment
run: |
echo "procs=$(nproc)" >> $GITHUB_ENV
echo "${{ github.workspace }}/.local/bin" >> $GITHUB_PATH
- name: Build pyosys
run: |
make config-clang
echo "ENABLE_VERIFIC := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_EDIF := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_LIBERTY := 1" >> Makefile.conf
echo "ENABLE_VERIFIC_YOSYSHQ_EXTENSIONS := 1" >> Makefile.conf
echo "ENABLE_CCACHE := 1" >> Makefile.conf
echo "ENABLE_PYOSYS := 1" >> Makefile.conf
echo "PYTHON_DESTDIR := /usr/lib/python3/site-packages" >> Makefile.conf
make -j$procs
- name: Install pyosys
run: |
make install DESTDIR=${GITHUB_WORKSPACE}/.local PREFIX=
- name: Run pyosys tests
run: |
export PYTHONPATH=${GITHUB_WORKSPACE}/.local/usr/lib/python3/site-packages:$PYTHONPATH
python3 tests/pyosys/run_tests.py

25
.github/workflows/update-flake-lock.yml vendored Normal file
View file

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

View file

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

View file

@ -1,31 +0,0 @@
name: Visual Studio Build
on: [push, pull_request]
jobs:
yosys-vcxsrc:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build
run: make vcxsrc YOSYS_VER=latest
- uses: actions/upload-artifact@v4
with:
name: vcxsrc
path: yosys-win32-vcxsrc-latest.zip
build:
runs-on: windows-2019
needs: yosys-vcxsrc
steps:
- uses: actions/download-artifact@v4
with:
name: vcxsrc
path: .
- name: unzip
run: unzip yosys-win32-vcxsrc-latest.zip
- name: setup-msbuild
uses: microsoft/setup-msbuild@v1
- name: MSBuild
working-directory: yosys-win32-vcxsrc-latest
run: msbuild YosysVS.sln /p:PlatformToolset=v142 /p:Configuration=Release /p:WindowsTargetPlatformVersion=10.0.17763.0

View file

@ -1,30 +0,0 @@
name: WASI Build
on: [push, pull_request]
jobs:
wasi:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build
run: |
WASI_SDK=wasi-sdk-19.0
WASI_SDK_URL=https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-19/wasi-sdk-19.0-linux.tar.gz
if ! [ -d ${WASI_SDK} ]; then curl -L ${WASI_SDK_URL} | tar xzf -; fi
mkdir -p build
cat > build/Makefile.conf <<END
export PATH := $(pwd)/${WASI_SDK}/bin:${PATH}
WASI_SYSROOT := $(pwd)/${WASI_SDK}/share/wasi-sysroot
CONFIG := wasi
PREFIX := /
ENABLE_TCL := 0
ENABLE_READLINE := 0
ENABLE_PLUGINS := 0
ENABLE_ZLIB := 0
END
make -C build -f ../Makefile CXX=clang -j$(nproc)

136
.github/workflows/wheels.yml vendored Normal file
View file

@ -0,0 +1,136 @@
name: Build Wheels for PyPI
# run every Sunday at 10 AM
on:
workflow_dispatch:
schedule:
- cron: "0 10 * * 0"
jobs:
build_wheels:
strategy:
fail-fast: false
matrix:
os: [
{
name: "Ubuntu 22.04",
family: "linux",
runner: "ubuntu-22.04",
archs: "x86_64",
},
{
name: "Ubuntu 22.04",
family: "linux",
runner: "ubuntu-22.04-arm",
archs: "aarch64",
},
{
name: "macOS 15 x64",
family: "macos",
runner: "macos-15-intel",
archs: "x86_64",
},
{
name: "macOS 15 arm64",
family: "macos",
runner: "macos-15",
archs: "arm64",
},
## Windows is disabled because of an issue with compiling FFI as
## under MinGW in the GitHub Actions environment (SHELL variable has
## whitespace.)
# {
# name: "Windows Server 2019",
# family: "windows",
# runner: "windows-2019",
# archs: "AMD64",
# },
]
name: Build Wheels | ${{ matrix.os.name }} | ${{ matrix.os.archs }}
runs-on: ${{ matrix.os.runner }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: true
persist-credentials: false
- uses: actions/setup-python@v5
- name: Get FFI
shell: bash
run: |
mkdir -p ffi
curl -L https://github.com/libffi/libffi/releases/download/v3.4.8/libffi-3.4.8.tar.gz | tar --strip-components=1 -xzC ffi
- if: ${{ matrix.os.family == 'linux' }}
name: "[Linux] Bison 3.8.2"
shell: bash
run: |
mkdir -p bison
curl -L https://ftpmirror.gnu.org/gnu/bison/bison-3.8.2.tar.gz | tar --strip-components=1 -xzC bison
## Software installed by default in GitHub Action Runner VMs:
## https://github.com/actions/runner-images
- if: ${{ matrix.os.family == 'macos' }}
name: "[macOS] Flex/Bison"
run: |
brew install flex bison
echo "PATH=$(brew --prefix flex)/bin:$(brew --prefix bison)/bin:$PATH" >> $GITHUB_ENV
- if: ${{ matrix.os.family == 'windows' }}
name: "[Windows] Flex/Bison"
run: |
choco install winflexbison3
- if: ${{ matrix.os.family == 'macos' && matrix.os.archs == 'arm64' }}
name: "[macOS/arm64] Install Python 3.8 (see: https://cibuildwheel.pypa.io/en/stable/faq/#macos-building-cpython-38-wheels-on-arm64)"
uses: actions/setup-python@v5
with:
python-version: 3.8
- name: Build wheels
uses: pypa/cibuildwheel@v2.21.1
env:
# * APIs not supported by PyPy
# * Musllinux disabled because it increases build time from 48m to ~3h
CIBW_SKIP: >
pp*
*musllinux*
CIBW_ARCHS: ${{ matrix.os.archs }}
CIBW_BUILD_VERBOSITY: "1"
# manylinux2014 (default) does not have a modern enough C++ compiler for Yosys
CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_28
CIBW_MANYLINUX_AARCH64_IMAGE: manylinux_2_28
CIBW_BEFORE_ALL: bash ./.github/workflows/wheels/cibw_before_all.sh
CIBW_ENVIRONMENT: >
OPTFLAGS=-O3
PKG_CONFIG_PATH=./ffi/pfx/lib/pkgconfig
PATH="$PWD/bison/src:$PATH"
CIBW_ENVIRONMENT_MACOS: >
OPTFLAGS=-O3
PKG_CONFIG_PATH=./ffi/pfx/lib/pkgconfig
MACOSX_DEPLOYMENT_TARGET=11
makeFlags='CONFIG=clang'
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
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')
runs-on: ubuntu-latest
# Specifying a GitHub environment is optional, but strongly encouraged
environment: pypi
permissions:
# IMPORTANT: this permission is mandatory for Trusted Publishing
id-token: write
needs: build_wheels
steps:
- uses: actions/download-artifact@v4
with:
path: "."
pattern: python-wheels-*
merge-multiple: true
- run: |
ls
mkdir -p ./dist
mv *.whl ./dist
- name: Publish
uses: pypa/gh-action-pypi-publish@release/v1

View file

@ -0,0 +1,52 @@
#!/usr/bin/env python3
# Copyright (C) 2024 Efabless Corporation
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
"""
This runs the cibuildwheel step from the wheels workflow locally.
"""
import os
import yaml
import platform
import subprocess
from pathlib import Path
__yosys_root__ = Path(__file__).absolute().parents[3]
for source in ["ffi", "bison"]:
if not (__yosys_root__ / source).is_dir():
print(
"You need to download ffi and bison in a similar manner to wheels.yml first."
)
exit(-1)
with open(__yosys_root__ / ".github" / "workflows" / "wheels.yml") as f:
workflow = yaml.safe_load(f)
env = os.environ.copy()
steps = workflow["jobs"]["build_wheels"]["steps"]
cibw_step = None
for step in steps:
if (step.get("uses") or "").startswith("pypa/cibuildwheel"):
cibw_step = step
break
for key, value in cibw_step["env"].items():
if key.endswith("WIN") or key.endswith("MAC"):
continue
env[key] = value
env["CIBW_ARCHS"] = os.getenv("CIBW_ARCHS", platform.machine())
subprocess.check_call(["cibuildwheel"], env=env)

View file

@ -0,0 +1,37 @@
#!/bin/bash
set -e -x
# Build-time dependencies
## Linux Docker Images
if command -v yum &> /dev/null; then
yum install -y flex # manylinux's bison versions are hopelessly out of date
fi
if command -v apk &> /dev/null; then
apk add flex bison
fi
if ! printf '%s\n' '%require "3.8"' '%%' 'start: ;' | bison -o /dev/null /dev/stdin ; then
(
set -e -x
cd bison
./configure
make clean
make install -j$(getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n hw.ncpu)
)
fi
## macOS/Windows -- installed in GitHub Action itself, not container
# Runtime Dependencies
## Build Static FFI (platform-dependent but not Python version dependent)
(
set -e -x
cd ffi
## Ultimate libyosys.so will be shared, so we need fPIC for the static libraries
CFLAGS=-fPIC CXXFLAGS=-fPIC ./configure --prefix=$PWD/pfx
make clean
make install -j$(getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n hw.ncpu)
## Forces static library to be used in all situations
sed -i.bak 's@-L${toolexeclibdir} -lffi@${toolexeclibdir}/libffi.a@' ./pfx/lib/pkgconfig/libffi.pc
)

View file

@ -0,0 +1,13 @@
set -e
set -x
# Don't use Python objects from previous compiles
make clean-py
# DEBUG: show python3 and python3-config outputs
if [ "$(uname)" != "Linux" ]; then
# https://github.com/pypa/cibuildwheel/issues/2021
ln -s $(dirname $(readlink -f $(which python3)))/python3-config $(dirname $(which python3))/python3-config
fi
python3 --version
python3-config --includes

80
.gitignore vendored
View file

@ -1,25 +1,29 @@
## user config
/Makefile.conf
## homebrew
/Brewfile.lock.json
## build artifacts
/.git-abc-submodule-hash
# compiler intermediate files
*.o
*.d
*.dwo
.*.swp
*.gch
*.gcda
*.gcno
*~
__pycache__
/.cproject
/.project
/.settings
/qtcreator.files
/qtcreator.includes
/qtcreator.config
/qtcreator.creator
/qtcreator.creator.user
/coverage.info
/coverage_html
/Makefile.conf
/abc
/viz.js
*.so.dSYM/
## test artifacts
**/run-test.mk
*.err
*.log
*.tmp
# compiler output files
/kernel/version_*.cc
/share
/yosys
/yosys.exe
/yosys.js
@ -35,14 +39,50 @@ __pycache__
/yosys-witness-script.py
/yosys-filterlib
/yosys-filterlib.exe
/kernel/*.pyh
/kernel/python_wrappers.cc
/kernel/version_*.cc
/share
/yosys-win32-mxebin-*
/yosys-win32-vcxsrc-*
/yosysjs-*
/libyosys.so
# build directories
/tests/unit/bintest/
/tests/unit/objtest/
/tests/ystests
/build
/result
/dist
# pyosys
/kernel/*.pyh
/kernel/python_wrappers.cc
/ffi
/bison
/venv
/*.whl
/*.egg-info
# yosysjs dependency
/viz.js
# other
/coverage.info
/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
__pycache__
*~
.*.swp
/.cache
/.vscode
/.cproject
/.project
/.settings
/qtcreator.files
/qtcreator.includes
/qtcreator.config
/qtcreator.creator
/qtcreator.creator.user
/compile_commands.json

7
.gitmodules vendored Normal file
View file

@ -0,0 +1,7 @@
[submodule "abc"]
path = abc
url = https://github.com/YosysHQ/abc
# Don't use paths as names to avoid git archive problems
[submodule "cxxopts"]
path = libs/cxxopts
url = https://github.com/jarro2783/cxxopts

View file

@ -1,6 +1,6 @@
Marcelina Kościelnicka <mwk@0x04.net>
Marcelina Kościelnicka <mwk@0x04.net> <koriakin@0x04.net>
Marcelina Kościelnicka <mwk@0x04.net> <marcin@symbioticeda.com>
Wanda Phinode <wanda@phinode.net> <mwk@0x04.net>
Wanda Phinode <wanda@phinode.net> <koriakin@0x04.net>
Wanda Phinode <wanda@phinode.net> <marcin@symbioticeda.com>
Claire Xenia Wolf <claire@yosyshq.com> <claire@clairexen.net>
Claire Xenia Wolf <claire@yosyshq.com> <claire@symbioticeda.com>
Claire Xenia Wolf <claire@yosyshq.com> <clifford@symbioticeda.com>

20
.readthedocs.yaml Normal file
View file

@ -0,0 +1,20 @@
# .readthedocs.yaml
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
version: 2
build:
os: ubuntu-22.04
tools:
python: '3.12'
formats:
- pdf
sphinx:
configuration: docs/source/conf.py
fail_on_warning: true
python:
install:
- requirements: docs/source/requirements.txt

View file

@ -6,7 +6,9 @@ brew "git"
brew "graphviz"
brew "pkg-config"
brew "python3"
brew "tcl-tk"
brew "uv"
brew "xdot"
brew "bash"
brew 'boost-python3'
brew "llvm@20"
brew "lld"
brew "googletest"

346
CHANGELOG
View file

@ -2,9 +2,353 @@
List of major changes and improvements between releases
=======================================================
Yosys 0.38 .. Yosys 0.39-dev
Yosys 0.61 .. Yosys 0.62-dev
--------------------------
Yosys 0.60 .. Yosys 0.61
--------------------------
* Various
- Removed "cover" pass for coverage tracking.
- Avoid merging formal properties with "opt_merge" pass.
- Parallelize "opt_merge" pass.
* New commands and options
- Added "design_equal" pass to support fuzz-test comparison.
- Added "lut2bmux" pass to convert $lut to $bmux.
- Added "-legalize" option to "read_rtlil" pass to prevent
semantic errors.
Yosys 0.59 .. Yosys 0.60
--------------------------
* Various
- read_verilog: suport unsized parameters.
- Added static library compile option.
* New commands and options
- Added "sdc" pass for reading SDC files.
- Added experimental "sdc_expand" and "opensta" for OpenSTA integration.
- Added "icell_liberty" pass for used internal cells.
Yosys 0.58 .. Yosys 0.59
--------------------------
* Various
- Pyosys is rewritten using pybind11.
- alumacc: merge independent of sign.
- write_btor: Include $assert and $assume cells in -ywmap output.
- RTLIL parser rewritten for efficiency.
- Wildcards enabled for Liberty file consuming.
- timeest: Add top ports launching/sampling.
* New commands and options
- Added "-apply_derived_type" option to "box_derive" pass.
- Added "-publish_icells" option to "chtype" pass.
- Added "-width" option to "sim" pass.
- Added "sort" pass for sorting the design objects.
- Merged "synth_ecp5" and "synth_nexus" into "synth_lattice" pass.
- Added "-strict-gw5a-dffs" and "-setundef" options to "synth_gowin" pass.
Yosys 0.57 .. Yosys 0.58
--------------------------
* Various
- Run ABC passes in parallel.
- Extending support for buffer normalization.
- Overhaul of logging APIs.
- read_blif: Represent sequential elements with gate cells.
- Support multiple lib files in abc9_exe.
* New commands and options
- Added "-wireshape" option to "show" command to allow
control the shape of wire nodes.
- Added "-relativeshare" option to "read_verilog", "synth"
and "techmap" pass for synthesis reproducibility testing.
- "write_rtlil" pass no longer sorts design, added "-sort"
option to match old behavior
- Added "-sva-continue-on-err" to "verific" pass to allow
processing designs that includes unsupported SVA.
Yosys 0.56 .. Yosys 0.57
--------------------------
* New commands and options
- Added "-initstates" option to "abstract" pass.
- Added "-set-assumes" option to "equiv_induct"
and "equiv_simple" passes.
- Added "-always" option to "raise_error" pass.
- Added "-hierarchy" option to "stat" pass.
- Added "-noflatten" option to "synth_quicklogic" pass.
* Various
- smtbmc: Support skipping steps in cover mode.
- write_btor: support $buf.
- read_verilog: support package import.
Yosys 0.55 .. Yosys 0.56
--------------------------
* New commands and options
- Added "-unescape" option to "rename" pass.
- Added "-assert2cover" option to "chformal" pass.
- Added "linecoverage" pass to generate lcov report from selection.
- Added "opt_hier" pass to enable hierarchical optimization.
- Added "-hieropt" option to "synth" pass.
- Added "-expect-return", "-err-grep" and "-suffix" options
to "bugpoint" pass.
- Added "raise_error" dev pass.
* Various
- Added groups to command reference documentation.
- Added bugpoint guide to documentation.
- verific: correctly reset Verific flags after import.
Yosys 0.54 .. Yosys 0.55
--------------------------
* Various
- read_verilog: Implemented SystemVerilog unique/priority if.
- "attrmap" pass is able to alter memory attributes.
- verific: Support SVA followed-by operator in cover mode.
Yosys 0.53 .. Yosys 0.54
--------------------------
* New commands and options
- Added "-genlib" option to "abc_new" and "abc9_exe" passes.
- Added "-verbose" and "-quiet" options to "libcache" pass.
- Added "-no-sort" option to "write_aiger" pass.
* Various
- Added "muldiv_c" peepopt.
- Accept (and ignore) SystemVerilog unique/priority if.
- "read_verilog" copy inout ports in and out of functions/tasks.
- Enable single-bit vector wires in RTLIL.
* Xilinx support
- Single-port URAM mapping to support memories 2048 x 144b
Yosys 0.52 .. Yosys 0.53
--------------------------
* New commands and options
- Added "constmap" pass for technology mapping of coarse constant value.
- Added "timeest" pass to estimate the critical path in clock domain.
- Added "-blackbox" option to "cutpoint" pass to cut all instances of
blackboxes.
- Added "-noscopeinfo" option to "cutpoint" pass.
- Added "-nocleanup" option to "flatten" pass to prevent removal of
unused submodules.
- Added "-declockgate" option to "formalff" pass that turns clock
gating into clock enables.
* Various
- Added "$scopeinfo" cells to preserve information during "cutpoint" pass.
- Added dataflow tracking documentation.
- share: Restrict activation patterns to potentially relevant signal.
- liberty: More robust parsing.
- verific: bit blast RAM if using mem2reg attribute.
Yosys 0.51 .. Yosys 0.52
--------------------------
* New commands and options
- Added "-pattern-limit" option to "share" pass to limit analysis effort.
- Added "libcache" pass to control caching of technology library
data parsed from liberty files.
- Added "read_verilog_file_list" to parse verilog file list.
* Various
- Added $macc_v2 cell.
- Improve lexer performance and zlib support for "read_liberty".
- opt_expr: optimize pow of 2 cells.
Yosys 0.50 .. Yosys 0.51
--------------------------
* New commands and options
- Added "abstract" pass to allow reducing and never increasing
the constraints on a circuit's behavior in a formal verification setting.
* Various
- "splitcells" pass now splits "aldff" cells.
- FunctionalIR documentation
* QuickLogic support
- Added IOFF inference for qlf_k6n10f
* Intel support
- Fixed RAM and DSP support.
- Overall performance improvement for "synth_intel".
Yosys 0.49 .. Yosys 0.50
--------------------------
* Various
- "write_verilog" emits "$check" cell names as labels.
Yosys 0.48 .. Yosys 0.49
--------------------------
* Various
- "$scopeinfo" cells are now part of JSON export by default.
- Added option to specify hierarchical separator for "flatten".
- Improved "wreduce" to handle more cases of operator size reduction.
- Updated hashing interface, see docs/source/yosys_internals/hashing.rst
for breaking API changes.
* New commands and options
- Added "-noscopeinfo" option to "json" and "write_json" pass.
Yosys 0.47 .. Yosys 0.48
--------------------------
* Various
- Removed "read_ilang" deprecated pass.
- Enhanced boxing features in the experimental "abc_new" command.
- Added new Tcl methods for design inspection.
- Added clock enable inference to "dfflibmap".
- Added a Han-Carlson and Sklansky option for $lcu mapping.
* New commands and options
- Added "-nopeepopt" option to "clk2fflogic" pass.
- Added "-liberty" and "-dont_use" options to "clockgate" pass.
- Added "-ignore_buses" option to "read_liberty" pass.
- Added "-dont_map" option to "techmap" pass.
- Added "-selected" option to "write_json" pass.
- Added "wrapcell" command for creating wrapper modules
around selected cells.
- Added "portarcs" command for deriving propagation timing arcs.
- Added "setenv" command for setting environment variables.
* Gowin support
- Added "-family" option to "synth_gowin" pass.
- Cell definitions split by family.
* Verific support
- Improved blackbox support.
Yosys 0.46 .. Yosys 0.47
--------------------------
* Various
- Added cxxopts library for handling command line arguments.
- Added docs generation from cells help output.
* New commands and options
- Added "-json" option to "synth_xilinx" pass.
- Added "-derive_luts" option to "cellmatch" pass.
- Added "t:@<name>" syntax to "select" pass.
- Added "-list-mod" option to "select" pass.
- Removed deprecated "qwp" pass.
* Verific support
- Initial state handling for VHDL assertions.
Yosys 0.45 .. Yosys 0.46
--------------------------
* Various
- Added new "functional backend" infrastructure with three example
backends (C++, SMTLIB and Rosette).
- Added new coarse-grain buffer cell type "$buf" to RTLIL.
- Added "-y" command line option to execute a Python script with
libyosys available as a built-in module.
- Added support for casting to type in Verilog frontend.
* New commands and options
- Added "clockgate" pass for automatic clock gating cell insertion.
- Added "bufnorm" experimental pass to convert design into
buffered-normalized form.
- Added experimental "aiger2" and "xaiger2" backends, and an
experimental "abc_new" command
- Added "-force-detailed-loop-check" option to "check" pass.
- Added "-unit_delay" option to "read_liberty" pass.
* Verific support
- Added left and right bound properties to wires when using
specific VHDL types.
Yosys 0.44 .. Yosys 0.45
--------------------------
* Various
- Added cell types help messages.
* New back-ends
- Added initial NG-Ultra support. ( synth_nanoxplore )
Yosys 0.43 .. Yosys 0.44
--------------------------
* Various
- Added ENABLE_LTO compile option to enable link time
optimizations.
- Build support for Haiku OS.
* New commands and options
- Added "keep_hierarchy" pass to add attribute with
same name to modules based on cost.
- Added options "-noopt","-bloat" and "-check_cost" to
"test_cell" pass.
* New back-ends
- Added initial PolarFire support. ( synth_microchip )
Yosys 0.42 .. Yosys 0.43
--------------------------
* Various
- C++ compiler with C++17 support is required.
- Support for IO liberty files for verification.
- Limit padding from shiftadd for "peepopt" pass.
* Verific support
- Support building Yosys with various Verific library
configurations. Can be built now without YosysHQ
specific patch and extension library.
Yosys 0.41 .. Yosys 0.42
--------------------------
* New commands and options
- Added "box_derive" pass to derive box modules.
- Added option "assert-mod-count" to "select" pass.
- Added option "-header","-push" and "-pop" to "log" pass.
* Intel support
- Dropped Quartus support in "synth_intel_alm" pass.
Yosys 0.40 .. Yosys 0.41
--------------------------
* New commands and options
- Added "cellmatch" pass for picking out standard cells automatically.
* Various
- Extended the experimental incremental JSON API to allow arbitrary
smtlib subexpressions.
- Added support for using ABCs library merging when providing multiple
liberty files.
* Verific support
- Expose library name as module attribute.
Yosys 0.39 .. Yosys 0.40
--------------------------
* New commands and options
- Added option "-vhdl2019" to "read" and "verific" pass.
* Various
- Major documentation overhaul.
- Added port statistics to "stat" command.
- Added new formatting features to cxxrtl backend.
* Verific support
- Added better support for VHDL constants import.
- Added support for VHDL 2009.
Yosys 0.38 .. Yosys 0.39
--------------------------
* New commands and options
- Added option "-extra-map" to "synth" pass.
- Added option "-dont_use" to "dfflibmap" pass.
- Added option "-href" to "show" command.
- Added option "-noscopeinfo" to "flatten" pass.
- Added option "-scopename" to "flatten" pass.
* SystemVerilog
- Added support for packed multidimensional arrays.
* Various
- Added "$scopeinfo" cells to preserve information about
the hierarchy during flattening.
- Added sequential area output to "stat -liberty".
- Added ability to record/replay diagnostics in cxxrtl backend.
* Verific support
- Added attributes to module instantiation.
Yosys 0.37 .. Yosys 0.38
--------------------------
* New commands and options

View file

@ -10,6 +10,7 @@
# PATH (can use glob) USERNAME(S)
CODEOWNERS @nakengelhardt
passes/cmds/scratchpad.cc @nakengelhardt
frontends/rpc/ @whitequark
backends/cxxrtl/ @whitequark
@ -18,7 +19,9 @@ passes/techmap/flowmap.cc @whitequark
passes/opt/opt_lut.cc @whitequark
passes/techmap/abc9*.cc @eddiehung @Ravenslofty
backends/aiger/xaiger.cc @eddiehung
docs/ @KrystalDelusion
docs/source/using_yosys/synthesis/abc.rst @KrystalDelusion @Ravenslofty
.github/workflows/*.yml @mmicko
## External Contributors
# Only users with write permission to the repository get review
@ -27,15 +30,16 @@ backends/aiger/xaiger.cc @eddiehung
# These still override previous lines, so be careful not to
# accidentally disable any of the above rules.
frontends/verilog/ @zachjs
frontends/ast/ @zachjs
frontends/verilog/ @widlarizer
frontends/ast/ @widlarizer
techlibs/intel_alm/ @Ravenslofty
techlibs/gowin/ @pepijndevos
techlibs/gatemate/ @pu-cc
# pyosys
misc/*.py @btut
pyosys/* @donn
setup.py @donn
backends/firrtl @ucbjrl @azidar

128
CODE_OF_CONDUCT.md Normal file
View file

@ -0,0 +1,128 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at contact@yosyshq.com and/or
claire@clairexen.net.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

70
CONTRIBUTING.md Normal file
View file

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

View file

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

View file

@ -8,6 +8,7 @@ RUN apt-get update -qq \
&& DEBIAN_FRONTEND=noninteractive apt-get -y install --no-install-recommends \
ca-certificates \
clang \
lld \
curl \
libffi-dev \
libreadline-dev \

820
Makefile

File diff suppressed because it is too large Load diff

512
README.md
View file

@ -1,22 +1,3 @@
```
yosys -- Yosys Open SYnthesis Suite
Copyright (C) 2012 - 2024 Claire Xenia Wolf <claire@yosyshq.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
```
yosys Yosys Open SYnthesis Suite
===================================
@ -33,6 +14,10 @@ Yosys is free software licensed under the ISC license (a GPL
compatible license that is similar in terms to the MIT license
or the 2-clause BSD license).
Third-party software distributed alongside this software
is licensed under compatible licenses.
Please refer to `abc` and `libs` subdirectories for their license terms.
Web Site and Other Resources
============================
@ -40,17 +25,20 @@ Web Site and Other Resources
More information and documentation can be found on the Yosys web site:
- https://yosyshq.net/yosys/
The "Documentation" page on the web site contains links to more resources,
including a manual that even describes some of the Yosys internals:
- https://yosyshq.net/yosys/documentation.html
If you have any Yosys-related questions, please post them on the Discourse group:
- https://yosyshq.discourse.group
The directory `guidelines` contains additional information
for people interested in using the Yosys C++ APIs.
Documentation from this repository is automatically built and available on Read
the Docs:
- https://yosyshq.readthedocs.io/projects/yosys
Users interested in formal verification might want to use the formal verification
front-end for Yosys, SymbiYosys:
- https://symbiyosys.readthedocs.io/en/latest/
- https://github.com/YosysHQ/SymbiYosys
Users interested in formal verification might want to use the formal
verification front-end for Yosys, SBY:
- https://yosyshq.readthedocs.io/projects/sby/
- https://github.com/YosysHQ/sby
The Yosys blog has news and articles from users:
- https://blog.yosyshq.com
Installation
@ -68,51 +56,54 @@ For more information about the difference between Tabby CAD Suite and the OSS CA
Many Linux distributions also provide Yosys binaries, some more up to date than others. Check with your package manager!
Building from Source
====================
You need a C++ compiler with C++11 support (up-to-date CLANG or GCC is
For more details, and instructions for other platforms, check [building from
source](https://yosyshq.readthedocs.io/projects/yosys/en/latest/getting_started/installation.html#building-from-source)
on Read the Docs.
When cloning Yosys, some required libraries are included as git submodules. Make
sure to call e.g.
$ git clone --recurse-submodules https://github.com/YosysHQ/yosys.git
or
$ git clone https://github.com/YosysHQ/yosys.git
$ cd yosys
$ git submodule update --init --recursive
You need a C++ compiler with C++17 support (up-to-date CLANG or GCC is
recommended) and some standard tools such as GNU Flex, GNU Bison, and GNU Make.
TCL, readline and libffi are optional (see ``ENABLE_*`` settings in Makefile).
Xdot (graphviz) is used by the ``show`` command in yosys to display schematics.
For example on Ubuntu Linux 16.04 LTS the following commands will install all
For example on Ubuntu Linux 22.04 LTS the following commands will install all
prerequisites for building yosys:
$ sudo apt-get install build-essential clang bison flex \
libreadline-dev gawk tcl-dev libffi-dev git \
graphviz xdot pkg-config python3 libboost-system-dev \
libboost-python-dev libboost-filesystem-dev zlib1g-dev
$ sudo apt-get install gawk git make python3 lld bison clang flex \
libffi-dev libfl-dev libreadline-dev pkg-config tcl-dev zlib1g-dev \
graphviz xdot
$ curl -LsSf https://astral.sh/uv/install.sh | sh
Similarily, on Mac OS X Homebrew can be used to install dependencies (from within cloned yosys repository):
$ brew tap Homebrew/bundle && brew bundle
or MacPorts:
$ sudo port install bison flex readline gawk libffi \
git graphviz pkgconfig python36 boost zlib tcl
On FreeBSD use the following command to install all prerequisites:
# pkg install bison flex readline gawk libffi\
git graphviz pkgconf python3 python36 tcl-wrapper boost-libs
On FreeBSD system use gmake instead of make. To run tests use:
% MAKE=gmake CC=cc gmake test
For Cygwin use the following command to install all prerequisites, or select these additional packages:
setup-x86_64.exe -q --packages=bison,flex,gcc-core,gcc-g++,git,libffi-devel,libreadline-devel,make,pkg-config,python3,tcl-devel,boost-build,zlib-devel
To configure the build system to use a specific compiler, use one of
The environment variable `CXX` can be used to control the C++ compiler used, or
run one of the following to override it:
$ make config-clang
$ make config-gcc
For other compilers and build configurations it might be
necessary to make some changes to the config section of the
Makefile.
The Makefile has many variables influencing the build process. These can be
adjusted by modifying the Makefile.conf file which is created at the `make
config-...` step (see above), or they can be set by passing an option to the
make command directly:
$ make CXX=$CXX
For other compilers and build configurations it might be necessary to make some
changes to the config section of the Makefile. It's also an alternative way to
set the make variables mentioned above.
$ vi Makefile # ..or..
$ vi Makefile.conf
@ -122,10 +113,9 @@ To build Yosys simply type 'make' in this directory.
$ make
$ sudo make install
Note that this also downloads, builds and installs ABC (using yosys-abc
as executable name).
Tests are located in the tests subdirectory and can be executed using the test target. Note that you need gawk as well as a recent version of iverilog (i.e. build from git). Then, execute tests via:
Tests are located in the tests subdirectory and can be executed using the test
target. Note that you need gawk as well as a recent version of iverilog (i.e.
build from git). Then, execute tests via:
$ make test
@ -136,6 +126,7 @@ To use a separate (out-of-tree) build directory, provide a path to the Makefile.
Out-of-tree builds require a clean source tree.
Getting Started
===============
@ -240,365 +231,14 @@ The command ``prep`` provides a good default word-level synthesis script, as
used in SMT-based formal verification.
Unsupported Verilog-2005 Features
=================================
The following Verilog-2005 features are not supported by
Yosys and there are currently no plans to add support
for them:
- Non-synthesizable language features as defined in
IEC 62142(E):2005 / IEEE Std. 1364.1(E):2002
- The ``tri``, ``triand`` and ``trior`` net types
- The ``config`` and ``disable`` keywords and library map files
Verilog Attributes and non-standard features
============================================
- The ``full_case`` attribute on case statements is supported
(also the non-standard ``// synopsys full_case`` directive)
- The ``parallel_case`` attribute on case statements is supported
(also the non-standard ``// synopsys parallel_case`` directive)
- The ``// synopsys translate_off`` and ``// synopsys translate_on``
directives are also supported (but the use of ``` `ifdef .. `endif ```
is strongly recommended instead).
- The ``nomem2reg`` attribute on modules or arrays prohibits the
automatic early conversion of arrays to separate registers. This
is potentially dangerous. Usually the front-end has good reasons
for converting an array to a list of registers. Prohibiting this
step will likely result in incorrect synthesis results.
- The ``mem2reg`` attribute on modules or arrays forces the early
conversion of arrays to separate registers.
- The ``nomeminit`` attribute on modules or arrays prohibits the
creation of initialized memories. This effectively puts ``mem2reg``
on all memories that are written to in an ``initial`` block and
are not ROMs.
- The ``nolatches`` attribute on modules or always-blocks
prohibits the generation of logic-loops for latches. Instead
all not explicitly assigned values default to x-bits. This does
not affect clocked storage elements such as flip-flops.
- The ``nosync`` attribute on registers prohibits the generation of a
storage element. The register itself will always have all bits set
to 'x' (undefined). The variable may only be used as blocking assigned
temporary variable within an always block. This is mostly used internally
by Yosys to synthesize Verilog functions and access arrays.
- The ``nowrshmsk`` attribute on a register prohibits the generation of
shift-and-mask type circuits for writing to bit slices of that register.
- The ``onehot`` attribute on wires mark them as one-hot state register. This
is used for example for memory port sharing and set by the fsm_map pass.
- The ``blackbox`` attribute on modules is used to mark empty stub modules
that have the same ports as the real thing but do not contain information
on the internal configuration. This modules are only used by the synthesis
passes to identify input and output ports of cells. The Verilog backend
also does not output blackbox modules on default. ``read_verilog``, unless
called with ``-noblackbox`` will automatically set the blackbox attribute
on any empty module it reads.
- The ``noblackbox`` attribute set on an empty module prevents ``read_verilog``
from automatically setting the blackbox attribute on the module.
- The ``whitebox`` attribute on modules triggers the same behavior as
``blackbox``, but is for whitebox modules, i.e. library modules that
contain a behavioral model of the cell type.
- The ``lib_whitebox`` attribute overwrites ``whitebox`` when ``read_verilog``
is run in `-lib` mode. Otherwise it's automatically removed.
- The ``dynports`` attribute is used by the Verilog front-end to mark modules
that have ports with a width that depends on a parameter.
- The ``hdlname`` attribute is used by some passes to document the original
(HDL) name of a module when renaming a module. It should contain a single
name, or, when describing a hierarchical name in a flattened design, multiple
names separated by a single space character.
- The ``keep`` attribute on cells and wires is used to mark objects that should
never be removed by the optimizer. This is used for example for cells that
have hidden connections that are not part of the netlist, such as IO pads.
Setting the ``keep`` attribute on a module has the same effect as setting it
on all instances of the module.
- The ``keep_hierarchy`` attribute on cells and modules keeps the ``flatten``
command from flattening the indicated cells and modules.
- The ``init`` attribute on wires is set by the frontend when a register is
initialized "FPGA-style" with ``reg foo = val``. It can be used during
synthesis to add the necessary reset logic.
- The ``top`` attribute on a module marks this module as the top of the
design hierarchy. The ``hierarchy`` command sets this attribute when called
with ``-top``. Other commands, such as ``flatten`` and various backends
use this attribute to determine the top module.
- The ``src`` attribute is set on cells and wires created by to the string
``<hdl-file-name>:<line-number>`` by the HDL front-end and is then carried
through the synthesis. When entities are combined, a new |-separated
string is created that contains all the string from the original entities.
- The ``defaultvalue`` attribute is used to store default values for
module inputs. The attribute is attached to the input wire by the HDL
front-end when the input is declared with a default value.
- The ``parameter`` and ``localparam`` attributes are used to mark wires
that represent module parameters or localparams (when the HDL front-end
is run in ``-pwires`` mode).
- Wires marked with the ``hierconn`` attribute are connected to wires with the
same name (format ``cell_name.identifier``) when they are imported from
sub-modules by ``flatten``.
- The ``clkbuf_driver`` attribute can be set on an output port of a blackbox
module to mark it as a clock buffer output, and thus prevent ``clkbufmap``
from inserting another clock buffer on a net driven by such output.
- The ``clkbuf_sink`` attribute can be set on an input port of a module to
request clock buffer insertion by the ``clkbufmap`` pass.
- The ``clkbuf_inv`` attribute can be set on an output port of a module
with the value set to the name of an input port of that module. When
the ``clkbufmap`` would otherwise insert a clock buffer on this output,
it will instead try inserting the clock buffer on the input port (this
is used to implement clock inverter cells that clock buffer insertion
will "see through").
- The ``clkbuf_inhibit`` is the default attribute to set on a wire to prevent
automatic clock buffer insertion by ``clkbufmap``. This behaviour can be
overridden by providing a custom selection to ``clkbufmap``.
- The ``invertible_pin`` attribute can be set on a port to mark it as
invertible via a cell parameter. The name of the inversion parameter
is specified as the value of this attribute. The value of the inversion
parameter must be of the same width as the port, with 1 indicating
an inverted bit and 0 indicating a non-inverted bit.
- The ``iopad_external_pin`` attribute on a blackbox module's port marks
it as the external-facing pin of an I/O pad, and prevents ``iopadmap``
from inserting another pad cell on it.
- The module attribute ``abc9_lut`` is an integer attribute indicating to
`abc9` that this module describes a LUT with an area cost of this value, and
propagation delays described using `specify` statements.
- The module attribute ``abc9_box`` is a boolean specifying a black/white-box
definition, with propagation delays described using `specify` statements, for
use by `abc9`.
- The port attribute ``abc9_carry`` marks the carry-in (if an input port) and
carry-out (if output port) ports of a box. This information is necessary for
`abc9` to preserve the integrity of carry-chains. Specifying this attribute
onto a bus port will affect only its most significant bit.
- The module attribute ``abc9_flop`` is a boolean marking the module as a
flip-flop. This allows `abc9` to analyse its contents in order to perform
sequential synthesis.
- The frontend sets attributes ``always_comb``, ``always_latch`` and
``always_ff`` on processes derived from SystemVerilog style always blocks
according to the type of the always. These are checked for correctness in
``proc_dlatch``.
- The cell attribute ``wildcard_port_conns`` represents wildcard port
connections (SystemVerilog ``.*``). These are resolved to concrete
connections to matching wires in ``hierarchy``.
- In addition to the ``(* ... *)`` attribute syntax, Yosys supports
the non-standard ``{* ... *}`` attribute syntax to set default attributes
for everything that comes after the ``{* ... *}`` statement. (Reset
by adding an empty ``{* *}`` statement.)
- In module parameter and port declarations, and cell port and parameter
lists, a trailing comma is ignored. This simplifies writing Verilog code
generators a bit in some cases.
- Modules can be declared with ``module mod_name(...);`` (with three dots
instead of a list of module ports). With this syntax it is sufficient
to simply declare a module port as 'input' or 'output' in the module
body.
- When defining a macro with `define, all text between triple double quotes
is interpreted as macro body, even if it contains unescaped newlines. The
triple double quotes are removed from the macro body. For example:
`define MY_MACRO(a, b) """
assign a = 23;
assign b = 42;
"""
- The attribute ``via_celltype`` can be used to implement a Verilog task or
function by instantiating the specified cell type. The value is the name
of the cell type to use. For functions the name of the output port can
be specified by appending it to the cell type separated by a whitespace.
The body of the task or function is unused in this case and can be used
to specify a behavioral model of the cell type for simulation. For example:
module my_add3(A, B, C, Y);
parameter WIDTH = 8;
input [WIDTH-1:0] A, B, C;
output [WIDTH-1:0] Y;
...
endmodule
module top;
...
(* via_celltype = "my_add3 Y" *)
(* via_celltype_defparam_WIDTH = 32 *)
function [31:0] add3;
input [31:0] A, B, C;
begin
add3 = A + B + C;
end
endfunction
...
endmodule
- The ``wiretype`` attribute is added by the verilog parser for wires of a
typedef'd type to indicate the type identifier.
- Various ``enum_value_{value}`` attributes are added to wires of an enumerated type
to give a map of possible enum items to their values.
- The ``enum_base_type`` attribute is added to enum items to indicate which
enum they belong to (enums -- anonymous and otherwise -- are
automatically named with an auto-incrementing counter). Note that enums
are currently not strongly typed.
- A limited subset of DPI-C functions is supported. The plugin mechanism
(see ``help plugin``) can be used to load .so files with implementations
of DPI-C routines. As a non-standard extension it is possible to specify
a plugin alias using the ``<alias>:`` syntax. For example:
module dpitest;
import "DPI-C" function foo:round = real my_round (real);
parameter real r = my_round(12.345);
endmodule
$ yosys -p 'plugin -a foo -i /lib/libm.so; read_verilog dpitest.v'
- Sized constants (the syntax ``<size>'s?[bodh]<value>``) support constant
expressions as ``<size>``. If the expression is not a simple identifier, it
must be put in parentheses. Examples: ``WIDTH'd42``, ``(4+2)'b101010``
- The system tasks ``$finish``, ``$stop`` and ``$display`` are supported in
initial blocks in an unconditional context (only if/case statements on
expressions over parameters and constant values are allowed). The intended
use for this is synthesis-time DRC.
- There is limited support for converting ``specify`` .. ``endspecify``
statements to special ``$specify2``, ``$specify3``, and ``$specrule`` cells,
for use in blackboxes and whiteboxes. Use ``read_verilog -specify`` to
enable this functionality. (By default these blocks are ignored.)
- The ``reprocess_after`` internal attribute is used by the Verilog frontend to
mark cells with bindings which might depend on the specified instantiated
module. Modules with such cells will be reprocessed during the ``hierarchy``
pass once the referenced module definition(s) become available.
- The ``smtlib2_module`` attribute can be set on a blackbox module to specify a
formal model directly using SMT-LIB 2. For such a module, the
``smtlib2_comb_expr`` attribute can be used on output ports to define their
value using an SMT-LIB 2 expression. For example:
(* blackbox *)
(* smtlib2_module *)
module submod(a, b);
input [7:0] a;
(* smtlib2_comb_expr = "(bvnot a)" *)
output [7:0] b;
endmodule
Non-standard or SystemVerilog features for formal verification
==============================================================
- Support for ``assert``, ``assume``, ``restrict``, and ``cover`` is enabled
when ``read_verilog`` is called with ``-formal``.
- The system task ``$initstate`` evaluates to 1 in the initial state and
to 0 otherwise.
- The system function ``$anyconst`` evaluates to any constant value. This is
equivalent to declaring a reg as ``rand const``, but also works outside
of checkers. (Yosys also supports ``rand const`` outside checkers.)
- The system function ``$anyseq`` evaluates to any value, possibly a different
value in each cycle. This is equivalent to declaring a reg as ``rand``,
but also works outside of checkers. (Yosys also supports ``rand``
variables outside checkers.)
- The system functions ``$allconst`` and ``$allseq`` can be used to construct
formal exist-forall problems. Assumptions only hold if the trace satisfies
the assumption for all ``$allconst/$allseq`` values. For assertions and cover
statements it is sufficient if just one ``$allconst/$allseq`` value triggers
the property (similar to ``$anyconst/$anyseq``).
- Wires/registers declared using the ``anyconst/anyseq/allconst/allseq`` attribute
(for example ``(* anyconst *) reg [7:0] foobar;``) will behave as if driven
by a ``$anyconst/$anyseq/$allconst/$allseq`` function.
- The SystemVerilog tasks ``$past``, ``$stable``, ``$rose`` and ``$fell`` are
supported in any clocked block.
- The syntax ``@($global_clock)`` can be used to create FFs that have no
explicit clock input (``$ff`` cells). The same can be achieved by using
``@(posedge <netname>)`` or ``@(negedge <netname>)`` when ``<netname>``
is marked with the ``(* gclk *)`` Verilog attribute.
Supported features from SystemVerilog
=====================================
When ``read_verilog`` is called with ``-sv``, it accepts some language features
from SystemVerilog:
- The ``assert`` statement from SystemVerilog is supported in its most basic
form. In module context: ``assert property (<expression>);`` and within an
always block: ``assert(<expression>);``. It is transformed to an ``$assert`` cell.
- The ``assume``, ``restrict``, and ``cover`` statements from SystemVerilog are
also supported. The same limitations as with the ``assert`` statement apply.
- The keywords ``always_comb``, ``always_ff`` and ``always_latch``, ``logic``
and ``bit`` are supported.
- Declaring free variables with ``rand`` and ``rand const`` is supported.
- Checkers without a port list that do not need to be instantiated (but instead
behave like a named block) are supported.
- SystemVerilog packages are supported. Once a SystemVerilog file is read
into a design with ``read_verilog``, all its packages are available to
SystemVerilog files being read into the same design afterwards.
- typedefs are supported (including inside packages)
- type casts are currently not supported
- enums are supported (including inside packages)
- but are currently not strongly typed
- packed structs and unions are supported
- arrays of packed structs/unions are currently not supported
- structure literals are currently not supported
- multidimensional arrays are supported
- array assignment of unpacked arrays is currently not supported
- array literals are currently not supported
- SystemVerilog interfaces (SVIs) are supported. Modports for specifying whether
ports are inputs or outputs are supported.
- Assignments within expressions are supported.
Additional information
======================
The ``read_verilog`` command, used by default when calling ``read`` with Verilog
source input, does not perform syntax checking. You should instead lint your
source with another tool such as
[Verilator](https://www.veripool.org/verilator/) first, e.g. by calling
``verilator --lint-only``.
Building the documentation
@ -608,12 +248,24 @@ Note that there is no need to build the manual if you just want to read it.
Simply visit https://yosys.readthedocs.io/en/latest/ instead.
In addition to those packages listed above for building Yosys from source, the
following are used for building the website:
following are used for building the website:
$ sudo apt-get install pdf2svg faketime
$ sudo apt install pdf2svg faketime
Or for MacOS, using homebrew:
$ brew install pdf2svg libfaketime
PDFLaTeX, included with most LaTeX distributions, is also needed during the
build process for the website.
build process for the website. Or, run the following:
$ sudo apt install texlive-latex-base texlive-latex-extra latexmk
Or for MacOS, using homebrew:
$ brew install basictex
$ sudo tlmgr update --self
$ sudo tlmgr install collection-latexextra latexmk tex-gyre
The Python package, Sphinx, is needed along with those listed in
`docs/source/requirements.txt`:
@ -622,5 +274,13 @@ The Python package, Sphinx, is needed along with those listed in
From the root of the repository, run `make docs`. This will build/rebuild yosys
as necessary before generating the website documentation from the yosys help
commands. To build for pdf instead of html, call
commands. To build for pdf instead of html, call
`make docs DOC_TARGET=latexpdf`.
It is recommended to use the `ENABLE_HELP_SOURCE` make option for Yosys builds
that will be used to build the documentation. This option enables source
location tracking for passes and improves the command reference through grouping
related commands and allowing for the documentation to link to the corresponding
source files. Without this, a warning will be raised during the Sphinx build
about `Found commands assigned to group unknown` and `make docs` is configured
to fail on warnings by default.

1
abc Submodule

@ -0,0 +1 @@
Subproject commit 799ba632239b2a4db2bacda81de4e6efdc486b0c

View file

@ -132,7 +132,7 @@ struct AigerWriter
return a;
}
AigerWriter(Module *module, bool zinit_mode, bool imode, bool omode, bool bmode, bool lmode) : module(module), zinit_mode(zinit_mode), sigmap(module)
AigerWriter(Module *module, bool no_sort, bool zinit_mode, bool imode, bool omode, bool bmode, bool lmode) : module(module), zinit_mode(zinit_mode), sigmap(module)
{
pool<SigBit> undriven_bits;
pool<SigBit> unused_bits;
@ -152,6 +152,37 @@ struct AigerWriter
if (wire->port_input)
sigmap.add(wire);
// handle ports
// provided the input_bits and output_bits don't get sorted they
// will be returned in reverse order, so add them in reverse to
// match
for (auto riter = module->ports.rbegin(); riter != module->ports.rend(); ++riter) {
auto *wire = module->wire(*riter);
for (int i = 0; i < GetSize(wire); i++)
{
SigBit wirebit(wire, i);
SigBit bit = sigmap(wirebit);
if (bit.wire == nullptr) {
if (wire->port_output) {
aig_map[wirebit] = (bit == State::S1) ? 1 : 0;
output_bits.insert(wirebit);
}
continue;
}
if (wire->port_input)
input_bits.insert(bit);
if (wire->port_output) {
if (bit != wirebit)
alias_map[wirebit] = bit;
output_bits.insert(wirebit);
}
}
}
// handle wires
for (auto wire : module->wires())
{
if (wire->attributes.count(ID::init)) {
@ -167,25 +198,13 @@ struct AigerWriter
SigBit wirebit(wire, i);
SigBit bit = sigmap(wirebit);
if (bit.wire == nullptr) {
if (wire->port_output) {
aig_map[wirebit] = (bit == State::S1) ? 1 : 0;
output_bits.insert(wirebit);
}
if (bit.wire == nullptr)
continue;
if (wire->port_input || wire->port_output)
continue;
}
undriven_bits.insert(bit);
unused_bits.insert(bit);
if (wire->port_input)
input_bits.insert(bit);
if (wire->port_output) {
if (bit != wirebit)
alias_map[wirebit] = bit;
output_bits.insert(wirebit);
}
}
if (wire->width == 1) {
@ -200,12 +219,6 @@ struct AigerWriter
}
}
for (auto bit : input_bits)
undriven_bits.erase(bit);
for (auto bit : output_bits)
unused_bits.erase(bit);
for (auto cell : module->cells())
{
if (cell->type == ID($_NOT_))
@ -343,8 +356,11 @@ struct AigerWriter
}
init_map.sort();
input_bits.sort();
output_bits.sort();
// we are relying here on unsorted pools iterating last-in-first-out
if (!no_sort) {
input_bits.sort();
output_bits.sort();
}
not_map.sort();
ff_map.sort();
and_map.sort();
@ -662,8 +678,7 @@ struct AigerWriter
f << std::endl;
}
}
f << stringf("c\nGenerated by %s\n", yosys_version_str);
f << stringf("c\nGenerated by %s\n", yosys_maybe_version());
}
void write_map(std::ostream &f, bool verbose_map, bool no_startoffset)
@ -698,7 +713,7 @@ struct AigerWriter
}
if (wire->port_output) {
int o = ordered_outputs.at(sig[i]);
int o = ordered_outputs.at(SigSpec(wire, i));
output_lines[o] += stringf("output %d %d %s\n", o, index, log_id(wire));
}
@ -746,7 +761,7 @@ struct AigerWriter
{
json.begin_object();
json.entry("version", "Yosys Witness Aiger map");
json.entry("gennerator", yosys_version_str);
json.entry("gennerator", yosys_maybe_version());
json.entry("latch_count", aig_l);
json.entry("input_count", aig_i);
@ -902,6 +917,9 @@ struct AigerBackend : public Backend {
log(" -symbols\n");
log(" include a symbol table in the generated AIGER file\n");
log("\n");
log(" -no-sort\n");
log(" don't sort input/output ports\n");
log("\n");
log(" -map <filename>\n");
log(" write an extra file with port and latch symbols\n");
log("\n");
@ -926,6 +944,7 @@ struct AigerBackend : public Backend {
bool zinit_mode = false;
bool miter_mode = false;
bool symbols_mode = false;
bool no_sort = false;
bool verbose_map = false;
bool imode = false;
bool omode = false;
@ -956,6 +975,10 @@ struct AigerBackend : public Backend {
symbols_mode = true;
continue;
}
if (args[argidx] == "-no-sort") {
no_sort = true;
continue;
}
if (map_filename.empty() && args[argidx] == "-map" && argidx+1 < args.size()) {
map_filename = args[++argidx];
continue;
@ -1009,7 +1032,7 @@ struct AigerBackend : public Backend {
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));
AigerWriter writer(top_module, zinit_mode, imode, omode, bmode, lmode);
AigerWriter writer(top_module, no_sort, zinit_mode, imode, omode, bmode, lmode);
writer.write_aiger(*f, ascii_mode, miter_mode, symbols_mode);
if (!map_filename.empty()) {
@ -1017,7 +1040,7 @@ struct AigerBackend : public Backend {
std::ofstream mapf;
mapf.open(map_filename.c_str(), std::ofstream::trunc);
if (mapf.fail())
log_error("Can't open file `%s' for writing: %s\n", map_filename.c_str(), strerror(errno));
log_error("Can't open file `%s' for writing: %s\n", map_filename, strerror(errno));
writer.write_map(mapf, verbose_map, no_startoffset);
}
@ -1028,7 +1051,7 @@ struct AigerBackend : public Backend {
PrettyJson json;
if (!json.write_to_file(yw_map_filename))
log_error("Can't open file `%s' for writing: %s\n", yw_map_filename.c_str(), strerror(errno));
log_error("Can't open file `%s' for writing: %s\n", yw_map_filename, strerror(errno));
writer.write_ywmap(json);
}
}

View file

@ -18,32 +18,6 @@
*
*/
// https://stackoverflow.com/a/46137633
#ifdef _MSC_VER
#include <stdlib.h>
#define bswap32 _byteswap_ulong
#elif defined(__APPLE__)
#include <libkern/OSByteOrder.h>
#define bswap32 OSSwapInt32
#elif defined(__GNUC__)
#define bswap32 __builtin_bswap32
#else
#include <cstdint>
inline static uint32_t bswap32(uint32_t x)
{
// https://stackoverflow.com/a/27796212
register uint32_t value = number_to_be_reversed;
uint8_t lolo = (value >> 0) & 0xFF;
uint8_t lohi = (value >> 8) & 0xFF;
uint8_t hilo = (value >> 16) & 0xFF;
uint8_t hihi = (value >> 24) & 0xFF;
return (hihi << 24)
| (hilo << 16)
| (lohi << 8)
| (lolo << 0);
}
#endif
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
#include "kernel/utils.h"
@ -52,16 +26,6 @@ inline static uint32_t bswap32(uint32_t x)
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
inline int32_t to_big_endian(int32_t i32) {
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
return bswap32(i32);
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
return i32;
#else
#error "Unknown endianness"
#endif
}
void aiger_encode(std::ostream &f, int x)
{
log_assert(x >= 0);
@ -89,6 +53,8 @@ struct XAigerWriter
dict<SigBit, float> arrival_times;
vector<pair<int, int>> aig_gates;
vector<SigBit> bit2aig_stack;
int next_loop_check = 1024;
vector<int> aig_outputs;
int aig_m = 0, aig_i = 0, aig_l = 0, aig_o = 0, aig_a = 0;
@ -112,6 +78,24 @@ struct XAigerWriter
return it->second;
}
if (GetSize(bit2aig_stack)== next_loop_check) {
for (int i = 0; i < next_loop_check; ++i)
{
SigBit report_bit = bit2aig_stack[i];
if (report_bit != bit)
continue;
for (int j = i; j < next_loop_check; ++j) {
report_bit = bit2aig_stack[j];
if (report_bit.is_wire() && report_bit.wire->name.isPublic())
break;
}
log_error("Found combinatorial logic loop while processing signal %s.\n", log_signal(report_bit));
}
next_loop_check *= 2;
}
bit2aig_stack.push_back(bit);
// NB: Cannot use iterator returned from aig_map.insert()
// since this function is called recursively
@ -129,6 +113,8 @@ struct XAigerWriter
a = bit2aig(alias_map.at(bit));
}
bit2aig_stack.pop_back();
if (bit == State::Sx || bit == State::Sz) {
log_debug("Design contains 'x' or 'z' bits. Treating as 1'b0.\n");
a = aig_map.at(State::S0);
@ -537,9 +523,12 @@ struct XAigerWriter
f << "c";
auto write_buffer = [](std::stringstream &buffer, int i32) {
int32_t i32_be = to_big_endian(i32);
buffer.write(reinterpret_cast<const char*>(&i32_be), sizeof(i32_be));
auto write_buffer = [](std::ostream &buffer, unsigned int u32) {
typedef unsigned char uchar;
unsigned char u32_be[4] = {
(uchar) (u32 >> 24), (uchar) (u32 >> 16), (uchar) (u32 >> 8), (uchar) u32
};
buffer.write((char *) u32_be, sizeof(u32_be));
};
std::stringstream h_buffer;
auto write_h_buffer = std::bind(write_buffer, std::ref(h_buffer), std::placeholders::_1);
@ -640,14 +629,12 @@ struct XAigerWriter
f << "r";
std::string buffer_str = r_buffer.str();
int32_t buffer_size_be = to_big_endian(buffer_str.size());
f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be));
write_buffer(f, buffer_str.size());
f.write(buffer_str.data(), buffer_str.size());
f << "s";
buffer_str = s_buffer.str();
buffer_size_be = to_big_endian(buffer_str.size());
f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be));
write_buffer(f, buffer_str.size());
f.write(buffer_str.data(), buffer_str.size());
RTLIL::Design *holes_design;
@ -664,22 +651,19 @@ struct XAigerWriter
f << "a";
std::string buffer_str = a_buffer.str();
int32_t buffer_size_be = to_big_endian(buffer_str.size());
f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be));
write_buffer(f, buffer_str.size());
f.write(buffer_str.data(), buffer_str.size());
}
}
f << "h";
std::string buffer_str = h_buffer.str();
int32_t buffer_size_be = to_big_endian(buffer_str.size());
f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be));
write_buffer(f, buffer_str.size());
f.write(buffer_str.data(), buffer_str.size());
f << "i";
buffer_str = i_buffer.str();
buffer_size_be = to_big_endian(buffer_str.size());
f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be));
write_buffer(f, buffer_str.size());
f.write(buffer_str.data(), buffer_str.size());
//f << "o";
//buffer_str = o_buffer.str();
@ -687,7 +671,7 @@ struct XAigerWriter
//f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be));
//f.write(buffer_str.data(), buffer_str.size());
f << stringf("Generated by %s\n", yosys_version_str);
f << stringf("Generated by %s\n", yosys_maybe_version());
design->scratchpad_set_int("write_xaiger.num_ands", and_map.size());
design->scratchpad_set_int("write_xaiger.num_wires", aig_map.size());
@ -804,7 +788,7 @@ struct XAigerBackend : public Backend {
std::ofstream mapf;
mapf.open(map_filename.c_str(), std::ofstream::trunc);
if (mapf.fail())
log_error("Can't open file `%s' for writing: %s\n", map_filename.c_str(), strerror(errno));
log_error("Can't open file `%s' for writing: %s\n", map_filename, strerror(errno));
writer.write_map(mapf);
}
}

View file

@ -0,0 +1 @@
OBJS += backends/aiger2/aiger.o

1488
backends/aiger2/aiger.cc Normal file

File diff suppressed because it is too large Load diff

View file

@ -157,14 +157,14 @@ struct BlifDumper
f << stringf("%c", ch);
f << stringf("\"\n");
} else
f << stringf("%s\n", param.second.as_string().c_str());
f << stringf("%s\n", param.second.as_string());
}
}
void dump()
{
f << stringf("\n");
f << stringf(".model %s\n", str(module->name).c_str());
f << stringf(".model %s\n", str(module->name));
std::map<int, RTLIL::Wire*> inputs, outputs;
@ -179,7 +179,7 @@ struct BlifDumper
for (auto &it : inputs) {
RTLIL::Wire *wire = it.second;
for (int i = 0; i < wire->width; i++)
f << stringf(" %s", str(RTLIL::SigSpec(wire, i)).c_str());
f << stringf(" %s", str(RTLIL::SigSpec(wire, i)));
}
f << stringf("\n");
@ -187,7 +187,7 @@ struct BlifDumper
for (auto &it : outputs) {
RTLIL::Wire *wire = it.second;
for (int i = 0; i < wire->width; i++)
f << stringf(" %s", str(RTLIL::SigSpec(wire, i)).c_str());
f << stringf(" %s", str(RTLIL::SigSpec(wire, i)));
}
f << stringf("\n");
@ -200,7 +200,7 @@ struct BlifDumper
if (!config->impltf_mode) {
if (!config->false_type.empty()) {
if (config->false_type == "+")
f << stringf(".names %s\n", config->false_out.c_str());
f << stringf(".names %s\n", config->false_out);
else if (config->false_type != "-")
f << stringf(".%s %s %s=$false\n", subckt_or_gate(config->false_type),
config->false_type.c_str(), config->false_out.c_str());
@ -208,7 +208,7 @@ struct BlifDumper
f << stringf(".names $false\n");
if (!config->true_type.empty()) {
if (config->true_type == "+")
f << stringf(".names %s\n1\n", config->true_out.c_str());
f << stringf(".names %s\n1\n", config->true_out);
else if (config->true_type != "-")
f << stringf(".%s %s %s=$true\n", subckt_or_gate(config->true_type),
config->true_type.c_str(), config->true_out.c_str());
@ -216,7 +216,7 @@ struct BlifDumper
f << stringf(".names $true\n1\n");
if (!config->undef_type.empty()) {
if (config->undef_type == "+")
f << stringf(".names %s\n", config->undef_out.c_str());
f << stringf(".names %s\n", config->undef_out);
else if (config->undef_type != "-")
f << stringf(".%s %s %s=$undef\n", subckt_or_gate(config->undef_type),
config->undef_type.c_str(), config->undef_out.c_str());
@ -331,31 +331,31 @@ struct BlifDumper
}
if (!config->icells_mode && cell->type == ID($_FF_)) {
f << stringf(".latch %s %s%s\n", str(cell->getPort(ID::D)).c_str(), str(cell->getPort(ID::Q)).c_str(),
f << stringf(".latch %s %s%s\n", str(cell->getPort(ID::D)), str(cell->getPort(ID::Q)),
str_init(cell->getPort(ID::Q)).c_str());
goto internal_cell;
}
if (!config->icells_mode && cell->type == ID($_DFF_N_)) {
f << stringf(".latch %s %s fe %s%s\n", str(cell->getPort(ID::D)).c_str(), str(cell->getPort(ID::Q)).c_str(),
f << stringf(".latch %s %s fe %s%s\n", str(cell->getPort(ID::D)), str(cell->getPort(ID::Q)),
str(cell->getPort(ID::C)).c_str(), str_init(cell->getPort(ID::Q)).c_str());
goto internal_cell;
}
if (!config->icells_mode && cell->type == ID($_DFF_P_)) {
f << stringf(".latch %s %s re %s%s\n", str(cell->getPort(ID::D)).c_str(), str(cell->getPort(ID::Q)).c_str(),
f << stringf(".latch %s %s re %s%s\n", str(cell->getPort(ID::D)), str(cell->getPort(ID::Q)),
str(cell->getPort(ID::C)).c_str(), str_init(cell->getPort(ID::Q)).c_str());
goto internal_cell;
}
if (!config->icells_mode && cell->type == ID($_DLATCH_N_)) {
f << stringf(".latch %s %s al %s%s\n", str(cell->getPort(ID::D)).c_str(), str(cell->getPort(ID::Q)).c_str(),
f << stringf(".latch %s %s al %s%s\n", str(cell->getPort(ID::D)), str(cell->getPort(ID::Q)),
str(cell->getPort(ID::E)).c_str(), str_init(cell->getPort(ID::Q)).c_str());
goto internal_cell;
}
if (!config->icells_mode && cell->type == ID($_DLATCH_P_)) {
f << stringf(".latch %s %s ah %s%s\n", str(cell->getPort(ID::D)).c_str(), str(cell->getPort(ID::Q)).c_str(),
f << stringf(".latch %s %s ah %s%s\n", str(cell->getPort(ID::D)), str(cell->getPort(ID::Q)),
str(cell->getPort(ID::E)).c_str(), str_init(cell->getPort(ID::Q)).c_str());
goto internal_cell;
}
@ -366,10 +366,10 @@ struct BlifDumper
auto width = cell->parameters.at(ID::WIDTH).as_int();
log_assert(inputs.size() == width);
for (int i = width-1; i >= 0; i--)
f << stringf(" %s", str(inputs.extract(i, 1)).c_str());
f << stringf(" %s", str(inputs.extract(i, 1)));
auto &output = cell->getPort(ID::Y);
log_assert(output.size() == 1);
f << stringf(" %s", str(output).c_str());
f << stringf(" %s", str(output));
f << stringf("\n");
RTLIL::SigSpec mask = cell->parameters.at(ID::LUT);
for (int i = 0; i < (1 << width); i++)
@ -387,15 +387,15 @@ struct BlifDumper
auto &inputs = cell->getPort(ID::A);
auto width = cell->parameters.at(ID::WIDTH).as_int();
auto depth = cell->parameters.at(ID::DEPTH).as_int();
vector<State> table = cell->parameters.at(ID::TABLE).bits;
vector<State> table = cell->parameters.at(ID::TABLE).to_bits();
while (GetSize(table) < 2*width*depth)
table.push_back(State::S0);
log_assert(inputs.size() == width);
for (int i = 0; i < width; i++)
f << stringf(" %s", str(inputs.extract(i, 1)).c_str());
f << stringf(" %s", str(inputs.extract(i, 1)));
auto &output = cell->getPort(ID::Y);
log_assert(output.size() == 1);
f << stringf(" %s", str(output).c_str());
f << stringf(" %s", str(output));
f << stringf("\n");
for (int i = 0; i < depth; i++) {
for (int j = 0; j < width; j++) {
@ -410,11 +410,11 @@ struct BlifDumper
goto internal_cell;
}
f << stringf(".%s %s", subckt_or_gate(cell->type.str()), str(cell->type).c_str());
f << stringf(".%s %s", subckt_or_gate(cell->type.str()), str(cell->type));
for (auto &conn : cell->connections())
{
if (conn.second.size() == 1) {
f << stringf(" %s=%s", str(conn.first).c_str(), str(conn.second[0]).c_str());
f << stringf(" %s=%s", str(conn.first), str(conn.second[0]));
continue;
}
@ -423,11 +423,11 @@ struct BlifDumper
if (w == nullptr) {
for (int i = 0; i < GetSize(conn.second); i++)
f << stringf(" %s[%d]=%s", str(conn.first).c_str(), i, str(conn.second[i]).c_str());
f << stringf(" %s[%d]=%s", str(conn.first), i, str(conn.second[i]));
} else {
for (int i = 0; i < std::min(GetSize(conn.second), GetSize(w)); i++) {
SigBit sig(w, i);
f << stringf(" %s[%d]=%s", str(conn.first).c_str(), sig.wire->upto ?
f << stringf(" %s[%d]=%s", str(conn.first), sig.wire->upto ?
sig.wire->start_offset+sig.wire->width-sig.offset-1 :
sig.wire->start_offset+sig.offset, str(conn.second[i]).c_str());
}
@ -436,7 +436,7 @@ struct BlifDumper
f << stringf("\n");
if (config->cname_mode)
f << stringf(".cname %s\n", str(cell->name).c_str());
f << stringf(".cname %s\n", str(cell->name));
if (config->attr_mode)
dump_params(".attr", cell->attributes);
if (config->param_mode)
@ -445,7 +445,7 @@ struct BlifDumper
if (0) {
internal_cell:
if (config->iname_mode)
f << stringf(".cname %s\n", str(cell->name).c_str());
f << stringf(".cname %s\n", str(cell->name));
if (config->iattr_mode)
dump_params(".attr", cell->attributes);
}
@ -461,12 +461,12 @@ struct BlifDumper
continue;
if (config->conn_mode)
f << stringf(".conn %s %s\n", str(rhs_bit).c_str(), str(lhs_bit).c_str());
f << stringf(".conn %s %s\n", str(rhs_bit), str(lhs_bit));
else if (!config->buf_type.empty())
f << stringf(".%s %s %s=%s %s=%s\n", subckt_or_gate(config->buf_type), config->buf_type.c_str(),
f << stringf(".%s %s %s=%s %s=%s\n", subckt_or_gate(config->buf_type), config->buf_type,
config->buf_in.c_str(), str(rhs_bit).c_str(), config->buf_out.c_str(), str(lhs_bit).c_str());
else
f << stringf(".names %s %s\n1 1\n", str(rhs_bit).c_str(), str(lhs_bit).c_str());
f << stringf(".names %s %s\n1 1\n", str(rhs_bit), str(lhs_bit));
}
f << stringf(".end\n");
@ -649,7 +649,7 @@ struct BlifBackend : public Backend {
if (module->get_bool_attribute(ID::top))
top_module_name = module->name.str();
*f << stringf("# Generated by %s\n", yosys_version_str);
*f << stringf("# Generated by %s\n", yosys_maybe_version());
std::vector<RTLIL::Module*> mod_list;
@ -674,7 +674,7 @@ struct BlifBackend : public Backend {
}
if (!top_module_name.empty())
log_error("Can't find top module `%s'!\n", top_module_name.c_str());
log_error("Can't find top module `%s'!\n", top_module_name);
for (auto module : mod_list)
BlifDumper::dump(*f, module, design, config);

View file

@ -30,6 +30,7 @@
#include "kernel/mem.h"
#include "kernel/json.h"
#include "kernel/yw.h"
#include "kernel/utils.h"
#include <string>
USING_YOSYS_NAMESPACE
@ -97,24 +98,22 @@ struct BtorWorker
vector<ywmap_btor_sig> ywmap_states;
dict<SigBit, int> ywmap_clock_bits;
dict<SigBit, int> ywmap_clock_inputs;
vector<Cell *> ywmap_asserts;
vector<Cell *> ywmap_assumes;
PrettyJson ywmap_json;
void btorf(const char *fmt, ...) YS_ATTRIBUTE(format(printf, 2, 3))
template <typename... Args>
void btorf(FmtString<TypeIdentity<Args>...> fmt, const Args &... args)
{
va_list ap;
va_start(ap, fmt);
f << indent << vstringf(fmt, ap);
va_end(ap);
f << indent << fmt.format(args...);
}
void infof(const char *fmt, ...) YS_ATTRIBUTE(format(printf, 2, 3))
template <typename... Args>
void infof(FmtString<TypeIdentity<Args>...> fmt, const Args &... args)
{
va_list ap;
va_start(ap, fmt);
info_lines.push_back(vstringf(fmt, ap));
va_end(ap);
info_lines.push_back(fmt.format(args...));
}
template<typename T>
@ -128,7 +127,7 @@ struct BtorWorker
std::replace(src.begin(), src.end(), ' ', '_');
if (srcsymbols.count(src) || module->count_id("\\" + src)) {
for (int i = 1;; i++) {
string s = stringf("%s-%d", src.c_str(), i);
string s = stringf("%s-%d", src, i);
if (!srcsymbols.count(s) && !module->count_id("\\" + s)) {
src = s;
break;
@ -191,7 +190,7 @@ struct BtorWorker
void btorf_push(const string &id)
{
if (verbose) {
f << indent << stringf(" ; begin %s\n", id.c_str());
f << indent << stringf(" ; begin %s\n", id);
indent += " ";
}
}
@ -200,7 +199,7 @@ struct BtorWorker
{
if (verbose) {
indent = indent.substr(4);
f << indent << stringf(" ; end %s\n", id.c_str());
f << indent << stringf(" ; end %s\n", id);
}
}
@ -245,7 +244,7 @@ struct BtorWorker
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.c_str());
log_error("Found topological loop while processing cell %s. Active cells:%s\n", log_id(cell), cell_list);
}
cell_recursion_guard.insert(cell);
@ -321,12 +320,12 @@ struct BtorWorker
btorf("%d slt %d %d %d\n", nid_b_ltz, sid_bit, nid_b, nid_zero);
nid = next_nid++;
btorf("%d ite %d %d %d %d%s\n", nid, sid, nid_b_ltz, nid_l, nid_r, getinfo(cell).c_str());
btorf("%d ite %d %d %d %d%s\n", nid, sid, nid_b_ltz, nid_l, nid_r, getinfo(cell));
}
else
{
nid = next_nid++;
btorf("%d %s %d %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str());
btorf("%d %s %d %d %d%s\n", nid, btor_op, sid, nid_a, nid_b, getinfo(cell));
}
SigSpec sig = sigmap(cell->getPort(ID::Y));
@ -367,7 +366,7 @@ struct BtorWorker
int sid = get_bv_sid(width);
int nid = next_nid++;
btorf("%d %c%s %d %d %d%s\n", nid, a_signed || b_signed ? 's' : 'u', btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str());
btorf("%d %c%s %d %d %d%s\n", nid, a_signed || b_signed ? 's' : 'u', btor_op, sid, nid_a, nid_b, getinfo(cell));
SigSpec sig = sigmap(cell->getPort(ID::Y));
@ -393,12 +392,12 @@ struct BtorWorker
if (cell->type == ID($_ANDNOT_)) {
btorf("%d not %d %d\n", nid1, sid, nid_b);
btorf("%d and %d %d %d%s\n", nid2, sid, nid_a, nid1, getinfo(cell).c_str());
btorf("%d and %d %d %d%s\n", nid2, sid, nid_a, nid1, getinfo(cell));
}
if (cell->type == ID($_ORNOT_)) {
btorf("%d not %d %d\n", nid1, sid, nid_b);
btorf("%d or %d %d %d%s\n", nid2, sid, nid_a, nid1, getinfo(cell).c_str());
btorf("%d or %d %d %d%s\n", nid2, sid, nid_a, nid1, getinfo(cell));
}
SigSpec sig = sigmap(cell->getPort(ID::Y));
@ -420,13 +419,13 @@ struct BtorWorker
if (cell->type == ID($_OAI3_)) {
btorf("%d or %d %d %d\n", nid1, sid, nid_a, nid_b);
btorf("%d and %d %d %d\n", nid2, sid, nid1, nid_c);
btorf("%d not %d %d%s\n", nid3, sid, nid2, getinfo(cell).c_str());
btorf("%d not %d %d%s\n", nid3, sid, nid2, getinfo(cell));
}
if (cell->type == ID($_AOI3_)) {
btorf("%d and %d %d %d\n", nid1, sid, nid_a, nid_b);
btorf("%d or %d %d %d\n", nid2, sid, nid1, nid_c);
btorf("%d not %d %d%s\n", nid3, sid, nid2, getinfo(cell).c_str());
btorf("%d not %d %d%s\n", nid3, sid, nid2, getinfo(cell));
}
SigSpec sig = sigmap(cell->getPort(ID::Y));
@ -451,14 +450,14 @@ struct BtorWorker
btorf("%d or %d %d %d\n", nid1, sid, nid_a, nid_b);
btorf("%d or %d %d %d\n", nid2, sid, nid_c, nid_d);
btorf("%d and %d %d %d\n", nid3, sid, nid1, nid2);
btorf("%d not %d %d%s\n", nid4, sid, nid3, getinfo(cell).c_str());
btorf("%d not %d %d%s\n", nid4, sid, nid3, getinfo(cell));
}
if (cell->type == ID($_AOI4_)) {
btorf("%d and %d %d %d\n", nid1, sid, nid_a, nid_b);
btorf("%d and %d %d %d\n", nid2, sid, nid_c, nid_d);
btorf("%d or %d %d %d\n", nid3, sid, nid1, nid2);
btorf("%d not %d %d%s\n", nid4, sid, nid3, getinfo(cell).c_str());
btorf("%d not %d %d%s\n", nid4, sid, nid3, getinfo(cell));
}
SigSpec sig = sigmap(cell->getPort(ID::Y));
@ -490,9 +489,9 @@ struct BtorWorker
int nid = next_nid++;
if (cell->type.in(ID($lt), ID($le), ID($ge), ID($gt))) {
btorf("%d %c%s %d %d %d%s\n", nid, a_signed || b_signed ? 's' : 'u', btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str());
btorf("%d %c%s %d %d %d%s\n", nid, a_signed || b_signed ? 's' : 'u', btor_op, sid, nid_a, nid_b, getinfo(cell));
} else {
btorf("%d %s %d %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str());
btorf("%d %s %d %d %d%s\n", nid, btor_op, sid, nid_a, nid_b, getinfo(cell));
}
SigSpec sig = sigmap(cell->getPort(ID::Y));
@ -508,7 +507,7 @@ struct BtorWorker
goto okay;
}
if (cell->type.in(ID($not), ID($neg), ID($_NOT_), ID($pos)))
if (cell->type.in(ID($not), ID($neg), ID($_NOT_), ID($pos), ID($buf), ID($_BUF_)))
{
string btor_op;
if (cell->type.in(ID($not), ID($_NOT_))) btor_op = "not";
@ -520,14 +519,14 @@ struct BtorWorker
int nid_a = get_sig_nid(cell->getPort(ID::A), width, a_signed);
SigSpec sig = sigmap(cell->getPort(ID::Y));
// the $pos cell just passes through, all other cells need an actual operation applied
// the $pos/$buf cells just pass through, all other cells need an actual operation applied
int nid = nid_a;
if (cell->type != ID($pos))
if (!cell->type.in(ID($pos), ID($buf), ID($_BUF_)))
{
log_assert(!btor_op.empty());
int sid = get_bv_sid(width);
nid = next_nid++;
btorf("%d %s %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, getinfo(cell).c_str());
btorf("%d %s %d %d%s\n", nid, btor_op, sid, nid_a, getinfo(cell));
}
if (GetSize(sig) < width) {
@ -567,9 +566,9 @@ struct BtorWorker
int nid = next_nid++;
if (btor_op != "not")
btorf("%d %s %d %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str());
btorf("%d %s %d %d %d%s\n", nid, btor_op, sid, nid_a, nid_b, getinfo(cell));
else
btorf("%d %s %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, getinfo(cell).c_str());
btorf("%d %s %d %d%s\n", nid, btor_op, sid, nid_a, getinfo(cell));
SigSpec sig = sigmap(cell->getPort(ID::Y));
@ -600,11 +599,11 @@ struct BtorWorker
if (cell->type == ID($reduce_xnor)) {
int nid2 = next_nid++;
btorf("%d %s %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, getinfo(cell).c_str());
btorf("%d %s %d %d%s\n", nid, btor_op, sid, nid_a, getinfo(cell));
btorf("%d not %d %d\n", nid2, sid, nid);
nid = nid2;
} else {
btorf("%d %s %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, getinfo(cell).c_str());
btorf("%d %s %d %d%s\n", nid, btor_op, sid, nid_a, getinfo(cell));
}
SigSpec sig = sigmap(cell->getPort(ID::Y));
@ -639,9 +638,9 @@ struct BtorWorker
int tmp = nid;
nid = next_nid++;
btorf("%d ite %d %d %d %d\n", tmp, sid, nid_s, nid_b, nid_a);
btorf("%d not %d %d%s\n", nid, sid, tmp, getinfo(cell).c_str());
btorf("%d not %d %d%s\n", nid, sid, tmp, getinfo(cell));
} else {
btorf("%d ite %d %d %d %d%s\n", nid, sid, nid_s, nid_b, nid_a, getinfo(cell).c_str());
btorf("%d ite %d %d %d %d%s\n", nid, sid, nid_s, nid_b, nid_a, getinfo(cell));
}
add_nid_sig(nid, sig_y);
@ -664,7 +663,7 @@ struct BtorWorker
int nid_s = get_sig_nid(sig_s.extract(i));
int nid2 = next_nid++;
if (i == GetSize(sig_s)-1)
btorf("%d ite %d %d %d %d%s\n", nid2, sid, nid_s, nid_b, nid, getinfo(cell).c_str());
btorf("%d ite %d %d %d %d%s\n", nid2, sid, nid_s, nid_b, nid, getinfo(cell));
else
btorf("%d ite %d %d %d %d\n", nid2, sid, nid_s, nid_b, nid);
nid = nid2;
@ -708,12 +707,13 @@ struct BtorWorker
}
}
Const initval;
Const::Builder initval_bits(GetSize(sig_q));
for (int i = 0; i < GetSize(sig_q); i++)
if (initbits.count(sig_q[i]))
initval.bits.push_back(initbits.at(sig_q[i]) ? State::S1 : State::S0);
initval_bits.push_back(initbits.at(sig_q[i]) ? State::S1 : State::S0);
else
initval.bits.push_back(State::Sx);
initval_bits.push_back(State::Sx);
Const initval = initval_bits.build();
int nid_init_val = -1;
@ -752,7 +752,7 @@ struct BtorWorker
int sid = get_bv_sid(GetSize(sig_y));
int nid = next_nid++;
btorf("%d state %d%s\n", nid, sid, getinfo(cell).c_str());
btorf("%d state %d%s\n", nid, sid, getinfo(cell));
ywmap_state(sig_y);
@ -775,7 +775,7 @@ struct BtorWorker
int one_nid = get_sig_nid(State::S1);
int zero_nid = get_sig_nid(State::S0);
initstate_nid = next_nid++;
btorf("%d state %d%s\n", initstate_nid, sid, getinfo(cell).c_str());
btorf("%d state %d%s\n", initstate_nid, sid, getinfo(cell));
btorf("%d init %d %d %d\n", next_nid++, sid, initstate_nid, one_nid);
btorf("%d next %d %d %d\n", next_nid++, sid, initstate_nid, zero_nid);
@ -832,7 +832,10 @@ struct BtorWorker
}
}
if (constword)
// If not fully defined, undef bits should be able to take a
// different value for each address so we can't initialise from
// one value (and btor2parser doesn't like it)
if (constword && firstword.is_fully_def())
{
if (verbose)
btorf("; initval = %s\n", log_signal(firstword));
@ -1039,15 +1042,16 @@ struct BtorWorker
{
if (bit.wire == nullptr)
{
Const c(bit.data);
while (i+GetSize(c) < GetSize(sig) && sig[i+GetSize(c)].wire == nullptr)
c.bits.push_back(sig[i+GetSize(c)].data);
Const::Builder c_bits;
c_bits.push_back(bit.data);
while (i + GetSize(c_bits) < GetSize(sig) && sig[i + GetSize(c_bits)].wire == nullptr)
c_bits.push_back(sig[i + GetSize(c_bits)].data);
Const c = c_bits.build();
if (consts.count(c) == 0) {
int sid = get_bv_sid(GetSize(c));
int nid = next_nid++;
btorf("%d const %d %s\n", nid, sid, c.as_string().c_str());
btorf("%d const %d %s\n", nid, sid, c.as_string());
consts[c] = nid;
nid_width[nid] = GetSize(c);
}
@ -1077,6 +1081,7 @@ struct BtorWorker
btorf("%d input %d\n", nid, sid);
ywmap_input(s);
nid_width[nid] = GetSize(s);
add_nid_sig(nid, s);
for (int j = 0; j < GetSize(s); j++)
nidbits.push_back(make_pair(nid, j));
@ -1210,7 +1215,7 @@ struct BtorWorker
int sid = get_bv_sid(GetSize(sig));
int nid = next_nid++;
btorf("%d input %d%s\n", nid, sid, getinfo(wire).c_str());
btorf("%d input %d%s\n", nid, sid, getinfo(wire));
ywmap_input(wire);
add_nid_sig(nid, sig);
@ -1255,7 +1260,7 @@ struct BtorWorker
btorf_push(stringf("output %s", log_id(wire)));
int nid = get_sig_nid(wire);
btorf("%d output %d%s\n", next_nid++, nid, getinfo(wire).c_str());
btorf("%d output %d%s\n", next_nid++, nid, getinfo(wire));
btorf_pop(stringf("output %s", log_id(wire)));
}
@ -1277,6 +1282,8 @@ struct BtorWorker
btorf("%d or %d %d %d\n", nid_a_or_not_en, sid, nid_a, nid_not_en);
btorf("%d constraint %d\n", nid, nid_a_or_not_en);
if (ywmap_json.active()) ywmap_assumes.emplace_back(cell);
btorf_pop(log_id(cell));
}
@ -1297,10 +1304,12 @@ struct BtorWorker
bad_properties.push_back(nid_en_and_not_a);
} else {
if (cover_mode) {
infof("bad %d%s\n", nid_en_and_not_a, getinfo(cell, true).c_str());
infof("bad %d%s\n", nid_en_and_not_a, getinfo(cell, true));
} else {
int nid = next_nid++;
btorf("%d bad %d%s\n", nid, nid_en_and_not_a, getinfo(cell, true).c_str());
btorf("%d bad %d%s\n", nid, nid_en_and_not_a, getinfo(cell, true));
if (ywmap_json.active()) ywmap_asserts.emplace_back(cell);
}
}
@ -1322,7 +1331,7 @@ struct BtorWorker
bad_properties.push_back(nid_en_and_a);
} else {
int nid = next_nid++;
btorf("%d bad %d%s\n", nid, nid_en_and_a, getinfo(cell, true).c_str());
btorf("%d bad %d%s\n", nid, nid_en_and_a, getinfo(cell, true));
}
btorf_pop(log_id(cell));
@ -1343,7 +1352,7 @@ struct BtorWorker
continue;
int this_nid = next_nid++;
btorf("%d uext %d %d %d%s\n", this_nid, sid, nid, 0, getinfo(wire).c_str());
btorf("%d uext %d %d %d%s\n", this_nid, sid, nid, 0, getinfo(wire));
if (info_clocks.count(nid))
info_clocks[this_nid] |= info_clocks[nid];
@ -1366,7 +1375,7 @@ struct BtorWorker
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).c_str());
btorf("%d next %d %d %d%s\n", next_nid++, sid, nid, nid_q, getinfo(cell));
btorf_pop(stringf("next %s", log_id(cell)));
}
@ -1425,7 +1434,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)).c_str());
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)));
}
@ -1458,6 +1467,7 @@ struct BtorWorker
log_assert(cursor == 0);
log_assert(GetSize(todo) == 1);
btorf("%d bad %d\n", nid, todo[cursor]);
// What do we do with ywmap_asserts when using single_bad?
}
}
@ -1484,7 +1494,7 @@ struct BtorWorker
std::ofstream f;
f.open(info_filename.c_str(), std::ofstream::trunc);
if (f.fail())
log_error("Can't open file `%s' for writing: %s\n", info_filename.c_str(), strerror(errno));
log_error("Can't open file `%s' for writing: %s\n", info_filename, strerror(errno));
for (auto &it : info_lines)
f << it;
f.close();
@ -1494,7 +1504,7 @@ struct BtorWorker
{
ywmap_json.begin_object();
ywmap_json.entry("version", "Yosys Witness BTOR map");
ywmap_json.entry("generator", yosys_version_str);
ywmap_json.entry("generator", yosys_maybe_version());
ywmap_json.name("clocks");
ywmap_json.begin_array();
@ -1523,6 +1533,18 @@ struct BtorWorker
emit_ywmap_btor_sig(entry);
ywmap_json.end_array();
ywmap_json.name("asserts");
ywmap_json.begin_array();
for (Cell *cell : ywmap_asserts)
ywmap_json.value(witness_path(cell));
ywmap_json.end_array();
ywmap_json.name("assumes");
ywmap_json.begin_array();
for (Cell *cell : ywmap_assumes)
ywmap_json.value(witness_path(cell));
ywmap_json.end_array();
ywmap_json.end_object();
}
}
@ -1608,7 +1630,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_version_str, log_id(topmod));
yosys_maybe_version(), log_id(topmod));
BtorWorker(*f, topmod, verbose, single_bad, cover_mode, print_internal_names, info_filename, ywmap_filename);

View file

@ -10,11 +10,11 @@ cd test_cells.tmp
for fn in test_*.il; do
../../../yosys -p "
read_ilang $fn
read_rtlil $fn
rename gold gate
synth
read_ilang $fn
read_rtlil $fn
miter -equiv -make_assert -flatten gold gate main
hierarchy -top main
write_btor ${fn%.il}.btor

View file

@ -47,7 +47,7 @@ struct Scheduler {
struct Vertex {
T *data;
Vertex *prev, *next;
pool<Vertex*, hash_ptr_ops> preds, succs;
pool<Vertex*> preds, succs;
Vertex() : data(NULL), prev(this), next(this) {}
Vertex(T *data) : data(data), prev(NULL), next(NULL) {}
@ -200,7 +200,7 @@ bool is_extending_cell(RTLIL::IdString type)
bool is_inlinable_cell(RTLIL::IdString type)
{
return is_unary_cell(type) || is_binary_cell(type) || type.in(
ID($mux), ID($concat), ID($slice), ID($pmux), ID($bmux), ID($demux));
ID($mux), ID($concat), ID($slice), ID($pmux), ID($bmux), ID($demux), ID($bwmux));
}
bool is_ff_cell(RTLIL::IdString type)
@ -300,10 +300,10 @@ struct FlowGraph {
};
std::vector<Node*> nodes;
dict<const RTLIL::Wire*, pool<Node*, hash_ptr_ops>> wire_comb_defs, wire_sync_defs, wire_uses;
dict<Node*, pool<const RTLIL::Wire*>, hash_ptr_ops> node_comb_defs, node_sync_defs, node_uses;
dict<const RTLIL::Wire*, pool<Node*>> wire_comb_defs, wire_sync_defs, wire_uses;
dict<Node*, pool<const RTLIL::Wire*>> node_comb_defs, node_sync_defs, node_uses;
dict<const RTLIL::Wire*, bool> wire_def_inlinable;
dict<const RTLIL::Wire*, dict<Node*, bool, hash_ptr_ops>> wire_use_inlinable;
dict<const RTLIL::Wire*, dict<Node*, bool>> wire_use_inlinable;
dict<RTLIL::SigBit, bool> bit_has_state;
~FlowGraph()
@ -328,7 +328,7 @@ struct FlowGraph {
node_comb_defs[node].insert(chunk.wire);
}
}
for (auto bit : sig.bits())
for (auto bit : sig)
bit_has_state[bit] |= is_ff;
// Only comb defs of an entire wire in the right order can be inlined.
if (!is_ff && sig.is_wire()) {
@ -365,7 +365,7 @@ struct FlowGraph {
return false;
}
bool is_inlinable(const RTLIL::Wire *wire, const pool<Node*, hash_ptr_ops> &nodes) const
bool is_inlinable(const RTLIL::Wire *wire, const pool<Node*> &nodes) const
{
// Can the wire be inlined, knowing that the given nodes are reachable?
if (nodes.size() != 1)
@ -606,22 +606,30 @@ std::vector<std::string> split_by(const std::string &str, const std::string &sep
return result;
}
std::string escape_cxx_string(const std::string &input)
std::string escape_c_string(const std::string &input)
{
std::string output = "\"";
std::string output;
output.push_back('"');
for (auto c : input) {
if (::isprint(c)) {
if (c == '\\')
if (c == '\\' || c == '"')
output.push_back('\\');
output.push_back(c);
} else {
char l = c & 0xf, h = (c >> 4) & 0xf;
output.append("\\x");
output.push_back((h < 10 ? '0' + h : 'a' + h - 10));
output.push_back((l < 10 ? '0' + l : 'a' + l - 10));
char l = c & 0x7, m = (c >> 3) & 0x7, h = (c >> 6) & 0x3;
output.append("\\");
output.push_back('0' + h);
output.push_back('0' + m);
output.push_back('0' + l);
}
}
output.push_back('"');
return output;
}
std::string escape_cxx_string(const std::string &input)
{
std::string output = escape_c_string(input);
if (output.find('\0') != std::string::npos) {
output.insert(0, "std::string {");
output.append(stringf(", %zu}", input.size()));
@ -629,20 +637,6 @@ std::string escape_cxx_string(const std::string &input)
return output;
}
std::string basename(const std::string &filepath)
{
#ifdef _WIN32
const std::string dir_seps = "\\/";
#else
const std::string dir_seps = "/";
#endif
size_t sep_pos = filepath.find_last_of(dir_seps);
if (sep_pos != std::string::npos)
return filepath.substr(sep_pos + 1);
else
return filepath;
}
template<class T>
std::string get_hdl_name(T *object)
{
@ -762,7 +756,7 @@ struct CxxrtlWorker {
// 1b. Generated identifiers for internal names (beginning with `$`) start with `i_`.
// 2. An underscore is escaped with another underscore, i.e. `__`.
// 3. Any other non-alnum character is escaped with underscores around its lowercase hex code, e.g. `@` as `_40_`.
std::string mangle_name(const RTLIL::IdString &name)
std::string mangle_name(RTLIL::IdString name)
{
std::string mangled;
bool first = true;
@ -792,7 +786,7 @@ struct CxxrtlWorker {
return mangled;
}
std::string mangle_module_name(const RTLIL::IdString &name, bool is_blackbox = false)
std::string mangle_module_name(RTLIL::IdString name, bool is_blackbox = false)
{
// Class namespace.
if (is_blackbox)
@ -800,19 +794,19 @@ struct CxxrtlWorker {
return mangle_name(name);
}
std::string mangle_memory_name(const RTLIL::IdString &name)
std::string mangle_memory_name(RTLIL::IdString name)
{
// Class member namespace.
return "memory_" + mangle_name(name);
}
std::string mangle_cell_name(const RTLIL::IdString &name)
std::string mangle_cell_name(RTLIL::IdString name)
{
// Class member namespace.
return "cell_" + mangle_name(name);
}
std::string mangle_wire_name(const RTLIL::IdString &name)
std::string mangle_wire_name(RTLIL::IdString name)
{
// Class member namespace.
return mangle_name(name);
@ -856,7 +850,7 @@ struct CxxrtlWorker {
if (!module->has_attribute(ID(cxxrtl_template)))
return {};
if (module->attributes.at(ID(cxxrtl_template)).flags != RTLIL::CONST_FLAG_STRING)
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));
std::vector<std::string> param_names = split_by(module->get_string_attribute(ID(cxxrtl_template)), " \t");
@ -1130,7 +1124,7 @@ struct CxxrtlWorker {
f << indent << "// cell " << cell->name.str() << " syncs\n";
for (auto conn : cell->connections())
if (cell->output(conn.first))
if (is_cxxrtl_sync_port(cell, conn.first)) {
if (is_cxxrtl_sync_port(cell, conn.first) && !conn.second.empty()) {
f << indent;
dump_sigspec_lhs(conn.second, for_debug);
f << " = " << mangle(cell) << access << mangle_wire_name(conn.first) << ".curr;\n";
@ -1190,6 +1184,14 @@ struct CxxrtlWorker {
f << ">(";
dump_sigspec_rhs(cell->getPort(ID::S), for_debug);
f << ").val()";
// Bitwise muxes
} else if (cell->type == ID($bwmux)) {
dump_sigspec_rhs(cell->getPort(ID::A), for_debug);
f << ".bwmux(";
dump_sigspec_rhs(cell->getPort(ID::B), for_debug);
f << ",";
dump_sigspec_rhs(cell->getPort(ID::S), for_debug);
f << ").val()";
// Demuxes
} else if (cell->type == ID($demux)) {
dump_sigspec_rhs(cell->getPort(ID::A), for_debug);
@ -1517,7 +1519,7 @@ struct CxxrtlWorker {
}
// Internal cells
} else if (is_internal_cell(cell->type)) {
log_cmd_error("Unsupported internal cell `%s'.\n", cell->type.c_str());
log_cmd_error("Unsupported internal cell `%s'.\n", cell->type);
// User cells
} else if (for_debug) {
// Outlines are called on demand when computing the value of a debug item. Nothing to do here.
@ -1652,26 +1654,29 @@ struct CxxrtlWorker {
f << signal_temp << " == ";
dump_sigspec(compare, /*is_lhs=*/false, for_debug);
} else if (compare.is_fully_const()) {
RTLIL::Const compare_mask, compare_value;
RTLIL::Const::Builder compare_mask_builder(compare.size());
RTLIL::Const::Builder compare_value_builder(compare.size());
for (auto bit : compare.as_const()) {
switch (bit) {
case RTLIL::S0:
case RTLIL::S1:
compare_mask.bits.push_back(RTLIL::S1);
compare_value.bits.push_back(bit);
compare_mask_builder.push_back(RTLIL::S1);
compare_value_builder.push_back(bit);
break;
case RTLIL::Sx:
case RTLIL::Sz:
case RTLIL::Sa:
compare_mask.bits.push_back(RTLIL::S0);
compare_value.bits.push_back(RTLIL::S0);
compare_mask_builder.push_back(RTLIL::S0);
compare_value_builder.push_back(RTLIL::S0);
break;
default:
log_assert(false);
}
}
RTLIL::Const compare_mask = compare_mask_builder.build();
RTLIL::Const compare_value = compare_value_builder.build();
f << "and_uu<" << compare.size() << ">(" << signal_temp << ", ";
dump_const(compare_mask);
f << ") == ";
@ -2275,46 +2280,92 @@ struct CxxrtlWorker {
dec_indent();
}
void dump_metadata_map(const dict<RTLIL::IdString, RTLIL::Const> &metadata_map)
{
if (metadata_map.empty()) {
f << "metadata_map()";
return;
}
f << "metadata_map({\n";
inc_indent();
for (auto metadata_item : metadata_map) {
if (!metadata_item.first.isPublic())
continue;
if (metadata_item.second.size() > 64 && (metadata_item.second.flags & RTLIL::CONST_FLAG_STRING) == 0) {
f << indent << "/* attribute " << metadata_item.first.str().substr(1) << " is over 64 bits wide */\n";
continue;
}
f << indent << "{ " << escape_cxx_string(metadata_item.first.str().substr(1)) << ", ";
// In Yosys, a real is a type of string.
if (metadata_item.second.flags & RTLIL::CONST_FLAG_REAL) {
f << std::showpoint << std::stod(metadata_item.second.decode_string()) << std::noshowpoint;
} else if (metadata_item.second.flags & RTLIL::CONST_FLAG_STRING) {
f << escape_cxx_string(metadata_item.second.decode_string());
} else if (metadata_item.second.flags & RTLIL::CONST_FLAG_SIGNED) {
f << "INT64_C(" << metadata_item.second.as_int(/*is_signed=*/true) << ")";
} else {
f << "UINT64_C(" << metadata_item.second.as_int(/*is_signed=*/false) << ")";
}
f << " },\n";
void dump_serialized_metadata(const dict<RTLIL::IdString, RTLIL::Const> &metadata_map) {
// Creating thousands metadata_map objects using initializer lists in a single function results in one of:
// 1. Megabytes of stack usage (with __attribute__((optnone))).
// 2. Minutes of compile time (without __attribute__((optnone))).
// So, don't create them.
std::string data;
auto put_u64 = [&](uint64_t value) {
for (size_t count = 0; count < 8; count++) {
data += (char)(value >> 56);
value <<= 8;
}
dec_indent();
f << indent << "})";
};
for (auto metadata_item : metadata_map) {
if (!metadata_item.first.isPublic())
continue;
if (metadata_item.second.size() > 64 && (metadata_item.second.flags & RTLIL::CONST_FLAG_STRING) == 0) {
f << indent << "/* attribute " << metadata_item.first.str().substr(1) << " is over 64 bits wide */\n";
continue;
}
data += metadata_item.first.str().substr(1) + '\0';
// In Yosys, a real is a type of string.
if (metadata_item.second.flags & RTLIL::CONST_FLAG_REAL) {
double dvalue = std::stod(metadata_item.second.decode_string());
uint64_t uvalue;
static_assert(sizeof(dvalue) == sizeof(uvalue), "double must be 64 bits in size");
memcpy(&uvalue, &dvalue, sizeof(uvalue));
data += 'd';
put_u64(uvalue);
} else if (metadata_item.second.flags & RTLIL::CONST_FLAG_STRING) {
data += 's';
data += metadata_item.second.decode_string();
data += '\0';
} else if (metadata_item.second.flags & RTLIL::CONST_FLAG_SIGNED) {
data += 'i';
put_u64((uint64_t)metadata_item.second.as_int(/*is_signed=*/true));
} else {
data += 'u';
put_u64(metadata_item.second.as_int(/*is_signed=*/false));
}
}
f << escape_c_string(data);
}
void dump_debug_attrs(const RTLIL::AttrObject *object)
void dump_metadata_map(const dict<RTLIL::IdString, RTLIL::Const> &metadata_map) {
if (metadata_map.empty()) {
f << "metadata_map()";
} else {
f << "metadata_map({\n";
inc_indent();
for (auto metadata_item : metadata_map) {
if (!metadata_item.first.isPublic())
continue;
if (metadata_item.second.size() > 64 && (metadata_item.second.flags & RTLIL::CONST_FLAG_STRING) == 0) {
f << indent << "/* attribute " << metadata_item.first.str().substr(1) << " is over 64 bits wide */\n";
continue;
}
f << indent << "{ " << escape_cxx_string(metadata_item.first.str().substr(1)) << ", ";
// In Yosys, a real is a type of string.
if (metadata_item.second.flags & RTLIL::CONST_FLAG_REAL) {
f << std::showpoint << std::stod(metadata_item.second.decode_string()) << std::noshowpoint;
} else if (metadata_item.second.flags & RTLIL::CONST_FLAG_STRING) {
f << escape_cxx_string(metadata_item.second.decode_string());
} else if (metadata_item.second.flags & RTLIL::CONST_FLAG_SIGNED) {
f << "INT64_C(" << metadata_item.second.as_int(/*is_signed=*/true) << ")";
} else {
f << "UINT64_C(" << metadata_item.second.as_int(/*is_signed=*/false) << ")";
}
f << " },\n";
}
dec_indent();
f << indent << "})";
}
}
void dump_debug_attrs(const RTLIL::AttrObject *object, bool serialize = true)
{
dict<RTLIL::IdString, RTLIL::Const> attributes = object->attributes;
// Inherently necessary to get access to the object, so a waste of space to emit.
attributes.erase(ID::hdlname);
// Internal Yosys attribute that should be removed but isn't.
attributes.erase(ID::module_not_derived);
dump_metadata_map(attributes);
if (serialize) {
dump_serialized_metadata(attributes);
} else {
dump_metadata_map(attributes);
}
}
void dump_debug_info_method(RTLIL::Module *module)
@ -2337,7 +2388,7 @@ struct CxxrtlWorker {
// The module is responsible for adding its own scope.
f << indent << "scopes->add(path.empty() ? path : path.substr(0, path.size() - 1), ";
f << escape_cxx_string(get_hdl_name(module)) << ", ";
dump_debug_attrs(module);
dump_debug_attrs(module, /*serialize=*/false);
f << ", std::move(cell_attrs));\n";
count_scopes++;
// If there were any submodules that were flattened, the module is also responsible for adding them.
@ -2347,11 +2398,16 @@ struct CxxrtlWorker {
auto module_attrs = scopeinfo_attributes(cell, ScopeinfoAttrs::Module);
auto cell_attrs = scopeinfo_attributes(cell, ScopeinfoAttrs::Cell);
cell_attrs.erase(ID::module_not_derived);
f << indent << "scopes->add(path + " << escape_cxx_string(get_hdl_name(cell)) << ", ";
f << escape_cxx_string(cell->get_string_attribute(ID(module))) << ", ";
dump_metadata_map(module_attrs);
f << indent << "scopes->add(path, " << escape_cxx_string(get_hdl_name(cell)) << ", ";
if (module_attrs.count(ID(hdlname))) {
f << escape_cxx_string(module_attrs.at(ID(hdlname)).decode_string());
} else {
f << escape_cxx_string(cell->get_string_attribute(ID(module)));
}
f << ", ";
dump_metadata_map(cell_attrs);
dump_serialized_metadata(module_attrs);
f << ", ";
dump_serialized_metadata(cell_attrs);
f << ");\n";
} else log_assert(false && "Unknown $scopeinfo type");
count_scopes++;
@ -2362,8 +2418,6 @@ struct CxxrtlWorker {
inc_indent();
for (auto wire : module->wires()) {
const auto &debug_wire_type = debug_wire_types[wire];
if (!wire->name.isPublic())
continue;
count_public_wires++;
switch (debug_wire_type.type) {
case WireType::BUFFERED:
@ -2371,6 +2425,9 @@ struct CxxrtlWorker {
// Member wire
std::vector<std::string> flags;
if (!wire->name.isPublic())
flags.push_back("GENERATED");
if (wire->port_input && wire->port_output)
flags.push_back("INOUT");
else if (wire->port_output)
@ -2419,20 +2476,22 @@ struct CxxrtlWorker {
if (has_driven_sync + has_driven_comb + has_undriven > 1)
count_mixed_driver++;
f << indent << "items->add(path + " << escape_cxx_string(get_hdl_name(wire));
f << ", debug_item(" << mangle(wire) << ", " << wire->start_offset;
bool first = true;
for (auto flag : flags) {
if (first) {
first = false;
f << ", ";
} else {
f << "|";
}
f << "debug_item::" << flag;
}
f << "), ";
f << indent << "items->add(path, " << escape_cxx_string(get_hdl_name(wire)) << ", ";
dump_debug_attrs(wire);
f << ", " << mangle(wire);
if (wire->start_offset != 0 || !flags.empty()) {
f << ", " << wire->start_offset;
bool first = true;
for (auto flag : flags) {
if (first) {
first = false;
f << ", ";
} else {
f << "|";
}
f << "debug_item::" << flag;
}
}
f << ");\n";
count_member_wires++;
break;
@ -2440,16 +2499,18 @@ struct CxxrtlWorker {
case WireType::ALIAS: {
// Alias of a member wire
const RTLIL::Wire *aliasee = debug_wire_type.sig_subst.as_wire();
f << indent << "items->add(path + " << escape_cxx_string(get_hdl_name(wire));
f << ", debug_item(";
f << indent << "items->add(path, " << escape_cxx_string(get_hdl_name(wire)) << ", ";
dump_debug_attrs(wire);
f << ", ";
// If the aliasee is an outline, then the alias must be an outline, too; otherwise downstream
// tooling has no way to find out about the outline.
if (debug_wire_types[aliasee].is_outline())
f << "debug_eval_outline";
else
f << "debug_alias()";
f << ", " << mangle(aliasee) << ", " << wire->start_offset << "), ";
dump_debug_attrs(aliasee);
f << ", " << mangle(aliasee);
if (wire->start_offset != 0)
f << ", " << wire->start_offset;
f << ");\n";
count_alias_wires++;
break;
@ -2459,18 +2520,22 @@ struct CxxrtlWorker {
f << indent << "static const value<" << wire->width << "> const_" << mangle(wire) << " = ";
dump_const(debug_wire_type.sig_subst.as_const());
f << ";\n";
f << indent << "items->add(path + " << escape_cxx_string(get_hdl_name(wire));
f << ", debug_item(const_" << mangle(wire) << ", " << wire->start_offset << "), ";
f << indent << "items->add(path, " << escape_cxx_string(get_hdl_name(wire)) << ", ";
dump_debug_attrs(wire);
f << ", const_" << mangle(wire);
if (wire->start_offset != 0)
f << ", " << wire->start_offset;
f << ");\n";
count_const_wires++;
break;
}
case WireType::OUTLINE: {
// Localized or inlined, but rematerializable wire
f << indent << "items->add(path + " << escape_cxx_string(get_hdl_name(wire));
f << ", debug_item(debug_eval_outline, " << mangle(wire) << ", " << wire->start_offset << "), ";
f << indent << "items->add(path, " << escape_cxx_string(get_hdl_name(wire)) << ", ";
dump_debug_attrs(wire);
f << ", debug_eval_outline, " << mangle(wire);
if (wire->start_offset != 0)
f << ", " << wire->start_offset;
f << ");\n";
count_inline_wires++;
break;
@ -2486,15 +2551,14 @@ struct CxxrtlWorker {
for (auto &mem : mod_memories[module]) {
if (!mem.memid.isPublic())
continue;
f << indent << "items->add(path + " << escape_cxx_string(mem.packed ? get_hdl_name(mem.cell) : get_hdl_name(mem.mem));
f << ", debug_item(" << mangle(&mem) << ", ";
f << mem.start_offset << "), ";
f << indent << "items->add(path, " << escape_cxx_string(mem.packed ? get_hdl_name(mem.cell) : get_hdl_name(mem.mem)) << ", ";
if (mem.packed) {
dump_debug_attrs(mem.cell);
} else {
dump_debug_attrs(mem.mem);
}
f << ");\n";
f << ", " << mangle(&mem) << ", ";
f << mem.start_offset << ");\n";
}
}
dec_indent();
@ -2506,7 +2570,7 @@ struct CxxrtlWorker {
const char *access = is_cxxrtl_blackbox_cell(cell) ? "->" : ".";
f << indent << mangle(cell) << access;
f << "debug_info(items, scopes, path + " << escape_cxx_string(get_hdl_name(cell) + ' ') << ", ";
dump_debug_attrs(cell);
dump_debug_attrs(cell, /*serialize=*/false);
f << ");\n";
}
}
@ -2780,7 +2844,7 @@ struct CxxrtlWorker {
}
if (split_intf)
f << "#include \"" << basename(intf_filename) << "\"\n";
f << "#include \"" << name_from_file_path(intf_filename) << "\"\n";
else
f << "#include <cxxrtl/cxxrtl.h>\n";
f << "\n";
@ -2967,7 +3031,7 @@ struct CxxrtlWorker {
if (init == RTLIL::Const()) {
init = RTLIL::Const(State::Sx, GetSize(bit.wire));
}
init[bit.offset] = port.init_value[i];
init.set(bit.offset, port.init_value[i]);
}
}
}
@ -3019,7 +3083,7 @@ struct CxxrtlWorker {
// without feedback arcs can generally be evaluated in a single pass, i.e. it always requires only
// a single delta cycle.
Scheduler<FlowGraph::Node> scheduler;
dict<FlowGraph::Node*, Scheduler<FlowGraph::Node>::Vertex*, hash_ptr_ops> node_vertex_map;
dict<FlowGraph::Node*, Scheduler<FlowGraph::Node>::Vertex*> node_vertex_map;
for (auto node : flow.nodes)
node_vertex_map[node] = scheduler.add(node);
for (auto node_comb_def : flow.node_comb_defs) {
@ -3034,7 +3098,7 @@ struct CxxrtlWorker {
// Find out whether the order includes any feedback arcs.
std::vector<FlowGraph::Node*> node_order;
pool<FlowGraph::Node*, hash_ptr_ops> evaluated_nodes;
pool<FlowGraph::Node*> evaluated_nodes;
pool<const RTLIL::Wire*> feedback_wires;
for (auto vertex : scheduler.schedule()) {
auto node = vertex->data;
@ -3078,7 +3142,7 @@ struct CxxrtlWorker {
}
// Discover nodes reachable from primary outputs (i.e. members) and collect reachable wire users.
pool<FlowGraph::Node*, hash_ptr_ops> worklist;
pool<FlowGraph::Node*> worklist;
for (auto node : flow.nodes) {
if (node->type == FlowGraph::Node::Type::CELL_EVAL && !is_internal_cell(node->cell->type))
worklist.insert(node); // node evaluates a submodule
@ -3098,8 +3162,8 @@ struct CxxrtlWorker {
worklist.insert(node); // node drives public wires
}
}
dict<const RTLIL::Wire*, pool<FlowGraph::Node*, hash_ptr_ops>> live_wires;
pool<FlowGraph::Node*, hash_ptr_ops> live_nodes;
dict<const RTLIL::Wire*, pool<FlowGraph::Node*>> live_wires;
pool<FlowGraph::Node*> live_nodes;
while (!worklist.empty()) {
auto node = worklist.pop();
live_nodes.insert(node);
@ -3202,6 +3266,7 @@ struct CxxrtlWorker {
debug_wire_type = wire_type; // wire is a member
if (!debug_alias) continue;
if (wire->port_input || wire->port_output) continue; // preserve input/output metadata in flags
const RTLIL::Wire *it = wire;
while (flow.is_inlinable(it)) {
log_assert(flow.wire_comb_defs[it].size() == 1);
@ -3228,15 +3293,15 @@ struct CxxrtlWorker {
// Discover nodes reachable from primary outputs (i.e. outlines) up until primary inputs (i.e. members)
// and collect reachable wire users.
pool<FlowGraph::Node*, hash_ptr_ops> worklist;
pool<FlowGraph::Node*> worklist;
for (auto node : flow.nodes) {
if (flow.node_comb_defs.count(node))
for (auto wire : flow.node_comb_defs[node])
if (debug_wire_types[wire].is_outline())
worklist.insert(node); // node drives outline
}
dict<const RTLIL::Wire*, pool<FlowGraph::Node*, hash_ptr_ops>> debug_live_wires;
pool<FlowGraph::Node*, hash_ptr_ops> debug_live_nodes;
dict<const RTLIL::Wire*, pool<FlowGraph::Node*>> debug_live_wires;
pool<FlowGraph::Node*> debug_live_nodes;
while (!worklist.empty()) {
auto node = worklist.pop();
debug_live_nodes.insert(node);
@ -3402,8 +3467,8 @@ struct CxxrtlWorker {
};
struct CxxrtlBackend : public Backend {
static const int DEFAULT_OPT_LEVEL = 6;
static const int DEFAULT_DEBUG_LEVEL = 4;
static constexpr int DEFAULT_OPT_LEVEL = 6;
static constexpr int DEFAULT_DEBUG_LEVEL = 4;
CxxrtlBackend() : Backend("cxxrtl", "convert design to C++ RTL simulation") { }
void help() override
@ -3724,7 +3789,7 @@ struct CxxrtlBackend : public Backend {
if (args[argidx] == "-print-output" && argidx+1 < args.size()) {
worker.print_output = args[++argidx];
if (!(worker.print_output == "std::cout" || worker.print_output == "std::cerr")) {
log_cmd_error("Invalid output stream \"%s\".\n", worker.print_output.c_str());
log_cmd_error("Invalid output stream \"%s\".\n", worker.print_output);
worker.print_output = "std::cout";
}
continue;

View file

@ -47,7 +47,7 @@ cxxrtl_handle cxxrtl_create_at(cxxrtl_toplevel design, const char *top_path_) {
cxxrtl_handle handle = new _cxxrtl_handle;
handle->module = std::move(design->module);
handle->module->debug_info(handle->objects, top_path);
handle->module->debug_info(&handle->objects, nullptr, top_path);
delete design;
return handle;
}

View file

@ -200,6 +200,10 @@ enum cxxrtl_flag {
// node, such as inputs and dangling wires.
CXXRTL_UNDRIVEN = 1 << 4,
// Generated correspond to netlist nodes that correspond to state with an internal name, that
// need to be saved, but wouldn't otherwise have a debug item generated.
CXXRTL_GENERATED = 1 << 5,
// More object flags may be added in the future, but the existing ones will never change.
};

View file

@ -498,6 +498,11 @@ struct value : public expr_base<value<Bits>> {
return result;
}
CXXRTL_ALWAYS_INLINE
value<Bits> bwmux(const value<Bits> &b, const value<Bits> &s) const {
return (bit_and(s.bit_not())).bit_or(b.bit_and(s));
}
template<size_t ResultBits, size_t SelBits>
value<ResultBits> demux(const value<SelBits> &sel) const {
static_assert(Bits << SelBits == ResultBits, "invalid sizes used in demux()");
@ -625,11 +630,11 @@ struct value : public expr_base<value<Bits>> {
value<Bits + 1> remainder;
value<Bits + 1> dividend = sext<Bits + 1>();
value<Bits + 1> divisor = other.template sext<Bits + 1>();
if (dividend.is_neg()) dividend = dividend.neg();
if (divisor.is_neg()) divisor = divisor.neg();
if (is_neg()) dividend = dividend.neg();
if (other.is_neg()) divisor = divisor.neg();
std::tie(quotient, remainder) = dividend.udivmod(divisor);
if (dividend.is_neg() != divisor.is_neg()) quotient = quotient.neg();
if (dividend.is_neg()) remainder = remainder.neg();
if (is_neg() != other.is_neg()) quotient = quotient.neg();
if (is_neg()) remainder = remainder.neg();
return {quotient.template trunc<Bits>(), remainder.template trunc<Bits>()};
}
};
@ -941,6 +946,55 @@ struct metadata {
assert(value_type == DOUBLE);
return double_value;
}
// Internal CXXRTL use only.
static std::map<std::string, metadata> deserialize(const char *ptr) {
std::map<std::string, metadata> result;
std::string name;
// Grammar:
// string ::= [^\0]+ \0
// metadata ::= [uid] .{8} | s <string>
// map ::= ( <string> <metadata> )* \0
for (;;) {
if (*ptr) {
name += *ptr++;
} else if (!name.empty()) {
ptr++;
auto get_u64 = [&]() {
uint64_t result = 0;
for (size_t count = 0; count < 8; count++)
result = (result << 8) | *ptr++;
return result;
};
char type = *ptr++;
if (type == 'u') {
uint64_t value = get_u64();
result.emplace(name, value);
} else if (type == 'i') {
int64_t value = (int64_t)get_u64();
result.emplace(name, value);
} else if (type == 'd') {
double dvalue;
uint64_t uvalue = get_u64();
static_assert(sizeof(dvalue) == sizeof(uvalue), "double must be 64 bits in size");
memcpy(&dvalue, &uvalue, sizeof(dvalue));
result.emplace(name, dvalue);
} else if (type == 's') {
std::string value;
while (*ptr)
value += *ptr++;
ptr++;
result.emplace(name, value);
} else {
assert(false && "Unknown type specifier");
return result;
}
name.clear();
} else {
return result;
}
}
}
};
typedef std::map<std::string, metadata> metadata_map;
@ -1010,22 +1064,24 @@ struct observer {
// Default member initializers would make this a non-aggregate-type in C++11, so they are commented out.
struct fmt_part {
enum {
STRING = 0,
LITERAL = 0,
INTEGER = 1,
CHARACTER = 2,
VLOG_TIME = 3,
STRING = 2,
UNICHAR = 3,
VLOG_TIME = 4,
} type;
// STRING type
// LITERAL type
std::string str;
// INTEGER/CHARACTER types
// INTEGER/STRING/UNICHAR types
// + value<Bits> val;
// INTEGER/CHARACTER/VLOG_TIME types
// INTEGER/STRING/VLOG_TIME types
enum {
RIGHT = 0,
LEFT = 1,
NUMERIC = 2,
} justify; // = RIGHT;
char padding; // = '\0';
size_t width; // = 0;
@ -1033,7 +1089,14 @@ struct fmt_part {
// INTEGER type
unsigned base; // = 10;
bool signed_; // = false;
bool plus; // = false;
enum {
MINUS = 0,
PLUS_MINUS = 1,
SPACE_MINUS = 2,
} sign; // = MINUS;
bool hex_upper; // = false;
bool show_base; // = false;
bool group; // = false;
// VLOG_TIME type
bool realtime; // = false;
@ -1049,11 +1112,12 @@ struct fmt_part {
// We might want to replace some of these bit() calls with direct
// chunk access if it turns out to be slow enough to matter.
std::string buf;
std::string prefix;
switch (type) {
case STRING:
case LITERAL:
return str;
case CHARACTER: {
case STRING: {
buf.reserve(Bits/8);
for (int i = 0; i < Bits; i += 8) {
char ch = 0;
@ -1067,35 +1131,76 @@ struct fmt_part {
break;
}
case UNICHAR: {
uint32_t codepoint = val.template zcast<32>().template get<uint32_t>();
if (codepoint >= 0x10000)
buf += (char)(0xf0 | (codepoint >> 18));
else if (codepoint >= 0x800)
buf += (char)(0xe0 | (codepoint >> 12));
else if (codepoint >= 0x80)
buf += (char)(0xc0 | (codepoint >> 6));
else
buf += (char)codepoint;
if (codepoint >= 0x10000)
buf += (char)(0x80 | ((codepoint >> 12) & 0x3f));
if (codepoint >= 0x800)
buf += (char)(0x80 | ((codepoint >> 6) & 0x3f));
if (codepoint >= 0x80)
buf += (char)(0x80 | ((codepoint >> 0) & 0x3f));
break;
}
case INTEGER: {
size_t width = Bits;
bool negative = signed_ && val.is_neg();
if (negative) {
prefix = "-";
val = val.neg();
} else {
switch (sign) {
case MINUS: break;
case PLUS_MINUS: prefix = "+"; break;
case SPACE_MINUS: prefix = " "; break;
}
}
size_t val_width = Bits;
if (base != 10) {
width = 0;
val_width = 1;
for (size_t index = 0; index < Bits; index++)
if (val.bit(index))
width = index + 1;
val_width = index + 1;
}
if (base == 2) {
for (size_t i = width; i > 0; i--)
buf += (val.bit(i - 1) ? '1' : '0');
if (show_base)
prefix += "0b";
for (size_t index = 0; index < val_width; index++) {
if (group && index > 0 && index % 4 == 0)
buf += '_';
buf += (val.bit(index) ? '1' : '0');
}
} else if (base == 8 || base == 16) {
if (show_base)
prefix += (base == 16) ? (hex_upper ? "0X" : "0x") : "0o";
size_t step = (base == 16) ? 4 : 3;
for (size_t index = 0; index < width; index += step) {
for (size_t index = 0; index < val_width; index += step) {
if (group && index > 0 && index % (4 * step) == 0)
buf += '_';
uint8_t value = val.bit(index) | (val.bit(index + 1) << 1) | (val.bit(index + 2) << 2);
if (step == 4)
value |= val.bit(index + 3) << 3;
buf += "0123456789abcdef"[value];
buf += (hex_upper ? "0123456789ABCDEF" : "0123456789abcdef")[value];
}
std::reverse(buf.begin(), buf.end());
} else if (base == 10) {
bool negative = signed_ && val.is_neg();
if (negative)
val = val.neg();
if (show_base)
prefix += "0d";
if (val.is_zero())
buf += '0';
value<(Bits > 4 ? Bits : 4)> xval = val.template zext<(Bits > 4 ? Bits : 4)>();
size_t index = 0;
while (!xval.is_zero()) {
if (group && index > 0 && index % 3 == 0)
buf += '_';
value<(Bits > 4 ? Bits : 4)> quotient, remainder;
if (Bits >= 4)
std::tie(quotient, remainder) = xval.udivmod(value<(Bits > 4 ? Bits : 4)>{10u});
@ -1103,11 +1208,18 @@ struct fmt_part {
std::tie(quotient, remainder) = std::make_pair(value<(Bits > 4 ? Bits : 4)>{0u}, xval);
buf += '0' + remainder.template trunc<4>().template get<uint8_t>();
xval = quotient;
index++;
}
if (negative || plus)
buf += negative ? '-' : '+';
std::reverse(buf.begin(), buf.end());
} else assert(false && "Unsupported base for fmt_part");
if (justify == NUMERIC && group && padding == '0') {
int group_size = base == 10 ? 3 : 4;
while (prefix.size() + buf.size() < width) {
if (buf.size() % (group_size + 1) == group_size)
buf += '_';
buf += '0';
}
}
std::reverse(buf.begin(), buf.end());
break;
}
@ -1123,17 +1235,29 @@ struct fmt_part {
std::string str;
assert(width == 0 || padding != '\0');
if (justify == RIGHT && buf.size() < width) {
size_t pad_width = width - buf.size();
if (padding == '0' && (buf.front() == '+' || buf.front() == '-')) {
str += buf.front();
buf.erase(0, 1);
}
str += std::string(pad_width, padding);
if (prefix.size() + buf.size() < width) {
size_t pad_width = width - prefix.size() - buf.size();
switch (justify) {
case LEFT:
str += prefix;
str += buf;
str += std::string(pad_width, padding);
break;
case RIGHT:
str += std::string(pad_width, padding);
str += prefix;
str += buf;
break;
case NUMERIC:
str += prefix;
str += std::string(pad_width, padding);
str += buf;
break;
}
} else {
str += prefix;
str += buf;
}
str += buf;
if (justify == LEFT && buf.size() < width)
str += std::string(width - buf.size(), padding);
return str;
}
};
@ -1170,6 +1294,7 @@ struct debug_item : ::cxxrtl_object {
DRIVEN_SYNC = CXXRTL_DRIVEN_SYNC,
DRIVEN_COMB = CXXRTL_DRIVEN_COMB,
UNDRIVEN = CXXRTL_UNDRIVEN,
GENERATED = CXXRTL_GENERATED,
};
debug_item(const ::cxxrtl_object &object) : cxxrtl_object(object) {}
@ -1347,6 +1472,12 @@ struct debug_items {
});
}
// This overload exists to reduce excessive stack slot allocation in `CXXRTL_EXTREMELY_COLD void debug_info()`.
template<class... T>
void add(const std::string &base_path, const char *path, const char *serialized_item_attrs, T&&... args) {
add(base_path + path, debug_item(std::forward<T>(args)...), metadata::deserialize(serialized_item_attrs));
}
size_t count(const std::string &path) const {
if (table.count(path) == 0)
return 0;
@ -1393,6 +1524,11 @@ struct debug_scopes {
scope.cell_attrs = std::unique_ptr<debug_attrs>(new debug_attrs { cell_attrs });
}
// This overload exists to reduce excessive stack slot allocation in `CXXRTL_EXTREMELY_COLD void debug_info()`.
void add(const std::string &base_path, const char *path, const char *module_name, const char *serialized_module_attrs, const char *serialized_cell_attrs) {
add(base_path + path, module_name, metadata::deserialize(serialized_module_attrs), metadata::deserialize(serialized_cell_attrs));
}
size_t contains(const std::string &path) const {
return table.count(path);
}
@ -1452,7 +1588,7 @@ struct module {
// Compatibility method.
#if __has_attribute(deprecated)
__attribute__((deprecated("Use `debug_info(path, &items, /*scopes=*/nullptr);` instead. (`path` could be \"top \".)")))
__attribute__((deprecated("Use `debug_info(&items, /*scopes=*/nullptr, path);` instead.")))
#endif
void debug_info(debug_items &items, std::string path) {
debug_info(&items, /*scopes=*/nullptr, path);
@ -1634,7 +1770,7 @@ value<BitsY> shr_uu(const value<BitsA> &a, const value<BitsB> &b) {
template<size_t BitsY, size_t BitsA, size_t BitsB>
CXXRTL_ALWAYS_INLINE
value<BitsY> shr_su(const value<BitsA> &a, const value<BitsB> &b) {
return a.shr(b).template scast<BitsY>();
return a.template scast<BitsY>().shr(b);
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
@ -1875,7 +2011,7 @@ std::pair<value<BitsY>, value<BitsY>> divmod_uu(const value<BitsA> &a, const val
value<Bits> quotient;
value<Bits> remainder;
value<Bits> dividend = a.template zext<Bits>();
value<Bits> divisor = b.template zext<Bits>();
value<Bits> divisor = b.template trunc<BitsB>().template zext<Bits>();
std::tie(quotient, remainder) = dividend.udivmod(divisor);
return {quotient.template trunc<BitsY>(), remainder.template trunc<BitsY>()};
}

View file

@ -512,9 +512,10 @@ public:
spool &operator=(const spool &) = delete;
~spool() {
if (int fd = writefd.exchange(-1))
int fd;
if ((fd = writefd.exchange(-1)) != -1)
close(fd);
if (int fd = readfd.exchange(-1))
if ((fd = readfd.exchange(-1)) != -1)
close(fd);
}

View file

@ -50,9 +50,13 @@ class vcd_writer {
void emit_scope(const std::vector<std::string> &scope) {
assert(!streaming);
while (current_scope.size() > scope.size() ||
(current_scope.size() > 0 &&
current_scope[current_scope.size() - 1] != scope[current_scope.size() - 1])) {
size_t same_scope_count = 0;
while ((same_scope_count < current_scope.size()) &&
(same_scope_count < scope.size()) &&
(current_scope[same_scope_count] == scope[same_scope_count])) {
same_scope_count++;
}
while (current_scope.size() > same_scope_count) {
buffer += "$upscope $end\n";
current_scope.pop_back();
}
@ -123,6 +127,8 @@ class vcd_writer {
bool bit_curr = var.curr[bit / (8 * sizeof(chunk_t))] & (1 << (bit % (8 * sizeof(chunk_t))));
buffer += (bit_curr ? '1' : '0');
}
if (var.width == 0)
buffer += '0';
buffer += ' ';
emit_ident(var.ident);
buffer += '\n';

View file

@ -30,9 +30,9 @@
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
#define EDIF_DEF(_id) edif_names(RTLIL::unescape_id(_id), true).c_str()
#define EDIF_DEFR(_id, _ren, _bl, _br) edif_names(RTLIL::unescape_id(_id), true, _ren, _bl, _br).c_str()
#define EDIF_REF(_id) edif_names(RTLIL::unescape_id(_id), false).c_str()
#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)
struct EdifNames
{
@ -48,8 +48,8 @@ struct EdifNames
if (define) {
std::string new_id = operator()(id, false);
if (port_rename)
return stringf("(rename %s \"%s%c%d:%d%c\")", new_id.c_str(), id.c_str(), delim_left, range_left, range_right, delim_right);
return new_id != id ? stringf("(rename %s \"%s\")", new_id.c_str(), id.c_str()) : id;
return stringf("(rename %s \"%s%c%d:%d%c\")", new_id, id, delim_left, range_left, range_right, delim_right);
return new_id != id ? stringf("(rename %s \"%s\")", new_id, id) : id;
}
if (name_map.count(id) > 0)
@ -231,7 +231,8 @@ struct EdifBackend : public Backend {
*f << stringf(" (edifVersion 2 0 0)\n");
*f << stringf(" (edifLevel 0)\n");
*f << stringf(" (keywordMap (keywordLevel 0))\n");
*f << stringf(" (comment \"Generated by %s\")\n", yosys_version_str);
*f << stringf(" (comment \"Generated by %s\")\n", yosys_maybe_version());
*f << stringf(" (external LIB\n");
*f << stringf(" (edifLevel 0)\n");
@ -333,21 +334,21 @@ struct EdifBackend : public Backend {
auto add_prop = [&](IdString name, Const val) {
if ((val.flags & RTLIL::CONST_FLAG_STRING) != 0)
*f << stringf("\n (property %s (string \"%s\"))", EDIF_DEF(name), val.decode_string().c_str());
else if (val.bits.size() <= 32 && RTLIL::SigSpec(val).is_fully_def())
*f << stringf("\n (property %s (string \"%s\"))", EDIF_DEF(name), val.decode_string());
else if (val.size() <= 32 && RTLIL::SigSpec(val).is_fully_def())
*f << stringf("\n (property %s (integer %u))", EDIF_DEF(name), val.as_int());
else {
std::string hex_string = "";
for (size_t i = 0; i < val.bits.size(); i += 4) {
for (auto i = 0; i < val.size(); i += 4) {
int digit_value = 0;
if (i+0 < val.bits.size() && val.bits.at(i+0) == RTLIL::State::S1) digit_value |= 1;
if (i+1 < val.bits.size() && val.bits.at(i+1) == RTLIL::State::S1) digit_value |= 2;
if (i+2 < val.bits.size() && val.bits.at(i+2) == RTLIL::State::S1) digit_value |= 4;
if (i+3 < val.bits.size() && val.bits.at(i+3) == RTLIL::State::S1) digit_value |= 8;
if (i+0 < val.size() && val.at(i+0) == RTLIL::State::S1) digit_value |= 1;
if (i+1 < val.size() && val.at(i+1) == RTLIL::State::S1) digit_value |= 2;
if (i+2 < val.size() && val.at(i+2) == RTLIL::State::S1) digit_value |= 4;
if (i+3 < val.size() && val.at(i+3) == RTLIL::State::S1) digit_value |= 8;
char digit_str[2] = { "0123456789abcdef"[digit_value], 0 };
hex_string = std::string(digit_str) + hex_string;
}
*f << stringf("\n (property %s (string \"%d'h%s\"))", EDIF_DEF(name), GetSize(val.bits), hex_string.c_str());
*f << stringf("\n (property %s (string \"%d'h%s\"))", EDIF_DEF(name), GetSize(val), hex_string);
}
};
for (auto module : sorted_modules)
@ -512,13 +513,13 @@ struct EdifBackend : public Backend {
if (sig.wire == NULL && sig != RTLIL::State::S0 && sig != RTLIL::State::S1) {
if (sig == RTLIL::State::Sx) {
for (auto &ref : it.second)
log_warning("Exporting x-bit on %s as zero bit.\n", ref.first.c_str());
log_warning("Exporting x-bit on %s as zero bit.\n", ref.first);
sig = RTLIL::State::S0;
} else if (sig == RTLIL::State::Sz) {
continue;
} else {
for (auto &ref : it.second)
log_error("Don't know how to handle %s on %s.\n", log_signal(sig), ref.first.c_str());
log_error("Don't know how to handle %s on %s.\n", log_signal(sig), ref.first);
log_abort();
}
}
@ -535,7 +536,7 @@ struct EdifBackend : public Backend {
}
*f << stringf(" (net %s (joined\n", EDIF_DEF(netname));
for (auto &ref : it.second)
*f << stringf(" %s\n", ref.first.c_str());
*f << stringf(" %s\n", ref.first);
if (sig.wire == NULL) {
if (nogndvcc)
log_error("Design contains constant nodes (map with \"hilomap\" first).\n");
@ -576,7 +577,7 @@ struct EdifBackend : public Backend {
auto &refs = net_join_db.at(mapped_sig);
for (auto &ref : refs)
if (ref.second)
*f << stringf(" %s\n", ref.first.c_str());
*f << stringf(" %s\n", ref.first);
*f << stringf(" )");
if (attr_properties && raw_sig.wire != NULL)

View file

@ -149,7 +149,7 @@ std::string dump_const(const RTLIL::Const &data)
// Numeric (non-real) parameter.
else
{
int width = data.bits.size();
int width = data.size();
// If a standard 32-bit int, then emit standard int value like "56" or
// "-56". Firrtl supports negative-valued int literals.
@ -163,7 +163,7 @@ std::string dump_const(const RTLIL::Const &data)
for (int i = 0; i < width; i++)
{
switch (data.bits[i])
switch (data[i])
{
case State::S0: break;
case State::S1: int_val |= (1 << i); break;
@ -205,7 +205,7 @@ std::string dump_const(const RTLIL::Const &data)
for (int i = width - 1; i >= 0; i--)
{
log_assert(i < width);
switch (data.bits[i])
switch (data[i])
{
case State::S0: res_str += "0"; break;
case State::S1: res_str += "1"; break;
@ -253,7 +253,7 @@ void emit_extmodule(RTLIL::Cell *cell, RTLIL::Module *mod_instance, std::ostream
const std::string extmoduleFileinfo = getFileinfo(cell);
// Emit extmodule header.
f << stringf(" extmodule %s: %s\n", exported_name.c_str(), extmoduleFileinfo.c_str());
f << stringf(" extmodule %s: %s\n", exported_name, extmoduleFileinfo);
// Emit extmodule ports.
for (auto wire : mod_instance->wires())
@ -280,7 +280,7 @@ void emit_extmodule(RTLIL::Cell *cell, RTLIL::Module *mod_instance, std::ostream
// Emit extmodule "defname" field. This is the name of the verilog blackbox
// that is used when verilog is emitted, so we use the name of mod_instance
// here.
f << stringf("%sdefname = %s\n", indent.c_str(), blackbox_name.c_str());
f << stringf("%sdefname = %s\n", indent, blackbox_name);
// Emit extmodule generic parameters.
for (const auto &p : cell->parameters)
@ -301,7 +301,7 @@ void emit_extmodule(RTLIL::Cell *cell, RTLIL::Module *mod_instance, std::ostream
param_name.end()
);
f << stringf("%sparameter %s = %s\n", indent.c_str(), param_name.c_str(), param_value.c_str());
f << stringf("%sparameter %s = %s\n", indent, param_name, param_value);
}
f << "\n";
@ -347,7 +347,7 @@ void emit_elaborated_extmodules(RTLIL::Design *design, std::ostream &f)
auto modInstance = design->module(cell->type);
// Ensure that we actually have a module instance
if (modInstance == nullptr) {
log_error("Unknown cell type %s\n", cell->type.c_str());
log_error("Unknown cell type %s\n", cell->type);
return;
}
@ -417,7 +417,7 @@ struct FirrtlWorker
else
{
string wire_id = make_id(chunk.wire->name);
new_expr = stringf("bits(%s, %d, %d)", wire_id.c_str(), chunk.offset + chunk.width - 1, chunk.offset);
new_expr = stringf("bits(%s, %d, %d)", wire_id, chunk.offset + chunk.width - 1, chunk.offset);
}
if (expr.empty())
@ -465,7 +465,7 @@ struct FirrtlWorker
// If there is no instance for this, just return.
if (instModule == NULL)
{
log_warning("No instance for %s.%s\n", cell_type.c_str(), cell_name.c_str());
log_warning("No instance for %s.%s\n", cell_type, cell_name);
return;
}
@ -477,7 +477,7 @@ struct FirrtlWorker
instanceOf;
std::string cellFileinfo = getFileinfo(cell);
wire_exprs.push_back(stringf("%s" "inst %s%s of %s %s", indent.c_str(), cell_name.c_str(), cell_name_comment.c_str(), instanceName.c_str(), cellFileinfo.c_str()));
wire_exprs.push_back(stringf("%s" "inst %s%s of %s %s", indent, cell_name, cell_name_comment, instanceName, cellFileinfo));
for (auto it = cell->connections().begin(); it != cell->connections().end(); ++it) {
if (it->second.size() > 0) {
@ -490,7 +490,7 @@ struct FirrtlWorker
const SigSpec *sinkSig = nullptr;
switch (dir) {
case FD_INOUT:
log_warning("Instance port connection %s.%s is INOUT; treating as OUT\n", cell_type.c_str(), log_signal(it->second));
log_warning("Instance port connection %s.%s is INOUT; treating as OUT\n", cell_type, log_signal(it->second));
YS_FALLTHROUGH
case FD_OUT:
sourceExpr = firstName;
@ -498,27 +498,27 @@ struct FirrtlWorker
sinkSig = &secondSig;
break;
case FD_NODIRECTION:
log_warning("Instance port connection %s.%s is NODIRECTION; treating as IN\n", cell_type.c_str(), log_signal(it->second));
log_warning("Instance port connection %s.%s is NODIRECTION; treating as IN\n", cell_type, log_signal(it->second));
YS_FALLTHROUGH
case FD_IN:
sourceExpr = secondExpr;
sinkExpr = firstName;
break;
default:
log_error("Instance port %s.%s unrecognized connection direction 0x%x !\n", cell_type.c_str(), log_signal(it->second), dir);
log_error("Instance port %s.%s unrecognized connection direction 0x%x !\n", cell_type, log_signal(it->second), dir);
break;
}
// Check for subfield assignment.
std::string bitsString = "bits(";
if (sinkExpr.compare(0, bitsString.length(), bitsString) == 0) {
if (sinkSig == nullptr)
log_error("Unknown subfield %s.%s\n", cell_type.c_str(), sinkExpr.c_str());
log_error("Unknown subfield %s.%s\n", cell_type, sinkExpr);
// Don't generate the assignment here.
// Add the source and sink to the "reverse_wire_map" and we'll output the assignment
// as part of the coalesced subfield assignments for this wire.
register_reverse_wire_map(sourceExpr, *sinkSig);
} else {
wire_exprs.push_back(stringf("\n%s%s <= %s %s", indent.c_str(), sinkExpr.c_str(), sourceExpr.c_str(), cellFileinfo.c_str()));
wire_exprs.push_back(stringf("\n%s%s <= %s %s", indent, sinkExpr, sourceExpr, cellFileinfo));
}
}
}
@ -535,7 +535,7 @@ struct FirrtlWorker
int max_shift_width_bits = FIRRTL_MAX_DSH_WIDTH_ERROR - 1;
string max_shift_string = stringf("UInt<%d>(%d)", max_shift_width_bits, (1<<max_shift_width_bits) - 1);
// Deal with the difference in semantics between FIRRTL and verilog
result = stringf("mux(gt(%s, %s), %s, bits(%s, %d, 0))", b_expr.c_str(), max_shift_string.c_str(), max_shift_string.c_str(), b_expr.c_str(), max_shift_width_bits - 1);
result = stringf("mux(gt(%s, %s), %s, bits(%s, %d, 0))", b_expr, max_shift_string, max_shift_string, b_expr, max_shift_width_bits - 1);
}
return result;
}
@ -543,7 +543,7 @@ struct FirrtlWorker
void emit_module()
{
std::string moduleFileinfo = getFileinfo(module);
f << stringf(" module %s: %s\n", make_id(module->name), moduleFileinfo.c_str());
f << stringf(" module %s: %s\n", make_id(module->name), moduleFileinfo);
vector<string> port_decls, wire_decls, mem_exprs, cell_exprs, wire_exprs;
std::vector<Mem> memories = Mem::get_all_memories(module);
@ -565,12 +565,12 @@ struct FirrtlWorker
{
if (wire->port_input && wire->port_output)
log_error("Module port %s.%s is inout!\n", log_id(module), log_id(wire));
port_decls.push_back(stringf("%s%s %s: UInt<%d> %s\n", indent.c_str(), wire->port_input ? "input" : "output",
port_decls.push_back(stringf("%s%s %s: UInt<%d> %s\n", indent, wire->port_input ? "input" : "output",
wireName, wire->width, wireFileinfo.c_str()));
}
else
{
wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent.c_str(), wireName, wire->width, wireFileinfo.c_str()));
wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent, wireName, wire->width, wireFileinfo));
}
}
@ -602,7 +602,7 @@ struct FirrtlWorker
if (cell->type.in(ID($not), ID($logic_not), ID($_NOT_), ID($neg), ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_bool), ID($reduce_xnor)))
{
string a_expr = make_expr(cell->getPort(ID::A));
wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent.c_str(), y_id.c_str(), y_width, cellFileinfo.c_str()));
wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent, y_id, y_width, cellFileinfo));
if (a_signed) {
a_expr = "asSInt(" + a_expr + ")";
@ -610,7 +610,7 @@ struct FirrtlWorker
// Don't use the results of logical operations (a single bit) to control padding
if (!(cell->type.in(ID($eq), ID($eqx), ID($gt), ID($ge), ID($lt), ID($le), ID($ne), ID($nex), ID($reduce_bool), ID($logic_not)) && y_width == 1) ) {
a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width);
a_expr = stringf("pad(%s, %d)", a_expr, y_width);
}
// Assume the FIRRTL width is a single bit.
@ -622,27 +622,27 @@ struct FirrtlWorker
firrtl_width = a_width;
} else if (cell->type == ID($logic_not)) {
primop = "eq";
a_expr = stringf("%s, UInt(0)", a_expr.c_str());
a_expr = stringf("%s, UInt(0)", a_expr);
}
else if (cell->type == ID($reduce_and)) primop = "andr";
else if (cell->type == ID($reduce_or)) primop = "orr";
else if (cell->type == ID($reduce_xor)) primop = "xorr";
else if (cell->type == ID($reduce_xnor)) {
primop = "not";
a_expr = stringf("xorr(%s)", a_expr.c_str());
a_expr = stringf("xorr(%s)", a_expr);
}
else if (cell->type == ID($reduce_bool)) {
primop = "neq";
// Use the sign of the a_expr and its width as the type (UInt/SInt) and width of the comparand.
a_expr = stringf("%s, %cInt<%d>(0)", a_expr.c_str(), a_signed ? 'S' : 'U', a_width);
a_expr = stringf("%s, %cInt<%d>(0)", a_expr, a_signed ? 'S' : 'U', a_width);
}
string expr = stringf("%s(%s)", primop.c_str(), a_expr.c_str());
string expr = stringf("%s(%s)", primop, a_expr);
if ((firrtl_is_signed && !always_uint))
expr = stringf("asUInt(%s)", expr.c_str());
expr = stringf("asUInt(%s)", expr);
cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str()));
cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent, y_id, expr, cellFileinfo));
register_reverse_wire_map(y_id, cell->getPort(ID::Y));
continue;
@ -654,13 +654,13 @@ struct FirrtlWorker
string a_expr = make_expr(cell->getPort(ID::A));
string b_expr = make_expr(cell->getPort(ID::B));
std::string cellFileinfo = getFileinfo(cell);
wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent.c_str(), y_id.c_str(), y_width, cellFileinfo.c_str()));
wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent, y_id, y_width, cellFileinfo));
if (a_signed) {
a_expr = "asSInt(" + a_expr + ")";
// Expand the "A" operand to the result width
if (a_width < y_width) {
a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width);
a_expr = stringf("pad(%s, %d)", a_expr, y_width);
a_width = y_width;
}
}
@ -670,7 +670,7 @@ struct FirrtlWorker
b_expr = "asSInt(" + b_expr + ")";
// Expand the "B" operand to the result width
if (b_width < y_width) {
b_expr = stringf("pad(%s, %d)", b_expr.c_str(), y_width);
b_expr = stringf("pad(%s, %d)", b_expr, y_width);
b_width = y_width;
}
}
@ -680,11 +680,11 @@ struct FirrtlWorker
if (cell->type.in(ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($xor), ID($_XOR_), ID($xnor), ID($and), ID($_AND_), ID($or), ID($_OR_)))
{
if (a_width < y_width) {
a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width);
a_expr = stringf("pad(%s, %d)", a_expr, y_width);
a_width = y_width;
}
if (b_width < y_width) {
b_expr = stringf("pad(%s, %d)", b_expr.c_str(), y_width);
b_expr = stringf("pad(%s, %d)", b_expr, y_width);
b_width = y_width;
}
}
@ -856,23 +856,23 @@ struct FirrtlWorker
string expr;
// Deal with $xnor == ~^ (not xor)
if (primop == "xnor") {
expr = stringf("not(xor(%s, %s))", a_expr.c_str(), b_expr.c_str());
expr = stringf("not(xor(%s, %s))", a_expr, b_expr);
} else {
expr = stringf("%s(%s, %s)", primop.c_str(), a_expr.c_str(), b_expr.c_str());
expr = stringf("%s(%s, %s)", primop, a_expr, b_expr);
}
// Deal with FIRRTL's "shift widens" semantics, or the need to widen the FIRRTL result.
// If the operation is signed, the FIRRTL width will be 1 one bit larger.
if (extract_y_bits) {
expr = stringf("bits(%s, %d, 0)", expr.c_str(), y_width - 1);
expr = stringf("bits(%s, %d, 0)", expr, y_width - 1);
} else if (firrtl_is_signed && (firrtl_width + 1) < y_width) {
expr = stringf("pad(%s, %d)", expr.c_str(), y_width);
expr = stringf("pad(%s, %d)", expr, y_width);
}
if ((firrtl_is_signed && !always_uint))
expr = stringf("asUInt(%s)", expr.c_str());
expr = stringf("asUInt(%s)", expr);
cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str()));
cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent, y_id, expr, cellFileinfo));
register_reverse_wire_map(y_id, cell->getPort(ID::Y));
continue;
@ -885,11 +885,11 @@ struct FirrtlWorker
string a_expr = make_expr(cell->getPort(ID::A));
string b_expr = make_expr(cell->getPort(ID::B));
string s_expr = make_expr(cell->getPort(ID::S));
wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent.c_str(), y_id.c_str(), width, cellFileinfo.c_str()));
wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent, y_id, width, cellFileinfo));
string expr = stringf("mux(%s, %s, %s)", s_expr.c_str(), b_expr.c_str(), a_expr.c_str());
string expr = stringf("mux(%s, %s, %s)", s_expr, b_expr, a_expr);
cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str()));
cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent, y_id, expr, cellFileinfo));
register_reverse_wire_map(y_id, cell->getPort(ID::Y));
continue;
@ -911,9 +911,9 @@ struct FirrtlWorker
string expr = make_expr(cell->getPort(ID::D));
string clk_expr = "asClock(" + make_expr(cell->getPort(ID::CLK)) + ")";
wire_decls.push_back(stringf("%sreg %s: UInt<%d>, %s %s\n", indent.c_str(), y_id.c_str(), width, clk_expr.c_str(), cellFileinfo.c_str()));
wire_decls.push_back(stringf("%sreg %s: UInt<%d>, %s %s\n", indent, y_id, width, clk_expr, cellFileinfo));
cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str()));
cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent, y_id, expr, cellFileinfo));
register_reverse_wire_map(y_id, cell->getPort(ID::Q));
continue;
@ -926,7 +926,7 @@ struct FirrtlWorker
string a_expr = make_expr(cell->getPort(ID::A));
// Get the initial bit selector
string b_expr = make_expr(cell->getPort(ID::B));
wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent.c_str(), y_id.c_str(), y_width));
wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent, y_id, y_width));
if (cell->getParam(ID::B_SIGNED).as_bool()) {
// Use validif to constrain the selection (test the sign bit)
@ -934,9 +934,9 @@ struct FirrtlWorker
int b_sign = cell->parameters.at(ID::B_WIDTH).as_int() - 1;
b_expr = stringf("validif(not(bits(%s, %d, %d)), %s)", b_string, b_sign, b_sign, b_string);
}
string expr = stringf("dshr(%s, %s)", a_expr.c_str(), b_expr.c_str());
string expr = stringf("dshr(%s, %s)", a_expr, b_expr);
cell_exprs.push_back(stringf("%s%s <= %s\n", indent.c_str(), y_id.c_str(), expr.c_str()));
cell_exprs.push_back(stringf("%s%s <= %s\n", indent, y_id, expr));
register_reverse_wire_map(y_id, cell->getPort(ID::Y));
continue;
}
@ -948,21 +948,21 @@ struct FirrtlWorker
string b_expr = make_expr(cell->getPort(ID::B));
auto b_string = b_expr.c_str();
string expr;
wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent.c_str(), y_id.c_str(), y_width));
wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent, y_id, y_width));
if (cell->getParam(ID::B_SIGNED).as_bool()) {
// We generate a left or right shift based on the sign of b.
std::string dshl = stringf("bits(dshl(%s, %s), 0, %d)", a_expr.c_str(), gen_dshl(b_expr, b_width).c_str(), y_width);
std::string dshr = stringf("dshr(%s, %s)", a_expr.c_str(), b_string);
std::string dshl = stringf("bits(dshl(%s, %s), 0, %d)", a_expr, gen_dshl(b_expr, b_width), y_width);
std::string dshr = stringf("dshr(%s, %s)", a_expr, b_string);
expr = stringf("mux(%s < 0, %s, %s)",
b_string,
dshl.c_str(),
dshr.c_str()
);
} else {
expr = stringf("dshr(%s, %s)", a_expr.c_str(), b_string);
expr = stringf("dshr(%s, %s)", a_expr, b_string);
}
cell_exprs.push_back(stringf("%s%s <= %s\n", indent.c_str(), y_id.c_str(), expr.c_str()));
cell_exprs.push_back(stringf("%s%s <= %s\n", indent, y_id, expr));
register_reverse_wire_map(y_id, cell->getPort(ID::Y));
continue;
}
@ -973,10 +973,10 @@ struct FirrtlWorker
// Verilog appears to treat the result as signed, so if the result is wider than "A",
// we need to pad.
if (a_width < y_width) {
a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width);
a_expr = stringf("pad(%s, %d)", a_expr, y_width);
}
wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent.c_str(), y_id.c_str(), y_width));
cell_exprs.push_back(stringf("%s%s <= %s\n", indent.c_str(), y_id.c_str(), a_expr.c_str()));
wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent, y_id, y_width));
cell_exprs.push_back(stringf("%s%s <= %s\n", indent, y_id, a_expr));
register_reverse_wire_map(y_id, cell->getPort(ID::Y));
continue;
}
@ -999,7 +999,7 @@ struct FirrtlWorker
for (int i = 0; i < GetSize(mem.rd_ports); i++)
{
auto &port = mem.rd_ports[i];
string port_name(stringf("%s.r%d", mem_id.c_str(), i));
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));
@ -1010,17 +1010,17 @@ struct FirrtlWorker
string ena_expr = make_expr(State::S1);
string clk_expr = make_expr(State::S0);
rpe << stringf("%s%s.addr <= %s\n", indent.c_str(), port_name.c_str(), addr_expr.c_str());
rpe << stringf("%s%s.en <= %s\n", indent.c_str(), port_name.c_str(), ena_expr.c_str());
rpe << stringf("%s%s.clk <= asClock(%s)\n", indent.c_str(), port_name.c_str(), clk_expr.c_str());
rpe << stringf("%s%s.addr <= %s\n", indent, port_name, addr_expr);
rpe << stringf("%s%s.en <= %s\n", indent, port_name, ena_expr);
rpe << stringf("%s%s.clk <= asClock(%s)\n", indent, port_name, clk_expr);
cell_exprs.push_back(rpe.str());
register_reverse_wire_map(stringf("%s.data", port_name.c_str()), port.data);
register_reverse_wire_map(stringf("%s.data", port_name), port.data);
}
for (int i = 0; i < GetSize(mem.wr_ports); i++)
{
auto &port = mem.wr_ports[i];
string port_name(stringf("%s.w%d", mem_id.c_str(), i));
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));
@ -1037,18 +1037,18 @@ struct FirrtlWorker
string ena_expr = make_expr(port.en[0]);
string clk_expr = make_expr(port.clk);
string mask_expr = make_expr(State::S1);
wpe << stringf("%s%s.data <= %s\n", indent.c_str(), port_name.c_str(), data_expr.c_str());
wpe << stringf("%s%s.addr <= %s\n", indent.c_str(), port_name.c_str(), addr_expr.c_str());
wpe << stringf("%s%s.en <= %s\n", indent.c_str(), port_name.c_str(), ena_expr.c_str());
wpe << stringf("%s%s.clk <= asClock(%s)\n", indent.c_str(), port_name.c_str(), clk_expr.c_str());
wpe << stringf("%s%s.mask <= %s\n", indent.c_str(), port_name.c_str(), mask_expr.c_str());
wpe << stringf("%s%s.data <= %s\n", indent, port_name, data_expr);
wpe << stringf("%s%s.addr <= %s\n", indent, port_name, addr_expr);
wpe << stringf("%s%s.en <= %s\n", indent, port_name, ena_expr);
wpe << stringf("%s%s.clk <= asClock(%s)\n", indent, port_name, clk_expr);
wpe << stringf("%s%s.mask <= %s\n", indent, port_name, mask_expr);
cell_exprs.push_back(wpe.str());
}
std::ostringstream me;
me << stringf(" mem %s:\n", mem_id.c_str());
me << stringf(" mem %s:\n", mem_id);
me << stringf(" data-type => UInt<%d>\n", mem.width);
me << stringf(" depth => %d\n", mem.size);
for (int i = 0; i < GetSize(mem.rd_ports); i++)
@ -1068,8 +1068,8 @@ struct FirrtlWorker
int y_width = GetSize(conn.first);
string expr = make_expr(conn.second);
wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent.c_str(), y_id.c_str(), y_width));
cell_exprs.push_back(stringf("%s%s <= %s\n", indent.c_str(), y_id.c_str(), expr.c_str()));
wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent, y_id, y_width));
cell_exprs.push_back(stringf("%s%s <= %s\n", indent, y_id, expr));
register_reverse_wire_map(y_id, conn.first);
}
@ -1112,7 +1112,7 @@ struct FirrtlWorker
chunk_width++;
}
new_expr = stringf("bits(%s, %d, %d)", start_map.first.c_str(),
new_expr = stringf("bits(%s, %d, %d)", start_map.first,
start_map.second + chunk_width - 1, start_map.second);
is_valid = true;
}
@ -1135,13 +1135,13 @@ struct FirrtlWorker
if (is_valid) {
if (make_unconn_id) {
wire_decls.push_back(stringf("%swire %s: UInt<1> %s\n", indent.c_str(), unconn_id.c_str(), wireFileinfo.c_str()));
wire_decls.push_back(stringf("%swire %s: UInt<1> %s\n", indent, unconn_id, wireFileinfo));
// `invalid` is a firrtl construction for simulation so we will not
// tag it with a @[fileinfo] tag as it doesn't directly correspond to
// a specific line of verilog code.
wire_decls.push_back(stringf("%s%s is invalid\n", indent.c_str(), unconn_id.c_str()));
wire_decls.push_back(stringf("%s%s is invalid\n", indent, unconn_id));
}
wire_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), make_id(wire->name), expr.c_str(), wireFileinfo.c_str()));
wire_exprs.push_back(stringf("%s%s <= %s %s\n", indent, make_id(wire->name), expr, wireFileinfo));
} else {
if (make_unconn_id) {
unconn_id.clear();
@ -1149,7 +1149,7 @@ struct FirrtlWorker
// `invalid` is a firrtl construction for simulation so we will not
// tag it with a @[fileinfo] tag as it doesn't directly correspond to
// a specific line of verilog code.
wire_decls.push_back(stringf("%s%s is invalid\n", indent.c_str(), make_id(wire->name)));
wire_decls.push_back(stringf("%s%s is invalid\n", indent, make_id(wire->name)));
}
}
@ -1215,9 +1215,6 @@ struct FirrtlBackend : public Backend {
}
extra_args(f, filename, args, argidx);
if (!design->full_selection())
log_cmd_error("This command only operates on fully selected designs!\n");
log_header(design, "Executing FIRRTL backend.\n");
log_push();
@ -1226,11 +1223,12 @@ struct FirrtlBackend : public Backend {
Pass::call(design, "demuxmap");
Pass::call(design, "bwmuxmap");
used_names.clear();
namecache.clear();
autoid_counter = 0;
// Get the top module, or a reasonable facsimile - we need something for the circuit name.
Module *top = design->top_module();
Module *top = nullptr;
Module *last = nullptr;
// Generate module and wire names.
for (auto module : design->modules()) {
@ -1251,7 +1249,7 @@ struct FirrtlBackend : public Backend {
log_cmd_error("There is no top module in this design!\n");
std::string circuitFileinfo = getFileinfo(top);
*f << stringf("circuit %s: %s\n", make_id(top->name), circuitFileinfo.c_str());
*f << stringf("circuit %s: %s\n", make_id(top->name), circuitFileinfo);
emit_elaborated_extmodules(design, *f);
@ -1265,6 +1263,7 @@ struct FirrtlBackend : public Backend {
}
}
used_names.clear();
namecache.clear();
autoid_counter = 0;
}

View file

@ -0,0 +1,4 @@
OBJS += backends/functional/cxx.o
OBJS += backends/functional/smtlib.o
OBJS += backends/functional/smtlib_rosette.o
OBJS += backends/functional/test_generic.o

277
backends/functional/cxx.cc Normal file
View file

@ -0,0 +1,277 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2024 Emily Schmidt <emily@yosyshq.com>
* Copyright (C) 2024 National Technology and Engineering Solutions of Sandia, LLC
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "kernel/yosys.h"
#include "kernel/functional.h"
#include <ctype.h>
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
const char *reserved_keywords[] = {
"alignas","alignof","and","and_eq","asm","atomic_cancel","atomic_commit",
"atomic_noexcept","auto","bitand","bitor","bool","break","case",
"catch","char","char16_t","char32_t","char8_t","class","co_await",
"co_return","co_yield","compl","concept","const","const_cast","consteval",
"constexpr","constinit","continue","decltype","default","delete",
"do","double","dynamic_cast","else","enum","explicit","export",
"extern","false","float","for","friend","goto","if","inline",
"int","long","mutable","namespace","new","noexcept","not","not_eq",
"nullptr","operator","or","or_eq","private","protected","public",
"reflexpr","register","reinterpret_cast","requires","return","short",
"signed","sizeof","static","static_log_assert","static_cast","struct",
"switch","synchronized","template","this","thread_local","throw",
"true","try","typedef","typeid","typename","union","unsigned",
"using","virtual","void","volatile","wchar_t","while","xor","xor_eq",
nullptr
};
template<typename Id> struct CxxScope : public Functional::Scope<Id> {
CxxScope() {
for(const char **p = reserved_keywords; *p != nullptr; p++)
this->reserve(*p);
}
bool is_character_legal(char c, int index) override {
return isascii(c) && (isalpha(c) || (isdigit(c) && index > 0) || c == '_' || c == '$');
}
};
struct CxxType {
Functional::Sort sort;
CxxType(Functional::Sort sort) : sort(sort) {}
std::string to_string() const {
if(sort.is_memory()) {
return stringf("Memory<%d, %d>", sort.addr_width(), sort.data_width());
} else if(sort.is_signal()) {
return stringf("Signal<%d>", sort.width());
} else {
log_error("unknown sort");
}
}
};
using CxxWriter = Functional::Writer;
struct CxxStruct {
std::string name;
dict<IdString, CxxType> types;
CxxScope<IdString> scope;
CxxStruct(std::string name) : name(name)
{
scope.reserve("fn");
scope.reserve("visit");
}
void insert(IdString name, CxxType type) {
scope(name, name);
types.insert({name, type});
}
void print(CxxWriter &f) {
f.print("\tstruct {} {{\n", name);
for (auto p : types) {
f.print("\t\t{} {};\n", p.second.to_string(), scope(p.first, p.first));
}
f.print("\n\t\ttemplate <typename T> 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}}\n");
f.print("\t}};\n\n");
};
std::string operator[](IdString field) {
return scope(field, field);
}
};
std::string cxx_const(RTLIL::Const const &value) {
std::stringstream ss;
ss << "Signal<" << value.size() << ">(" << std::hex << std::showbase;
if(value.size() > 32) ss << "{";
for(int i = 0; i < value.size(); i += 32) {
if(i > 0) ss << ", ";
ss << value.extract(i, 32).as_int();
}
if(value.size() > 32) ss << "}";
ss << ")";
return ss.str();
}
template<class NodePrinter> struct CxxPrintVisitor : public Functional::AbstractVisitor<void> {
using Node = Functional::Node;
CxxWriter &f;
NodePrinter np;
CxxStruct &input_struct;
CxxStruct &state_struct;
CxxPrintVisitor(CxxWriter &f, NodePrinter np, CxxStruct &input_struct, CxxStruct &state_struct) : f(f), np(np), input_struct(input_struct), state_struct(state_struct) { }
template<typename... Args> void print(const char *fmt, Args&&... args) {
f.print_with(np, fmt, std::forward<Args>(args)...);
}
void buf(Node, Node n) override { print("{}", n); }
void slice(Node, Node a, int offset, int out_width) override { print("{0}.slice<{2}>({1})", a, offset, out_width); }
void zero_extend(Node, Node a, int out_width) override { print("{}.zero_extend<{}>()", a, out_width); }
void sign_extend(Node, Node a, int out_width) override { print("{}.sign_extend<{}>()", a, out_width); }
void concat(Node, Node a, Node b) override { print("{}.concat({})", a, b); }
void add(Node, Node a, Node b) override { print("{} + {}", a, b); }
void sub(Node, Node a, Node b) override { print("{} - {}", a, b); }
void mul(Node, Node a, Node b) override { print("{} * {}", a, b); }
void unsigned_div(Node, Node a, Node b) override { print("{} / {}", a, b); }
void unsigned_mod(Node, Node a, Node b) override { print("{} % {}", a, b); }
void bitwise_and(Node, Node a, Node b) override { print("{} & {}", a, b); }
void bitwise_or(Node, Node a, Node b) override { print("{} | {}", a, b); }
void bitwise_xor(Node, Node a, Node b) override { print("{} ^ {}", a, b); }
void bitwise_not(Node, Node a) override { print("~{}", a); }
void unary_minus(Node, Node a) override { print("-{}", a); }
void reduce_and(Node, Node a) override { print("{}.all()", a); }
void reduce_or(Node, Node a) override { print("{}.any()", a); }
void reduce_xor(Node, Node a) override { print("{}.parity()", a); }
void equal(Node, Node a, Node b) override { print("{} == {}", a, b); }
void not_equal(Node, Node a, Node b) override { print("{} != {}", a, b); }
void signed_greater_than(Node, Node a, Node b) override { print("{}.signed_greater_than({})", a, b); }
void signed_greater_equal(Node, Node a, Node b) override { print("{}.signed_greater_equal({})", a, b); }
void unsigned_greater_than(Node, Node a, Node b) override { print("{} > {}", a, b); }
void unsigned_greater_equal(Node, Node a, Node b) override { print("{} >= {}", a, b); }
void logical_shift_left(Node, Node a, Node b) override { print("{} << {}", a, b); }
void logical_shift_right(Node, Node a, Node b) override { print("{} >> {}", a, b); }
void arithmetic_shift_right(Node, Node a, Node b) override { print("{}.arithmetic_shift_right({})", a, b); }
void mux(Node, Node a, Node b, Node s) override { print("{2}.any() ? {1} : {0}", a, b, s); }
void constant(Node, RTLIL::Const const & value) override { print("{}", cxx_const(value)); }
void input(Node, IdString name, IdString kind) override { log_assert(kind == ID($input)); print("input.{}", input_struct[name]); }
void state(Node, IdString name, IdString kind) override { log_assert(kind == ID($state)); print("current_state.{}", state_struct[name]); }
void memory_read(Node, Node mem, Node addr) override { print("{}.read({})", mem, addr); }
void memory_write(Node, Node mem, Node addr, Node data) override { print("{}.write({}, {})", mem, addr, data); }
};
bool equal_def(RTLIL::Const const &a, RTLIL::Const const &b) {
if(a.size() != b.size()) return false;
for(int i = 0; i < a.size(); i++)
if((a[i] == State::S1) != (b[i] == State::S1))
return false;
return true;
}
struct CxxModule {
Functional::IR ir;
CxxStruct input_struct, output_struct, state_struct;
std::string module_name;
explicit CxxModule(Module *module) :
ir(Functional::IR::from_module(module)),
input_struct("Inputs"),
output_struct("Outputs"),
state_struct("State")
{
for (auto input : ir.inputs())
input_struct.insert(input->name, input->sort);
for (auto output : ir.outputs())
output_struct.insert(output->name, output->sort);
for (auto state : ir.states())
state_struct.insert(state->name, state->sort);
module_name = CxxScope<int>().unique_name(module->name);
}
void write_header(CxxWriter &f) {
f.print("#include \"sim.h\"\n\n");
}
void write_struct_def(CxxWriter &f) {
f.print("struct {} {{\n", module_name);
input_struct.print(f);
output_struct.print(f);
state_struct.print(f);
f.print("\tstatic void eval(Inputs const &, Outputs &, State const &, State &);\n");
f.print("\tstatic void initialize(State &);\n");
f.print("}};\n\n");
}
void write_initial_def(CxxWriter &f) {
f.print("void {0}::initialize({0}::State &state)\n{{\n", module_name);
for (auto state : ir.states()) {
if (state->sort.is_signal())
f.print("\tstate.{} = {};\n", state_struct[state->name], cxx_const(state->initial_value_signal()));
else if (state->sort.is_memory()) {
f.print("\t{{\n");
f.print("\t\tstd::array<Signal<{}>, {}> mem;\n", state->sort.data_width(), 1<<state->sort.addr_width());
const auto &contents = state->initial_value_memory();
f.print("\t\tmem.fill({});\n", cxx_const(contents.default_value()));
for(auto range : contents)
for(auto addr = range.base(); addr < range.limit(); addr++)
if(!equal_def(range[addr], contents.default_value()))
f.print("\t\tmem[{}] = {};\n", addr, cxx_const(range[addr]));
f.print("\t\tstate.{} = mem;\n", state_struct[state->name]);
f.print("\t}}\n");
}
}
f.print("}}\n\n");
}
void write_eval_def(CxxWriter &f) {
f.print("void {0}::eval({0}::Inputs const &input, {0}::Outputs &output, {0}::State const &current_state, {0}::State &next_state)\n{{\n", module_name);
CxxScope<int> locals;
locals.reserve("input");
locals.reserve("output");
locals.reserve("current_state");
locals.reserve("next_state");
auto node_name = [&](Functional::Node n) { return locals(n.id(), n.name()); };
CxxPrintVisitor printVisitor(f, node_name, input_struct, state_struct);
for (auto node : ir) {
f.print("\t{} {} = ", CxxType(node.sort()).to_string(), node_name(node));
node.visit(printVisitor);
f.print(";\n");
}
for (auto state : ir.states())
f.print("\tnext_state.{} = {};\n", state_struct[state->name], node_name(state->next_value()));
for (auto output : ir.outputs())
f.print("\toutput.{} = {};\n", output_struct[output->name], node_name(output->value()));
f.print("}}\n\n");
}
};
struct FunctionalCxxBackend : public Backend
{
FunctionalCxxBackend() : Backend("functional_cxx", "convert design to C++ using the functional backend") {}
void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log("TODO: add help message\n");
log("\n");
}
void printCxx(std::ostream &stream, std::string, Module *module)
{
CxxWriter f(stream);
CxxModule mod(module);
mod.write_header(f);
mod.write_struct_def(f);
mod.write_eval_def(f);
mod.write_initial_def(f);
}
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
{
log_header(design, "Executing Functional C++ backend.\n");
size_t argidx = 1;
extra_args(f, filename, args, argidx, design);
for (auto module : design->selected_modules()) {
log("Dumping module `%s'.\n", module->name);
printCxx(*f, filename, module);
}
}
} FunctionalCxxBackend;
PRIVATE_NAMESPACE_END

View file

@ -0,0 +1,418 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2024 Emily Schmidt <emily@yosyshq.com>
* Copyright (C) 2024 National Technology and Engineering Solutions of Sandia, LLC
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#ifndef SIM_H
#define SIM_H
#include <array>
#include <cassert>
#include <string>
#include <iostream>
#include <algorithm>
template<size_t n>
class Signal {
template<size_t m> friend class Signal;
std::array<bool, n> _bits;
public:
Signal() { }
Signal(uint32_t val)
{
for(size_t i = 0; i < n; i++)
if(i < 32)
_bits[i] = val & (1<<i);
else
_bits[i] = false;
}
Signal(std::initializer_list<uint32_t> vals)
{
size_t k, i;
k = 0;
for (auto val : vals) {
for(i = 0; i < 32; i++)
if(i + k < n)
_bits[i + k] = val & (1<<i);
k += 32;
}
for(; k < n; k++)
_bits[k] = false;
}
template<typename T>
static Signal from_array(T vals)
{
size_t k, i;
Signal ret;
k = 0;
for (auto val : vals) {
for(i = 0; i < 32; i++)
if(i + k < n)
ret._bits[i + k] = val & (1<<i);
k += 32;
}
for(; k < n; k++)
ret._bits[k] = false;
return ret;
}
static Signal from_signed(int32_t val)
{
Signal<n> ret;
for(size_t i = 0; i < n; i++)
if(i < 32)
ret._bits[i] = val & (1<<i);
else
ret._bits[i] = val < 0;
return ret;
}
static Signal repeat(bool b)
{
Signal<n> ret;
for(size_t i = 0; i < n; i++)
ret._bits[i] = b;
return ret;
}
int size() const { return n; }
bool operator[](int i) const { assert(n >= 0 && i < n); return _bits[i]; }
template<size_t m>
Signal<m> slice(size_t offset) const
{
Signal<m> ret;
assert(offset + m <= n);
std::copy(_bits.begin() + offset, _bits.begin() + offset + m, ret._bits.begin());
return ret;
}
bool any() const
{
for(int i = 0; i < n; i++)
if(_bits[i])
return true;
return false;
}
bool all() const
{
for(int i = 0; i < n; i++)
if(!_bits[i])
return false;
return true;
}
bool parity() const
{
bool result = false;
for(int i = 0; i < n; i++)
result ^= _bits[i];
return result;
}
bool sign() const { return _bits[n-1]; }
template<typename T>
T as_numeric() const
{
T ret = 0;
for(size_t i = 0; i < std::min<size_t>(sizeof(T) * 8, n); i++)
if(_bits[i])
ret |= ((T)1)<<i;
return ret;
}
template<typename T>
T as_numeric_clamped() const
{
for(size_t i = sizeof(T) * 8; i < n; i++)
if(_bits[i])
return ~((T)0);
return as_numeric<T>();
}
uint32_t as_int() const { return as_numeric<uint32_t>(); }
private:
std::string as_string_p2(int b) const {
std::string ret;
for(int i = (n - 1) - (n - 1) % b; i >= 0; i -= b)
ret += "0123456789abcdef"[(*this >> Signal<32>(i)).as_int() & ((1<<b)-1)];
return ret;
}
std::string as_string_b10() const {
std::string ret;
if(n < 4) return std::string() + (char)('0' + as_int());
Signal<n> t = *this;
Signal<n> b = 10;
do{
ret += (char)('0' + (t % b).as_int());
t = t / b;
}while(t.any());
std::reverse(ret.begin(), ret.end());
return ret;
}
public:
std::string as_string(int base = 16, bool showbase = true) const {
std::string ret;
if(showbase) {
ret += std::to_string(n);
switch(base) {
case 2: ret += "'b"; break;
case 8: ret += "'o"; break;
case 10: ret += "'d"; break;
case 16: ret += "'h"; break;
default: assert(0);
}
}
switch(base) {
case 2: return ret + as_string_p2(1);
case 8: return ret + as_string_p2(3);
case 10: return ret + as_string_b10();
case 16: return ret + as_string_p2(4);
default: assert(0);
}
}
friend std::ostream &operator << (std::ostream &os, Signal<n> const &s) { return os << s.as_string(); }
Signal<n> operator ~() const
{
Signal<n> ret;
for(size_t i = 0; i < n; i++)
ret._bits[i] = !_bits[i];
return ret;
}
Signal<n> operator -() const
{
Signal<n> ret;
int x = 1;
for(size_t i = 0; i < n; i++) {
x += (int)!_bits[i];
ret._bits[i] = (x & 1) != 0;
x >>= 1;
}
return ret;
}
Signal<n> operator +(Signal<n> const &b) const
{
Signal<n> ret;
int x = 0;
for(size_t i = 0; i < n; i++){
x += (int)_bits[i] + (int)b._bits[i];
ret._bits[i] = x & 1;
x >>= 1;
}
return ret;
}
Signal<n> operator -(Signal<n> const &b) const
{
Signal<n> ret;
int x = 1;
for(size_t i = 0; i < n; i++){
x += (int)_bits[i] + (int)!b._bits[i];
ret._bits[i] = x & 1;
x >>= 1;
}
return ret;
}
Signal<n> operator *(Signal<n> const &b) const
{
Signal<n> ret;
int x = 0;
for(size_t i = 0; i < n; i++){
for(size_t j = 0; j <= i; j++)
x += (int)_bits[j] & (int)b._bits[i-j];
ret._bits[i] = x & 1;
x >>= 1;
}
return ret;
}
private:
Signal<n> divmod(Signal<n> const &b, bool modulo) const
{
if(!b.any()) return 0;
Signal<n> q = 0;
Signal<n> r = 0;
for(size_t i = n; i-- != 0; ){
r = r << Signal<1>(1);
r._bits[0] = _bits[i];
if(r >= b){
r = r - b;
q._bits[i] = true;
}
}
return modulo ? r : q;
}
public:
Signal<n> operator /(Signal<n> const &b) const { return divmod(b, false); }
Signal<n> operator %(Signal<n> const &b) const { return divmod(b, true); }
bool operator ==(Signal<n> const &b) const
{
for(size_t i = 0; i < n; i++)
if(_bits[i] != b._bits[i])
return false;
return true;
}
bool operator >=(Signal<n> const &b) const
{
for(size_t i = n; i-- != 0; )
if(_bits[i] != b._bits[i])
return _bits[i];
return true;
}
bool operator >(Signal<n> const &b) const
{
for(size_t i = n; i-- != 0; )
if(_bits[i] != b._bits[i])
return _bits[i];
return false;
}
bool operator !=(Signal<n> const &b) const { return !(*this == b); }
bool operator <=(Signal<n> const &b) const { return b <= *this; }
bool operator <(Signal<n> const &b) const { return b < *this; }
bool signed_greater_than(Signal<n> const &b) const
{
if(_bits[n-1] != b._bits[n-1])
return b._bits[n-1];
return *this > b;
}
bool signed_greater_equal(Signal<n> const &b) const
{
if(_bits[n-1] != b._bits[n-1])
return b._bits[n-1];
return *this >= b;
}
Signal<n> operator &(Signal<n> const &b) const
{
Signal<n> ret;
for(size_t i = 0; i < n; i++)
ret._bits[i] = _bits[i] && b._bits[i];
return ret;
}
Signal<n> operator |(Signal<n> const &b) const
{
Signal<n> ret;
for(size_t i = 0; i < n; i++)
ret._bits[i] = _bits[i] || b._bits[i];
return ret;
}
Signal<n> operator ^(Signal<n> const &b) const
{
Signal<n> ret;
for(size_t i = 0; i < n; i++)
ret._bits[i] = _bits[i] != b._bits[i];
return ret;
}
template<size_t nb>
Signal<n> operator <<(Signal<nb> const &b) const
{
Signal<n> ret = 0;
size_t amount = b.template as_numeric_clamped<size_t>();
if(amount < n)
std::copy(_bits.begin(), _bits.begin() + (n - amount), ret._bits.begin() + amount);
return ret;
}
template<size_t nb>
Signal<n> operator >>(Signal<nb> const &b) const
{
Signal<n> ret = 0;
size_t amount = b.template as_numeric_clamped<size_t>();
if(amount < n)
std::copy(_bits.begin() + amount, _bits.end(), ret._bits.begin());
return ret;
}
template<size_t nb>
Signal<n> arithmetic_shift_right(Signal<nb> const &b) const
{
Signal<n> ret = Signal::repeat(sign());
size_t amount = b.template as_numeric_clamped<size_t>();
if(amount < n)
std::copy(_bits.begin() + amount, _bits.end(), ret._bits.begin());
return ret;
}
template<size_t m>
Signal<n+m> concat(Signal<m> const& b) const
{
Signal<n + m> ret;
std::copy(_bits.begin(), _bits.end(), ret._bits.begin());
std::copy(b._bits.begin(), b._bits.end(), ret._bits.begin() + n);
return ret;
}
template<size_t m>
Signal<m> zero_extend() const
{
assert(m >= n);
Signal<m> ret = 0;
std::copy(_bits.begin(), _bits.end(), ret._bits.begin());
return ret;
}
template<size_t m>
Signal<m> sign_extend() const
{
assert(m >= n);
Signal<m> ret = Signal<m>::repeat(sign());
std::copy(_bits.begin(), _bits.end(), ret._bits.begin());
return ret;
}
};
template<size_t a, size_t d>
class Memory {
std::array<Signal<d>, 1<<a> _contents;
public:
Memory() {}
Memory(std::array<Signal<d>, 1<<a> const &contents) : _contents(contents) {}
Signal<d> read(Signal<a> addr) const
{
return _contents[addr.template as_numeric<size_t>()];
}
Memory write(Signal<a> addr, Signal<d> data) const
{
Memory ret = *this;
ret._contents[addr.template as_numeric<size_t>()] = data;
return ret;
}
};
#endif

View file

@ -0,0 +1,295 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2024 Emily Schmidt <emily@yosyshq.com>
* Copyright (C) 2024 National Technology and Engineering Solutions of Sandia, LLC
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "kernel/functional.h"
#include "kernel/yosys.h"
#include "kernel/sexpr.h"
#include <ctype.h>
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
using SExprUtil::list;
const char *reserved_keywords[] = {
// reserved keywords from the smtlib spec
"BINARY", "DECIMAL", "HEXADECIMAL", "NUMERAL", "STRING", "_", "!", "as", "let", "exists", "forall", "match", "par",
"assert", "check-sat", "check-sat-assuming", "declare-const", "declare-datatype", "declare-datatypes",
"declare-fun", "declare-sort", "define-fun", "define-fun-rec", "define-funs-rec", "define-sort",
"exit", "get-assertions", "symbol", "sort", "get-assignment", "get-info", "get-model",
"get-option", "get-proof", "get-unsat-assumptions", "get-unsat-core", "get-value",
"pop", "push", "reset", "reset-assertions", "set-info", "set-logic", "set-option",
// reserved for our own purposes
"pair", "Pair", "first", "second",
"inputs", "state",
nullptr
};
struct SmtScope : public Functional::Scope<int> {
SmtScope() {
for(const char **p = reserved_keywords; *p != nullptr; p++)
reserve(*p);
}
bool is_character_legal(char c, int index) override {
return isascii(c) && (isalpha(c) || (isdigit(c) && index > 0) || strchr("~!@$%^&*_-+=<>.?/", c));
}
};
struct SmtSort {
Functional::Sort sort;
SmtSort(Functional::Sort sort) : sort(sort) {}
SExpr to_sexpr() const {
if(sort.is_memory()) {
return list("Array", list("_", "BitVec", sort.addr_width()), list("_", "BitVec", sort.data_width()));
} else if(sort.is_signal()) {
return list("_", "BitVec", sort.width());
} else {
log_error("unknown sort");
}
}
};
class SmtStruct {
struct Field {
SmtSort sort;
std::string accessor;
};
idict<IdString> field_names;
vector<Field> fields;
SmtScope &scope;
public:
std::string name;
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));
fields.emplace_back(Field{sort, accessor});
}
void write_definition(SExprWriter &w) {
w.open(list("declare-datatype", name));
w.open(list());
w.open(list(name));
for(const auto &field : fields)
w << list(field.accessor, field.sort.to_sexpr());
w.close(3);
}
template<typename Fn> void write_value(SExprWriter &w, Fn fn) {
if(field_names.empty()) {
// Zero-argument constructors in SMTLIB must not be called as functions.
w << name;
} else {
w.open(list(name));
for(auto field_name : field_names) {
w << fn(field_name);
w.comment(RTLIL::unescape_id(field_name), true);
}
w.close();
}
}
SExpr access(SExpr record, IdString name) {
size_t i = field_names.at(name);
return list(fields[i].accessor, std::move(record));
}
};
std::string smt_const(RTLIL::Const const &c) {
std::string s = "#b";
for(int i = c.size(); i-- > 0; )
s += c[i] == State::S1 ? '1' : '0';
return s;
}
struct SmtPrintVisitor : public Functional::AbstractVisitor<SExpr> {
using Node = Functional::Node;
std::function<SExpr(Node)> n;
SmtStruct &input_struct;
SmtStruct &state_struct;
SmtPrintVisitor(SmtStruct &input_struct, SmtStruct &state_struct) : input_struct(input_struct), state_struct(state_struct) {}
SExpr from_bool(SExpr &&arg) {
return list("ite", std::move(arg), "#b1", "#b0");
}
SExpr to_bool(SExpr &&arg) {
return list("=", std::move(arg), "#b1");
}
SExpr extract(SExpr &&arg, int offset, int out_width = 1) {
return list(list("_", "extract", offset + out_width - 1, offset), std::move(arg));
}
SExpr buf(Node, Node a) override { return n(a); }
SExpr slice(Node, Node a, int offset, int out_width) override { return extract(n(a), offset, out_width); }
SExpr zero_extend(Node, Node a, int out_width) override { return list(list("_", "zero_extend", out_width - a.width()), n(a)); }
SExpr sign_extend(Node, Node a, int out_width) override { return list(list("_", "sign_extend", out_width - a.width()), n(a)); }
SExpr concat(Node, Node a, Node b) override { return list("concat", n(b), n(a)); }
SExpr add(Node, Node a, Node b) override { return list("bvadd", n(a), n(b)); }
SExpr sub(Node, Node a, Node b) override { return list("bvsub", n(a), n(b)); }
SExpr mul(Node, Node a, Node b) override { return list("bvmul", n(a), n(b)); }
SExpr unsigned_div(Node, Node a, Node b) override { return list("bvudiv", n(a), n(b)); }
SExpr unsigned_mod(Node, Node a, Node b) override { return list("bvurem", n(a), n(b)); }
SExpr bitwise_and(Node, Node a, Node b) override { return list("bvand", n(a), n(b)); }
SExpr bitwise_or(Node, Node a, Node b) override { return list("bvor", n(a), n(b)); }
SExpr bitwise_xor(Node, Node a, Node b) override { return list("bvxor", n(a), n(b)); }
SExpr bitwise_not(Node, Node a) override { return list("bvnot", n(a)); }
SExpr unary_minus(Node, Node a) override { return list("bvneg", n(a)); }
SExpr reduce_and(Node, Node a) override { return from_bool(list("=", n(a), smt_const(RTLIL::Const(State::S1, a.width())))); }
SExpr reduce_or(Node, Node a) override { return from_bool(list("distinct", n(a), smt_const(RTLIL::Const(State::S0, a.width())))); }
SExpr reduce_xor(Node, Node a) override {
vector<SExpr> s { "bvxor" };
for(int i = 0; i < a.width(); i++)
s.push_back(extract(n(a), i));
return s;
}
SExpr equal(Node, Node a, Node b) override { return from_bool(list("=", n(a), n(b))); }
SExpr not_equal(Node, Node a, Node b) override { return from_bool(list("distinct", n(a), n(b))); }
SExpr signed_greater_than(Node, Node a, Node b) override { return from_bool(list("bvsgt", n(a), n(b))); }
SExpr signed_greater_equal(Node, Node a, Node b) override { return from_bool(list("bvsge", n(a), n(b))); }
SExpr unsigned_greater_than(Node, Node a, Node b) override { return from_bool(list("bvugt", n(a), n(b))); }
SExpr unsigned_greater_equal(Node, Node a, Node b) override { return from_bool(list("bvuge", n(a), n(b))); }
SExpr extend(SExpr &&a, int in_width, int out_width) {
if(in_width < out_width)
return list(list("_", "zero_extend", out_width - in_width), std::move(a));
else
return std::move(a);
}
SExpr logical_shift_left(Node, Node a, Node b) override { return list("bvshl", n(a), extend(n(b), b.width(), a.width())); }
SExpr logical_shift_right(Node, Node a, Node b) override { return list("bvlshr", n(a), extend(n(b), b.width(), a.width())); }
SExpr arithmetic_shift_right(Node, Node a, Node b) override { return list("bvashr", n(a), extend(n(b), b.width(), a.width())); }
SExpr mux(Node, Node a, Node b, Node s) override { return list("ite", to_bool(n(s)), n(b), n(a)); }
SExpr constant(Node, RTLIL::Const const &value) override { return smt_const(value); }
SExpr memory_read(Node, Node mem, Node addr) override { return list("select", n(mem), n(addr)); }
SExpr memory_write(Node, Node mem, Node addr, Node data) override { return list("store", n(mem), n(addr), n(data)); }
SExpr input(Node, IdString name, IdString kind) override { log_assert(kind == ID($input)); return input_struct.access("inputs", name); }
SExpr state(Node, IdString name, IdString kind) override { log_assert(kind == ID($state)); return state_struct.access("state", name); }
};
struct SmtModule {
Functional::IR ir;
SmtScope scope;
std::string name;
SmtStruct input_struct;
SmtStruct output_struct;
SmtStruct state_struct;
SmtModule(Module *module)
: ir(Functional::IR::from_module(module))
, scope()
, name(scope.unique_name(module->name))
, input_struct(scope.unique_name(module->name.str() + "_Inputs"), scope)
, output_struct(scope.unique_name(module->name.str() + "_Outputs"), scope)
, state_struct(scope.unique_name(module->name.str() + "_State"), scope)
{
scope.reserve(name + "-initial");
for (auto input : ir.inputs())
input_struct.insert(input->name, input->sort);
for (auto output : ir.outputs())
output_struct.insert(output->name, output->sort);
for (auto state : ir.states())
state_struct.insert(state->name, state->sort);
}
void write_eval(SExprWriter &w)
{
w.push();
w.open(list("define-fun", name,
list(list("inputs", input_struct.name),
list("state", state_struct.name)),
list("Pair", output_struct.name, state_struct.name)));
auto inlined = [&](Functional::Node n) {
return n.fn() == Functional::Fn::constant;
};
SmtPrintVisitor visitor(input_struct, state_struct);
auto node_to_sexpr = [&](Functional::Node n) -> SExpr {
if(inlined(n))
return n.visit(visitor);
else
return scope(n.id(), n.name());
};
visitor.n = node_to_sexpr;
for(auto n : ir)
if(!inlined(n)) {
w.open(list("let", list(list(node_to_sexpr(n), n.visit(visitor)))), false);
w.comment(SmtSort(n.sort()).to_sexpr().to_string(), true);
}
w.open(list("pair"));
output_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.output(name).value()); });
state_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.state(name).next_value()); });
w.pop();
}
void write_initial(SExprWriter &w)
{
std::string initial = name + "-initial";
w << list("declare-const", initial, state_struct.name);
for (auto state : ir.states()) {
if(state->sort.is_signal())
w << list("assert", list("=", state_struct.access(initial, state->name), smt_const(state->initial_value_signal())));
else if(state->sort.is_memory()) {
const auto &contents = state->initial_value_memory();
for(int i = 0; i < 1<<state->sort.addr_width(); i++) {
auto addr = smt_const(RTLIL::Const(i, state->sort.addr_width()));
w << list("assert", list("=", list("select", state_struct.access(initial, state->name), addr), smt_const(contents[i])));
}
}
}
}
void write(std::ostream &out)
{
SExprWriter w(out);
input_struct.write_definition(w);
output_struct.write_definition(w);
state_struct.write_definition(w);
w << list("declare-datatypes",
list(list("Pair", 2)),
list(list("par", list("X", "Y"), list(list("pair", list("first", "X"), list("second", "Y"))))));
write_eval(w);
write_initial(w);
}
};
struct FunctionalSmtBackend : public Backend {
FunctionalSmtBackend() : Backend("functional_smt2", "Generate SMT-LIB from Functional IR") {}
void help() override { log("\nFunctional SMT Backend.\n\n"); }
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
{
log_header(design, "Executing Functional SMT Backend.\n");
size_t argidx = 1;
extra_args(f, filename, args, argidx, design);
for (auto module : design->selected_modules()) {
log("Processing module `%s`.\n", module->name);
SmtModule smt(module);
smt.write(*f);
}
}
} FunctionalSmtBackend;
PRIVATE_NAMESPACE_END

View file

@ -0,0 +1,373 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2024 Emily Schmidt <emily@yosyshq.com>
* Copyright (C) 2024 National Technology and Engineering Solutions of Sandia, LLC
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "kernel/functional.h"
#include "kernel/yosys.h"
#include "kernel/sexpr.h"
#include <ctype.h>
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
using SExprUtil::list;
const char *reserved_keywords[] = {
// reserved keywords from the racket spec
"struct", "lambda", "values", "extract", "concat", "bv", "let", "define", "cons", "list", "read", "write",
"stream", "error", "raise", "exit", "for", "begin", "when", "unless", "module", "require", "provide", "apply",
"if", "cond", "even", "odd", "any", "and", "or", "match", "command-line", "ffi-lib", "thread", "kill", "sync",
"future", "touch", "subprocess", "make-custodian", "custodian-shutdown-all", "current-custodian", "make", "tcp",
"connect", "prepare", "malloc", "free", "_fun", "_cprocedure", "build", "path", "file", "peek", "bytes",
"flush", "with", "lexer", "parser", "syntax", "interface", "send", "make-object", "new", "instantiate",
"define-generics", "set",
// reserved for our own purposes
"inputs", "state", "name",
nullptr
};
struct SmtrScope : public Functional::Scope<int> {
SmtrScope() {
for(const char **p = reserved_keywords; *p != nullptr; p++)
reserve(*p);
}
bool is_character_legal(char c, int index) override {
return isascii(c) && (isalpha(c) || (isdigit(c) && index > 0) || strchr("@$%^&_+=.", c));
}
};
struct SmtrSort {
Functional::Sort sort;
SmtrSort(Functional::Sort sort) : sort(sort) {}
SExpr to_sexpr() const {
if(sort.is_memory()) {
return list("list", list("bitvector", sort.addr_width()), list("bitvector", sort.data_width()));
} else if(sort.is_signal()) {
return list("bitvector", sort.width());
} else {
log_error("unknown sort");
}
}
};
class SmtrStruct {
struct Field {
SmtrSort sort;
std::string accessor;
std::string name;
};
idict<IdString> field_names;
vector<Field> fields;
SmtrScope &global_scope;
SmtrScope local_scope;
public:
std::string name;
SmtrStruct(std::string name, SmtrScope &scope) : global_scope(scope), local_scope(), name(name) {}
void insert(IdString field_name, SmtrSort sort) {
field_names(field_name);
auto base_name = local_scope.unique_name(field_name);
auto accessor = name + "-" + base_name;
global_scope.reserve(accessor);
fields.emplace_back(Field{sort, accessor, base_name});
}
void write_definition(SExprWriter &w) {
vector<SExpr> field_list;
for(const auto &field : fields) {
field_list.emplace_back(field.name);
}
w.push();
w.open(list("struct", name, field_list, "#:transparent"));
if (field_names.size()) {
for (const auto &field : fields) {
auto bv_type = field.sort.to_sexpr();
w.comment(field.name + " " + bv_type.to_string());
}
}
w.pop();
}
template<typename Fn> void write_value(SExprWriter &w, Fn fn) {
w.open(list(name));
for(auto field_name : field_names) {
w << fn(field_name);
w.comment(RTLIL::unescape_id(field_name), true);
}
w.close();
}
SExpr access(SExpr record, IdString name) {
size_t i = field_names.at(name);
return list(fields[i].accessor, std::move(record));
}
};
std::string smt_const(RTLIL::Const const &c) {
std::string s = "#b";
for(int i = c.size(); i-- > 0; )
s += c[i] == State::S1 ? '1' : '0';
return s;
}
struct SmtrPrintVisitor : public Functional::AbstractVisitor<SExpr> {
using Node = Functional::Node;
std::function<SExpr(Node)> n;
SmtrStruct &input_struct;
SmtrStruct &state_struct;
SmtrPrintVisitor(SmtrStruct &input_struct, SmtrStruct &state_struct) : input_struct(input_struct), state_struct(state_struct) {}
SExpr from_bool(SExpr &&arg) {
return list("bool->bitvector", std::move(arg));
}
SExpr to_bool(SExpr &&arg) {
return list("bitvector->bool", std::move(arg));
}
SExpr to_list(SExpr &&arg) {
return list("bitvector->bits", std::move(arg));
}
SExpr buf(Node, Node a) override { return n(a); }
SExpr slice(Node, Node a, int offset, int out_width) override { return list("extract", offset + out_width - 1, offset, n(a)); }
SExpr zero_extend(Node, Node a, int out_width) override { return list("zero-extend", n(a), list("bitvector", out_width)); }
SExpr sign_extend(Node, Node a, int out_width) override { return list("sign-extend", n(a), list("bitvector", out_width)); }
SExpr concat(Node, Node a, Node b) override { return list("concat", n(b), n(a)); }
SExpr add(Node, Node a, Node b) override { return list("bvadd", n(a), n(b)); }
SExpr sub(Node, Node a, Node b) override { return list("bvsub", n(a), n(b)); }
SExpr mul(Node, Node a, Node b) override { return list("bvmul", n(a), n(b)); }
SExpr unsigned_div(Node, Node a, Node b) override { return list("bvudiv", n(a), n(b)); }
SExpr unsigned_mod(Node, Node a, Node b) override { return list("bvurem", n(a), n(b)); }
SExpr bitwise_and(Node, Node a, Node b) override { return list("bvand", n(a), n(b)); }
SExpr bitwise_or(Node, Node a, Node b) override { return list("bvor", n(a), n(b)); }
SExpr bitwise_xor(Node, Node a, Node b) override { return list("bvxor", n(a), n(b)); }
SExpr bitwise_not(Node, Node a) override { return list("bvnot", n(a)); }
SExpr unary_minus(Node, Node a) override { return list("bvneg", n(a)); }
SExpr reduce_and(Node, Node a) override { return list("apply", "bvand", to_list(n(a))); }
SExpr reduce_or(Node, Node a) override { return list("apply", "bvor", to_list(n(a))); }
SExpr reduce_xor(Node, Node a) override { return list("apply", "bvxor", to_list(n(a))); }
SExpr equal(Node, Node a, Node b) override { return from_bool(list("bveq", n(a), n(b))); }
SExpr not_equal(Node, Node a, Node b) override { return from_bool(list("not", list("bveq", n(a), n(b)))); }
SExpr signed_greater_than(Node, Node a, Node b) override { return from_bool(list("bvsgt", n(a), n(b))); }
SExpr signed_greater_equal(Node, Node a, Node b) override { return from_bool(list("bvsge", n(a), n(b))); }
SExpr unsigned_greater_than(Node, Node a, Node b) override { return from_bool(list("bvugt", n(a), n(b))); }
SExpr unsigned_greater_equal(Node, Node a, Node b) override { return from_bool(list("bvuge", n(a), n(b))); }
SExpr extend(SExpr &&a, int in_width, int out_width) {
if(in_width < out_width)
return list("zero-extend", std::move(a), list("bitvector", out_width));
else
return std::move(a);
}
SExpr logical_shift_left(Node, Node a, Node b) override { return list("bvshl", n(a), extend(n(b), b.width(), a.width())); }
SExpr logical_shift_right(Node, Node a, Node b) override { return list("bvlshr", n(a), extend(n(b), b.width(), a.width())); }
SExpr arithmetic_shift_right(Node, Node a, Node b) override { return list("bvashr", n(a), extend(n(b), b.width(), a.width())); }
SExpr mux(Node, Node a, Node b, Node s) override { return list("if", to_bool(n(s)), n(b), n(a)); }
SExpr constant(Node, RTLIL::Const const& value) override { return list("bv", smt_const(value), value.size()); }
SExpr memory_read(Node, Node mem, Node addr) override { return list("list-ref-bv", n(mem), n(addr)); }
SExpr memory_write(Node, Node mem, Node addr, Node data) override { return list("list-set-bv", n(mem), n(addr), n(data)); }
SExpr input(Node, IdString name, IdString kind) override { log_assert(kind == ID($input)); return input_struct.access("inputs", name); }
SExpr state(Node, IdString name, IdString kind) override { log_assert(kind == ID($state)); return state_struct.access("state", name); }
};
struct SmtrModule {
Functional::IR ir;
SmtrScope scope;
std::string name;
bool use_assoc_list_helpers;
std::optional<std::string> input_helper_name;
std::optional<std::string> output_helper_name;
SmtrStruct input_struct;
SmtrStruct output_struct;
SmtrStruct state_struct;
SmtrModule(Module *module, bool assoc_list_helpers)
: ir(Functional::IR::from_module(module)), scope(), name(scope.unique_name(module->name)), use_assoc_list_helpers(assoc_list_helpers),
input_struct(scope.unique_name(module->name.str() + "_Inputs"), scope),
output_struct(scope.unique_name(module->name.str() + "_Outputs"), scope),
state_struct(scope.unique_name(module->name.str() + "_State"), scope)
{
scope.reserve(name + "_initial");
if (assoc_list_helpers) {
input_helper_name = scope.unique_name(module->name.str() + "_inputs_helper");
scope.reserve(*input_helper_name);
output_helper_name = scope.unique_name(module->name.str() + "_outputs_helper");
scope.reserve(*output_helper_name);
}
for (auto input : ir.inputs())
input_struct.insert(input->name, input->sort);
for (auto output : ir.outputs())
output_struct.insert(output->name, output->sort);
for (auto state : ir.states())
state_struct.insert(state->name, state->sort);
}
void write_eval(SExprWriter &w)
{
w.push();
w.open(list("define", list(name, "inputs", "state")));
auto inlined = [&](Functional::Node n) {
return n.fn() == Functional::Fn::constant;
};
SmtrPrintVisitor visitor(input_struct, state_struct);
auto node_to_sexpr = [&](Functional::Node n) -> SExpr {
if(inlined(n))
return n.visit(visitor);
else
return scope(n.id(), n.name());
};
visitor.n = node_to_sexpr;
for(auto n : ir)
if(!inlined(n)) {
w.open(list("let", list(list(node_to_sexpr(n), n.visit(visitor)))), false);
w.comment(SmtrSort(n.sort()).to_sexpr().to_string(), true);
}
w.open(list("cons"));
output_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.output(name).value()); });
state_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.state(name).next_value()); });
w.pop();
}
void write_initial(SExprWriter &w)
{
w.push();
auto initial = name + "_initial";
w.open(list("define", initial));
w.open(list(state_struct.name));
for (auto state : ir.states()) {
if (state->sort.is_signal())
w << list("bv", smt_const(state->initial_value_signal()), state->sort.width());
else if (state->sort.is_memory()) {
const auto &contents = state->initial_value_memory();
w.open(list("list"));
for(int i = 0; i < 1<<state->sort.addr_width(); i++) {
w << list("bv", smt_const(contents[i]), state->sort.data_width());
}
w.close();
}
}
w.pop();
}
void write_assoc_list_helpers(SExprWriter &w)
{
log_assert(output_helper_name && input_helper_name);
// Input struct keyword-based constructor.
w.push();
w.open(list("define"));
const auto inputs_name = "inputs";
w.open(list(*input_helper_name, inputs_name));
w.close();
w.open(list(input_struct.name));
for (auto input : ir.inputs()) {
w.push();
w.open(list("let"));
w.push();
w.open(list());
w.open(list("assoc-result"));
w << list("assoc", "\"" + RTLIL::unescape_id(input->name) + "\"", inputs_name);
w.pop();
w.open(list("if", "assoc-result"));
w << list("cdr", "assoc-result");
w.open(list("begin"));
w << list("fprintf", list("current-error-port"), "\"%s not found in inputs\"");
w << "'not-found";
w.pop();
}
w.pop();
// Output struct keyword-based destructuring
w.push();
w.open(list("define"));
const auto outputs_name = "outputs";
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.pop();
}
void write(std::ostream &out)
{
SExprWriter w(out);
input_struct.write_definition(w);
output_struct.write_definition(w);
state_struct.write_definition(w);
if (use_assoc_list_helpers) {
write_assoc_list_helpers(w);
}
write_eval(w);
write_initial(w);
}
};
struct FunctionalSmtrBackend : public Backend {
FunctionalSmtrBackend() : Backend("functional_rosette", "Generate Rosette compatible Racket from Functional IR") {}
void help() override {
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" write_functional_rosette [options] [filename]\n");
log("\n");
log("Functional Rosette Backend.\n");
log("\n");
log(" -provides\n");
log(" include 'provide' statement(s) for loading output as a module\n");
log(" -assoc-list-helpers\n");
log(" provide helper functions which convert inputs/outputs from/to association lists\n");
log(" \n");
log("\n");
}
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
{
auto provides = false;
auto assoc_list_helpers = false;
log_header(design, "Executing Functional Rosette Backend.\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
if (args[argidx] == "-provides")
provides = true;
else if (args[argidx] == "-assoc-list-helpers")
assoc_list_helpers = true;
else
break;
}
extra_args(f, filename, args, argidx);
*f << "#lang rosette/safe\n";
if (provides) {
*f << "(provide (all-defined-out))\n";
}
for (auto module : design->selected_modules()) {
log("Processing module `%s`.\n", module->name.c_str());
SmtrModule smtr(module, assoc_list_helpers);
smtr.write(*f);
}
}
} FunctionalSmtrBackend;
PRIVATE_NAMESPACE_END

View file

@ -0,0 +1,158 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2024 Emily Schmidt <emily@yosyshq.com>
* Copyright (C) 2024 National Technology and Engineering Solutions of Sandia, LLC
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "kernel/yosys.h"
#include "kernel/functional.h"
#include <random>
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
struct MemContentsTest {
int addr_width, data_width;
MemContents state;
using addr_t = MemContents::addr_t;
std::map<addr_t, RTLIL::Const> reference;
MemContentsTest(int addr_width, int data_width) : addr_width(addr_width), data_width(data_width), state(addr_width, data_width, RTLIL::Const(State::S0, data_width)) {}
void check() {
state.check();
for(auto addr = 0; addr < (1<<addr_width); addr++) {
auto it = reference.find(addr);
if(it != reference.end()) {
if(state.count_range(addr, addr + 1) != 1) goto error;
if(it->second != state[addr]) goto error;
} else {
if(state.count_range(addr, addr + 1) != 0) goto error;
}
}
return;
error:
printf("FAIL\n");
int digits = (data_width + 3) / 4;
for(auto addr = 0; addr < (1<<addr_width); addr++) {
if(addr % 8 == 0) printf("%.8x ", addr);
auto it = reference.find(addr);
bool ref_def = it != reference.end();
RTLIL::Const ref_value = ref_def ? it->second : state.default_value();
std::string ref_string = stringf("%.*x", digits, ref_value.as_int());
bool sta_def = state.count_range(addr, addr + 1) == 1;
RTLIL::Const sta_value = state[addr];
std::string sta_string = stringf("%.*x", digits, sta_value.as_int());
if(ref_def && sta_def) {
if(ref_value == sta_value) printf("%s%s", ref_string.c_str(), string(digits, ' ').c_str());
else printf("%s%s", ref_string.c_str(), sta_string.c_str());
} else if(ref_def) {
printf("%s%s", ref_string.c_str(), string(digits, 'M').c_str());
} else if(sta_def) {
printf("%s%s", sta_string.c_str(), string(digits, 'X').c_str());
} else {
printf("%s", string(2*digits, ' ').c_str());
}
printf(" ");
if(addr % 8 == 7) printf("\n");
}
printf("\n");
//log_abort();
}
void clear_range(addr_t begin_addr, addr_t end_addr) {
for(auto addr = begin_addr; addr != end_addr; addr++)
reference.erase(addr);
state.clear_range(begin_addr, end_addr);
check();
}
void insert_concatenated(addr_t addr, RTLIL::Const const &values) {
addr_t words = ((addr_t) values.size() + data_width - 1) / data_width;
for(addr_t i = 0; i < words; i++) {
reference.erase(addr + i);
reference.emplace(addr + i, values.extract(i * data_width, data_width));
}
state.insert_concatenated(addr, values);
check();
}
template<typename Rnd> void run(Rnd &rnd, int n) {
std::uniform_int_distribution<addr_t> addr_dist(0, (1<<addr_width) - 1);
std::poisson_distribution<addr_t> length_dist(10);
std::uniform_int_distribution<uint64_t> data_dist(0, ((uint64_t)1<<data_width) - 1);
while(n-- > 0) {
addr_t low = addr_dist(rnd);
//addr_t length = std::min((1<<addr_width) - low, length_dist(rnd));
//addr_t high = low + length - 1;
addr_t high = addr_dist(rnd);
if(low > high) std::swap(low, high);
if((rnd() & 7) == 0) {
log_debug("clear %.2x to %.2x\n", (int)low, (int)high);
clear_range(low, high + 1);
} else {
log_debug("insert %.2x to %.2x\n", (int)low, (int)high);
RTLIL::Const values;
for(addr_t addr = low; addr <= high; addr++) {
RTLIL::Const word(data_dist(rnd), data_width);
values.append(word);
}
insert_concatenated(low, values);
}
}
}
};
struct FunctionalTestGeneric : public Pass
{
FunctionalTestGeneric() : Pass("test_generic", "test the generic compute graph") {
internal();
}
void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log("TODO: add help message\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
log_header(design, "Executing Test Generic.\n");
size_t argidx = 1;
extra_args(args, argidx, design);
/*
MemContentsTest test(8, 16);
std::random_device seed_dev;
std::mt19937 rnd(23); //seed_dev());
test.run(rnd, 1000);
*/
for (auto module : design->selected_modules()) {
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";
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";
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";
}
}
} FunctionalCxxBackend;
PRIVATE_NAMESPACE_END

View file

@ -100,13 +100,13 @@ struct IntersynthBackend : public Backend {
}
extra_args(f, filename, args, argidx);
log("Output filename: %s\n", filename.c_str());
log("Output filename: %s\n", filename);
for (auto filename : libfiles) {
std::ifstream f;
f.open(filename.c_str());
if (f.fail())
log_error("Can't open lib file `%s'.\n", filename.c_str());
log_error("Can't open lib file `%s'.\n", filename);
RTLIL::Design *lib = new RTLIL::Design;
Frontend::frontend_call(lib, &f, filename, (filename.size() > 3 && filename.compare(filename.size()-3, std::string::npos, ".il") == 0 ? "rtlil" : "verilog"));
libs.push_back(lib);
@ -172,15 +172,15 @@ struct IntersynthBackend : public Backend {
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).c_str());
node_code += stringf(" %s %s", log_id(port.first), netname(conntypes_code, celltypes_code, constcells_code, sig));
}
}
for (auto &param : cell->parameters) {
celltype_code += stringf(" cfg:%d %s", int(param.second.bits.size()), log_id(param.first));
if (param.second.bits.size() != 32) {
celltype_code += stringf(" cfg:%d %s", int(param.second.size()), log_id(param.first));
if (param.second.size() != 32) {
node_code += stringf(" %s '", log_id(param.first));
for (int i = param.second.bits.size()-1; i >= 0; i--)
node_code += param.second.bits[i] == State::S1 ? "1" : "0";
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());
}
@ -199,13 +199,13 @@ struct IntersynthBackend : public Backend {
if (!flag_notypes) {
*f << stringf("### Connection Types\n");
for (auto code : conntypes_code)
*f << stringf("%s", code.c_str());
*f << stringf("%s", code);
*f << stringf("\n### Cell Types\n");
for (auto code : celltypes_code)
*f << stringf("%s", code.c_str());
*f << stringf("%s", code);
}
*f << stringf("\n### Netlists\n");
*f << stringf("%s", netlists_code.c_str());
*f << stringf("%s", netlists_code);
for (auto lib : libs)
delete lib;

View file

@ -124,8 +124,8 @@ struct JnyWriter
design->sort();
f << "{\n";
f << " \"$schema\": \"https://raw.githubusercontent.com/YosysHQ/yosys/master/misc/jny.schema.json\",\n";
f << stringf(" \"generator\": \"%s\",\n", escape_string(yosys_version_str).c_str());
f << " \"$schema\": \"https://raw.githubusercontent.com/YosysHQ/yosys/main/misc/jny.schema.json\",\n";
f << stringf(" \"generator\": \"%s\",\n", escape_string(yosys_maybe_version()));
f << " \"version\": \"0.0.1\",\n";
f << " \"invocation\": \"" << escape_string(invk) << "\",\n";
f << " \"features\": [";
@ -232,7 +232,7 @@ struct JnyWriter
const auto _indent = gen_indent(indent_level);
f << _indent << "{\n";
f << stringf(" %s\"name\": \"%s\",\n", _indent.c_str(), escape_string(RTLIL::unescape_id(mod->name)).c_str());
f << stringf(" %s\"name\": \"%s\",\n", _indent, escape_string(RTLIL::unescape_id(mod->name)));
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.c_str(), escape_string(RTLIL::unescape_id(con.first)).c_str());
f << stringf(" %s\"name\": \"%s\",\n", _indent, escape_string(RTLIL::unescape_id(con.first)));
f << _indent << " \"direction\": \"";
if (port_cell->input(con.first))
f << "i";
@ -290,7 +290,7 @@ struct JnyWriter
if (con.second.size() == 1)
f << _indent << " \"range\": [0, 0]\n";
else
f << stringf(" %s\"range\": [%d, %d]\n", _indent.c_str(), con.second.size(), 0);
f << stringf(" %s\"range\": [%d, %d]\n", _indent, con.second.size(), 0);
f << _indent << " }";
first_port = false;
@ -304,7 +304,7 @@ struct JnyWriter
const auto _indent = gen_indent(indent_level);
f << _indent << "{\n";
f << stringf(" %s\"type\": \"%s\",\n", _indent.c_str(), sort.first.c_str());
f << stringf(" %s\"type\": \"%s\",\n", _indent, sort.first);
f << _indent << " \"ports\": [\n";
write_cell_ports(port_cell, indent_level + 2);
@ -351,10 +351,10 @@ struct JnyWriter
f << stringf(",\n");
const auto param_val = param.second;
if (!param_val.empty()) {
f << stringf(" %s\"%s\": ", _indent.c_str(), escape_string(RTLIL::unescape_id(param.first)).c_str());
f << stringf(" %s\"%s\": ", _indent, escape_string(RTLIL::unescape_id(param.first)));
write_param_val(param_val);
} else {
f << stringf(" %s\"%s\": true", _indent.c_str(), escape_string(RTLIL::unescape_id(param.first)).c_str());
f << stringf(" %s\"%s\": true", _indent, escape_string(RTLIL::unescape_id(param.first)));
}
first_param = false;
@ -366,7 +366,7 @@ struct JnyWriter
log_assert(cell != nullptr);
f << _indent << " {\n";
f << stringf(" %s\"name\": \"%s\"", _indent.c_str(), escape_string(RTLIL::unescape_id(cell->name)).c_str());
f << stringf(" %s\"name\": \"%s\"", _indent, escape_string(RTLIL::unescape_id(cell->name)));
if (_include_connections) {
f << ",\n" << _indent << " \"connections\": [\n";
@ -426,7 +426,7 @@ struct JnyBackend : public Backend {
log(" Don't include property information in the netlist output.\n");
log("\n");
log("The JSON schema for JNY output files is located in the \"jny.schema.json\" file\n");
log("which is located at \"https://raw.githubusercontent.com/YosysHQ/yosys/master/misc/jny.schema.json\"\n");
log("which is located at \"https://raw.githubusercontent.com/YosysHQ/yosys/main/misc/jny.schema.json\"\n");
log("\n");
}
@ -553,7 +553,7 @@ struct JnyPass : public Pass {
ff->open(filename.c_str(), std::ofstream::trunc);
if (ff->fail()) {
delete ff;
log_error("Can't open file `%s' for writing: %s\n", filename.c_str(), strerror(errno));
log_error("Can't open file `%s' for writing: %s\n", filename, strerror(errno));
}
f = ff;
invk << filename;
@ -568,7 +568,7 @@ struct JnyPass : public Pass {
if (!empty) {
delete f;
} else {
log("%s", buf.str().c_str());
log("%s", buf.str());
}
}

View file

@ -34,6 +34,7 @@ struct JsonWriter
bool use_selection;
bool aig_mode;
bool compat_int_mode;
bool scopeinfo_mode;
Design *design;
Module *module;
@ -43,9 +44,9 @@ struct JsonWriter
dict<SigBit, string> sigids;
pool<Aig> aig_models;
JsonWriter(std::ostream &f, bool use_selection, bool aig_mode, bool compat_int_mode) :
JsonWriter(std::ostream &f, bool use_selection, bool aig_mode, bool compat_int_mode, bool scopeinfo_mode) :
f(f), use_selection(use_selection), aig_mode(aig_mode),
compat_int_mode(compat_int_mode) { }
compat_int_mode(compat_int_mode), scopeinfo_mode(scopeinfo_mode) { }
string get_string(string str)
{
@ -134,7 +135,7 @@ struct JsonWriter
bool first = true;
for (auto &param : parameters) {
f << stringf("%s\n", first ? "" : ",");
f << stringf(" %s%s: ", for_module ? "" : " ", get_name(param.first).c_str());
f << stringf(" %s%s: ", for_module ? "" : " ", get_name(param.first));
write_parameter_value(param.second);
first = false;
}
@ -154,7 +155,7 @@ struct JsonWriter
log_error("Module %s contains processes, which are not supported by JSON backend (run `proc` first).\n", log_id(module));
}
f << stringf(" %s: {\n", get_name(module->name).c_str());
f << stringf(" %s: {\n", get_name(module->name));
f << stringf(" \"attributes\": {");
write_parameters(module->attributes, /*for_module=*/true);
@ -173,7 +174,7 @@ struct JsonWriter
if (use_selection && !module->selected(w))
continue;
f << stringf("%s\n", first ? "" : ",");
f << stringf(" %s: {\n", get_name(n).c_str());
f << stringf(" %s: {\n", get_name(n));
f << stringf(" \"direction\": \"%s\",\n", w->port_input ? w->port_output ? "inout" : "input" : "output");
if (w->start_offset)
f << stringf(" \"offset\": %d,\n", w->start_offset);
@ -181,7 +182,7 @@ struct JsonWriter
f << stringf(" \"upto\": 1,\n");
if (w->is_signed)
f << stringf(" \"signed\": %d,\n", w->is_signed);
f << stringf(" \"bits\": %s\n", get_bits(w).c_str());
f << stringf(" \"bits\": %s\n", get_bits(w));
f << stringf(" }");
first = false;
}
@ -192,18 +193,16 @@ struct JsonWriter
for (auto c : module->cells()) {
if (use_selection && !module->selected(c))
continue;
// Eventually we will want to emit $scopeinfo, but currently this
// will break JSON netlist consumers like nextpnr
if (c->type == ID($scopeinfo))
if (!scopeinfo_mode && c->type == ID($scopeinfo))
continue;
f << stringf("%s\n", first ? "" : ",");
f << stringf(" %s: {\n", get_name(c->name).c_str());
f << stringf(" %s: {\n", get_name(c->name));
f << stringf(" \"hide_name\": %s,\n", c->name[0] == '$' ? "1" : "0");
f << stringf(" \"type\": %s,\n", get_name(c->type).c_str());
f << stringf(" \"type\": %s,\n", get_name(c->type));
if (aig_mode) {
Aig aig(c);
if (!aig.name.empty()) {
f << stringf(" \"model\": \"%s\",\n", aig.name.c_str());
f << stringf(" \"model\": \"%s\",\n", aig.name);
aig_models.insert(aig);
}
}
@ -221,7 +220,7 @@ struct JsonWriter
if (c->input(conn.first))
direction = c->output(conn.first) ? "inout" : "input";
f << stringf("%s\n", first2 ? "" : ",");
f << stringf(" %s: \"%s\"", get_name(conn.first).c_str(), direction.c_str());
f << stringf(" %s: \"%s\"", get_name(conn.first), direction);
first2 = false;
}
f << stringf("\n },\n");
@ -230,7 +229,7 @@ struct JsonWriter
bool first2 = true;
for (auto &conn : c->connections()) {
f << stringf("%s\n", first2 ? "" : ",");
f << stringf(" %s: %s", get_name(conn.first).c_str(), get_bits(conn.second).c_str());
f << stringf(" %s: %s", get_name(conn.first), get_bits(conn.second));
first2 = false;
}
f << stringf("\n }\n");
@ -246,7 +245,7 @@ struct JsonWriter
if (use_selection && !module->selected(it.second))
continue;
f << stringf("%s\n", first ? "" : ",");
f << stringf(" %s: {\n", get_name(it.second->name).c_str());
f << stringf(" %s: {\n", get_name(it.second->name));
f << stringf(" \"hide_name\": %s,\n", it.second->name[0] == '$' ? "1" : "0");
f << stringf(" \"attributes\": {");
write_parameters(it.second->attributes);
@ -266,9 +265,9 @@ struct JsonWriter
if (use_selection && !module->selected(w))
continue;
f << stringf("%s\n", first ? "" : ",");
f << stringf(" %s: {\n", get_name(w->name).c_str());
f << stringf(" %s: {\n", get_name(w->name));
f << stringf(" \"hide_name\": %s,\n", w->name[0] == '$' ? "1" : "0");
f << stringf(" \"bits\": %s,\n", get_bits(w).c_str());
f << stringf(" \"bits\": %s,\n", get_bits(w));
if (w->start_offset)
f << stringf(" \"offset\": %d,\n", w->start_offset);
if (w->upto)
@ -292,7 +291,7 @@ struct JsonWriter
design->sort();
f << stringf("{\n");
f << stringf(" \"creator\": %s,\n", get_string(yosys_version_str).c_str());
f << stringf(" \"creator\": %s,\n", get_string(yosys_maybe_version()));
f << stringf(" \"modules\": {\n");
vector<Module*> modules = use_selection ? design->selected_modules() : design->modules();
bool first_module = true;
@ -309,7 +308,7 @@ struct JsonWriter
for (auto &aig : aig_models) {
if (!first_model)
f << stringf(",\n");
f << stringf(" \"%s\": [\n", aig.name.c_str());
f << stringf(" \"%s\": [\n", aig.name);
int node_idx = 0;
for (auto &node : aig.nodes) {
if (node_idx != 0)
@ -353,6 +352,12 @@ struct JsonBackend : public Backend {
log(" emit 32-bit or smaller fully-defined parameter values directly\n");
log(" as JSON numbers (for compatibility with old parsers)\n");
log("\n");
log(" -selected\n");
log(" output only select module\n");
log("\n");
log(" -noscopeinfo\n");
log(" don't include $scopeinfo cells in the output\n");
log("\n");
log("\n");
log("The general syntax of the JSON output created by this command is as follows:\n");
log("\n");
@ -403,7 +408,7 @@ struct JsonBackend : public Backend {
log("\n");
log("The \"offset\" and \"upto\" fields are skipped if their value would be 0.\n");
log("They don't affect connection semantics, and are only used to preserve original\n");
log("HDL bit indexing.");
log("HDL bit indexing.\n");
log("And <cell_details> is:\n");
log("\n");
log(" {\n");
@ -597,6 +602,8 @@ struct JsonBackend : public Backend {
{
bool aig_mode = false;
bool compat_int_mode = false;
bool use_selection = false;
bool scopeinfo_mode = true;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
@ -609,13 +616,21 @@ struct JsonBackend : public Backend {
compat_int_mode = true;
continue;
}
if (args[argidx] == "-selected") {
use_selection = true;
continue;
}
if (args[argidx] == "-noscopeinfo") {
scopeinfo_mode = false;
continue;
}
break;
}
extra_args(f, filename, args, argidx);
log_header(design, "Executing JSON backend.\n");
JsonWriter json_writer(*f, false, aig_mode, compat_int_mode);
JsonWriter json_writer(*f, use_selection, aig_mode, compat_int_mode, scopeinfo_mode);
json_writer.write_design(design);
}
} JsonBackend;
@ -640,6 +655,9 @@ struct JsonPass : public Pass {
log(" emit 32-bit or smaller fully-defined parameter values directly\n");
log(" as JSON numbers (for compatibility with old parsers)\n");
log("\n");
log(" -noscopeinfo\n");
log(" don't include $scopeinfo cells in the output\n");
log("\n");
log("See 'help write_json' for a description of the JSON format used.\n");
log("\n");
}
@ -648,6 +666,7 @@ struct JsonPass : public Pass {
std::string filename;
bool aig_mode = false;
bool compat_int_mode = false;
bool scopeinfo_mode = true;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
@ -664,6 +683,10 @@ struct JsonPass : public Pass {
compat_int_mode = true;
continue;
}
if (args[argidx] == "-noscopeinfo") {
scopeinfo_mode = false;
continue;
}
break;
}
extra_args(args, argidx, design);
@ -678,20 +701,20 @@ struct JsonPass : public Pass {
ff->open(filename.c_str(), std::ofstream::trunc);
if (ff->fail()) {
delete ff;
log_error("Can't open file `%s' for writing: %s\n", filename.c_str(), strerror(errno));
log_error("Can't open file `%s' for writing: %s\n", filename, strerror(errno));
}
f = ff;
} else {
f = &buf;
}
JsonWriter json_writer(*f, true, aig_mode, compat_int_mode);
JsonWriter json_writer(*f, true, aig_mode, compat_int_mode, scopeinfo_mode);
json_writer.write_design(design);
if (!empty) {
delete f;
} else {
log("%s", buf.str().c_str());
log("%s", buf.str());
}
}
} JsonPass;

View file

@ -24,22 +24,33 @@
#include "rtlil_backend.h"
#include "kernel/yosys.h"
#include "kernel/utils.h"
#include <errno.h>
#include <iterator>
USING_YOSYS_NAMESPACE
using namespace RTLIL_BACKEND;
YOSYS_NAMESPACE_BEGIN
void RTLIL_BACKEND::dump_attributes(std::ostream &f, std::string indent, const RTLIL::AttrObject *obj)
{
for (const auto& [name, value] : reversed(obj->attributes)) {
f << stringf("%s" "attribute %s ", indent, name);
dump_const(f, value);
f << stringf("\n");
}
}
void RTLIL_BACKEND::dump_const(std::ostream &f, const RTLIL::Const &data, int width, int offset, bool autoint)
{
if (width < 0)
width = data.bits.size() - offset;
if ((data.flags & RTLIL::CONST_FLAG_STRING) == 0 || width != (int)data.bits.size()) {
width = data.size() - offset;
if ((data.flags & RTLIL::CONST_FLAG_STRING) == 0 || width != (int)data.size()) {
if (width == 32 && autoint) {
int32_t val = 0;
for (int i = 0; i < width; i++) {
log_assert(offset+i < (int)data.bits.size());
switch (data.bits[offset+i]) {
log_assert(offset+i < (int)data.size());
switch (data[offset+i]) {
case State::S0: break;
case State::S1: val |= 1 << i; break;
default: val = -1; break;
@ -50,13 +61,18 @@ void RTLIL_BACKEND::dump_const(std::ostream &f, const RTLIL::Const &data, int wi
return;
}
}
f << stringf("%d'", width);
if ((data.flags & RTLIL::CONST_FLAG_UNSIZED) == 0) {
f << stringf("%d'", width);
}
if (data.flags & RTLIL::CONST_FLAG_SIGNED) {
f << stringf("s");
}
if (data.is_fully_undef_x_only()) {
f << "x";
} else {
for (int i = offset+width-1; i >= offset; i--) {
log_assert(i < (int)data.bits.size());
switch (data.bits[i]) {
log_assert(i < (int)data.size());
switch (data[i]) {
case State::S0: f << stringf("0"); break;
case State::S1: f << stringf("1"); break;
case RTLIL::Sx: f << stringf("x"); break;
@ -93,11 +109,11 @@ void RTLIL_BACKEND::dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk,
dump_const(f, chunk.data, chunk.width, chunk.offset, autoint);
} else {
if (chunk.width == chunk.wire->width && chunk.offset == 0)
f << stringf("%s", chunk.wire->name.c_str());
f << stringf("%s", chunk.wire->name);
else if (chunk.width == 1)
f << stringf("%s [%d]", chunk.wire->name.c_str(), chunk.offset);
f << stringf("%s [%d]", chunk.wire->name, chunk.offset);
else
f << stringf("%s [%d:%d]", chunk.wire->name.c_str(), chunk.offset+chunk.width-1, chunk.offset);
f << stringf("%s [%d:%d]", chunk.wire->name, chunk.offset+chunk.width-1, chunk.offset);
}
}
@ -107,8 +123,9 @@ void RTLIL_BACKEND::dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig, boo
dump_sigchunk(f, sig.as_chunk(), autoint);
} else {
f << stringf("{ ");
for (auto it = sig.chunks().rbegin(); it != sig.chunks().rend(); ++it) {
dump_sigchunk(f, *it, false);
auto chunks = sig.chunks();
for (const auto& chunk : reversed(chunks)) {
dump_sigchunk(f, chunk, false);
f << stringf(" ");
}
f << stringf("}");
@ -117,12 +134,12 @@ void RTLIL_BACKEND::dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig, boo
void RTLIL_BACKEND::dump_wire(std::ostream &f, std::string indent, const RTLIL::Wire *wire)
{
for (auto &it : wire->attributes) {
f << stringf("%s" "attribute %s ", indent.c_str(), it.first.c_str());
dump_const(f, it.second);
f << stringf("\n");
dump_attributes(f, indent, wire);
if (wire->driverCell_) {
f << stringf("%s" "# driver %s %s\n", indent,
wire->driverCell()->name, wire->driverPort());
}
f << stringf("%s" "wire ", indent.c_str());
f << stringf("%s" "wire ", indent);
if (wire->width != 1)
f << stringf("width %d ", wire->width);
if (wire->upto)
@ -137,101 +154,85 @@ void RTLIL_BACKEND::dump_wire(std::ostream &f, std::string indent, const RTLIL::
f << stringf("inout %d ", wire->port_id);
if (wire->is_signed)
f << stringf("signed ");
f << stringf("%s\n", wire->name.c_str());
f << stringf("%s\n", wire->name);
}
void RTLIL_BACKEND::dump_memory(std::ostream &f, std::string indent, const RTLIL::Memory *memory)
{
for (auto &it : memory->attributes) {
f << stringf("%s" "attribute %s ", indent.c_str(), it.first.c_str());
dump_const(f, it.second);
f << stringf("\n");
}
f << stringf("%s" "memory ", indent.c_str());
dump_attributes(f, indent, memory);
f << stringf("%s" "memory ", indent);
if (memory->width != 1)
f << stringf("width %d ", memory->width);
if (memory->size != 0)
f << stringf("size %d ", memory->size);
if (memory->start_offset != 0)
f << stringf("offset %d ", memory->start_offset);
f << stringf("%s\n", memory->name.c_str());
f << stringf("%s\n", memory->name);
}
void RTLIL_BACKEND::dump_cell(std::ostream &f, std::string indent, const RTLIL::Cell *cell)
{
for (auto &it : cell->attributes) {
f << stringf("%s" "attribute %s ", indent.c_str(), it.first.c_str());
dump_const(f, it.second);
dump_attributes(f, indent, cell);
f << stringf("%s" "cell %s %s\n", indent, cell->type, cell->name);
for (const auto& [name, param] : reversed(cell->parameters)) {
f << stringf("%s parameter%s%s%s %s ", indent,
(param.flags & RTLIL::CONST_FLAG_SIGNED) != 0 ? " signed" : "",
(param.flags & RTLIL::CONST_FLAG_REAL) != 0 ? " real" : "",
(param.flags & RTLIL::CONST_FLAG_UNSIZED) != 0 ? " unsized" : "",
name);
dump_const(f, param);
f << stringf("\n");
}
f << stringf("%s" "cell %s %s\n", indent.c_str(), cell->type.c_str(), cell->name.c_str());
for (auto &it : cell->parameters) {
f << stringf("%s parameter%s%s %s ", indent.c_str(),
(it.second.flags & RTLIL::CONST_FLAG_SIGNED) != 0 ? " signed" : "",
(it.second.flags & RTLIL::CONST_FLAG_REAL) != 0 ? " real" : "",
it.first.c_str());
dump_const(f, it.second);
for (const auto& [port, sig] : reversed(cell->connections_)) {
f << stringf("%s connect %s ", indent, port);
dump_sigspec(f, sig);
f << stringf("\n");
}
for (auto &it : cell->connections()) {
f << stringf("%s connect %s ", indent.c_str(), it.first.c_str());
dump_sigspec(f, it.second);
f << stringf("\n");
}
f << stringf("%s" "end\n", indent.c_str());
f << stringf("%s" "end\n", indent);
}
void RTLIL_BACKEND::dump_proc_case_body(std::ostream &f, std::string indent, const RTLIL::CaseRule *cs)
{
for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it)
{
f << stringf("%s" "assign ", indent.c_str());
dump_sigspec(f, it->first);
for (const auto& [lhs, rhs] : cs->actions) {
f << stringf("%s" "assign ", indent);
dump_sigspec(f, lhs);
f << stringf(" ");
dump_sigspec(f, it->second);
dump_sigspec(f, rhs);
f << stringf("\n");
}
for (auto it = cs->switches.begin(); it != cs->switches.end(); ++it)
dump_proc_switch(f, indent, *it);
for (const auto& sw : cs->switches)
dump_proc_switch(f, indent, sw);
}
void RTLIL_BACKEND::dump_proc_switch(std::ostream &f, std::string indent, const RTLIL::SwitchRule *sw)
{
for (auto it = sw->attributes.begin(); it != sw->attributes.end(); ++it) {
f << stringf("%s" "attribute %s ", indent.c_str(), it->first.c_str());
dump_const(f, it->second);
f << stringf("\n");
}
dump_attributes(f, indent, sw);
f << stringf("%s" "switch ", indent.c_str());
f << stringf("%s" "switch ", indent);
dump_sigspec(f, sw->signal);
f << stringf("\n");
for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it)
for (const auto case_ : sw->cases)
{
for (auto ait = (*it)->attributes.begin(); ait != (*it)->attributes.end(); ++ait) {
f << stringf("%s attribute %s ", indent.c_str(), ait->first.c_str());
dump_const(f, ait->second);
f << stringf("\n");
}
f << stringf("%s case ", indent.c_str());
for (size_t i = 0; i < (*it)->compare.size(); i++) {
dump_attributes(f, indent, case_);
f << stringf("%s case ", indent);
for (size_t i = 0; i < case_->compare.size(); i++) {
if (i > 0)
f << stringf(" , ");
dump_sigspec(f, (*it)->compare[i]);
dump_sigspec(f, case_->compare[i]);
}
f << stringf("\n");
dump_proc_case_body(f, indent + " ", *it);
dump_proc_case_body(f, indent + " ", case_);
}
f << stringf("%s" "end\n", indent.c_str());
f << stringf("%s" "end\n", indent);
}
void RTLIL_BACKEND::dump_proc_sync(std::ostream &f, std::string indent, const RTLIL::SyncRule *sy)
{
f << stringf("%s" "sync ", indent.c_str());
f << stringf("%s" "sync ", indent);
switch (sy->type) {
case RTLIL::ST0: f << stringf("low ");
if (0) case RTLIL::ST1: f << stringf("high ");
@ -246,21 +247,17 @@ void RTLIL_BACKEND::dump_proc_sync(std::ostream &f, std::string indent, const RT
case RTLIL::STi: f << stringf("init\n"); break;
}
for (auto &it: sy->actions) {
f << stringf("%s update ", indent.c_str());
dump_sigspec(f, it.first);
for (const auto& [lhs, rhs] : sy->actions) {
f << stringf("%s update ", indent);
dump_sigspec(f, lhs);
f << stringf(" ");
dump_sigspec(f, it.second);
dump_sigspec(f, rhs);
f << stringf("\n");
}
for (auto &it: sy->mem_write_actions) {
for (auto it2 = it.attributes.begin(); it2 != it.attributes.end(); ++it2) {
f << stringf("%s attribute %s ", indent.c_str(), it2->first.c_str());
dump_const(f, it2->second);
f << stringf("\n");
}
f << stringf("%s memwr %s ", indent.c_str(), it.memid.c_str());
dump_attributes(f, indent, &it);
f << stringf("%s memwr %s ", indent, it.memid);
dump_sigspec(f, it.address);
f << stringf(" ");
dump_sigspec(f, it.data);
@ -274,21 +271,17 @@ void RTLIL_BACKEND::dump_proc_sync(std::ostream &f, std::string indent, const RT
void RTLIL_BACKEND::dump_proc(std::ostream &f, std::string indent, const RTLIL::Process *proc)
{
for (auto it = proc->attributes.begin(); it != proc->attributes.end(); ++it) {
f << stringf("%s" "attribute %s ", indent.c_str(), it->first.c_str());
dump_const(f, it->second);
f << stringf("\n");
}
f << stringf("%s" "process %s\n", indent.c_str(), proc->name.c_str());
dump_attributes(f, indent, proc);
f << stringf("%s" "process %s\n", indent, proc->name);
dump_proc_case_body(f, indent + " ", &proc->root_case);
for (auto it = proc->syncs.begin(); it != proc->syncs.end(); ++it)
dump_proc_sync(f, indent + " ", *it);
f << stringf("%s" "end\n", indent.c_str());
for (auto* sync : proc->syncs)
dump_proc_sync(f, indent + " ", sync);
f << stringf("%s" "end\n", indent);
}
void RTLIL_BACKEND::dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right)
{
f << stringf("%s" "connect ", indent.c_str());
f << stringf("%s" "connect ", indent);
dump_sigspec(f, left);
f << stringf(" ");
dump_sigspec(f, right);
@ -297,18 +290,14 @@ void RTLIL_BACKEND::dump_conn(std::ostream &f, std::string indent, const RTLIL::
void RTLIL_BACKEND::dump_module(std::ostream &f, std::string indent, RTLIL::Module *module, RTLIL::Design *design, bool only_selected, bool flag_m, bool flag_n)
{
bool print_header = flag_m || design->selected_whole_module(module->name);
bool print_body = !flag_n || !design->selected_whole_module(module->name);
bool print_header = flag_m || module->is_selected_whole();
bool print_body = !flag_n || !module->is_selected_whole();
if (print_header)
{
for (auto it = module->attributes.begin(); it != module->attributes.end(); ++it) {
f << stringf("%s" "attribute %s ", indent.c_str(), it->first.c_str());
dump_const(f, it->second);
f << stringf("\n");
}
dump_attributes(f, indent, module);
f << stringf("%s" "module %s\n", indent.c_str(), module->name.c_str());
f << stringf("%s" "module %s\n", indent, module->name);
if (!module->avail_parameters.empty()) {
if (only_selected)
@ -316,9 +305,9 @@ void RTLIL_BACKEND::dump_module(std::ostream &f, std::string indent, RTLIL::Modu
for (const auto &p : module->avail_parameters) {
const auto &it = module->parameter_default_values.find(p);
if (it == module->parameter_default_values.end()) {
f << stringf("%s" " parameter %s\n", indent.c_str(), p.c_str());
f << stringf("%s" " parameter %s\n", indent, p);
} else {
f << stringf("%s" " parameter %s ", indent.c_str(), p.c_str());
f << stringf("%s" " parameter %s ", indent, p);
dump_const(f, it->second);
f << stringf("\n");
}
@ -328,40 +317,40 @@ void RTLIL_BACKEND::dump_module(std::ostream &f, std::string indent, RTLIL::Modu
if (print_body)
{
for (auto it : module->wires())
if (!only_selected || design->selected(module, it)) {
for (const auto& [_, wire] : reversed(module->wires_))
if (!only_selected || design->selected(module, wire)) {
if (only_selected)
f << stringf("\n");
dump_wire(f, indent + " ", it);
dump_wire(f, indent + " ", wire);
}
for (auto it : module->memories)
if (!only_selected || design->selected(module, it.second)) {
for (const auto& [_, mem] : reversed(module->memories))
if (!only_selected || design->selected(module, mem)) {
if (only_selected)
f << stringf("\n");
dump_memory(f, indent + " ", it.second);
dump_memory(f, indent + " ", mem);
}
for (auto it : module->cells())
if (!only_selected || design->selected(module, it)) {
for (const auto& [_, cell] : reversed(module->cells_))
if (!only_selected || design->selected(module, cell)) {
if (only_selected)
f << stringf("\n");
dump_cell(f, indent + " ", it);
dump_cell(f, indent + " ", cell);
}
for (auto it : module->processes)
if (!only_selected || design->selected(module, it.second)) {
for (const auto& [_, process] : reversed(module->processes))
if (!only_selected || design->selected(module, process)) {
if (only_selected)
f << stringf("\n");
dump_proc(f, indent + " ", it.second);
dump_proc(f, indent + " ", process);
}
bool first_conn_line = true;
for (auto it = module->connections().begin(); it != module->connections().end(); ++it) {
for (const auto& [lhs, rhs] : module->connections()) {
bool show_conn = !only_selected || design->selected_whole_module(module->name);
if (!show_conn) {
RTLIL::SigSpec sigs = it->first;
sigs.append(it->second);
RTLIL::SigSpec sigs = lhs;
sigs.append(rhs);
for (auto &c : sigs.chunks()) {
if (c.wire == NULL || !design->selected(module, c.wire))
continue;
@ -371,14 +360,14 @@ void RTLIL_BACKEND::dump_module(std::ostream &f, std::string indent, RTLIL::Modu
if (show_conn) {
if (only_selected && first_conn_line)
f << stringf("\n");
dump_conn(f, indent + " ", it->first, it->second);
dump_conn(f, indent + " ", lhs, rhs);
first_conn_line = false;
}
}
}
if (print_header)
f << stringf("%s" "end\n", indent.c_str());
f << stringf("%s" "end\n", indent);
}
void RTLIL_BACKEND::dump_design(std::ostream &f, RTLIL::Design *design, bool only_selected, bool flag_m, bool flag_n)
@ -387,7 +376,7 @@ void RTLIL_BACKEND::dump_design(std::ostream &f, RTLIL::Design *design, bool onl
if (!flag_m) {
int count_selected_mods = 0;
for (auto module : design->modules()) {
for (auto* module : design->modules()) {
if (design->selected_whole_module(module->name))
flag_m = true;
if (design->selected(module))
@ -403,7 +392,7 @@ void RTLIL_BACKEND::dump_design(std::ostream &f, RTLIL::Design *design, bool onl
f << stringf("autoidx %d\n", autoidx);
}
for (auto module : design->modules()) {
for (const auto& [_, module] : reversed(design->modules_)) {
if (!only_selected || design->selected(module)) {
if (only_selected)
f << stringf("\n");
@ -431,10 +420,14 @@ struct RTLILBackend : public Backend {
log(" -selected\n");
log(" only write selected parts of the design.\n");
log("\n");
log(" -sort\n");
log(" sort design in-place (used to be default).\n");
log("\n");
}
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
{
bool selected = false;
bool do_sort = false;
log_header(design, "Executing RTLIL backend.\n");
@ -445,33 +438,24 @@ struct RTLILBackend : public Backend {
selected = true;
continue;
}
if (arg == "-sort") {
do_sort = true;
continue;
}
break;
}
extra_args(f, filename, args, argidx);
design->sort();
log("Output filename: %s\n", filename);
log("Output filename: %s\n", filename.c_str());
*f << stringf("# Generated by %s\n", yosys_version_str);
if (do_sort)
design->sort();
*f << stringf("# Generated by %s\n", yosys_maybe_version());
RTLIL_BACKEND::dump_design(*f, design, selected, true, false);
}
} RTLILBackend;
struct IlangBackend : public Backend {
IlangBackend() : Backend("ilang", "(deprecated) alias of write_rtlil") { }
void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log("See `help write_rtlil`.\n");
log("\n");
}
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
{
RTLILBackend.execute(f, filename, args, design);
}
} IlangBackend;
struct DumpPass : public Pass {
DumpPass() : Pass("dump", "print parts of the design in RTLIL format") { }
void help() override
@ -535,10 +519,10 @@ struct DumpPass : public Pass {
if (!empty) {
rewrite_filename(filename);
std::ofstream *ff = new std::ofstream;
ff->open(filename.c_str(), append ? std::ofstream::app : std::ofstream::trunc);
ff->open(filename, append ? std::ofstream::app : std::ofstream::trunc);
if (ff->fail()) {
delete ff;
log_error("Can't open file `%s' for writing: %s\n", filename.c_str(), strerror(errno));
log_error("Can't open file `%s' for writing: %s\n", filename, strerror(errno));
}
f = ff;
} else {
@ -550,7 +534,7 @@ struct DumpPass : public Pass {
if (!empty) {
delete f;
} else {
log("%s", buf.str().c_str());
log("%s", buf.str());
}
}
} DumpPass;

View file

@ -31,6 +31,7 @@
YOSYS_NAMESPACE_BEGIN
namespace RTLIL_BACKEND {
void dump_attributes(std::ostream &f, std::string indent, const RTLIL::AttrObject *obj);
void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int offset = 0, bool autoint = true);
void dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool autoint = true);
void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig, bool autoint = true);

View file

@ -218,8 +218,8 @@ struct SimplecWorker
s[i] -= 'a' - 'A';
util_declarations.push_back("");
util_declarations.push_back(stringf("#ifndef %s", s.c_str()));
util_declarations.push_back(stringf("#define %s", s.c_str()));
util_declarations.push_back(stringf("#ifndef %s", s));
util_declarations.push_back(stringf("#define %s", s));
}
string util_get_bit(const string &signame, int n, int idx)
@ -232,33 +232,33 @@ struct SimplecWorker
if (generated_utils.count(util_name) == 0)
{
util_ifdef_guard(util_name);
util_declarations.push_back(stringf("static inline bool %s(const %s *sig)", util_name.c_str(), sigtype(n).c_str()));
util_declarations.push_back(stringf("static inline bool %s(const %s *sig)", util_name, sigtype(n)));
util_declarations.push_back(stringf("{"));
int word_idx = idx / max_uintsize, word_offset = idx % max_uintsize;
string value_name = stringf("value_%d_%d", std::min(n-1, (word_idx+1)*max_uintsize-1), word_idx*max_uintsize);
util_declarations.push_back(stringf(" return (sig->%s >> %d) & 1;", value_name.c_str(), word_offset));
util_declarations.push_back(stringf(" return (sig->%s >> %d) & 1;", value_name, word_offset));
util_declarations.push_back(stringf("}"));
util_declarations.push_back(stringf("#endif"));
generated_utils.insert(util_name);
}
return stringf("%s(&%s)", util_name.c_str(), signame.c_str());
return stringf("%s(&%s)", util_name, signame);
}
string util_set_bit(const string &signame, int n, int idx, const string &expr)
{
if (n == 1 && idx == 0)
return stringf(" %s.value_0_0 = %s;", signame.c_str(), expr.c_str());
return stringf(" %s.value_0_0 = %s;", signame, expr);
string util_name = stringf("yosys_simplec_set_bit_%d_of_%d", idx, n);
if (generated_utils.count(util_name) == 0)
{
util_ifdef_guard(util_name);
util_declarations.push_back(stringf("static inline void %s(%s *sig, bool value)", util_name.c_str(), sigtype(n).c_str()));
util_declarations.push_back(stringf("static inline void %s(%s *sig, bool value)", util_name, sigtype(n)));
util_declarations.push_back(stringf("{"));
int word_idx = idx / max_uintsize, word_offset = idx % max_uintsize;
@ -266,9 +266,9 @@ struct SimplecWorker
#if 0
util_declarations.push_back(stringf(" if (value)"));
util_declarations.push_back(stringf(" sig->%s |= 1UL << %d;", value_name.c_str(), word_offset));
util_declarations.push_back(stringf(" sig->%s |= 1UL << %d;", value_name, word_offset));
util_declarations.push_back(stringf(" else"));
util_declarations.push_back(stringf(" sig->%s &= ~(1UL << %d);", value_name.c_str(), word_offset));
util_declarations.push_back(stringf(" sig->%s &= ~(1UL << %d);", value_name, word_offset));
#else
util_declarations.push_back(stringf(" sig->%s = (sig->%s & ~((uint%d_t)1 << %d)) | ((uint%d_t)value << %d);",
value_name.c_str(), value_name.c_str(), max_uintsize, word_offset, max_uintsize, word_offset));
@ -279,7 +279,7 @@ struct SimplecWorker
generated_utils.insert(util_name);
}
return stringf(" %s(&%s, %s);", util_name.c_str(), signame.c_str(), expr.c_str());
return stringf(" %s(&%s, %s);", util_name, signame, expr);
}
void create_module_struct(Module *mod)
@ -339,38 +339,38 @@ struct SimplecWorker
for (int i = 0; i < GetSize(topo.sorted); i++)
topoidx[mod->cell(topo.sorted[i])] = i;
string ifdef_name = stringf("yosys_simplec_%s_state_t", cid(mod->name).c_str());
string ifdef_name = stringf("yosys_simplec_%s_state_t", cid(mod->name));
for (int i = 0; i < GetSize(ifdef_name); i++)
if ('a' <= ifdef_name[i] && ifdef_name[i] <= 'z')
ifdef_name[i] -= 'a' - 'A';
struct_declarations.push_back("");
struct_declarations.push_back(stringf("#ifndef %s", ifdef_name.c_str()));
struct_declarations.push_back(stringf("#define %s", ifdef_name.c_str()));
struct_declarations.push_back(stringf("struct %s_state_t", cid(mod->name).c_str()));
struct_declarations.push_back(stringf("#ifndef %s", ifdef_name));
struct_declarations.push_back(stringf("#define %s", ifdef_name));
struct_declarations.push_back(stringf("struct %s_state_t", cid(mod->name)));
struct_declarations.push_back("{");
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).c_str(), cid(w->name).c_str(), log_id(w)));
struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width), cid(w->name), log_id(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).c_str(), cid(w->name).c_str(), log_id(w)));
struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width), cid(w->name), log_id(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).c_str(), cid(w->name).c_str(), log_id(w)));
struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width), cid(w->name), log_id(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).c_str(), cid(c->name).c_str(), log_id(c)));
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_declarations.push_back("#endif");
@ -407,14 +407,14 @@ struct SimplecWorker
string b_expr = b.wire ? util_get_bit(work->prefix + cid(b.wire->name), b.wire->width, b.offset) : b.data ? "1" : "0";
string expr;
if (cell->type == ID($_AND_)) expr = stringf("%s & %s", a_expr.c_str(), b_expr.c_str());
if (cell->type == ID($_NAND_)) expr = stringf("!(%s & %s)", a_expr.c_str(), b_expr.c_str());
if (cell->type == ID($_OR_)) expr = stringf("%s | %s", a_expr.c_str(), b_expr.c_str());
if (cell->type == ID($_NOR_)) expr = stringf("!(%s | %s)", a_expr.c_str(), b_expr.c_str());
if (cell->type == ID($_XOR_)) expr = stringf("%s ^ %s", a_expr.c_str(), b_expr.c_str());
if (cell->type == ID($_XNOR_)) expr = stringf("!(%s ^ %s)", a_expr.c_str(), b_expr.c_str());
if (cell->type == ID($_ANDNOT_)) expr = stringf("%s & (!%s)", a_expr.c_str(), b_expr.c_str());
if (cell->type == ID($_ORNOT_)) expr = stringf("%s | (!%s)", a_expr.c_str(), b_expr.c_str());
if (cell->type == ID($_AND_)) expr = stringf("%s & %s", a_expr, b_expr);
if (cell->type == ID($_NAND_)) expr = stringf("!(%s & %s)", a_expr, b_expr);
if (cell->type == ID($_OR_)) expr = stringf("%s | %s", a_expr, b_expr);
if (cell->type == ID($_NOR_)) expr = stringf("!(%s | %s)", a_expr, b_expr);
if (cell->type == ID($_XOR_)) expr = stringf("%s ^ %s", a_expr, b_expr);
if (cell->type == ID($_XNOR_)) expr = stringf("!(%s ^ %s)", a_expr, b_expr);
if (cell->type == ID($_ANDNOT_)) expr = stringf("%s & (!%s)", a_expr, b_expr);
if (cell->type == ID($_ORNOT_)) expr = stringf("%s | (!%s)", a_expr, b_expr);
log_assert(y.wire);
funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) +
@ -436,8 +436,8 @@ struct SimplecWorker
string c_expr = c.wire ? util_get_bit(work->prefix + cid(c.wire->name), c.wire->width, c.offset) : c.data ? "1" : "0";
string expr;
if (cell->type == ID($_AOI3_)) expr = stringf("!((%s & %s) | %s)", a_expr.c_str(), b_expr.c_str(), c_expr.c_str());
if (cell->type == ID($_OAI3_)) expr = stringf("!((%s | %s) & %s)", a_expr.c_str(), b_expr.c_str(), c_expr.c_str());
if (cell->type == ID($_AOI3_)) expr = stringf("!((%s & %s) | %s)", a_expr, b_expr, c_expr);
if (cell->type == ID($_OAI3_)) expr = stringf("!((%s | %s) & %s)", a_expr, b_expr, c_expr);
log_assert(y.wire);
funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) +
@ -461,8 +461,8 @@ struct SimplecWorker
string d_expr = d.wire ? util_get_bit(work->prefix + cid(d.wire->name), d.wire->width, d.offset) : d.data ? "1" : "0";
string expr;
if (cell->type == ID($_AOI4_)) expr = stringf("!((%s & %s) | (%s & %s))", a_expr.c_str(), b_expr.c_str(), c_expr.c_str(), d_expr.c_str());
if (cell->type == ID($_OAI4_)) expr = stringf("!((%s | %s) & (%s | %s))", a_expr.c_str(), b_expr.c_str(), c_expr.c_str(), d_expr.c_str());
if (cell->type == ID($_AOI4_)) expr = stringf("!((%s & %s) | (%s & %s))", a_expr, b_expr, c_expr, d_expr);
if (cell->type == ID($_OAI4_)) expr = stringf("!((%s | %s) & (%s | %s))", a_expr, b_expr, c_expr, d_expr);
log_assert(y.wire);
funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) +
@ -484,9 +484,9 @@ struct SimplecWorker
string s_expr = s.wire ? util_get_bit(work->prefix + cid(s.wire->name), s.wire->width, s.offset) : s.data ? "1" : "0";
// casts to bool are a workaround for CBMC bug (https://github.com/diffblue/cbmc/issues/933)
string expr = stringf("%s ? %s(bool)%s : %s(bool)%s", s_expr.c_str(),
cell->type == ID($_NMUX_) ? "!" : "", b_expr.c_str(),
cell->type == ID($_NMUX_) ? "!" : "", a_expr.c_str());
string expr = stringf("%s ? %s(bool)%s : %s(bool)%s", s_expr,
cell->type == ID($_NMUX_) ? "!" : "", b_expr,
cell->type == ID($_NMUX_) ? "!" : "", a_expr);
log_assert(y.wire);
funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) +
@ -504,7 +504,7 @@ struct SimplecWorker
while (work->dirty)
{
if (verbose && (!work->dirty_bits.empty() || !work->dirty_cells.empty()))
log(" In %s:\n", work->log_prefix.c_str());
log(" In %s:\n", work->log_prefix);
while (!work->dirty_bits.empty() || !work->dirty_cells.empty())
{
@ -517,8 +517,8 @@ struct SimplecWorker
if (chunk.wire == nullptr)
continue;
if (verbose)
log(" Propagating %s.%s[%d:%d].\n", work->log_prefix.c_str(), log_id(chunk.wire), chunk.offset+chunk.width-1, chunk.offset);
funct_declarations.push_back(stringf(" // Updated signal in %s: %s", work->log_prefix.c_str(), log_signal(chunk)));
log(" Propagating %s.%s[%d:%d].\n", work->log_prefix, log_id(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)));
}
for (SigBit bit : dirtysig)
@ -539,7 +539,7 @@ struct SimplecWorker
work->parent->set_dirty(parent_bit);
if (verbose)
log(" Propagating %s.%s[%d] -> %s.%s[%d].\n", work->log_prefix.c_str(), log_id(bit.wire), bit.offset,
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);
}
@ -556,11 +556,11 @@ struct SimplecWorker
child->set_dirty(child_bit);
if (verbose)
log(" Propagating %s.%s[%d] -> %s.%s.%s[%d].\n", work->log_prefix.c_str(), log_id(bit.wire), bit.offset,
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);
} else {
if (verbose)
log(" Marking cell %s.%s (via %s.%s[%d]).\n", work->log_prefix.c_str(), log_id(std::get<0>(port)),
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);
work->set_dirty(std::get<0>(port));
}
@ -579,7 +579,7 @@ struct SimplecWorker
string hiername = work->log_prefix + "." + log_id(cell);
if (verbose)
log(" Evaluating %s (%s, best of %d).\n", hiername.c_str(), log_id(cell->type), GetSize(work->dirty_cells));
log(" Evaluating %s (%s, best of %d).\n", hiername, log_id(cell->type), GetSize(work->dirty_cells));
if (activated_cells.count(hiername))
reactivated_cells.insert(hiername);
@ -630,13 +630,13 @@ struct SimplecWorker
void make_func(HierDirtyFlags *work, const string &func_name, const vector<string> &preamble)
{
log("Generating function %s():\n", func_name.c_str());
log("Generating function %s():\n", func_name);
activated_cells.clear();
reactivated_cells.clear();
funct_declarations.push_back("");
funct_declarations.push_back(stringf("static void %s(struct %s_state_t *state)", func_name.c_str(), cid(work->module->name).c_str()));
funct_declarations.push_back(stringf("static void %s(struct %s_state_t *state)", func_name, cid(work->module->name)));
funct_declarations.push_back("{");
for (auto &line : preamble)
funct_declarations.push_back(line);
@ -657,7 +657,7 @@ struct SimplecWorker
{
SigSpec sig = sigmaps.at(module)(w);
Const val = w->attributes.at(ID::init);
val.bits.resize(GetSize(sig), State::Sx);
val.resize(GetSize(sig), State::Sx);
for (int i = 0; i < GetSize(sig); i++)
if (val[i] == State::S0 || val[i] == State::S1) {

View file

@ -24,6 +24,7 @@
#include "kernel/log.h"
#include "kernel/mem.h"
#include "libs/json11/json11.hpp"
#include "kernel/utils.h"
#include <string>
USING_YOSYS_NAMESPACE
@ -81,27 +82,27 @@ struct Smt2Worker
if (statebv)
{
if (width == 0) {
decl_str = stringf("(define-fun |%s| ((state |%s_s|)) Bool (= ((_ extract %d %d) state) #b1))", name.c_str(), get_id(module), statebv_width, statebv_width);
decl_str = stringf("(define-fun |%s| ((state |%s_s|)) Bool (= ((_ extract %d %d) state) #b1))", name, get_id(module), statebv_width, statebv_width);
statebv_width += 1;
} else {
decl_str = stringf("(define-fun |%s| ((state |%s_s|)) (_ BitVec %d) ((_ extract %d %d) state))", name.c_str(), get_id(module), width, statebv_width+width-1, statebv_width);
decl_str = stringf("(define-fun |%s| ((state |%s_s|)) (_ BitVec %d) ((_ extract %d %d) state))", name, get_id(module), width, statebv_width+width-1, statebv_width);
statebv_width += width;
}
}
else if (statedt)
{
if (width == 0) {
decl_str = stringf(" (|%s| Bool)", name.c_str());
decl_str = stringf(" (|%s| Bool)", name);
} else {
decl_str = stringf(" (|%s| (_ BitVec %d))", name.c_str(), width);
decl_str = stringf(" (|%s| (_ BitVec %d))", name, width);
}
}
else
{
if (width == 0) {
decl_str = stringf("(declare-fun |%s| (|%s_s|) Bool)", name.c_str(), get_id(module));
decl_str = stringf("(declare-fun |%s| (|%s_s|) Bool)", name, get_id(module));
} else {
decl_str = stringf("(declare-fun |%s| (|%s_s|) (_ BitVec %d))", name.c_str(), get_id(module), width);
decl_str = stringf("(declare-fun |%s| (|%s_s|) (_ BitVec %d))", name, get_id(module), width);
}
}
@ -129,7 +130,7 @@ struct Smt2Worker
for (auto &mem : memories)
{
if (is_smtlib2_module)
log_error("Memory %s.%s not allowed in module with smtlib2_module attribute", get_id(module), mem.memid.c_str());
log_error("Memory %s.%s not allowed in module with smtlib2_module attribute", get_id(module), mem.memid);
mem.narrow();
mem_dict[mem.memid] = &mem;
@ -329,13 +330,14 @@ struct Smt2Worker
{
sigmap.apply(bit);
if (bit_driver.count(bit)) {
export_cell(bit_driver.at(bit));
sigmap.apply(bit);
}
if (bit.wire == nullptr)
return bit == RTLIL::State::S1 ? "true" : "false";
if (bit_driver.count(bit))
export_cell(bit_driver.at(bit));
sigmap.apply(bit);
if (fcache.count(bit) == 0) {
if (verbose) log("%*s-> external bool: %s\n", 2+2*GetSize(recursive_cells), "",
log_signal(bit));
@ -381,7 +383,7 @@ struct Smt2Worker
}
if (fcache.count(sig[i]) && fcache.at(sig[i]).second == -1) {
subexpr.push_back(stringf("(ite %s #b1 #b0)", get_bool(sig[i], state_name).c_str()));
subexpr.push_back(stringf("(ite %s #b1 #b0)", get_bool(sig[i], state_name)));
continue;
}
@ -493,7 +495,7 @@ struct Smt2Worker
}
if (width != GetSize(sig_y) && type != 'b')
processed_expr = stringf("((_ extract %d 0) %s)", GetSize(sig_y)-1, processed_expr.c_str());
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));
@ -615,14 +617,14 @@ struct Smt2Worker
string infostr = cell->attributes.count(ID::src) ? cell->attributes.at(ID::src).decode_string().c_str() : get_id(cell);
if (cell->attributes.count(ID::reg))
infostr += " " + cell->attributes.at(ID::reg).decode_string();
decls.push_back(stringf("; yosys-smt2-%s %s#%d %d %s\n", cell->type.c_str() + 1, get_id(module), idcounter, GetSize(cell->getPort(QY)), infostr.c_str()));
decls.push_back(stringf("; yosys-smt2-%s %s#%d %d %s\n", cell->type.c_str() + 1, get_id(module), idcounter, GetSize(cell->getPort(QY)), infostr));
if (cell->getPort(QY).is_wire() && cell->getPort(QY).as_wire()->get_bool_attribute(ID::maximize)){
decls.push_back(stringf("; yosys-smt2-maximize %s#%d\n", get_id(module), idcounter));
log("Wire %s is maximized\n", cell->getPort(QY).as_wire()->name.str().c_str());
log("Wire %s is maximized\n", cell->getPort(QY).as_wire()->name.str());
}
else if (cell->getPort(QY).is_wire() && cell->getPort(QY).as_wire()->get_bool_attribute(ID::minimize)){
decls.push_back(stringf("; yosys-smt2-minimize %s#%d\n", get_id(module), idcounter));
log("Wire %s is minimized\n", cell->getPort(QY).as_wire()->name.str().c_str());
log("Wire %s is minimized\n", cell->getPort(QY).as_wire()->name.str());
}
bool init_only = cell->type.in(ID($anyconst), ID($anyinit), ID($allconst));
@ -720,7 +722,7 @@ struct Smt2Worker
2*GetSize(cell->getPort(ID::A).chunks()) < GetSize(cell->getPort(ID::A))) {
bool is_and = cell->type == ID($reduce_and);
string bits(GetSize(cell->getPort(ID::A)), is_and ? '1' : '0');
return export_bvop(cell, stringf("(%s A #b%s)", is_and ? "=" : "distinct", bits.c_str()), 'b');
return export_bvop(cell, stringf("(%s A #b%s)", is_and ? "=" : "distinct", bits), 'b');
}
if (cell->type == ID($reduce_and)) return export_reduce(cell, "(and A)", true);
@ -744,7 +746,7 @@ struct Smt2Worker
get_bv(sig_s);
for (int i = 0; i < GetSize(sig_s); i++)
processed_expr = stringf("(ite %s %s %s)", get_bool(sig_s[i]).c_str(),
processed_expr = stringf("(ite %s %s %s)", get_bool(sig_s[i]),
get_bv(sig_b.extract(i*width, width)).c_str(), processed_expr.c_str());
if (verbose)
@ -1077,24 +1079,24 @@ struct Smt2Worker
RTLIL::SigSpec sig = sigmap(wire);
Const val = wire->attributes.at(ID::init);
val.bits.resize(GetSize(sig), State::Sx);
val.resize(GetSize(sig), State::Sx);
if (bvmode && GetSize(sig) > 1) {
Const mask(State::S1, GetSize(sig));
bool use_mask = false;
for (int i = 0; i < GetSize(sig); i++)
if (val[i] != State::S0 && val[i] != State::S1) {
val[i] = State::S0;
mask[i] = State::S0;
val.set(i, State::S0);
mask.set(i, State::S0);
use_mask = true;
}
if (use_mask)
init_list.push_back(stringf("(= (bvand %s #b%s) #b%s) ; %s", get_bv(sig).c_str(), mask.as_string().c_str(), val.as_string().c_str(), get_id(wire)));
init_list.push_back(stringf("(= (bvand %s #b%s) #b%s) ; %s", get_bv(sig), mask.as_string(), val.as_string(), get_id(wire)));
else
init_list.push_back(stringf("(= %s #b%s) ; %s", get_bv(sig).c_str(), val.as_string().c_str(), get_id(wire)));
init_list.push_back(stringf("(= %s #b%s) ; %s", get_bv(sig), val.as_string(), get_id(wire)));
} else {
for (int i = 0; i < GetSize(sig); i++)
if (val[i] == State::S0 || val[i] == State::S1)
init_list.push_back(stringf("(= %s %s) ; %s", get_bool(sig[i]).c_str(), val[i] == State::S1 ? "true" : "false", get_id(wire)));
init_list.push_back(stringf("(= %s %s) ; %s", get_bool(sig[i]), val[i] == State::S1 ? "true" : "false", get_id(wire)));
}
}
@ -1129,7 +1131,7 @@ struct Smt2Worker
}
if (private_name && cell->attributes.count(ID::src))
decls.push_back(stringf("; yosys-smt2-%s %d %s %s\n", cell->type.c_str() + 1, id, get_id(cell), cell->attributes.at(ID::src).decode_string().c_str()));
decls.push_back(stringf("; yosys-smt2-%s %d %s %s\n", cell->type.c_str() + 1, id, get_id(cell), cell->attributes.at(ID::src).decode_string()));
else
decls.push_back(stringf("; yosys-smt2-%s %d %s\n", cell->type.c_str() + 1, id, get_id(cell)));
@ -1178,11 +1180,11 @@ struct Smt2Worker
SigSpec sig = sigmap(conn.second);
if (bvmode || GetSize(w) == 1) {
hier.push_back(stringf(" (= %s (|%s_n %s| %s)) ; %s.%s\n", (GetSize(w) > 1 ? get_bv(sig) : get_bool(sig)).c_str(),
hier.push_back(stringf(" (= %s (|%s_n %s| %s)) ; %s.%s\n", (GetSize(w) > 1 ? get_bv(sig) : get_bool(sig)),
get_id(cell->type), get_id(w), cell_state.c_str(), get_id(cell->type), get_id(w)));
} else {
for (int i = 0; i < GetSize(w); i++)
hier.push_back(stringf(" (= %s (|%s_n %s %d| %s)) ; %s.%s[%d]\n", get_bool(sig[i]).c_str(),
hier.push_back(stringf(" (= %s (|%s_n %s %d| %s)) ; %s.%s[%d]\n", get_bool(sig[i]),
get_id(cell->type), get_id(w), i, cell_state.c_str(), get_id(cell->type), get_id(w), i));
}
}
@ -1202,25 +1204,25 @@ struct Smt2Worker
{
std::string expr_d = get_bool(cell->getPort(ID::D));
std::string expr_q = get_bool(cell->getPort(ID::Q), "next_state");
trans.push_back(stringf(" (= %s %s) ; %s %s\n", expr_d.c_str(), expr_q.c_str(), get_id(cell), log_signal(cell->getPort(ID::Q))));
ex_state_eq.push_back(stringf("(= %s %s)", get_bool(cell->getPort(ID::Q)).c_str(), get_bool(cell->getPort(ID::Q), "other_state").c_str()));
trans.push_back(stringf(" (= %s %s) ; %s %s\n", expr_d, expr_q, get_id(cell), log_signal(cell->getPort(ID::Q))));
ex_state_eq.push_back(stringf("(= %s %s)", get_bool(cell->getPort(ID::Q)), get_bool(cell->getPort(ID::Q), "other_state")));
}
if (cell->type.in(ID($ff), ID($dff), ID($anyinit)))
{
std::string expr_d = get_bv(cell->getPort(ID::D));
std::string expr_q = get_bv(cell->getPort(ID::Q), "next_state");
trans.push_back(stringf(" (= %s %s) ; %s %s\n", expr_d.c_str(), expr_q.c_str(), get_id(cell), log_signal(cell->getPort(ID::Q))));
ex_state_eq.push_back(stringf("(= %s %s)", get_bv(cell->getPort(ID::Q)).c_str(), get_bv(cell->getPort(ID::Q), "other_state").c_str()));
trans.push_back(stringf(" (= %s %s) ; %s %s\n", expr_d, expr_q, get_id(cell), log_signal(cell->getPort(ID::Q))));
ex_state_eq.push_back(stringf("(= %s %s)", get_bv(cell->getPort(ID::Q)), get_bv(cell->getPort(ID::Q), "other_state")));
}
if (cell->type.in(ID($anyconst), ID($allconst)))
{
std::string expr_d = get_bv(cell->getPort(ID::Y));
std::string expr_q = get_bv(cell->getPort(ID::Y), "next_state");
trans.push_back(stringf(" (= %s %s) ; %s %s\n", expr_d.c_str(), expr_q.c_str(), get_id(cell), log_signal(cell->getPort(ID::Y))));
trans.push_back(stringf(" (= %s %s) ; %s %s\n", expr_d, expr_q, get_id(cell), log_signal(cell->getPort(ID::Y))));
if (cell->type == ID($anyconst))
ex_state_eq.push_back(stringf("(= %s %s)", get_bv(cell->getPort(ID::Y)).c_str(), get_bv(cell->getPort(ID::Y), "other_state").c_str()));
ex_state_eq.push_back(stringf("(= %s %s)", get_bv(cell->getPort(ID::Y)), get_bv(cell->getPort(ID::Y), "other_state")));
}
}
@ -1339,11 +1341,11 @@ struct Smt2Worker
std::string expr_d = stringf("(|%s#%d#%d| state)", get_id(module), arrayid, GetSize(mem->wr_ports));
std::string expr_q = stringf("(|%s#%d#0| next_state)", get_id(module), arrayid);
trans.push_back(stringf(" (= %s %s) ; %s\n", expr_d.c_str(), expr_q.c_str(), get_id(mem->memid)));
trans.push_back(stringf(" (= %s %s) ; %s\n", expr_d, expr_q, get_id(mem->memid)));
ex_state_eq.push_back(stringf("(= (|%s#%d#0| state) (|%s#%d#0| other_state))", get_id(module), arrayid, get_id(module), arrayid));
if (has_async_wr)
hier.push_back(stringf(" (= %s (|%s| state)) ; %s\n", expr_d.c_str(), final_memstate.c_str(), get_id(mem->memid)));
hier.push_back(stringf(" (= %s (|%s| state)) ; %s\n", expr_d, final_memstate, get_id(mem->memid)));
Const init_data = mem->get_init_data();
@ -1359,10 +1361,10 @@ struct Smt2Worker
for (int k = 0; k < GetSize(initword); k++) {
if (initword[k] == State::S0 || initword[k] == State::S1) {
gen_init_constr = true;
initmask[k] = State::S1;
initmask.set(k, State::S1);
} else {
initmask[k] = State::S0;
initword[k] = State::S0;
initmask.set(k, State::S0);
initword.set(k, State::S0);
}
}
@ -1400,7 +1402,7 @@ struct Smt2Worker
expr = "\n " + ex_state_eq.front() + "\n";
} else {
for (auto &str : ex_state_eq)
expr += stringf("\n %s", str.c_str());
expr += stringf("\n %s", str);
expr += "\n)";
}
}
@ -1413,7 +1415,7 @@ struct Smt2Worker
expr = "\n " + ex_input_eq.front() + "\n";
} else {
for (auto &str : ex_input_eq)
expr += stringf("\n %s", str.c_str());
expr += stringf("\n %s", str);
expr += "\n)";
}
}
@ -1427,7 +1429,7 @@ struct Smt2Worker
assert_expr = "\n " + assert_list.front() + "\n";
} else {
for (auto &str : assert_list)
assert_expr += stringf("\n %s", str.c_str());
assert_expr += stringf("\n %s", str);
assert_expr += "\n)";
}
}
@ -1440,7 +1442,7 @@ struct Smt2Worker
assume_expr = "\n " + assume_list.front() + "\n";
} else {
for (auto &str : assume_list)
assume_expr += stringf("\n %s", str.c_str());
assume_expr += stringf("\n %s", str);
assume_expr += "\n)";
}
}
@ -1453,7 +1455,7 @@ struct Smt2Worker
init_expr = "\n " + init_list.front() + "\n";
} else {
for (auto &str : init_list)
init_expr += stringf("\n %s", str.c_str());
init_expr += stringf("\n %s", str);
init_expr += "\n)";
}
}
@ -1774,7 +1776,7 @@ struct Smt2Backend : public Backend {
if (args[argidx] == "-tpl" && argidx+1 < args.size()) {
template_f.open(args[++argidx]);
if (template_f.fail())
log_error("Can't open template file `%s'.\n", args[argidx].c_str());
log_error("Can't open template file `%s'.\n", args[argidx]);
continue;
}
if (args[argidx] == "-bv" || args[argidx] == "-mem") {
@ -1829,7 +1831,7 @@ struct Smt2Backend : public Backend {
}
}
*f << stringf("; SMT-LIBv2 description generated by %s\n", yosys_version_str);
*f << stringf("; SMT-LIBv2 description generated by %s\n", yosys_maybe_version());
if (!bvmode)
*f << stringf("; yosys-smt2-nobv\n");
@ -1844,7 +1846,7 @@ struct Smt2Backend : public Backend {
*f << stringf("; yosys-smt2-stdt\n");
for (auto &it : solver_options)
*f << stringf("; yosys-smt2-solver-option %s %s\n", it.first.c_str(), it.second.c_str());
*f << stringf("; yosys-smt2-solver-option %s %s\n", it.first, it.second);
std::vector<RTLIL::Module*> sorted_modules;
@ -1911,7 +1913,7 @@ struct Smt2Backend : public Backend {
}
if (topmod)
*f << stringf("; yosys-smt2-topmod %s\n", topmod_id.c_str());
*f << stringf("; yosys-smt2-topmod %s\n", topmod_id);
*f << stringf("; end of yosys output\n");

View file

@ -57,6 +57,8 @@ keep_going = False
check_witness = False
detect_loops = False
incremental = None
track_assumes = False
minimize_assumes = False
so = SmtOpts()
@ -189,6 +191,14 @@ def help():
--incremental
run in incremental mode (experimental)
--track-assumes
track individual assumptions and report a subset of used
assumptions that are sufficient for the reported outcome. This
can be used to debug PREUNSAT failures as well as to find a
smaller set of sufficient assumptions.
--minimize-assumes
when using --track-assumes, solve for a minimal set of sufficient assumptions.
""" + so.helpmsg())
def usage():
@ -200,7 +210,8 @@ try:
opts, args = getopt.getopt(sys.argv[1:], so.shortopts + "t:higcm:", so.longopts +
["help", "final-only", "assume-skipped=", "smtc=", "cex=", "aig=", "aig-noheader", "yw=", "btorwit=", "presat",
"dump-vcd=", "dump-yw=", "dump-vlogtb=", "vlogtb-top=", "dump-smtc=", "dump-all", "noinfo", "append=",
"smtc-init", "smtc-top=", "noinit", "binary", "keep-going", "check-witness", "detect-loops", "incremental"])
"smtc-init", "smtc-top=", "noinit", "binary", "keep-going", "check-witness", "detect-loops", "incremental",
"track-assumes", "minimize-assumes"])
except:
usage()
@ -289,6 +300,10 @@ for o, a in opts:
elif o == "--incremental":
from smtbmc_incremental import Incremental
incremental = Incremental()
elif o == "--track-assumes":
track_assumes = True
elif o == "--minimize-assumes":
minimize_assumes = True
elif so.handle(o, a):
pass
else:
@ -447,6 +462,9 @@ def get_constr_expr(db, state, final=False, getvalues=False, individual=False):
smt = SmtIo(opts=so)
if track_assumes:
smt.smt2_options[':produce-unsat-assumptions'] = 'true'
if noinfo and vcdfile is None and vlogtbfile is None and outconstr is None:
smt.produce_models = False
@ -651,18 +669,12 @@ if aimfile is not None:
ywfile_hierwitness_cache = None
def ywfile_constraints(inywfile, constr_assumes, map_steps=None, skip_x=False):
def ywfile_hierwitness():
global ywfile_hierwitness_cache
if map_steps is None:
map_steps = {}
if ywfile_hierwitness_cache is None:
ywfile_hierwitness = smt.hierwitness(topmod, allregs=True, blackbox=True)
with open(inywfile, "r") as f:
inyw = ReadWitness(f)
if ywfile_hierwitness_cache is None:
ywfile_hierwitness_cache = smt.hierwitness(topmod, allregs=True, blackbox=True)
inits, seqs, clocks, mems = ywfile_hierwitness_cache
inits, seqs, clocks, mems = ywfile_hierwitness
smt_wires = defaultdict(list)
smt_mems = defaultdict(list)
@ -673,9 +685,158 @@ def ywfile_constraints(inywfile, constr_assumes, map_steps=None, skip_x=False):
for mem in mems:
smt_mems[mem["path"]].append(mem)
addr_re = re.compile(r'\\\[[0-9]+\]$')
bits_re = re.compile(r'[01?]*$')
ywfile_hierwitness_cache = inits, seqs, clocks, mems, smt_wires, smt_mems
return ywfile_hierwitness_cache
def_bits_re = re.compile(r'([01]+)')
def smt_extract_mask(smt_expr, mask):
chunks = []
def_bits = ''
mask_index_order = mask[::-1]
for matched in def_bits_re.finditer(mask_index_order):
chunks.append(matched.span())
def_bits += matched[0]
if not chunks:
return
if len(chunks) == 1:
start, end = chunks[0]
if start == 0 and end == len(mask_index_order):
combined_chunks = smt_expr
else:
combined_chunks = '((_ extract %d %d) %s)' % (end - 1, start, smt_expr)
else:
combined_chunks = '(let ((x %s)) (concat %s))' % (smt_expr, ' '.join(
'((_ extract %d %d) x)' % (end - 1, start)
for start, end in reversed(chunks)
))
return combined_chunks, ''.join(mask_index_order[start:end] for start, end in chunks)[::-1]
def smt_concat(exprs):
if not isinstance(exprs, (tuple, list)):
exprs = tuple(exprs)
if not exprs:
return ""
if len(exprs) == 1:
return exprs[1]
return "(concat %s)" % ' '.join(exprs)
def ywfile_signal(sig, step, mask=None):
assert sig.width > 0
inits, seqs, clocks, mems, smt_wires, smt_mems = ywfile_hierwitness()
sig_end = sig.offset + sig.width
output = []
def ywfile_signal_error(reason, detail=None):
msg = f"Yosys witness signal mismatch for {sig.pretty()}: {reason}"
if detail:
msg += f" ({detail})"
raise ValueError(msg)
if sig.path in smt_wires:
for wire in smt_wires[sig.path]:
width, offset = wire["width"], wire["offset"]
smt_bool = smt.net_width(topmod, wire["smtpath"]) == 1
offset = max(offset, 0)
end = width + offset
common_offset = max(sig.offset, offset)
common_end = min(sig_end, end)
if common_end <= common_offset:
continue
smt_expr = smt.witness_net_expr(topmod, f"s{step}", wire)
if not smt_bool:
slice_high = common_end - offset - 1
slice_low = common_offset - offset
smt_expr = "((_ extract %d %d) %s)" % (slice_high, slice_low, smt_expr)
else:
smt_expr = "(ite %s #b1 #b0)" % smt_expr
output.append(((common_offset - sig.offset), (common_end - sig.offset), smt_expr))
if sig.memory_path:
if sig.memory_path in smt_mems:
for mem in smt_mems[sig.memory_path]:
width, size, bv = mem["width"], mem["size"], mem["statebv"]
if sig.memory_addr is not None and sig.memory_addr >= size:
ywfile_signal_error(
"memory address out of bounds",
f"address={sig.memory_addr} size={size}",
)
smt_expr = smt.net_expr(topmod, f"s{step}", mem["smtpath"])
if bv:
word_low = sig.memory_addr * width
word_high = word_low + width - 1
smt_expr = "((_ extract %d %d) %s)" % (word_high, word_low, smt_expr)
else:
addr_width = (size - 1).bit_length()
addr_bits = f"{sig.memory_addr:0{addr_width}b}"
smt_expr = "(select %s #b%s )" % (smt_expr, addr_bits)
if sig.width < width:
slice_high = sig.offset + sig.width - 1
smt_expr = "((_ extract %d %d) %s)" % (slice_high, sig.offset, smt_expr)
output.append((0, sig.width, smt_expr))
else:
ywfile_signal_error("memory not found in design")
output.sort()
output = [chunk for chunk in output if chunk[0] != chunk[1]]
if not output:
if sig.memory_path:
ywfile_signal_error("memory signal has no matching bits in design")
else:
ywfile_signal_error("signal not found in design")
pos = 0
for start, end, smt_expr in output:
if start != pos:
ywfile_signal_error(
"signal width/offset mismatch",
f"expected coverage at bit {pos}",
)
pos = end
if pos != sig.width:
ywfile_signal_error(
"signal width/offset mismatch",
f"covered {pos} of {sig.width} bits",
)
if len(output) == 1:
return output[0][-1]
return smt_concat(smt_expr for start, end, smt_expr in reversed(output))
def ywfile_constraints(inywfile, constr_assumes, map_steps=None, skip_x=False):
global ywfile_hierwitness_cache
if map_steps is None:
map_steps = {}
with open(inywfile, "r") as f:
inyw = ReadWitness(f)
inits, seqs, clocks, mems, smt_wires, smt_mems = ywfile_hierwitness()
bits_re = re.compile(r'[01?]*$')
max_t = -1
for t, step in inyw.steps():
@ -687,77 +848,17 @@ def ywfile_constraints(inywfile, constr_assumes, map_steps=None, skip_x=False):
if not bits_re.match(bits):
raise ValueError("unsupported bit value in Yosys witness file")
sig_end = sig.offset + len(bits)
if sig.path in smt_wires:
for wire in smt_wires[sig.path]:
width, offset = wire["width"], wire["offset"]
if bits.count('?') == len(bits):
continue
smt_bool = smt.net_width(topmod, wire["smtpath"]) == 1
smt_expr = ywfile_signal(sig, map_steps.get(t, t))
offset = max(offset, 0)
smt_expr, bits = smt_extract_mask(smt_expr, bits)
end = width + offset
common_offset = max(sig.offset, offset)
common_end = min(sig_end, end)
if common_end <= common_offset:
continue
smt_constr = "(= %s #b%s)" % (smt_expr, bits)
constr_assumes[t].append((inywfile, smt_constr))
smt_expr = smt.witness_net_expr(topmod, f"s{map_steps.get(t, t)}", wire)
if not smt_bool:
slice_high = common_end - offset - 1
slice_low = common_offset - offset
smt_expr = "((_ extract %d %d) %s)" % (slice_high, slice_low, smt_expr)
bit_slice = bits[len(bits) - (common_end - sig.offset):len(bits) - (common_offset - sig.offset)]
if bit_slice.count("?") == len(bit_slice):
continue
if smt_bool:
assert width == 1
smt_constr = "(= %s %s)" % (smt_expr, "true" if bit_slice == "1" else "false")
else:
if "?" in bit_slice:
mask = bit_slice.replace("0", "1").replace("?", "0")
bit_slice = bit_slice.replace("?", "0")
smt_expr = "(bvand %s #b%s)" % (smt_expr, mask)
smt_constr = "(= %s #b%s)" % (smt_expr, bit_slice)
constr_assumes[t].append((inywfile, smt_constr))
if sig.memory_path:
if sig.memory_path in smt_mems:
for mem in smt_mems[sig.memory_path]:
width, size, bv = mem["width"], mem["size"], mem["statebv"]
smt_expr = smt.net_expr(topmod, f"s{map_steps.get(t, t)}", mem["smtpath"])
if bv:
word_low = sig.memory_addr * width
word_high = word_low + width - 1
smt_expr = "((_ extract %d %d) %s)" % (word_high, word_low, smt_expr)
else:
addr_width = (size - 1).bit_length()
addr_bits = f"{sig.memory_addr:0{addr_width}b}"
smt_expr = "(select %s #b%s )" % (smt_expr, addr_bits)
if len(bits) < width:
slice_high = sig.offset + len(bits) - 1
smt_expr = "((_ extract %d %d) %s)" % (slice_high, sig.offset, smt_expr)
bit_slice = bits
if "?" in bit_slice:
mask = bit_slice.replace("0", "1").replace("?", "0")
bit_slice = bit_slice.replace("?", "0")
smt_expr = "(bvand %s #b%s)" % (smt_expr, mask)
smt_constr = "(= %s #b%s)" % (smt_expr, bit_slice)
constr_assumes[t].append((inywfile, smt_constr))
max_t = t
return max_t
if inywfile is not None:
@ -1348,11 +1449,11 @@ def write_yw_trace(steps, index, allregs=False, filename=None):
exprs.extend(smt.witness_net_expr(topmod, f"s{k}", sig) for sig in sigs)
all_sigs.append(sigs)
all_sigs.append((step_values, sigs))
bvs = iter(smt.get_list(exprs))
for sigs in all_sigs:
for (step_values, sigs) in all_sigs:
for sig in sigs:
value = smt.bv2bin(next(bvs))
step_values[sig["sig"]] = value
@ -1381,6 +1482,10 @@ def write_trace(steps_start, steps_stop, index, allregs=False):
if outywfile is not None:
write_yw_trace(steps, index, allregs)
def escape_path_segment(segment):
if "." in segment:
return f"\\{segment} "
return segment
def print_failed_asserts_worker(mod, state, path, extrainfo, infomap, infokey=()):
assert mod in smt.modinfo
@ -1391,7 +1496,8 @@ def print_failed_asserts_worker(mod, state, path, extrainfo, infomap, infokey=()
for cellname, celltype in smt.modinfo[mod].cells.items():
cell_infokey = (mod, cellname, infokey)
if print_failed_asserts_worker(celltype, "(|%s_h %s| %s)" % (mod, cellname, state), path + "." + cellname, extrainfo, infomap, cell_infokey):
cell_path = path + "." + escape_path_segment(cellname)
if print_failed_asserts_worker(celltype, "(|%s_h %s| %s)" % (mod, cellname, state), cell_path, extrainfo, infomap, cell_infokey):
found_failed_assert = True
for assertfun, assertinfo in smt.modinfo[mod].asserts.items():
@ -1424,7 +1530,7 @@ def print_anyconsts_worker(mod, state, path):
assert mod in smt.modinfo
for cellname, celltype in smt.modinfo[mod].cells.items():
print_anyconsts_worker(celltype, "(|%s_h %s| %s)" % (mod, cellname, state), path + "." + cellname)
print_anyconsts_worker(celltype, "(|%s_h %s| %s)" % (mod, cellname, state), path + "." + escape_path_segment(cellname))
for fun, info in smt.modinfo[mod].anyconsts.items():
if info[1] is None:
@ -1444,18 +1550,21 @@ def print_anyconsts(state):
print_anyconsts_worker(topmod, "s%d" % state, topmod)
def get_cover_list(mod, base):
def get_cover_list(mod, base, path=None):
path = path or mod
assert mod in smt.modinfo
cover_expr = list()
# A tuple of path and cell name
cover_desc = list()
for expr, desc in smt.modinfo[mod].covers.items():
cover_expr.append("(ite (|%s| %s) #b1 #b0)" % (expr, base))
cover_desc.append(desc)
cover_desc.append((path, desc))
for cell, submod in smt.modinfo[mod].cells.items():
e, d = get_cover_list(submod, "(|%s_h %s| %s)" % (mod, cell, base))
cell_path = path + "." + escape_path_segment(cell)
e, d = get_cover_list(submod, "(|%s_h %s| %s)" % (mod, cell, base), cell_path)
cover_expr += e
cover_desc += d
@ -1471,7 +1580,8 @@ def get_assert_map(mod, base, path, key_base=()):
assert_map[(expr, key_base)] = ("(|%s| %s)" % (expr, base), path, desc)
for cell, submod in smt.modinfo[mod].cells.items():
assert_map.update(get_assert_map(submod, "(|%s_h %s| %s)" % (mod, cell, base), path + "." + cell, (mod, cell, key_base)))
cell_path = path + "." + escape_path_segment(cell)
assert_map.update(get_assert_map(submod, "(|%s_h %s| %s)" % (mod, cell, base), cell_path, (mod, cell, key_base)))
return assert_map
@ -1497,6 +1607,44 @@ def get_active_assert_map(step, active):
return assert_map
assume_enables = {}
def declare_assume_enables():
def recurse(mod, path, key_base=()):
for expr, desc in smt.modinfo[mod].assumes.items():
enable = f"|assume_enable {len(assume_enables)}|"
smt.smt2_assumptions[(expr, key_base)] = enable
smt.write(f"(declare-const {enable} Bool)")
assume_enables[(expr, key_base)] = (enable, path, desc)
for cell, submod in smt.modinfo[mod].cells.items():
recurse(submod, f"{path}.{cell}", (mod, cell, key_base))
recurse(topmod, topmod)
if track_assumes:
declare_assume_enables()
def smt_assert_design_assumes(step):
if not track_assumes:
smt_assert_consequent("(|%s_u| s%d)" % (topmod, step))
return
if not assume_enables:
return
def expr_for_assume(assume_key, base=None):
expr, key_base = assume_key
expr_prefix = f"(|{expr}| "
expr_suffix = ")"
while key_base:
mod, cell, key_base = key_base
expr_prefix += f"(|{mod}_h {cell}| "
expr_suffix += ")"
return f"{expr_prefix} s{step}{expr_suffix}"
for assume_key, (enable, path, desc) in assume_enables.items():
smt_assert_consequent(f"(=> {enable} {expr_for_assume(assume_key)})")
states = list()
asserts_antecedent_cache = [list()]
@ -1651,6 +1799,13 @@ def smt_check_sat(expected=["sat", "unsat"]):
smt_forall_assert()
return smt.check_sat(expected=expected)
def report_tracked_assumptions(msg):
if track_assumes:
print_msg(msg)
for key in smt.get_unsat_assumptions(minimize=minimize_assumes):
enable, path, descr = assume_enables[key]
print_msg(f" In {path}: {descr}")
if incremental:
incremental.mainloop()
@ -1664,7 +1819,7 @@ elif tempind:
break
smt_state(step)
smt_assert_consequent("(|%s_u| s%d)" % (topmod, step))
smt_assert_design_assumes(step)
smt_assert_antecedent("(|%s_h| s%d)" % (topmod, step))
smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, step))
smt_assert_consequent(get_constr_expr(constr_assumes, step))
@ -1707,6 +1862,7 @@ elif tempind:
else:
print_msg("Temporal induction successful.")
report_tracked_assumptions("Used assumptions:")
retstatus = "PASSED"
break
@ -1732,7 +1888,7 @@ elif covermode:
while step < num_steps:
smt_state(step)
smt_assert_consequent("(|%s_u| s%d)" % (topmod, step))
smt_assert_design_assumes(step)
smt_assert_antecedent("(|%s_h| s%d)" % (topmod, step))
smt_assert_consequent(get_constr_expr(constr_assumes, step))
@ -1747,12 +1903,18 @@ elif covermode:
smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, step-1, step))
smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, step))
if step < skip_steps:
print_msg("Skipping step %d.." % (step))
step += 1
continue
while "1" in cover_mask:
print_msg("Checking cover reachability in step %d.." % (step))
smt_push()
smt_assert("(distinct (covers_%d s%d) #b%s)" % (coveridx, step, "0" * len(cover_desc)))
if smt_check_sat() == "unsat":
report_tracked_assumptions("Used assumptions:")
smt_pop()
break
@ -1761,13 +1923,14 @@ elif covermode:
print_msg("Appending additional step %d." % i)
smt_state(i)
smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, i))
smt_assert_consequent("(|%s_u| s%d)" % (topmod, i))
smt_assert_design_assumes(i)
smt_assert_antecedent("(|%s_h| s%d)" % (topmod, i))
smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, i-1, i))
smt_assert_consequent(get_constr_expr(constr_assumes, i))
print_msg("Re-solving with appended steps..")
if smt_check_sat() == "unsat":
print("%s Cannot appended steps without violating assumptions!" % smt.timestamp())
report_tracked_assumptions("Conflicting assumptions:")
found_failed_assert = True
retstatus = "FAILED"
break
@ -1782,7 +1945,9 @@ elif covermode:
new_cover_mask.append(cover_mask[i])
continue
print_msg("Reached cover statement at %s in step %d." % (cover_desc[i], step))
path = cover_desc[i][0]
name = cover_desc[i][1]
print_msg("Reached cover statement in step %d at %s: %s" % (step, path, name))
new_cover_mask.append("0")
cover_mask = "".join(new_cover_mask)
@ -1812,7 +1977,7 @@ elif covermode:
if "1" in cover_mask:
for i in range(len(cover_mask)):
if cover_mask[i] == "1":
print_msg("Unreached cover statement at %s." % cover_desc[i])
print_msg("Unreached cover statement at %s: %s" % (cover_desc[i][0], cover_desc[i][1]))
else: # not tempind, covermode
active_assert_keys = get_assert_keys()
@ -1823,7 +1988,7 @@ else: # not tempind, covermode
retstatus = "PASSED"
while step < num_steps:
smt_state(step)
smt_assert_consequent("(|%s_u| s%d)" % (topmod, step))
smt_assert_design_assumes(step)
smt_assert_antecedent("(|%s_h| s%d)" % (topmod, step))
smt_assert_consequent(get_constr_expr(constr_assumes, step))
@ -1853,7 +2018,7 @@ else: # not tempind, covermode
if step+i < num_steps:
smt_state(step+i)
smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, step+i))
smt_assert_consequent("(|%s_u| s%d)" % (topmod, step+i))
smt_assert_design_assumes(step + i)
smt_assert_antecedent("(|%s_h| s%d)" % (topmod, step+i))
smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, step+i-1, step+i))
smt_assert_consequent(get_constr_expr(constr_assumes, step+i))
@ -1867,7 +2032,8 @@ else: # not tempind, covermode
print_msg("Checking assumptions in steps %d to %d.." % (step, last_check_step))
if smt_check_sat() == "unsat":
print("%s Assumptions are unsatisfiable!" % smt.timestamp())
print_msg("Assumptions are unsatisfiable!")
report_tracked_assumptions("Conficting assumptions:")
retstatus = "PREUNSAT"
break
@ -1920,13 +2086,14 @@ else: # not tempind, covermode
print_msg("Appending additional step %d." % i)
smt_state(i)
smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, i))
smt_assert_consequent("(|%s_u| s%d)" % (topmod, i))
smt_assert_design_assumes(i)
smt_assert_antecedent("(|%s_h| s%d)" % (topmod, i))
smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, i-1, i))
smt_assert_consequent(get_constr_expr(constr_assumes, i))
print_msg("Re-solving with appended steps..")
if smt_check_sat() == "unsat":
print("%s Cannot append steps without violating assumptions!" % smt.timestamp())
print_msg("Cannot append steps without violating assumptions!")
report_tracked_assumptions("Conflicting assumptions:")
retstatus = "FAILED"
break
print_anyconsts(step)

View file

@ -1,7 +1,7 @@
from collections import defaultdict
import json
import typing
from functools import partial
import ywio
if typing.TYPE_CHECKING:
import smtbmc
@ -15,6 +15,14 @@ class InteractiveError(Exception):
pass
def mkkey(data):
if isinstance(data, list):
return tuple(map(mkkey, data))
elif isinstance(data, dict):
raise InteractiveError(f"JSON objects found in assumption key: {data!r}")
return data
class Incremental:
def __init__(self):
self.traceidx = 0
@ -26,6 +34,7 @@ class Incremental:
self._witness_index = None
self._yw_constraints = {}
self._define_sorts = {}
def setup(self):
generic_assert_map = smtbmc.get_assert_map(
@ -73,17 +82,17 @@ class Incremental:
if min_len is not None and arg_len < min_len:
if min_len == max_len:
raise (
raise InteractiveError(
f"{json.dumps(expr[0])} expression must have "
f"{min_len} argument{'s' if min_len != 1 else ''}"
)
else:
raise (
raise InteractiveError(
f"{json.dumps(expr[0])} expression must have at least "
f"{min_len} argument{'s' if min_len != 1 else ''}"
)
if max_len is not None and arg_len > max_len:
raise (
raise InteractiveError(
f"{json.dumps(expr[0])} expression can have at most "
f"{min_len} argument{'s' if max_len != 1 else ''}"
)
@ -96,14 +105,31 @@ class Incremental:
smt_out.append(f"s{step}")
return "module", smtbmc.topmod
def expr_mod_constraint(self, expr, smt_out):
self.expr_arg_len(expr, 1)
def expr_cell(self, expr, smt_out):
self.expr_arg_len(expr, 2)
position = len(smt_out)
smt_out.append(None)
arg_sort = self.expr(expr[1], smt_out, required_sort=["module", None])
arg_sort = self.expr(expr[2], smt_out, required_sort=["module", None])
smt_out.append(")")
module = arg_sort[1]
cell = expr[1]
submod = smtbmc.smt.modinfo[module].cells.get(cell)
if submod is None:
raise InteractiveError(f"module {module!r} has no cell {cell!r}")
smt_out[position] = f"(|{module}_h {cell}| "
return ("module", submod)
def expr_mod_constraint(self, expr, smt_out):
suffix = expr[0][3:]
smt_out[position] = f"(|{module}{suffix}| "
self.expr_arg_len(expr, 1, 2 if suffix in ["_a", "_u", "_c"] else 1)
position = len(smt_out)
smt_out.append(None)
arg_sort = self.expr(expr[-1], smt_out, required_sort=["module", None])
module = arg_sort[1]
if len(expr) == 3:
smt_out[position] = f"(|{module}{suffix} {expr[1]}| "
else:
smt_out[position] = f"(|{module}{suffix}| "
smt_out.append(")")
return "Bool"
@ -150,11 +176,7 @@ class Incremental:
if len(expr) == 1:
smt_out.push({"and": "true", "or": "false"}[expr[0]])
elif len(expr) == 2:
arg_sort = self.expr(expr[1], smt_out)
if arg_sort != "Bool":
raise InteractiveError(
f"arguments of {json.dumps(expr[0])} must have sort Bool"
)
self.expr(expr[1], smt_out, required_sort="Bool")
else:
sep = f"({expr[0]} "
for arg in expr[1:]:
@ -164,7 +186,51 @@ class Incremental:
smt_out.append(")")
return "Bool"
def expr_bv_binop(self, expr, smt_out):
self.expr_arg_len(expr, 2)
smt_out.append(f"({expr[0]} ")
arg_sort = self.expr(expr[1], smt_out, required_sort=("BitVec", None))
smt_out.append(" ")
self.expr(expr[2], smt_out, required_sort=arg_sort)
smt_out.append(")")
return arg_sort
def expr_extract(self, expr, smt_out):
self.expr_arg_len(expr, 3)
hi = expr[1]
lo = expr[2]
smt_out.append(f"((_ extract {hi} {lo}) ")
arg_sort = self.expr(expr[3], smt_out, required_sort=("BitVec", None))
smt_out.append(")")
if not (isinstance(hi, int) and 0 <= hi < arg_sort[1]):
raise InteractiveError(
f"high bit index must be 0 <= index < {arg_sort[1]}, is {hi!r}"
)
if not (isinstance(lo, int) and 0 <= lo <= hi):
raise InteractiveError(
f"low bit index must be 0 <= index < {hi}, is {lo!r}"
)
return "BitVec", hi - lo + 1
def expr_bv(self, expr, smt_out):
self.expr_arg_len(expr, 1)
arg = expr[1]
if not isinstance(arg, str) or arg.count("0") + arg.count("1") != len(arg):
raise InteractiveError("bv argument must contain only 0 or 1 bits")
smt_out.append("#b" + arg)
return "BitVec", len(arg)
def expr_yw(self, expr, smt_out):
self.expr_arg_len(expr, 1, 2)
if len(expr) == 2:
name = None
step = expr[1]
@ -194,6 +260,40 @@ class Incremental:
return "Bool"
def expr_yw_sig(self, expr, smt_out):
self.expr_arg_len(expr, 3, 4)
step = expr[1]
path = expr[2]
offset = expr[3]
width = expr[4] if len(expr) == 5 else 1
if not isinstance(offset, int) or offset < 0:
raise InteractiveError(
f"offset must be a non-negative integer, got {json.dumps(offset)}"
)
if not isinstance(width, int) or width <= 0:
raise InteractiveError(
f"width must be a positive integer, got {json.dumps(width)}"
)
if not isinstance(path, list) or not all(isinstance(s, str) for s in path):
raise InteractiveError(
f"path must be a string list, got {json.dumps(path)}"
)
if step not in self.state_set:
raise InteractiveError(f"step {step} not declared")
smt_expr = smtbmc.ywfile_signal(
ywio.WitnessSig(path=path, offset=offset, width=width), step
)
smt_out.append(smt_expr)
return "BitVec", width
def expr_smtlib(self, expr, smt_out):
self.expr_arg_len(expr, 2)
@ -206,10 +306,15 @@ class Incremental:
f"got {json.dumps(smtlib_expr)}"
)
if not isinstance(sort, str):
raise InteractiveError(
f"raw SMT-LIB sort has to be a string, got {json.dumps(sort)}"
)
if (
isinstance(sort, list)
and len(sort) == 2
and sort[0] == "BitVec"
and (sort[1] is None or isinstance(sort[1], int))
):
sort = tuple(sort)
elif not isinstance(sort, str):
raise InteractiveError(f"unsupported raw SMT-LIB sort {json.dumps(sort)}")
smt_out.append(smtlib_expr)
return sort
@ -223,20 +328,27 @@ class Incremental:
subexpr = expr[2]
if not isinstance(label, str):
raise InteractiveError(f"expression label has to be a string")
raise InteractiveError("expression label has to be a string")
smt_out.append("(! ")
smt_out.appedd(label)
smt_out.append(" ")
sort = self.expr(subexpr, smt_out)
smt_out.append(" :named ")
smt_out.append(label)
smt_out.append(")")
return sort
def expr_def(self, expr, smt_out):
self.expr_arg_len(expr, 1)
sort = self._define_sorts.get(expr[1])
if sort is None:
raise InteractiveError(f"unknown definition {json.dumps(expr)}")
smt_out.append(expr[1])
return sort
expr_handlers = {
"step": expr_step,
"cell": expr_cell,
"mod_h": expr_mod_constraint,
"mod_is": expr_mod_constraint,
"mod_i": expr_mod_constraint,
@ -246,8 +358,15 @@ class Incremental:
"not": expr_not,
"and": expr_andor,
"or": expr_andor,
"bv": expr_bv,
"bvand": expr_bv_binop,
"bvor": expr_bv_binop,
"bvxor": expr_bv_binop,
"extract": expr_extract,
"def": expr_def,
"=": expr_eq,
"yw": expr_yw,
"yw_sig": expr_yw_sig,
"smtlib": expr_smtlib,
"!": expr_label,
}
@ -281,10 +400,13 @@ class Incremental:
raise InteractiveError(f"unknown expression {json.dumps(expr[0])}")
def expr_smt(self, expr, required_sort):
return self.expr_smt_and_sort(expr, required_sort)[0]
def expr_smt_and_sort(self, expr, required_sort=None):
smt_out = []
self.expr(expr, smt_out, required_sort=required_sort)
output_sort = self.expr(expr, smt_out, required_sort=required_sort)
out = "".join(smt_out)
return out
return out, output_sort
def cmd_new_step(self, cmd):
step = self.arg_step(cmd, declare=True)
@ -302,6 +424,29 @@ class Incremental:
assert_fn(self.expr_smt(cmd.get("expr"), "Bool"))
def cmd_assert_design_assumes(self, cmd):
step = self.arg_step(cmd)
smtbmc.smt_assert_design_assumes(step)
def cmd_get_design_assume(self, cmd):
key = mkkey(cmd.get("key"))
return smtbmc.assume_enables.get(key)
def cmd_update_assumptions(self, cmd):
expr = cmd.get("expr")
key = cmd.get("key")
key = mkkey(key)
result = smtbmc.smt.smt2_assumptions.pop(key, None)
if expr is not None:
expr = self.expr_smt(expr, "Bool")
smtbmc.smt.smt2_assumptions[key] = expr
return result
def cmd_get_unsat_assumptions(self, cmd):
return smtbmc.smt.get_unsat_assumptions(minimize=bool(cmd.get("minimize")))
def cmd_push(self, cmd):
smtbmc.smt_push()
@ -313,11 +458,35 @@ class Incremental:
def cmd_smtlib(self, cmd):
command = cmd.get("command")
response = cmd.get("response", False)
if not isinstance(command, str):
raise InteractiveError(
f"raw SMT-LIB command must be a string, found {json.dumps(command)}"
)
smtbmc.smt.write(command)
if response:
return smtbmc.smt.read()
def cmd_define(self, cmd):
expr = cmd.get("expr")
if expr is None:
raise InteractiveError("'define' copmmand requires 'expr' parameter")
expr, sort = self.expr_smt_and_sort(expr)
if isinstance(sort, tuple) and sort[0] == "module":
raise InteractiveError("'define' does not support module sorts")
define_name = f"|inc def {len(self._define_sorts)}|"
self._define_sorts[define_name] = sort
if isinstance(sort, tuple):
sort = f"(_ {' '.join(map(str, sort))})"
smtbmc.smt.write(f"(define-const {define_name} {sort} {expr})")
return {"name": define_name}
def cmd_design_hierwitness(self, cmd=None):
allregs = (cmd is None) or bool(cmd.get("allreges", False))
@ -369,6 +538,21 @@ class Incremental:
return dict(last_step=last_step)
def cmd_modinfo(self, cmd):
fields = cmd.get("fields", [])
mod = cmd.get("mod")
if mod is None:
mod = smtbmc.topmod
modinfo = smtbmc.smt.modinfo.get(mod)
if modinfo is None:
return None
result = dict(name=mod)
for field in fields:
result[field] = getattr(modinfo, field, None)
return result
def cmd_ping(self, cmd):
return cmd
@ -377,13 +561,19 @@ class Incremental:
"assert": cmd_assert,
"assert_antecedent": cmd_assert,
"assert_consequent": cmd_assert,
"assert_design_assumes": cmd_assert_design_assumes,
"get_design_assume": cmd_get_design_assume,
"update_assumptions": cmd_update_assumptions,
"get_unsat_assumptions": cmd_get_unsat_assumptions,
"push": cmd_push,
"pop": cmd_pop,
"check": cmd_check,
"smtlib": cmd_smtlib,
"define": cmd_define,
"design_hierwitness": cmd_design_hierwitness,
"write_yw_trace": cmd_write_yw_trace,
"read_yw_trace": cmd_read_yw_trace,
"modinfo": cmd_modinfo,
"ping": cmd_ping,
}

View file

@ -114,6 +114,7 @@ class SmtModInfo:
self.clocks = dict()
self.cells = dict()
self.asserts = dict()
self.assumes = dict()
self.covers = dict()
self.maximize = set()
self.minimize = set()
@ -141,6 +142,7 @@ class SmtIo:
self.recheck = False
self.smt2cache = [list()]
self.smt2_options = dict()
self.smt2_assumptions = dict()
self.p = None
self.p_index = solvers_index
solvers_index += 1
@ -158,6 +160,7 @@ class SmtIo:
self.noincr = opts.noincr
self.info_stmts = opts.info_stmts
self.nocomments = opts.nocomments
self.smt2_options.update(opts.smt2_options)
else:
self.solver = "yices"
@ -602,6 +605,12 @@ class SmtIo:
else:
self.modinfo[self.curmod].covers["%s_c %s" % (self.curmod, fields[2])] = fields[3]
if fields[1] == "yosys-smt2-assume":
if len(fields) > 4:
self.modinfo[self.curmod].assumes["%s_u %s" % (self.curmod, fields[2])] = f'{fields[4]} ({fields[3]})'
else:
self.modinfo[self.curmod].assumes["%s_u %s" % (self.curmod, fields[2])] = fields[3]
if fields[1] == "yosys-smt2-maximize":
self.modinfo[self.curmod].maximize.add(fields[2])
@ -785,8 +794,13 @@ class SmtIo:
return stmt
def check_sat(self, expected=["sat", "unsat", "unknown", "timeout", "interrupted"]):
if self.smt2_assumptions:
assume_exprs = " ".join(self.smt2_assumptions.values())
check_stmt = f"(check-sat-assuming ({assume_exprs}))"
else:
check_stmt = "(check-sat)"
if self.debug_print:
print("> (check-sat)")
print(f"> {check_stmt}")
if self.debug_file and not self.nocomments:
print("; running check-sat..", file=self.debug_file)
self.debug_file.flush()
@ -800,7 +814,7 @@ class SmtIo:
for cache_stmt in cache_ctx:
self.p_write(cache_stmt + "\n", False)
self.p_write("(check-sat)\n", True)
self.p_write(f"{check_stmt}\n", True)
if self.timeinfo:
i = 0
@ -868,7 +882,7 @@ class SmtIo:
if self.debug_file:
print("(set-info :status %s)" % result, file=self.debug_file)
print("(check-sat)", file=self.debug_file)
print(check_stmt, file=self.debug_file)
self.debug_file.flush()
if result not in expected:
@ -945,6 +959,55 @@ class SmtIo:
def bv2int(self, v):
return int(self.bv2bin(v), 2)
def get_raw_unsat_assumptions(self):
if not self.smt2_assumptions:
return []
self.write("(get-unsat-assumptions)")
exprs = set(self.unparse(part) for part in self.parse(self.read()))
unsat_assumptions = []
for key, value in self.smt2_assumptions.items():
# normalize expression
value = self.unparse(self.parse(value))
if value in exprs:
exprs.remove(value)
unsat_assumptions.append(key)
return unsat_assumptions
def get_unsat_assumptions(self, minimize=False):
if not minimize:
return self.get_raw_unsat_assumptions()
orig_assumptions = self.smt2_assumptions
self.smt2_assumptions = dict(orig_assumptions)
required_assumptions = {}
while True:
candidate_assumptions = {}
for key in self.get_raw_unsat_assumptions():
if key not in required_assumptions:
candidate_assumptions[key] = self.smt2_assumptions[key]
while candidate_assumptions:
candidate_key, candidate_assume = candidate_assumptions.popitem()
self.smt2_assumptions = {}
for key, assume in candidate_assumptions.items():
self.smt2_assumptions[key] = assume
for key, assume in required_assumptions.items():
self.smt2_assumptions[key] = assume
result = self.check_sat()
if result == 'unsat':
candidate_assumptions = None
else:
required_assumptions[candidate_key] = candidate_assume
if candidate_assumptions is not None:
self.smt2_assumptions = orig_assumptions
return list(required_assumptions)
def get(self, expr):
self.write("(get-value (%s))" % (expr))
return self.parse(self.read())[0][1]
@ -1091,7 +1154,7 @@ class SmtIo:
class SmtOpts:
def __init__(self):
self.shortopts = "s:S:v"
self.longopts = ["unroll", "noincr", "noprogress", "timeout=", "dump-smt2=", "logic=", "dummy=", "info=", "nocomments"]
self.longopts = ["unroll", "noincr", "noprogress", "timeout=", "dump-smt2=", "logic=", "dummy=", "info=", "nocomments", "smt2-option="]
self.solver = "yices"
self.solver_opts = list()
self.debug_print = False
@ -1104,6 +1167,7 @@ class SmtOpts:
self.logic = None
self.info_stmts = list()
self.nocomments = False
self.smt2_options = {}
def handle(self, o, a):
if o == "-s":
@ -1130,6 +1194,13 @@ class SmtOpts:
self.info_stmts.append(a)
elif o == "--nocomments":
self.nocomments = True
elif o == "--smt2-option":
args = a.split('=', 1)
if len(args) != 2:
print("--smt2-option expects an <option>=<value> argument")
sys.exit(1)
option, value = args
self.smt2_options[option] = value
else:
return False
return True
@ -1137,7 +1208,7 @@ class SmtOpts:
def helpmsg(self):
return """
-s <solver>
set SMT solver: z3, yices, boolector, bitwuzla, cvc4, mathsat, dummy
set SMT solver: z3, yices, boolector, bitwuzla, cvc4, cvc5, mathsat, dummy
default: yices
-S <opt>
@ -1153,6 +1224,9 @@ class SmtOpts:
if solver is "dummy", read solver output from that file
otherwise: write solver output to that file
--smt2-option <option>=<value>
enable an SMT-LIBv2 option.
-v
enable debug output

View file

@ -21,7 +21,7 @@ EOT
for x in $(set +x; ls test_*.il | sort -R); do
x=${x%.il}
cat > $x.ys <<- EOT
read_ilang $x.il
read_rtlil $x.il
copy gold gate
cd gate

View file

@ -59,7 +59,7 @@ struct SmvWorker
{
if (!idcache.count(id))
{
string name = stringf("_%s", id.c_str());
string name = stringf("_%s", id);
if (name.compare(0, 2, "_\\") == 0)
name = "_" + name.substr(2);
@ -163,15 +163,15 @@ struct SmvWorker
if (width >= 0) {
if (is_signed) {
if (GetSize(sig) > width)
s = stringf("signed(resize(%s, %d))", s.c_str(), width);
s = stringf("signed(resize(%s, %d))", s, width);
else
s = stringf("resize(signed(%s), %d)", s.c_str(), width);
s = stringf("resize(signed(%s), %d)", s, width);
} else
s = stringf("resize(%s, %d)", s.c_str(), width);
s = stringf("resize(%s, %d)", s, width);
} else if (is_signed)
s = stringf("signed(%s)", s.c_str());
s = stringf("signed(%s)", s);
else if (count_chunks > 1)
s = stringf("(%s)", s.c_str());
s = stringf("(%s)", s);
strbuf.push_back(s);
return strbuf.back().c_str();
@ -262,7 +262,7 @@ struct SmvWorker
if (cell->type == ID($sshr) && signed_a)
{
expr_a = rvalue_s(sig_a, width);
expr = stringf("resize(unsigned(%s %s %s), %d)", expr_a.c_str(), op.c_str(), rvalue(sig_b.extract(0, shift_b_width)), width_y);
expr = stringf("resize(unsigned(%s %s %s), %d)", expr_a, op, rvalue(sig_b.extract(0, shift_b_width)), width_y);
if (shift_b_width < GetSize(sig_b))
expr = stringf("%s != 0ud%d_0 ? (bool(%s) ? !0ud%d_0 : 0ud%d_0) : %s",
rvalue(sig_b.extract(shift_b_width, GetSize(sig_b) - shift_b_width)), GetSize(sig_b) - shift_b_width,
@ -278,8 +278,8 @@ struct SmvWorker
// f << stringf(" %s : unsigned word[%d]; -- neg(%s)\n", b_shl, GetSize(sig_b), log_signal(sig_b));
definitions.push_back(stringf("%s := unsigned(-%s);", b_shl, rvalue_s(sig_b)));
string expr_shl = stringf("resize(%s << %s[%d:0], %d)", expr_a.c_str(), b_shl, shift_b_width-1, width_y);
string expr_shr = stringf("resize(%s >> %s[%d:0], %d)", expr_a.c_str(), b_shr, shift_b_width-1, width_y);
string expr_shl = stringf("resize(%s << %s[%d:0], %d)", expr_a, b_shl, shift_b_width-1, width_y);
string expr_shr = stringf("resize(%s >> %s[%d:0], %d)", expr_a, b_shr, shift_b_width-1, width_y);
if (shift_b_width < GetSize(sig_b)) {
expr_shl = stringf("%s[%d:%d] != 0ud%d_0 ? 0ud%d_0 : %s", b_shl, GetSize(sig_b)-1, shift_b_width,
@ -288,7 +288,7 @@ struct SmvWorker
GetSize(sig_b)-shift_b_width, width_y, expr_shr.c_str());
}
expr = stringf("bool(%s) ? %s : %s", rvalue(sig_b[GetSize(sig_b)-1]), expr_shl.c_str(), expr_shr.c_str());
expr = stringf("bool(%s) ? %s : %s", rvalue(sig_b[GetSize(sig_b)-1]), expr_shl, expr_shr);
}
else
{
@ -297,13 +297,13 @@ struct SmvWorker
else
expr_a = stringf("resize(unsigned(%s), %d)", rvalue_s(sig_a, width_ay), width);
expr = stringf("resize(%s %s %s[%d:0], %d)", expr_a.c_str(), op.c_str(), rvalue_u(sig_b), shift_b_width-1, width_y);
expr = stringf("resize(%s %s %s[%d:0], %d)", expr_a, op, rvalue_u(sig_b), shift_b_width-1, width_y);
if (shift_b_width < GetSize(sig_b))
expr = stringf("%s[%d:%d] != 0ud%d_0 ? 0ud%d_0 : %s", rvalue_u(sig_b), GetSize(sig_b)-1, shift_b_width,
GetSize(sig_b)-shift_b_width, width_y, expr.c_str());
}
definitions.push_back(stringf("%s := %s;", lvalue(cell->getPort(ID::Y)), expr.c_str()));
definitions.push_back(stringf("%s := %s;", lvalue(cell->getPort(ID::Y)), expr));
continue;
}
@ -426,7 +426,7 @@ struct SmvWorker
if (cell->type == ID($reduce_or)) expr = stringf("%s != 0ub%d_0", expr_a, width_a);
if (cell->type == ID($reduce_bool)) expr = stringf("%s != 0ub%d_0", expr_a, width_a);
definitions.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr.c_str(), width_y));
definitions.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr, width_y));
continue;
}
@ -445,7 +445,7 @@ struct SmvWorker
if (cell->type == ID($reduce_xnor))
expr = "!(" + expr + ")";
definitions.push_back(stringf("%s := resize(%s, %d);", expr_y, expr.c_str(), width_y));
definitions.push_back(stringf("%s := resize(%s, %d);", expr_y, expr, width_y));
continue;
}
@ -463,7 +463,7 @@ struct SmvWorker
if (cell->type == ID($logic_and)) expr = expr_a + " & " + expr_b;
if (cell->type == ID($logic_or)) expr = expr_a + " | " + expr_b;
definitions.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr.c_str(), width_y));
definitions.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr, width_y));
continue;
}
@ -475,7 +475,7 @@ struct SmvWorker
string expr_a = stringf("(%s = 0ub%d_0)", rvalue(cell->getPort(ID::A)), width_a);
const char *expr_y = lvalue(cell->getPort(ID::Y));
definitions.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr_a.c_str(), width_y));
definitions.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr_a, width_y));
continue;
}
@ -491,7 +491,7 @@ struct SmvWorker
expr += stringf("bool(%s) ? %s : ", rvalue(sig_s[i]), rvalue(sig_b.extract(i*width, width)));
expr += rvalue(sig_a);
definitions.push_back(stringf("%s := %s;", lvalue(cell->getPort(ID::Y)), expr.c_str()));
definitions.push_back(stringf("%s := %s;", lvalue(cell->getPort(ID::Y)), expr));
continue;
}
@ -505,7 +505,7 @@ struct SmvWorker
if (cell->type.in(ID($_BUF_), ID($_NOT_)))
{
string op = cell->type == ID($_NOT_) ? "!" : "";
definitions.push_back(stringf("%s := %s%s;", lvalue(cell->getPort(ID::Y)), op.c_str(), rvalue(cell->getPort(ID::A))));
definitions.push_back(stringf("%s := %s%s;", lvalue(cell->getPort(ID::Y)), op, rvalue(cell->getPort(ID::A))));
continue;
}
@ -650,7 +650,7 @@ struct SmvWorker
for (int k = GetSize(sig)-1; k >= 0; k--)
bits += sig[k] == State::S1 ? '1' : '0';
expr = stringf("0ub%d_%s", GetSize(bits), bits.c_str()) + expr;
expr = stringf("0ub%d_%s", GetSize(bits), bits) + expr;
}
else if (sigmap(SigBit(wire, i)) == SigBit(wire, i))
{
@ -683,36 +683,36 @@ struct SmvWorker
}
}
definitions.push_back(stringf("%s := %s;", cid(wire->name), expr.c_str()));
definitions.push_back(stringf("%s := %s;", cid(wire->name), expr));
}
if (!inputvars.empty()) {
f << stringf(" IVAR\n");
for (const string &line : inputvars)
f << stringf(" %s\n", line.c_str());
f << stringf(" %s\n", line);
}
if (!vars.empty()) {
f << stringf(" VAR\n");
for (const string &line : vars)
f << stringf(" %s\n", line.c_str());
f << stringf(" %s\n", line);
}
if (!definitions.empty()) {
f << stringf(" DEFINE\n");
for (const string &line : definitions)
f << stringf(" %s\n", line.c_str());
f << stringf(" %s\n", line);
}
if (!assignments.empty()) {
f << stringf(" ASSIGN\n");
for (const string &line : assignments)
f << stringf(" %s\n", line.c_str());
f << stringf(" %s\n", line);
}
if (!invarspecs.empty()) {
for (const string &line : invarspecs)
f << stringf(" INVARSPEC %s\n", line.c_str());
f << stringf(" INVARSPEC %s\n", line);
}
}
};
@ -756,7 +756,7 @@ struct SmvBackend : public Backend {
if (args[argidx] == "-tpl" && argidx+1 < args.size()) {
template_f.open(args[++argidx]);
if (template_f.fail())
log_error("Can't open template file `%s'.\n", args[argidx].c_str());
log_error("Can't open template file `%s'.\n", args[argidx]);
continue;
}
if (args[argidx] == "-verbose") {
@ -795,9 +795,9 @@ struct SmvBackend : public Backend {
modules.erase(module);
if (module == nullptr)
log_error("Module '%s' not found.\n", stmt[1].c_str());
log_error("Module '%s' not found.\n", stmt[1]);
*f << stringf("-- SMV description generated by %s\n", yosys_version_str);
*f << stringf("-- SMV description generated by %s\n", yosys_maybe_version());
log("Creating SMV representation of module %s.\n", log_id(module));
SmvWorker worker(module, verbose, *f);
@ -816,7 +816,7 @@ struct SmvBackend : public Backend {
if (!modules.empty())
{
*f << stringf("-- SMV description generated by %s\n", yosys_version_str);
*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));

View file

@ -17,11 +17,11 @@ EOT
for fn in test_*.il; do
../../../yosys -p "
read_ilang $fn
read_rtlil $fn
rename gold gate
synth
read_ilang $fn
read_rtlil $fn
miter -equiv -flatten gold gate main
hierarchy -top main
write_smv -tpl template.txt ${fn#.il}.smv

View file

@ -51,16 +51,16 @@ static void print_spice_net(std::ostream &f, RTLIL::SigBit s, std::string &neg,
if (s.wire->port_id)
use_inames = true;
if (s.wire->width > 1)
f << stringf(" %s.%d", spice_id2str(s.wire->name, use_inames, inums).c_str(), s.offset);
f << stringf(" %s.%d", spice_id2str(s.wire->name, use_inames, inums), s.offset);
else
f << stringf(" %s", spice_id2str(s.wire->name, use_inames, inums).c_str());
f << stringf(" %s", spice_id2str(s.wire->name, use_inames, inums));
} else {
if (s == RTLIL::State::S0)
f << stringf(" %s", neg.c_str());
f << stringf(" %s", neg);
else if (s == RTLIL::State::S1)
f << stringf(" %s", pos.c_str());
f << stringf(" %s", pos);
else
f << stringf(" %s%d", ncpf.c_str(), nc_counter++);
f << stringf(" %s%d", ncpf, nc_counter++);
}
}
@ -119,7 +119,7 @@ static void print_spice_module(std::ostream &f, RTLIL::Module *module, RTLIL::De
}
}
f << stringf(" %s\n", spice_id2str(cell->type).c_str());
f << stringf(" %s\n", spice_id2str(cell->type));
}
for (auto &conn : module->connections())
@ -127,7 +127,7 @@ static void print_spice_module(std::ostream &f, RTLIL::Module *module, RTLIL::De
f << (buf == "DC" ? stringf("V%d", conn_counter++) : stringf("X%d", cell_counter++));
print_spice_net(f, conn.second.extract(i, 1), neg, pos, ncpf, nc_counter, use_inames, inums);
print_spice_net(f, conn.first.extract(i, 1), neg, pos, ncpf, nc_counter, use_inames, inums);
f << (buf == "DC" ? " DC 0\n" : stringf(" %s\n", buf.c_str()));
f << (buf == "DC" ? " DC 0\n" : stringf(" %s\n", buf));
}
}
@ -215,7 +215,7 @@ struct SpiceBackend : public Backend {
if (module->get_bool_attribute(ID::top))
top_module_name = module->name.str();
*f << stringf("* SPICE netlist generated by %s\n", yosys_version_str);
*f << stringf("* SPICE netlist generated by %s\n", yosys_maybe_version());
*f << stringf("\n");
for (auto module : design->modules())
@ -242,23 +242,23 @@ struct SpiceBackend : public Backend {
ports.at(wire->port_id-1) = wire;
}
*f << stringf(".SUBCKT %s", spice_id2str(module->name).c_str());
*f << stringf(".SUBCKT %s", spice_id2str(module->name));
for (RTLIL::Wire *wire : ports) {
log_assert(wire != NULL);
if (wire->width > 1) {
for (int i = 0; i < wire->width; i++)
*f << stringf(" %s.%d", spice_id2str(wire->name).c_str(), big_endian ? wire->width - 1 - i : i);
*f << stringf(" %s.%d", spice_id2str(wire->name), big_endian ? wire->width - 1 - i : i);
} else
*f << stringf(" %s", spice_id2str(wire->name).c_str());
*f << stringf(" %s", spice_id2str(wire->name));
}
*f << stringf("\n");
print_spice_module(*f, module, design, neg, pos, buf, ncpf, big_endian, use_inames);
*f << stringf(".ENDS %s\n\n", spice_id2str(module->name).c_str());
*f << stringf(".ENDS %s\n\n", spice_id2str(module->name));
}
if (!top_module_name.empty()) {
if (top_module == NULL)
log_error("Can't find top module `%s'!\n", top_module_name.c_str());
log_error("Can't find top module `%s'!\n", top_module_name);
print_spice_module(*f, top_module, design, neg, pos, buf, ncpf, big_endian, use_inames);
*f << stringf("\n");
}

File diff suppressed because it is too large Load diff

View file

@ -17,35 +17,23 @@
*
* ---
*
* A very simple and straightforward frontend for the RTLIL text
* representation.
* A simple and straightforward Verilog backend.
*
*/
#ifndef RTLIL_FRONTEND_H
#define RTLIL_FRONTEND_H
#ifndef VERILOG_BACKEND_H
#define VERILOG_BACKEND_H
#include "kernel/yosys.h"
#include <string>
YOSYS_NAMESPACE_BEGIN
namespace VERILOG_BACKEND {
namespace RTLIL_FRONTEND {
extern std::istream *lexin;
extern RTLIL::Design *current_design;
extern bool flag_nooverwrite;
extern bool flag_overwrite;
extern bool flag_lib;
}
const pool<string> verilog_keywords();
bool char_is_verilog_escaped(char c);
bool id_is_verilog_escaped(const std::string &str);
}; /* namespace VERILOG_BACKEND */
YOSYS_NAMESPACE_END
extern int rtlil_frontend_yydebug;
int rtlil_frontend_yylex(void);
void rtlil_frontend_yyerror(char const *s);
void rtlil_frontend_yyrestart(FILE *f);
int rtlil_frontend_yyparse(void);
int rtlil_frontend_yylex_destroy(void);
int rtlil_frontend_yyget_lineno(void);
#endif
#endif /* VERILOG_BACKEND_H */

18
docs/.gitignore vendored
View file

@ -1,11 +1,9 @@
/build/
/source/cmd
/source/temp
/images/*.log
/images/*.aux
/images/*.pdf
/images/*.svg
/images/011/*.log
/images/011/*.aux
/images/011/*.pdf
/images/011/*.svg
/source/generated
/source/_images/**/*.log
/source/_images/**/*.aux
/source/_images/**/*.pdf
/source/_images/**/*.svg
/source/_images/**/*.dot
/source/_images/code_examples
/venv

View file

@ -2,7 +2,7 @@
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXOPTS = -W --keep-going
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = build
@ -45,8 +45,11 @@ help:
@echo " dummy to check syntax errors of document sources"
.PHONY: clean
clean:
clean: clean-examples
rm -rf $(BUILDDIR)/*
rm -rf util/__pycache__
rm -rf source/generated
$(MAKE) -C source/_images clean
.PHONY: html
html:
@ -224,3 +227,36 @@ dummy:
$(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy
@echo
@echo "Build finished. Dummy builder generates no files."
PYTHON ?= python3
.PHONY: test test-examples test-macros examples
test: test-examples test-macros
FORCE:
Makefile-%: FORCE
$(MAKE) -C $(@D) $(*F)
CODE_EXAMPLES := $(wildcard source/code_examples/*/Makefile)
TEST_EXAMPLES := $(addsuffix -examples,$(CODE_EXAMPLES))
CLEAN_EXAMPLES := $(addsuffix -clean,$(CODE_EXAMPLES))
test-examples: $(TEST_EXAMPLES)
clean-examples: $(CLEAN_EXAMPLES)
examples: $(TEST_EXAMPLES)
test-macros:
$(PYTHON) tests/macro_commands.py
.PHONY: images
images:
$(MAKE) -C source/_images
$(MAKE) -C source/_images convert
.PHONY: gen
gen:
$(MAKE) examples
$(MAKE) images
.PHONY: reqs
reqs:
$(PYTHON) -m pip install -r source/requirements.txt

View file

@ -1,18 +0,0 @@
\documentclass[12pt,tikz]{standalone}
\pdfinfoomitdate 1
\pdfsuppressptexinfo 1
\pdftrailerid{}
\usepackage[utf8]{inputenc}
\usepackage{tikz}
\pagestyle{empty}
\begin{document}
\begin{tikzpicture}
\node[inner sep=0pt] at (0,0)
{\includegraphics[width=\linewidth]{example_00.pdf}};
\node[inner sep=0pt] at (0,-3.8)
{\includegraphics[width=\linewidth]{example_01.pdf}};
\node[inner sep=0pt] at (0,-7)
{\includegraphics[width=\linewidth]{example_02.pdf}};
\end{tikzpicture}
\end{document}

View file

@ -1,19 +0,0 @@
\documentclass[12pt,tikz]{standalone}
\pdfinfoomitdate 1
\pdfsuppressptexinfo 1
\pdftrailerid{}
\usepackage[utf8]{inputenc}
\pagestyle{empty}
\begin{document}
\begin{tikzpicture}
\node[inner sep=0pt] at (0,0)
{\hfill \includegraphics[width=4cm,trim=0 1cm 0 1cm]{sumprod_02.pdf}};
\node[inner sep=0pt] at (0,-2.8)
{\includegraphics[width=\linewidth,trim=0 0cm 0 1cm]{sumprod_03.pdf}};
\node[inner sep=0pt] at (0,-6.2)
{\includegraphics[width=\linewidth,trim=0 0cm 0 1cm]{sumprod_04.pdf}};
\node[inner sep=0pt] at (0,-9.2)
{\includegraphics[width=\linewidth,trim=0 1cm 0 1cm]{sumprod_05.pdf}};
\end{tikzpicture}
\end{document}

View file

@ -1,15 +0,0 @@
\documentclass[12pt,tikz]{standalone}
\pdfinfoomitdate 1
\pdfsuppressptexinfo 1
\pdftrailerid{}
\usepackage[utf8]{inputenc}
\pagestyle{empty}
\begin{document}
\begin{tikzpicture}
\node[inner sep=0pt] at (0,0)
{\includegraphics[height=\linewidth]{cmos_00.pdf}};
\node[inner sep=0pt] at (2,-8)
{\includegraphics[width=\linewidth]{cmos_01.pdf}};
\end{tikzpicture}
\end{document}

View file

@ -1,27 +0,0 @@
\documentclass[12pt,tikz]{standalone}
\pdfinfoomitdate 1
\pdfsuppressptexinfo 1
\pdftrailerid{}
\usepackage[utf8]{inputenc}
\pagestyle{empty}
\begin{document}
\begin{tikzpicture}
\node[inner sep=0pt] at (0,0)
{\includegraphics[width=\linewidth,trim=0 1.3cm 0 0cm]{submod_00.pdf}};
\node at (0, -2.5)
{\tt memdemo};
\node[inner sep=0pt] at (0,-5)
{\includegraphics[width=\linewidth,trim=0 1.3cm 0 0cm]{submod_01.pdf}};
\node at (0, -7.5)
{\tt scramble};
\node[inner sep=0pt] at (0, -11)
{\includegraphics[width=\linewidth,trim=0 1.3cm 0 0cm]{submod_02.pdf}};
\node at (0, -14.8)
{\tt outstage};
\node[inner sep=0pt] at (0,-16.6)
{\includegraphics[width=\linewidth,trim=0 1.3cm 0 0cm]{submod_03.pdf}};
\node at (0, -19)
{\tt selstage};
\end{tikzpicture}
\end{document}

View file

@ -1,44 +0,0 @@
all: dots tex svg tidy
TEX_SOURCE:= $(wildcard *.tex)
DOT_LOC:= ../source/APPNOTE_011_Design_Investigation
DOT_SOURCE:= $(wildcard $(DOT_LOC)/*.dot)
TEX_SOURCE+= 011/example_out.tex
011/example_out.pdf: 011/example_00.pdf 011/example_01.pdf 011/example_02.pdf
TEX_SOURCE+= 011/select_prod.tex
011/select_prod.pdf: 011/sumprod_02.pdf 011/sumprod_03.pdf 011/sumprod_04.pdf 011/sumprod_05.pdf
TEX_SOURCE+= 011/splitnets_libfile.tex
011/splitnets_libfile.pdf: 011/cmos_00.pdf 011/cmos_01.pdf
TEX_SOURCE+= 011/submod_dots.tex
011/submod_dots.pdf: 011/submod_00.pdf 011/submod_01.pdf 011/submod_02.pdf 011/submod_03.pdf
TEX_PDF:= $(patsubst %.tex,%.pdf,$(TEX_SOURCE))
DOT_PDF:= $(addprefix 011/,$(notdir $(patsubst %.dot,%.pdf,$(DOT_SOURCE))))
SVG_OUTPUT:= $(patsubst %.pdf,%.svg,$(TEX_PDF) $(DOT_PDF))
dots: $(DOT_PDF)
tex: $(TEX_PDF)
svg: $(SVG_OUTPUT)
011/%.pdf: $(DOT_LOC)/%.dot
faketime -f '2022-01-01 00:00:00 x0,001' dot -Tpdf -o $@ $<
011/%.pdf: 011/%.tex
cd 011 && faketime -f '2022-01-01 00:00:00 x0,001' pdflatex $(<F) --interaction=nonstopmode
%.pdf: %.tex
pdflatex $< --interaction=nonstopmode
%.svg: %.pdf
pdf2svg $< $@
.PHONY: clean tidy
tidy:
rm -f *.log
rm -f *.aux
rm -f 011/*.log 011/*.aux
clean: tidy
rm -f *.pdf
rm -f *.svg
rm -f 011/*.pdf 011/*.svg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View file

@ -1,34 +0,0 @@
digraph "cmos_demo" {
rankdir="LR";
remincross=true;
n4 [ shape=octagon, label="a", color="black", fontcolor="black" ];
n5 [ shape=octagon, label="b", color="black", fontcolor="black" ];
n6 [ shape=octagon, label="y", color="black", fontcolor="black" ];
c10 [ shape=record, label="{{<p7> A|<p8> B|<p9> Y}|$g0\nNOR|{}}" ];
c11 [ shape=record, label="{{<p7> A|<p9> Y}|$g1\nNOT|{}}" ];
c12 [ shape=record, label="{{<p7> A|<p9> Y}|$g2\nNOT|{}}" ];
c13 [ shape=record, label="{{<p7> A|<p8> B|<p9> Y}|$g3\nNOR|{}}" ];
x0 [ shape=record, style=rounded, label="<s0> 1:1 - 0:0 " ];
x0:e -> c13:p9:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", label=""];
c14 [ shape=record, label="{{<p7> A|<p8> B|<p9> Y}|$g4\nNOR|{}}" ];
x1 [ shape=record, style=rounded, label="<s0> 1:1 - 0:0 " ];
x1:e -> c14:p8:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", label=""];
x2 [ shape=record, style=rounded, label="<s0> 0:0 - 0:0 " ];
x2:e -> c14:p9:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", label=""];
n1 [ shape=diamond, label="$n4" ];
n1:e -> c10:p9:w [color="black", label=""];
n1:e -> c14:p7:w [color="black", label=""];
n2 [ shape=diamond, label="$n5" ];
n2:e -> c11:p9:w [color="black", label=""];
n2:e -> c13:p7:w [color="black", label=""];
n3 [ shape=diamond, label="$n6_1" ];
n3:e -> c12:p9:w [color="black", label=""];
n3:e -> c13:p8:w [color="black", label=""];
n4:e -> c10:p8:w [color="black", label=""];
n4:e -> c12:p7:w [color="black", label=""];
n5:e -> c10:p7:w [color="black", label=""];
n5:e -> c11:p7:w [color="black", label=""];
n6:e -> x0:s0:w [color="black", label=""];
n6:e -> x1:s0:w [color="black", label=""];
n6:e -> x2:s0:w [color="black", label=""];
}

View file

@ -1,23 +0,0 @@
digraph "cmos_demo" {
rankdir="LR";
remincross=true;
n4 [ shape=octagon, label="a", color="black", fontcolor="black" ];
n5 [ shape=octagon, label="b", color="black", fontcolor="black" ];
n6 [ shape=octagon, label="y[0]", color="black", fontcolor="black" ];
n7 [ shape=octagon, label="y[1]", color="black", fontcolor="black" ];
c11 [ shape=record, label="{{<p8> A|<p9> B}|$g0\nNOR|{<p10> Y}}" ];
c12 [ shape=record, label="{{<p8> A}|$g1\nNOT|{<p10> Y}}" ];
c13 [ shape=record, label="{{<p8> A}|$g2\nNOT|{<p10> Y}}" ];
c14 [ shape=record, label="{{<p8> A|<p9> B}|$g3\nNOR|{<p10> Y}}" ];
c15 [ shape=record, label="{{<p8> A|<p9> B}|$g4\nNOR|{<p10> Y}}" ];
c11:p10:e -> c15:p8:w [color="black", label=""];
c12:p10:e -> c14:p8:w [color="black", label=""];
c13:p10:e -> c14:p9:w [color="black", label=""];
n4:e -> c11:p9:w [color="black", label=""];
n4:e -> c13:p8:w [color="black", label=""];
n5:e -> c11:p8:w [color="black", label=""];
n5:e -> c12:p8:w [color="black", label=""];
c15:p10:e -> n6:w [color="black", label=""];
c14:p10:e -> n7:w [color="black", label=""];
n7:e -> c15:p9:w [color="black", label=""];
}

View file

@ -1,11 +0,0 @@
read_verilog example.v
show -format dot -prefix example_00
proc
show -format dot -prefix example_01
opt
show -format dot -prefix example_02
cd example
select t:$add
show -format dot -prefix example_03

View file

@ -1,23 +0,0 @@
digraph "example" {
rankdir="LR";
remincross=true;
n4 [ shape=octagon, label="a", color="black", fontcolor="black" ];
n5 [ shape=octagon, label="b", color="black", fontcolor="black" ];
n6 [ shape=octagon, label="c", color="black", fontcolor="black" ];
n7 [ shape=octagon, label="clk", color="black", fontcolor="black" ];
n8 [ shape=octagon, label="y", color="black", fontcolor="black" ];
c12 [ shape=record, label="{{<p9> A|<p10> B}|$2\n$add|{<p11> Y}}" ];
v0 [ label="2'00" ];
c14 [ shape=record, label="{{<p9> A|<p10> B|<p13> S}|$3\n$mux|{<p11> Y}}" ];
p1 [shape=box, style=rounded, label="PROC $1\nexample.v:3"];
c12:p11:e -> c14:p10:w [color="black", style="setlinewidth(3)", label=""];
c14:p11:e -> p1:w [color="black", style="setlinewidth(3)", label=""];
n4:e -> c12:p9:w [color="black", label=""];
n5:e -> c12:p10:w [color="black", label=""];
n6:e -> c14:p13:w [color="black", label=""];
n6:e -> p1:w [color="black", label=""];
n7:e -> p1:w [color="black", label=""];
p1:e -> n8:w [color="black", style="setlinewidth(3)", label=""];
n8:e -> p1:w [color="black", style="setlinewidth(3)", label=""];
v0:e -> c14:p9:w [color="black", style="setlinewidth(3)", label=""];
}

View file

@ -1,33 +0,0 @@
digraph "example" {
rankdir="LR";
remincross=true;
n6 [ shape=octagon, label="a", color="black", fontcolor="black" ];
n7 [ shape=octagon, label="b", color="black", fontcolor="black" ];
n8 [ shape=octagon, label="c", color="black", fontcolor="black" ];
n9 [ shape=octagon, label="clk", color="black", fontcolor="black" ];
n10 [ shape=octagon, label="y", color="black", fontcolor="black" ];
c14 [ shape=record, label="{{<p11> A|<p12> B}|$2\n$add|{<p13> Y}}" ];
c18 [ shape=record, label="{{<p15> CLK|<p16> D}|$7\n$dff|{<p17> Q}}" ];
c20 [ shape=record, label="{{<p11> A|<p12> B|<p19> S}|$5\n$mux|{<p13> Y}}" ];
v0 [ label="2'00" ];
c21 [ shape=record, label="{{<p11> A|<p12> B|<p19> S}|$3\n$mux|{<p13> Y}}" ];
x1 [shape=box, style=rounded, label="BUF"];
x2 [shape=box, style=rounded, label="BUF"];
n1 [ shape=diamond, label="$0\\y[1:0]" ];
x2:e:e -> n1:w [color="black", style="setlinewidth(3)", label=""];
c18:p17:e -> n10:w [color="black", style="setlinewidth(3)", label=""];
n10:e -> c20:p11:w [color="black", style="setlinewidth(3)", label=""];
c14:p13:e -> c21:p12:w [color="black", style="setlinewidth(3)", label=""];
n3 [ shape=point ];
c20:p13:e -> n3:w [color="black", style="setlinewidth(3)", label=""];
n3:e -> c18:p16:w [color="black", style="setlinewidth(3)", label=""];
n3:e -> x2:w:w [color="black", style="setlinewidth(3)", label=""];
x1:e:e -> c20:p19:w [color="black", label=""];
c21:p13:e -> c20:p12:w [color="black", style="setlinewidth(3)", label=""];
n6:e -> c14:p11:w [color="black", label=""];
n7:e -> c14:p12:w [color="black", label=""];
n8:e -> c21:p19:w [color="black", label=""];
n8:e -> x1:w:w [color="black", label=""];
n9:e -> c18:p15:w [color="black", label=""];
v0:e -> c21:p11:w [color="black", style="setlinewidth(3)", label=""];
}

View file

@ -1,20 +0,0 @@
digraph "example" {
rankdir="LR";
remincross=true;
n3 [ shape=octagon, label="a", color="black", fontcolor="black" ];
n4 [ shape=octagon, label="b", color="black", fontcolor="black" ];
n5 [ shape=octagon, label="c", color="black", fontcolor="black" ];
n6 [ shape=octagon, label="clk", color="black", fontcolor="black" ];
n7 [ shape=octagon, label="y", color="black", fontcolor="black" ];
c11 [ shape=record, label="{{<p8> A|<p9> B}|$2\n$add|{<p10> Y}}" ];
c15 [ shape=record, label="{{<p12> CLK|<p13> D}|$7\n$dff|{<p14> Q}}" ];
c17 [ shape=record, label="{{<p8> A|<p9> B|<p16> S}|$5\n$mux|{<p10> Y}}" ];
c17:p10:e -> c15:p13:w [color="black", style="setlinewidth(3)", label=""];
c11:p10:e -> c17:p9:w [color="black", style="setlinewidth(3)", label=""];
n3:e -> c11:p8:w [color="black", label=""];
n4:e -> c11:p9:w [color="black", label=""];
n5:e -> c17:p16:w [color="black", label=""];
n6:e -> c15:p12:w [color="black", label=""];
c15:p14:e -> n7:w [color="black", style="setlinewidth(3)", label=""];
n7:e -> c17:p8:w [color="black", style="setlinewidth(3)", label=""];
}

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