Merge remote-tracking branch 'origin/main' into icells_not_derived
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
$Format:%h$
|
||||
$Format:%H$
|
||||
|
|
|
|||
8
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
|
@ -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.
|
||||
|
||||
|
||||
|
|
|
|||
11
.github/ISSUE_TEMPLATE/config.yml
vendored
|
|
@ -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
|
|
@ -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
|
||||
9
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
|
|
@ -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
|
|
@ -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._
|
||||
91
.github/actions/setup-build-env/action.yml
vendored
Normal 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 }}
|
||||
70
.github/actions/setup-iverilog/action.yml
vendored
Normal 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 }}
|
||||
12
.github/workflows/codeql.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
18
.github/workflows/emcc.yml
vendored
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
123
.github/workflows/test-linux.yml
vendored
|
|
@ -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
|
||||
75
.github/workflows/test-macos.yml
vendored
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
10
.github/workflows/version.yml
vendored
|
|
@ -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 }}
|
||||
|
|
|
|||
31
.github/workflows/vs.yml
vendored
|
|
@ -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
|
||||
30
.github/workflows/wasi.yml
vendored
|
|
@ -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
|
|
@ -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
|
||||
52
.github/workflows/wheels/_run_cibw_linux.py
vendored
Normal 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)
|
||||
37
.github/workflows/wheels/cibw_before_all.sh
vendored
Normal 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
|
||||
)
|
||||
13
.github/workflows/wheels/cibw_before_build.sh
vendored
Normal 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
|
|
@ -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
|
|
@ -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
|
||||
6
.mailmap
|
|
@ -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
|
|
@ -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
|
||||
6
Brewfile
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
|||
12
CODEOWNERS
|
|
@ -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
|
|
@ -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
|
|
@ -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.
|
||||
2
COPYING
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
512
README.md
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
1
backends/aiger2/Makefile.inc
Normal file
|
|
@ -0,0 +1 @@
|
|||
OBJS += backends/aiger2/aiger.o
|
||||
1488
backends/aiger2/aiger.cc
Normal 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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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>()};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
4
backends/functional/Makefile.inc
Normal 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
|
|
@ -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 ¤t_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
|
||||
418
backends/functional/cxx_runtime/sim.h
Normal 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
|
||||
295
backends/functional/smtlib.cc
Normal 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
|
||||
373
backends/functional/smtlib_rosette.cc
Normal 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
|
||||
158
backends/functional/test_generic.cc
Normal 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
|
||||
|
|
@ -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 ¶m : 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;
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 ¶m : 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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
@ -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}
|
||||
|
|
@ -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}
|
||||
|
|
@ -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}
|
||||
|
|
@ -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
|
||||
|
Before Width: | Height: | Size: 9.5 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
|
@ -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=""];
|
||||
}
|
||||
|
|
@ -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=""];
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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=""];
|
||||
}
|
||||
|
|
@ -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=""];
|
||||
}
|
||||
|
|
@ -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=""];
|
||||
}
|
||||