mirror of
https://github.com/YosysHQ/yosys
synced 2026-07-21 22:55:51 +00:00
commit
efc78520af
257 changed files with 35348 additions and 1905 deletions
|
|
@ -10,3 +10,7 @@ insert_final_newline = true
|
|||
indent_style = space
|
||||
indent_size = 2
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.yml]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
|
|
|||
3
.github/actions/setup-build-env/action.yml
vendored
3
.github/actions/setup-build-env/action.yml
vendored
|
|
@ -14,7 +14,8 @@ runs:
|
|||
if: runner.os == 'macOS'
|
||||
shell: bash
|
||||
run: |
|
||||
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew install bison flex gawk libffi pkg-config bash autoconf llvm
|
||||
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew update
|
||||
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew install bison flex gawk libffi pkg-config bash autoconf llvm lld
|
||||
|
||||
- name: Linux runtime environment
|
||||
if: runner.os == 'Linux'
|
||||
|
|
|
|||
78
.github/workflows/prepare-docs.yml
vendored
Normal file
78
.github/workflows/prepare-docs.yml
vendored
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
name: Build docs artifact with Verific
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
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=${{ env.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' }}
|
||||
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
|
||||
make -j${{ env.procs }} ENABLE_LTO=1
|
||||
|
||||
- name: Prepare docs
|
||||
shell: bash
|
||||
run:
|
||||
make docs/prep -j${{ env.procs }} TARGETS= EXTRA_TARGETS=
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: cmd-ref-${{ github.sha }}
|
||||
path: |
|
||||
docs/source/cmd
|
||||
docs/source/generated
|
||||
docs/source/_images
|
||||
docs/source/code_examples
|
||||
|
||||
- name: Test build docs
|
||||
shell: bash
|
||||
run: |
|
||||
make -C docs html -j${{ env.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 }}
|
||||
10
.github/workflows/test-build.yml
vendored
10
.github/workflows/test-build.yml
vendored
|
|
@ -100,6 +100,16 @@ jobs:
|
|||
cd iverilog
|
||||
echo "IVERILOG_GIT=$(git rev-parse HEAD)" >> $GITHUB_ENV
|
||||
|
||||
- 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
|
||||
|
||||
- name: Cache iverilog
|
||||
id: cache-iverilog
|
||||
uses: actions/cache@v4
|
||||
|
|
|
|||
55
.github/workflows/test-verific.yml
vendored
55
.github/workflows/test-verific.yml
vendored
|
|
@ -11,13 +11,11 @@ jobs:
|
|||
- id: skip_check
|
||||
uses: fkirc/skip-duplicate-actions@v5
|
||||
with:
|
||||
paths_ignore: '["**/README.md"]'
|
||||
# don't cancel previous builds
|
||||
paths_ignore: '["**/README.md", "docs/**", "guidelines/**"]'
|
||||
# cancel previous builds if a new commit is pushed
|
||||
cancel_others: 'true'
|
||||
# only run on push *or* pull_request, not both
|
||||
concurrent_skipping: 'same_content_newer'
|
||||
# we have special actions when running on main, so this should be off
|
||||
skip_after_successful_duplicate: 'false'
|
||||
|
||||
test-verific:
|
||||
needs: pre-job
|
||||
|
|
@ -41,6 +39,7 @@ jobs:
|
|||
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${{ env.procs }} ENABLE_LTO=1
|
||||
|
||||
- name: Install Yosys
|
||||
|
|
@ -70,51 +69,3 @@ jobs:
|
|||
if: ${{ github.ref == 'refs/heads/main' }}
|
||||
run: |
|
||||
make -C sby run_ci
|
||||
|
||||
prepare-docs:
|
||||
name: Generate docs artifact
|
||||
needs: [pre-job, test-verific]
|
||||
if: needs.pre-job.outputs.should_skip != 'true'
|
||||
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
|
||||
make -j${{ env.procs }} ENABLE_LTO=1
|
||||
|
||||
- name: Prepare docs
|
||||
shell: bash
|
||||
run:
|
||||
make docs/source/cmd/abc.rst docs/gen_examples docs/gen_images docs/guidelines docs/usage docs/reqs TARGETS= EXTRA_TARGETS=
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: cmd-ref-${{ github.sha }}
|
||||
path: |
|
||||
docs/source/cmd
|
||||
docs/source/generated
|
||||
docs/source/_images
|
||||
docs/source/code_examples
|
||||
|
||||
- name: Trigger RTDs build
|
||||
if: ${{ github.ref == 'refs/heads/main' }}
|
||||
uses: dfm/rtds-action@v1.1.0
|
||||
with:
|
||||
webhook_url: ${{ secrets.RTDS_WEBHOOK_URL }}
|
||||
webhook_token: ${{ secrets.RTDS_WEBHOOK_TOKEN }}
|
||||
commit_ref: ${{ github.ref }}
|
||||
|
|
|
|||
136
.github/workflows/wheels.yml
vendored
Normal file
136
.github/workflows/wheels.yml
vendored
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
name: Build Wheels for PyPI
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build_wheels:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [
|
||||
{
|
||||
name: "Ubuntu 22.04",
|
||||
family: "linux",
|
||||
runner: "ubuntu-22.04",
|
||||
archs: "x86_64",
|
||||
},
|
||||
## Aarch64 is disabled for now: GitHub is committing to EOY
|
||||
## for free aarch64 runners for open-source projects and
|
||||
## emulation times out:
|
||||
## https://github.com/orgs/community/discussions/19197#discussioncomment-10550689
|
||||
# {
|
||||
# name: "Ubuntu 22.04",
|
||||
# family: "linux",
|
||||
# runner: "ubuntu-22.04",
|
||||
# archs: "aarch64",
|
||||
# },
|
||||
{
|
||||
name: "macOS 13",
|
||||
family: "macos",
|
||||
runner: "macos-13",
|
||||
archs: "x86_64",
|
||||
},
|
||||
{
|
||||
name: "macOS 14",
|
||||
family: "macos",
|
||||
runner: "macos-14",
|
||||
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
|
||||
- if: ${{ matrix.os.family == 'linux' }}
|
||||
name: "[Linux] Set up QEMU"
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- uses: actions/setup-python@v5
|
||||
- name: Get Boost Source
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p boost
|
||||
curl -L https://github.com/boostorg/boost/releases/download/boost-1.86.0/boost-1.86.0-b2-nodocs.tar.gz | tar --strip-components=1 -xzC boost
|
||||
- name: Get FFI
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p ffi
|
||||
curl -L https://github.com/libffi/libffi/releases/download/v3.4.6/libffi-3.4.6.tar.gz | tar --strip-components=1 -xzC ffi
|
||||
## 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:$PATH" >> $GITHUB_ENV
|
||||
echo "PATH=$(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: >
|
||||
CXXFLAGS=-I./boost/pfx/include
|
||||
LINKFLAGS=-L./boost/pfx/lib
|
||||
PKG_CONFIG_PATH=./ffi/pfx/lib/pkgconfig
|
||||
makeFlags='BOOST_PYTHON_LIB=./boost/pfx/lib/libboost_python*.a'
|
||||
CIBW_ENVIRONMENT_MACOS: >
|
||||
CXXFLAGS=-I./boost/pfx/include
|
||||
LINKFLAGS=-L./boost/pfx/lib
|
||||
PKG_CONFIG_PATH=./ffi/pfx/lib/pkgconfig
|
||||
MACOSX_DEPLOYMENT_TARGET=11
|
||||
makeFlags='BOOST_PYTHON_LIB=./boost/pfx/lib/libboost_python*.a CONFIG=clang'
|
||||
CIBW_BEFORE_BUILD: bash ./.github/workflows/wheels/cibw_before_build.sh
|
||||
CIBW_TEST_COMMAND: python3 {project}/tests/arch/ecp5/add_sub.py
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: python-wheels-${{ matrix.os.runner }}
|
||||
path: ./wheelhouse/*.whl
|
||||
upload_wheels:
|
||||
name: Upload Wheels
|
||||
runs-on: ubuntu-latest
|
||||
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
|
||||
with:
|
||||
password: ${{ secrets.PYPI_TOKEN }}
|
||||
repository-url: ${{ vars.PYPI_INDEX || 'https://upload.pypi.org/legacy/' }}
|
||||
44
.github/workflows/wheels/_run_cibw_linux.py
vendored
Normal file
44
.github/workflows/wheels/_run_cibw_linux.py
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
#!/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
|
||||
|
||||
__dir__ = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
workflow = yaml.safe_load(open(os.path.join(os.path.dirname(__dir__), "wheels.yml")))
|
||||
|
||||
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") or platform.machine()
|
||||
subprocess.check_call(["cibuildwheel"], env=env)
|
||||
23
.github/workflows/wheels/cibw_before_all.sh
vendored
Normal file
23
.github/workflows/wheels/cibw_before_all.sh
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
set -e
|
||||
set -x
|
||||
|
||||
# Build-time dependencies
|
||||
## Linux Docker Images
|
||||
if command -v yum &> /dev/null; then
|
||||
yum install -y flex bison
|
||||
fi
|
||||
|
||||
if command -v apk &> /dev/null; then
|
||||
apk add flex bison
|
||||
fi
|
||||
|
||||
## macOS/Windows -- installed in GitHub Action itself, not container
|
||||
|
||||
# Build Static FFI (platform-dependent but not Python version dependent)
|
||||
cd ffi
|
||||
## Ultimate libyosys.so will be shared, so we need fPIC for the static libraries
|
||||
CFLAGS=-fPIC CXXFLAGS=-fPIC ./configure --prefix=$PWD/pfx
|
||||
## Without this, SHELL has a space in its path which breaks the makefile
|
||||
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
|
||||
34
.github/workflows/wheels/cibw_before_build.sh
vendored
Normal file
34
.github/workflows/wheels/cibw_before_build.sh
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
set -e
|
||||
set -x
|
||||
|
||||
# Don't use objects from previous compiles on Windows/macOS
|
||||
make clean
|
||||
|
||||
# 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
|
||||
|
||||
# Build boost
|
||||
cd ./boost
|
||||
## Delete the artefacts from previous builds (if any)
|
||||
rm -rf ./pfx
|
||||
## Bootstrap bjam
|
||||
./bootstrap.sh --prefix=./pfx
|
||||
## Build Boost against current version of Python, only for
|
||||
## static linkage (Boost is statically linked because system boost packages
|
||||
## wildly vary in versions, including the libboost_python3 version)
|
||||
./b2\
|
||||
-j$(getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n hw.ncpu)\
|
||||
--prefix=./pfx\
|
||||
--with-filesystem\
|
||||
--with-system\
|
||||
--with-python\
|
||||
cxxflags="$(python3-config --includes) -std=c++17 -fPIC"\
|
||||
cflags="$(python3-config --includes) -fPIC"\
|
||||
link=static\
|
||||
variant=release\
|
||||
install
|
||||
8
.gitignore
vendored
8
.gitignore
vendored
|
|
@ -45,4 +45,10 @@ __pycache__
|
|||
/tests/unit/bintest/
|
||||
/tests/unit/objtest/
|
||||
/tests/ystests
|
||||
/result
|
||||
/result
|
||||
/dist
|
||||
/*.egg-info
|
||||
/build
|
||||
/venv
|
||||
/boost
|
||||
/ffi
|
||||
|
|
|
|||
6
.gitmodules
vendored
6
.gitmodules
vendored
|
|
@ -1,4 +1,6 @@
|
|||
[submodule "abc"]
|
||||
path = abc
|
||||
url = https://github.com/RapidFlex/abc.git
|
||||
branch = yosys-experimental
|
||||
url = https://github.com/YosysHQ/abc
|
||||
[submodule "libs/cxxopts"]
|
||||
path = libs/cxxopts
|
||||
url = https://github.com/jarro2783/cxxopts
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ formats:
|
|||
|
||||
sphinx:
|
||||
configuration: docs/source/conf.py
|
||||
fail_on_warning: true
|
||||
|
||||
python:
|
||||
install:
|
||||
|
|
|
|||
1
Brewfile
1
Brewfile
|
|
@ -11,3 +11,4 @@ brew "xdot"
|
|||
brew "bash"
|
||||
brew "boost-python3"
|
||||
brew "llvm"
|
||||
brew "lld"
|
||||
|
|
|
|||
33
CHANGELOG
33
CHANGELOG
|
|
@ -2,9 +2,40 @@
|
|||
List of major changes and improvements between releases
|
||||
=======================================================
|
||||
|
||||
Yosys 0.44 .. Yosys 0.45-dev
|
||||
Yosys 0.46 .. Yosys 0.47-dev
|
||||
--------------------------
|
||||
|
||||
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
|
||||
|
|
|
|||
50
Makefile
50
Makefile
|
|
@ -38,6 +38,7 @@ ENABLE_LTO := 0
|
|||
ENABLE_CCACHE := 0
|
||||
# sccache is not always a drop-in replacement for ccache in practice
|
||||
ENABLE_SCCACHE := 0
|
||||
ENABLE_FUNCTIONAL_TESTS := 0
|
||||
LINK_CURSES := 0
|
||||
LINK_TERMCAP := 0
|
||||
LINK_ABC := 0
|
||||
|
|
@ -153,7 +154,7 @@ ifeq ($(OS), Haiku)
|
|||
CXXFLAGS += -D_DEFAULT_SOURCE
|
||||
endif
|
||||
|
||||
YOSYS_VER := 0.44+60
|
||||
YOSYS_VER := 0.46+34
|
||||
|
||||
# Note: We arrange for .gitcommit to contain the (short) commit hash in
|
||||
# tarballs generated with git-archive(1) using .gitattributes. The git repo
|
||||
|
|
@ -169,7 +170,7 @@ endif
|
|||
OBJS = kernel/version_$(GIT_REV).o
|
||||
|
||||
bumpversion:
|
||||
sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 80ba43d.. | wc -l`/;" Makefile
|
||||
sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline e97731b.. | wc -l`/;" Makefile
|
||||
|
||||
ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 ABC_USE_NAMESPACE=abc VERBOSE=$(Q)
|
||||
|
||||
|
|
@ -598,6 +599,7 @@ $(eval $(call add_include_file,kernel/celltypes.h))
|
|||
$(eval $(call add_include_file,kernel/consteval.h))
|
||||
$(eval $(call add_include_file,kernel/constids.inc))
|
||||
$(eval $(call add_include_file,kernel/cost.h))
|
||||
$(eval $(call add_include_file,kernel/drivertools.h))
|
||||
$(eval $(call add_include_file,kernel/ff.h))
|
||||
$(eval $(call add_include_file,kernel/ffinit.h))
|
||||
$(eval $(call add_include_file,kernel/ffmerge.h))
|
||||
|
|
@ -616,6 +618,7 @@ $(eval $(call add_include_file,kernel/register.h))
|
|||
$(eval $(call add_include_file,kernel/rtlil.h))
|
||||
$(eval $(call add_include_file,kernel/satgen.h))
|
||||
$(eval $(call add_include_file,kernel/scopeinfo.h))
|
||||
$(eval $(call add_include_file,kernel/sexpr.h))
|
||||
$(eval $(call add_include_file,kernel/sigtools.h))
|
||||
$(eval $(call add_include_file,kernel/timinginfo.h))
|
||||
$(eval $(call add_include_file,kernel/utils.h))
|
||||
|
|
@ -637,7 +640,8 @@ $(eval $(call add_include_file,backends/rtlil/rtlil_backend.h))
|
|||
|
||||
OBJS += kernel/driver.o kernel/register.o kernel/rtlil.o kernel/log.o kernel/calc.o kernel/yosys.o
|
||||
OBJS += kernel/binding.o
|
||||
OBJS += kernel/cellaigs.o kernel/celledges.o kernel/cost.o kernel/satgen.o kernel/scopeinfo.o kernel/qcsat.o kernel/mem.o kernel/ffmerge.o kernel/ff.o kernel/yw.o kernel/json.o kernel/fmt.o
|
||||
OBJS += kernel/cellaigs.o kernel/celledges.o kernel/cost.o kernel/satgen.o kernel/scopeinfo.o kernel/qcsat.o kernel/mem.o kernel/ffmerge.o kernel/ff.o kernel/yw.o kernel/json.o kernel/fmt.o kernel/sexpr.o
|
||||
OBJS += kernel/drivertools.o kernel/functional.o
|
||||
ifeq ($(ENABLE_ZLIB),1)
|
||||
OBJS += kernel/fstdata.o
|
||||
endif
|
||||
|
|
@ -733,12 +737,18 @@ compile-only: $(OBJS) $(GENFILES) $(EXTRA_TARGETS)
|
|||
@echo " Compile successful."
|
||||
@echo ""
|
||||
|
||||
.PHONY: share
|
||||
share: $(EXTRA_TARGETS)
|
||||
@echo ""
|
||||
@echo " Share directory created."
|
||||
@echo ""
|
||||
|
||||
$(PROGRAM_PREFIX)yosys$(EXE): $(OBJS)
|
||||
$(P) $(CXX) -o $(PROGRAM_PREFIX)yosys$(EXE) $(EXE_LINKFLAGS) $(LINKFLAGS) $(OBJS) $(LIBS) $(LIBS_VERIFIC)
|
||||
|
||||
libyosys.so: $(filter-out kernel/driver.o,$(OBJS))
|
||||
ifeq ($(OS), Darwin)
|
||||
$(P) $(CXX) -o libyosys.so -shared -Wl,-install_name,$(LIBDIR)/libyosys.so $(LINKFLAGS) $^ $(LIBS) $(LIBS_VERIFIC)
|
||||
$(P) $(CXX) -o libyosys.so -shared -undefined dynamic_lookup -Wl,-install_name,$(LIBDIR)/libyosys.so $(LINKFLAGS) $^ $(LIBS) $(LIBS_VERIFIC)
|
||||
else
|
||||
$(P) $(CXX) -o libyosys.so -shared -Wl,-soname,$(LIBDIR)/libyosys.so $(LINKFLAGS) $^ $(LIBS) $(LIBS_VERIFIC)
|
||||
endif
|
||||
|
|
@ -877,6 +887,7 @@ endif
|
|||
+cd tests/arch/anlogic && bash run-test.sh $(SEEDOPT)
|
||||
+cd tests/arch/gowin && bash run-test.sh $(SEEDOPT)
|
||||
+cd tests/arch/intel_alm && bash run-test.sh $(SEEDOPT)
|
||||
+cd tests/arch/nanoxplore && bash run-test.sh $(SEEDOPT)
|
||||
+cd tests/arch/nexus && bash run-test.sh $(SEEDOPT)
|
||||
+cd tests/arch/quicklogic/pp3 && bash run-test.sh $(SEEDOPT)
|
||||
+cd tests/arch/quicklogic/qlf_k6n10f && bash run-test.sh $(SEEDOPT)
|
||||
|
|
@ -888,6 +899,9 @@ endif
|
|||
+cd tests/xprop && bash run-test.sh $(SEEDOPT)
|
||||
+cd tests/fmt && bash run-test.sh
|
||||
+cd tests/cxxrtl && bash run-test.sh
|
||||
ifeq ($(ENABLE_FUNCTIONAL_TESTS),1)
|
||||
+cd tests/functional && bash run-test.sh
|
||||
endif
|
||||
@echo ""
|
||||
@echo " Passed \"make test\"."
|
||||
@echo ""
|
||||
|
|
@ -916,8 +930,8 @@ ystests: $(TARGETS) $(EXTRA_TARGETS)
|
|||
|
||||
# Unit test
|
||||
unit-test: libyosys.so
|
||||
@$(MAKE) -C $(UNITESTPATH) CXX="$(CXX)" CPPFLAGS="$(CPPFLAGS)" \
|
||||
CXXFLAGS="$(CXXFLAGS)" LIBS="$(LIBS)" ROOTPATH="$(CURDIR)"
|
||||
@$(MAKE) -C $(UNITESTPATH) CXX="$(CXX)" CC="$(CC)" CPPFLAGS="$(CPPFLAGS)" \
|
||||
CXXFLAGS="$(CXXFLAGS)" LINKFLAGS="$(LINKFLAGS)" LIBS="$(LIBS)" ROOTPATH="$(CURDIR)"
|
||||
|
||||
clean-unit-test:
|
||||
@$(MAKE) -C $(UNITESTPATH) clean
|
||||
|
|
@ -971,16 +985,17 @@ docs/source/cmd/abc.rst: $(TARGETS) $(EXTRA_TARGETS)
|
|||
./$(PROGRAM_PREFIX)yosys -p 'help -write-rst-command-reference-manual'
|
||||
|
||||
PHONY: docs/gen_examples docs/gen_images docs/guidelines docs/usage docs/reqs
|
||||
docs/gen_examples:
|
||||
docs/gen_examples: $(TARGETS)
|
||||
$(Q) $(MAKE) -C docs examples
|
||||
|
||||
docs/gen_images:
|
||||
docs/gen_images: $(TARGETS)
|
||||
$(Q) $(MAKE) -C docs images
|
||||
|
||||
DOCS_GUIDELINE_FILES := GettingStarted CodingStyle
|
||||
docs/guidelines docs/source/generated:
|
||||
DOCS_GUIDELINE_SOURCE := $(addprefix guidelines/,$(DOCS_GUIDELINE_FILES))
|
||||
docs/guidelines docs/source/generated: $(DOCS_GUIDELINE_SOURCE)
|
||||
$(Q) mkdir -p docs/source/generated
|
||||
$(Q) cp -f $(addprefix guidelines/,$(DOCS_GUIDELINE_FILES)) docs/source/generated
|
||||
$(Q) cp -f $(DOCS_GUIDELINE_SOURCE) docs/source/generated
|
||||
|
||||
# some commands return an error and print the usage text to stderr
|
||||
define DOC_USAGE_STDERR
|
||||
|
|
@ -1009,8 +1024,11 @@ docs/usage: $(addprefix docs/source/generated/,$(DOCS_USAGE_STDOUT) $(DOCS_USAGE
|
|||
docs/reqs:
|
||||
$(Q) $(MAKE) -C docs reqs
|
||||
|
||||
.PHONY: docs/prep
|
||||
docs/prep: docs/source/cmd/abc.rst docs/gen_examples docs/gen_images docs/guidelines docs/usage
|
||||
|
||||
DOC_TARGET ?= html
|
||||
docs: docs/source/cmd/abc.rst docs/gen_examples docs/gen_images docs/guidelines docs/usage docs/reqs
|
||||
docs: docs/prep
|
||||
$(Q) $(MAKE) -C docs $(DOC_TARGET)
|
||||
|
||||
clean:
|
||||
|
|
@ -1045,6 +1063,16 @@ coverage:
|
|||
lcov --capture -d . --no-external -o coverage.info
|
||||
genhtml coverage.info --output-directory coverage_html
|
||||
|
||||
clean_coverage:
|
||||
find . -name "*.gcda" -type f -delete
|
||||
|
||||
FUNC_KERNEL := functional.cc functional.h sexpr.cc sexpr.h compute_graph.h
|
||||
FUNC_INCLUDES := $(addprefix --include *,functional/* $(FUNC_KERNEL))
|
||||
coverage_functional:
|
||||
rm -rf coverage.info coverage_html
|
||||
lcov --capture -d backends/functional -d kernel $(FUNC_INCLUDES) --no-external -o coverage.info
|
||||
genhtml coverage.info --output-directory coverage_html
|
||||
|
||||
qtcreator:
|
||||
echo "$(CXXFLAGS)" | grep -o '\-D[^ ]*' | tr ' ' '\n' | sed 's/-D/#define /' | sed 's/=/ /'> qtcreator.config
|
||||
{ for file in $(basename $(OBJS)); do \
|
||||
|
|
|
|||
10
README.md
10
README.md
|
|
@ -629,11 +629,21 @@ following are used for building the website:
|
|||
|
||||
$ 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. 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`:
|
||||
|
||||
|
|
|
|||
2
abc
2
abc
|
|
@ -1 +1 @@
|
|||
Subproject commit 2188bc71228b0788569d83ad2b7e7b91ca5dcc09
|
||||
Subproject commit cac8f99eaa220a5e3db5caeb87cef0a975c953a2
|
||||
|
|
@ -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);
|
||||
|
|
@ -537,9 +501,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 +607,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 +629,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();
|
||||
|
|
|
|||
1
backends/aiger2/Makefile.inc
Normal file
1
backends/aiger2/Makefile.inc
Normal file
|
|
@ -0,0 +1 @@
|
|||
OBJS += backends/aiger2/aiger.o
|
||||
1471
backends/aiger2/aiger.cc
Normal file
1471
backends/aiger2/aiger.cc
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -387,7 +387,7 @@ 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);
|
||||
|
|
|
|||
|
|
@ -711,9 +711,9 @@ struct BtorWorker
|
|||
Const initval;
|
||||
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);
|
||||
|
||||
int nid_init_val = -1;
|
||||
|
||||
|
|
@ -1042,7 +1042,7 @@ struct BtorWorker
|
|||
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);
|
||||
c.bits().push_back(sig[i+GetSize(c)].data);
|
||||
|
||||
if (consts.count(c) == 0) {
|
||||
int sid = get_bv_sid(GetSize(c));
|
||||
|
|
|
|||
|
|
@ -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()) {
|
||||
|
|
@ -864,7 +864,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");
|
||||
|
|
@ -1665,15 +1665,15 @@ struct CxxrtlWorker {
|
|||
switch (bit) {
|
||||
case RTLIL::S0:
|
||||
case RTLIL::S1:
|
||||
compare_mask.bits.push_back(RTLIL::S1);
|
||||
compare_value.bits.push_back(bit);
|
||||
compare_mask.bits().push_back(RTLIL::S1);
|
||||
compare_value.bits().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.bits().push_back(RTLIL::S0);
|
||||
compare_value.bits().push_back(RTLIL::S0);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -3028,7 +3028,7 @@ struct CxxrtlWorker {
|
|||
if (init == RTLIL::Const()) {
|
||||
init = RTLIL::Const(State::Sx, GetSize(bit.wire));
|
||||
}
|
||||
init[bit.offset] = port.init_value[i];
|
||||
init.bits()[bit.offset] = port.init_value[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1127,7 +1127,7 @@ struct fmt_part {
|
|||
}
|
||||
|
||||
case UNICHAR: {
|
||||
uint32_t codepoint = val.template get<uint32_t>();
|
||||
uint32_t codepoint = val.template zcast<32>().template get<uint32_t>();
|
||||
if (codepoint >= 0x10000)
|
||||
buf += (char)(0xf0 | (codepoint >> 18));
|
||||
else if (codepoint >= 0x800)
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -334,20 +334,20 @@ 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())
|
||||
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 (size_t 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.c_str());
|
||||
}
|
||||
};
|
||||
for (auto module : sorted_modules)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
4
backends/functional/Makefile.inc
Normal file
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
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.c_str());
|
||||
printCxx(*f, filename, module);
|
||||
}
|
||||
}
|
||||
} FunctionalCxxBackend;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
418
backends/functional/cxx_runtime/sim.h
Normal file
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
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.c_str());
|
||||
SmtModule smt(module);
|
||||
smt.write(*f);
|
||||
}
|
||||
}
|
||||
} FunctionalSmtBackend;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
308
backends/functional/smtlib_rosette.cc
Normal file
308
backends/functional/smtlib_rosette.cc
Normal file
|
|
@ -0,0 +1,308 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
SmtrStruct input_struct;
|
||||
SmtrStruct output_struct;
|
||||
SmtrStruct state_struct;
|
||||
|
||||
SmtrModule(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(std::ostream &out)
|
||||
{
|
||||
SExprWriter w(out);
|
||||
|
||||
input_struct.write_definition(w);
|
||||
output_struct.write_definition(w);
|
||||
state_struct.write_definition(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();
|
||||
|
||||
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();
|
||||
}
|
||||
};
|
||||
|
||||
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] [selection] [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("\n");
|
||||
}
|
||||
|
||||
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
auto provides = 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
|
||||
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);
|
||||
smtr.write(*f);
|
||||
}
|
||||
}
|
||||
} FunctionalSmtrBackend;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
156
backends/functional/test_generic.cc
Normal file
156
backends/functional/test_generic.cc
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* 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.bits().insert(values.bits().end(), word.begin(), word.end());
|
||||
}
|
||||
insert_concatenated(low, values);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct FunctionalTestGeneric : public Pass
|
||||
{
|
||||
FunctionalTestGeneric() : Pass("test_generic", "test the generic compute graph") {}
|
||||
|
||||
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.c_str());
|
||||
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
|
||||
|
|
@ -176,11 +176,11 @@ struct IntersynthBackend : public Backend {
|
|||
}
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,13 +33,13 @@ YOSYS_NAMESPACE_BEGIN
|
|||
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;
|
||||
|
|
@ -58,8 +58,8 @@ void RTLIL_BACKEND::dump_const(std::ostream &f, const RTLIL::Const &data, int wi
|
|||
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;
|
||||
|
|
@ -125,6 +125,10 @@ void RTLIL_BACKEND::dump_wire(std::ostream &f, std::string indent, const RTLIL::
|
|||
dump_const(f, it.second);
|
||||
f << stringf("\n");
|
||||
}
|
||||
if (wire->driverCell_) {
|
||||
f << stringf("%s" "# driver %s %s\n", indent.c_str(),
|
||||
wire->driverCell()->name.c_str(), wire->driverPort().c_str());
|
||||
}
|
||||
f << stringf("%s" "wire ", indent.c_str());
|
||||
if (wire->width != 1)
|
||||
f << stringf("width %d ", wire->width);
|
||||
|
|
|
|||
|
|
@ -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.bits().resize(GetSize(sig), State::Sx);
|
||||
|
||||
for (int i = 0; i < GetSize(sig); i++)
|
||||
if (val[i] == State::S0 || val[i] == State::S1) {
|
||||
|
|
|
|||
|
|
@ -1077,14 +1077,14 @@ struct Smt2Worker
|
|||
|
||||
RTLIL::SigSpec sig = sigmap(wire);
|
||||
Const val = wire->attributes.at(ID::init);
|
||||
val.bits.resize(GetSize(sig), State::Sx);
|
||||
val.bits().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.bits()[i] = State::S0;
|
||||
mask.bits()[i] = State::S0;
|
||||
use_mask = true;
|
||||
}
|
||||
if (use_mask)
|
||||
|
|
@ -1359,10 +1359,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.bits()[k] = State::S1;
|
||||
} else {
|
||||
initmask[k] = State::S0;
|
||||
initword[k] = State::S0;
|
||||
initmask.bits()[k] = State::S0;
|
||||
initword.bits()[k] = State::S0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1454,6 +1454,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
|
||||
|
|
@ -1464,7 +1468,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():
|
||||
|
|
@ -1497,7 +1502,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:
|
||||
|
|
@ -1517,18 +1522,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
|
||||
|
||||
|
|
@ -1544,7 +1552,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
|
||||
|
||||
|
|
@ -1903,7 +1912,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)
|
||||
|
|
@ -1933,7 +1944,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()
|
||||
|
|
|
|||
|
|
@ -191,7 +191,7 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o
|
|||
{
|
||||
bool set_signed = (data.flags & RTLIL::CONST_FLAG_SIGNED) != 0;
|
||||
if (width < 0)
|
||||
width = data.bits.size() - offset;
|
||||
width = data.size() - offset;
|
||||
if (width == 0) {
|
||||
// See IEEE 1364-2005 Clause 5.1.14.
|
||||
f << "{0{1'b0}}";
|
||||
|
|
@ -199,14 +199,14 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o
|
|||
}
|
||||
if (nostr)
|
||||
goto dump_hex;
|
||||
if ((data.flags & RTLIL::CONST_FLAG_STRING) == 0 || width != (int)data.bits.size()) {
|
||||
if ((data.flags & RTLIL::CONST_FLAG_STRING) == 0 || width != (int)data.size()) {
|
||||
if (width == 32 && !no_decimal && !nodec) {
|
||||
int32_t val = 0;
|
||||
for (int i = offset+width-1; i >= offset; i--) {
|
||||
log_assert(i < (int)data.bits.size());
|
||||
if (data.bits[i] != State::S0 && data.bits[i] != State::S1)
|
||||
log_assert(i < (int)data.size());
|
||||
if (data[i] != State::S0 && data[i] != State::S1)
|
||||
goto dump_hex;
|
||||
if (data.bits[i] == State::S1)
|
||||
if (data[i] == State::S1)
|
||||
val |= 1 << (i - offset);
|
||||
}
|
||||
if (decimal)
|
||||
|
|
@ -221,8 +221,8 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o
|
|||
goto dump_bin;
|
||||
vector<char> bin_digits, hex_digits;
|
||||
for (int i = offset; i < offset+width; i++) {
|
||||
log_assert(i < (int)data.bits.size());
|
||||
switch (data.bits[i]) {
|
||||
log_assert(i < (int)data.size());
|
||||
switch (data[i]) {
|
||||
case State::S0: bin_digits.push_back('0'); break;
|
||||
case State::S1: bin_digits.push_back('1'); break;
|
||||
case RTLIL::Sx: bin_digits.push_back('x'); break;
|
||||
|
|
@ -275,8 +275,8 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o
|
|||
if (width == 0)
|
||||
f << stringf("0");
|
||||
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;
|
||||
|
|
@ -318,10 +318,10 @@ void dump_reg_init(std::ostream &f, SigSpec sig)
|
|||
|
||||
for (auto bit : active_sigmap(sig)) {
|
||||
if (active_initdata.count(bit)) {
|
||||
initval.bits.push_back(active_initdata.at(bit));
|
||||
initval.bits().push_back(active_initdata.at(bit));
|
||||
gotinit = true;
|
||||
} else {
|
||||
initval.bits.push_back(State::Sx);
|
||||
initval.bits().push_back(State::Sx);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -751,7 +751,7 @@ void dump_memory(std::ostream &f, std::string indent, Mem &mem)
|
|||
if (port.wide_log2) {
|
||||
Const addr_lo;
|
||||
for (int i = 0; i < port.wide_log2; i++)
|
||||
addr_lo.bits.push_back(State(sub >> i & 1));
|
||||
addr_lo.bits().push_back(State(sub >> i & 1));
|
||||
os << "{";
|
||||
os << temp_id;
|
||||
os << ", ";
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXOPTS = -W --keep-going
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = build
|
||||
|
|
@ -238,7 +238,7 @@ Makefile-%: FORCE
|
|||
$(MAKE) -C $(@D) $(*F)
|
||||
|
||||
CODE_EXAMPLES := $(wildcard source/code_examples/*/Makefile)
|
||||
TEST_EXAMPLES := $(addsuffix -all,$(CODE_EXAMPLES))
|
||||
TEST_EXAMPLES := $(addsuffix -examples,$(CODE_EXAMPLES))
|
||||
CLEAN_EXAMPLES := $(addsuffix -clean,$(CODE_EXAMPLES))
|
||||
test-examples: $(TEST_EXAMPLES)
|
||||
clean-examples: $(CLEAN_EXAMPLES)
|
||||
|
|
@ -250,6 +250,7 @@ test-macros:
|
|||
.PHONY: images
|
||||
images:
|
||||
$(MAKE) -C source/_images
|
||||
$(MAKE) -C source/_images convert
|
||||
|
||||
.PHONY: reqs
|
||||
reqs:
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
all: examples all_tex tidy
|
||||
all: examples all_tex
|
||||
|
||||
# set a fake time in pdf generation to prevent unnecessary differences in output
|
||||
FAKETIME := TZ='Z' faketime -f '2022-01-01 00:00:00 x0,001'
|
||||
|
|
@ -8,24 +8,22 @@ FAKETIME := TZ='Z' faketime -f '2022-01-01 00:00:00 x0,001'
|
|||
CODE_EXAMPLES := ../code_examples/*/Makefile
|
||||
examples: $(CODE_EXAMPLES)
|
||||
|
||||
# target to convert specified dot file(s)
|
||||
# target to convert all dot files
|
||||
# needs to be run *after* examples, otherwise no dot files will be found
|
||||
.PHONY: convert
|
||||
TARG_DOT ?=
|
||||
convert: $(TARG_DOT:.dot=.pdf) $(TARG_DOT:.dot=.svg)
|
||||
DOT_FILES := $(shell find . -name *.dot)
|
||||
convert: $(DOT_FILES:.dot=.pdf) $(DOT_FILES:.dot=.svg)
|
||||
|
||||
# use empty FORCE target because .PHONY ignores % expansion, using find allows
|
||||
# us to generate everything in one pass, since we don't know all of the possible
|
||||
# outputs until the sub-makes run
|
||||
# use empty FORCE target because .PHONY ignores % expansion
|
||||
FORCE:
|
||||
../%/Makefile: FORCE
|
||||
@make -C $(@D) dots
|
||||
@mkdir -p $*
|
||||
@find $(@D) -name *.dot -exec cp -u {} -t $* \;
|
||||
@find $* -name *.dot -printf "%p " | xargs -i make --no-print-directory convert TARG_DOT="{}"
|
||||
@find $(@D) -name *.dot -exec rsync -t {} $* \;
|
||||
|
||||
# find and build all tex files
|
||||
.PHONY: all_tex
|
||||
TEX_FILES := $(wildcard **/*.tex)
|
||||
TEX_FILES := $(shell find . -name *.tex)
|
||||
all_tex: $(TEX_FILES:.tex=.pdf) $(TEX_FILES:.tex=.svg)
|
||||
|
||||
%.pdf: %.dot
|
||||
|
|
|
|||
3
docs/source/code_examples/.gitignore
vendored
3
docs/source/code_examples/.gitignore
vendored
|
|
@ -1,2 +1,5 @@
|
|||
*.dot
|
||||
*.pdf
|
||||
*.out
|
||||
*.log
|
||||
*.stat
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@ PROGRAM_PREFIX :=
|
|||
|
||||
YOSYS ?= ../../../../$(PROGRAM_PREFIX)yosys
|
||||
|
||||
.PHONY: all dots
|
||||
all: dots test0.log test1.log test2.log
|
||||
.PHONY: all dots examples
|
||||
all: dots examples
|
||||
dots: test1.dot
|
||||
examples: test0.log test1.log test2.log
|
||||
|
||||
CXXFLAGS=$(shell $(YOSYS)-config --cxxflags)
|
||||
DATDIR=$(shell $(YOSYS)-config --datdir)
|
||||
|
|
|
|||
|
|
@ -10,8 +10,10 @@ MAPDOT_NAMES += rdata_map_ffs rdata_map_luts rdata_map_cells
|
|||
DOTS := $(addsuffix .dot,$(DOT_NAMES))
|
||||
MAPDOTS := $(addsuffix .dot,$(MAPDOT_NAMES))
|
||||
|
||||
all: dots fifo.out fifo.stat
|
||||
.PHONY: all dots examples
|
||||
all: dots examples
|
||||
dots: $(DOTS) $(MAPDOTS)
|
||||
examples: fifo.out fifo.stat
|
||||
|
||||
$(DOTS) fifo.out: fifo.v fifo.ys
|
||||
$(YOSYS) fifo.ys -l fifo.out -Q -T
|
||||
|
|
@ -22,3 +24,4 @@ $(MAPDOTS) fifo.stat: fifo.v fifo_map.ys
|
|||
.PHONY: clean
|
||||
clean:
|
||||
rm -f *.dot
|
||||
rm -f fifo.out fifo.stat
|
||||
|
|
|
|||
|
|
@ -1,425 +0,0 @@
|
|||
|
||||
-- Executing script file `fifo.ys' --
|
||||
$ yosys fifo.v
|
||||
|
||||
-- Parsing `fifo.v' using frontend ` -vlog2k' --
|
||||
|
||||
1. Executing Verilog-2005 frontend: fifo.v
|
||||
Parsing Verilog input from `fifo.v' to AST representation.
|
||||
Storing AST representation for module `$abstract\addr_gen'.
|
||||
Storing AST representation for module `$abstract\fifo'.
|
||||
Successfully finished Verilog frontend.
|
||||
echo on
|
||||
|
||||
yosys> hierarchy -top addr_gen
|
||||
|
||||
2. Executing HIERARCHY pass (managing design hierarchy).
|
||||
|
||||
3. Executing AST frontend in derive mode using pre-parsed AST for module `\addr_gen'.
|
||||
Generating RTLIL representation for module `\addr_gen'.
|
||||
|
||||
3.1. Analyzing design hierarchy..
|
||||
Top module: \addr_gen
|
||||
|
||||
3.2. Analyzing design hierarchy..
|
||||
Top module: \addr_gen
|
||||
Removing unused module `$abstract\fifo'.
|
||||
Removing unused module `$abstract\addr_gen'.
|
||||
Removed 2 unused modules.
|
||||
|
||||
yosys> select -module addr_gen
|
||||
|
||||
yosys [addr_gen]> select -list
|
||||
addr_gen
|
||||
addr_gen/$1\addr[7:0]
|
||||
addr_gen/$add$fifo.v:19$3_Y
|
||||
addr_gen/$eq$fifo.v:16$2_Y
|
||||
addr_gen/$0\addr[7:0]
|
||||
addr_gen/addr
|
||||
addr_gen/rst
|
||||
addr_gen/clk
|
||||
addr_gen/en
|
||||
addr_gen/$add$fifo.v:19$3
|
||||
addr_gen/$eq$fifo.v:16$2
|
||||
addr_gen/$proc$fifo.v:0$4
|
||||
addr_gen/$proc$fifo.v:12$1
|
||||
|
||||
yosys [addr_gen]> select t:*
|
||||
|
||||
yosys [addr_gen]*> select -list
|
||||
addr_gen/$add$fifo.v:19$3
|
||||
addr_gen/$eq$fifo.v:16$2
|
||||
|
||||
yosys [addr_gen]*> select -set new_cells %
|
||||
|
||||
yosys [addr_gen]*> select -clear
|
||||
|
||||
yosys> show -format dot -prefix addr_gen_show addr_gen
|
||||
|
||||
4. Generating Graphviz representation of design.
|
||||
Writing dot description to `addr_gen_show.dot'.
|
||||
Dumping module addr_gen to page 1.
|
||||
|
||||
yosys> show -format dot -prefix new_cells_show -notitle @new_cells
|
||||
|
||||
5. Generating Graphviz representation of design.
|
||||
Writing dot description to `new_cells_show.dot'.
|
||||
Dumping selected parts of module addr_gen to page 1.
|
||||
|
||||
yosys> show -color maroon3 @new_cells -color cornflowerblue p:* -notitle -format dot -prefix addr_gen_hier
|
||||
|
||||
6. Generating Graphviz representation of design.
|
||||
Writing dot description to `addr_gen_hier.dot'.
|
||||
Dumping module addr_gen to page 1.
|
||||
|
||||
yosys> proc -noopt
|
||||
|
||||
7. Executing PROC pass (convert processes to netlists).
|
||||
|
||||
yosys> proc_clean
|
||||
|
||||
7.1. Executing PROC_CLEAN pass (remove empty switches from decision trees).
|
||||
Cleaned up 0 empty switches.
|
||||
|
||||
yosys> proc_rmdead
|
||||
|
||||
7.2. Executing PROC_RMDEAD pass (remove dead branches from decision trees).
|
||||
Marked 2 switch rules as full_case in process $proc$fifo.v:12$1 in module addr_gen.
|
||||
Removed a total of 0 dead cases.
|
||||
|
||||
yosys> proc_prune
|
||||
|
||||
7.3. Executing PROC_PRUNE pass (remove redundant assignments in processes).
|
||||
Removed 0 redundant assignments.
|
||||
Promoted 1 assignment to connection.
|
||||
|
||||
yosys> proc_init
|
||||
|
||||
7.4. Executing PROC_INIT pass (extract init attributes).
|
||||
Found init rule in `\addr_gen.$proc$fifo.v:0$4'.
|
||||
Set init value: \addr = 8'00000000
|
||||
|
||||
yosys> proc_arst
|
||||
|
||||
7.5. Executing PROC_ARST pass (detect async resets in processes).
|
||||
Found async reset \rst in `\addr_gen.$proc$fifo.v:12$1'.
|
||||
|
||||
yosys> proc_rom
|
||||
|
||||
7.6. Executing PROC_ROM pass (convert switches to ROMs).
|
||||
Converted 0 switches.
|
||||
<suppressed ~2 debug messages>
|
||||
|
||||
yosys> proc_mux
|
||||
|
||||
7.7. Executing PROC_MUX pass (convert decision trees to multiplexers).
|
||||
Creating decoders for process `\addr_gen.$proc$fifo.v:0$4'.
|
||||
Creating decoders for process `\addr_gen.$proc$fifo.v:12$1'.
|
||||
1/1: $0\addr[7:0]
|
||||
|
||||
yosys> proc_dlatch
|
||||
|
||||
7.8. Executing PROC_DLATCH pass (convert process syncs to latches).
|
||||
|
||||
yosys> proc_dff
|
||||
|
||||
7.9. Executing PROC_DFF pass (convert process syncs to FFs).
|
||||
Creating register for signal `\addr_gen.\addr' using process `\addr_gen.$proc$fifo.v:12$1'.
|
||||
created $adff cell `$procdff$10' with positive edge clock and positive level reset.
|
||||
|
||||
yosys> proc_memwr
|
||||
|
||||
7.10. Executing PROC_MEMWR pass (convert process memory writes to cells).
|
||||
|
||||
yosys> proc_clean
|
||||
|
||||
7.11. Executing PROC_CLEAN pass (remove empty switches from decision trees).
|
||||
Removing empty process `addr_gen.$proc$fifo.v:0$4'.
|
||||
Found and cleaned up 2 empty switches in `\addr_gen.$proc$fifo.v:12$1'.
|
||||
Removing empty process `addr_gen.$proc$fifo.v:12$1'.
|
||||
Cleaned up 2 empty switches.
|
||||
|
||||
yosys> select -set new_cells t:$mux t:*dff
|
||||
|
||||
yosys> show -color maroon3 @new_cells -notitle -format dot -prefix addr_gen_proc
|
||||
|
||||
8. Generating Graphviz representation of design.
|
||||
Writing dot description to `addr_gen_proc.dot'.
|
||||
Dumping module addr_gen to page 1.
|
||||
|
||||
yosys> opt_expr
|
||||
|
||||
9. Executing OPT_EXPR pass (perform const folding).
|
||||
Optimizing module addr_gen.
|
||||
|
||||
yosys> clean
|
||||
Removed 0 unused cells and 4 unused wires.
|
||||
|
||||
yosys> select -set new_cells t:$eq
|
||||
|
||||
yosys> show -color cornflowerblue @new_cells -notitle -format dot -prefix addr_gen_clean
|
||||
|
||||
10. Generating Graphviz representation of design.
|
||||
Writing dot description to `addr_gen_clean.dot'.
|
||||
Dumping module addr_gen to page 1.
|
||||
|
||||
yosys> design -reset
|
||||
|
||||
yosys> read_verilog fifo.v
|
||||
|
||||
11. Executing Verilog-2005 frontend: fifo.v
|
||||
Parsing Verilog input from `fifo.v' to AST representation.
|
||||
Generating RTLIL representation for module `\addr_gen'.
|
||||
Generating RTLIL representation for module `\fifo'.
|
||||
Successfully finished Verilog frontend.
|
||||
|
||||
yosys> hierarchy -check -top fifo
|
||||
|
||||
12. Executing HIERARCHY pass (managing design hierarchy).
|
||||
|
||||
12.1. Analyzing design hierarchy..
|
||||
Top module: \fifo
|
||||
Used module: \addr_gen
|
||||
Parameter \MAX_DATA = 256
|
||||
|
||||
12.2. Executing AST frontend in derive mode using pre-parsed AST for module `\addr_gen'.
|
||||
Parameter \MAX_DATA = 256
|
||||
Generating RTLIL representation for module `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000'.
|
||||
Parameter \MAX_DATA = 256
|
||||
Found cached RTLIL representation for module `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000'.
|
||||
|
||||
12.3. Analyzing design hierarchy..
|
||||
Top module: \fifo
|
||||
Used module: $paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000
|
||||
|
||||
12.4. Analyzing design hierarchy..
|
||||
Top module: \fifo
|
||||
Used module: $paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000
|
||||
Removing unused module `\addr_gen'.
|
||||
Removed 1 unused modules.
|
||||
|
||||
yosys> proc
|
||||
|
||||
13. Executing PROC pass (convert processes to netlists).
|
||||
|
||||
yosys> proc_clean
|
||||
|
||||
13.1. Executing PROC_CLEAN pass (remove empty switches from decision trees).
|
||||
Cleaned up 0 empty switches.
|
||||
|
||||
yosys> proc_rmdead
|
||||
|
||||
13.2. Executing PROC_RMDEAD pass (remove dead branches from decision trees).
|
||||
Marked 2 switch rules as full_case in process $proc$fifo.v:62$24 in module fifo.
|
||||
Marked 1 switch rules as full_case in process $proc$fifo.v:36$16 in module fifo.
|
||||
Marked 2 switch rules as full_case in process $proc$fifo.v:12$32 in module $paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000.
|
||||
Removed a total of 0 dead cases.
|
||||
|
||||
yosys> proc_prune
|
||||
|
||||
13.3. Executing PROC_PRUNE pass (remove redundant assignments in processes).
|
||||
Removed 0 redundant assignments.
|
||||
Promoted 6 assignments to connections.
|
||||
|
||||
yosys> proc_init
|
||||
|
||||
13.4. Executing PROC_INIT pass (extract init attributes).
|
||||
Found init rule in `\fifo.$proc$fifo.v:0$31'.
|
||||
Set init value: \count = 9'000000000
|
||||
Found init rule in `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000.$proc$fifo.v:0$35'.
|
||||
Set init value: \addr = 8'00000000
|
||||
|
||||
yosys> proc_arst
|
||||
|
||||
13.5. Executing PROC_ARST pass (detect async resets in processes).
|
||||
Found async reset \rst in `\fifo.$proc$fifo.v:62$24'.
|
||||
Found async reset \rst in `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000.$proc$fifo.v:12$32'.
|
||||
|
||||
yosys> proc_rom
|
||||
|
||||
13.6. Executing PROC_ROM pass (convert switches to ROMs).
|
||||
Converted 0 switches.
|
||||
<suppressed ~5 debug messages>
|
||||
|
||||
yosys> proc_mux
|
||||
|
||||
13.7. Executing PROC_MUX pass (convert decision trees to multiplexers).
|
||||
Creating decoders for process `\fifo.$proc$fifo.v:0$31'.
|
||||
Creating decoders for process `\fifo.$proc$fifo.v:62$24'.
|
||||
1/1: $0\count[8:0]
|
||||
Creating decoders for process `\fifo.$proc$fifo.v:36$16'.
|
||||
1/3: $1$memwr$\data$fifo.v:38$15_EN[7:0]$22
|
||||
2/3: $1$memwr$\data$fifo.v:38$15_DATA[7:0]$21
|
||||
3/3: $1$memwr$\data$fifo.v:38$15_ADDR[7:0]$20
|
||||
Creating decoders for process `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000.$proc$fifo.v:0$35'.
|
||||
Creating decoders for process `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000.$proc$fifo.v:12$32'.
|
||||
1/1: $0\addr[7:0]
|
||||
|
||||
yosys> proc_dlatch
|
||||
|
||||
13.8. Executing PROC_DLATCH pass (convert process syncs to latches).
|
||||
|
||||
yosys> proc_dff
|
||||
|
||||
13.9. Executing PROC_DFF pass (convert process syncs to FFs).
|
||||
Creating register for signal `\fifo.\count' using process `\fifo.$proc$fifo.v:62$24'.
|
||||
created $adff cell `$procdff$55' with positive edge clock and positive level reset.
|
||||
Creating register for signal `\fifo.\rdata' using process `\fifo.$proc$fifo.v:36$16'.
|
||||
created $dff cell `$procdff$56' with positive edge clock.
|
||||
Creating register for signal `\fifo.$memwr$\data$fifo.v:38$15_ADDR' using process `\fifo.$proc$fifo.v:36$16'.
|
||||
created $dff cell `$procdff$57' with positive edge clock.
|
||||
Creating register for signal `\fifo.$memwr$\data$fifo.v:38$15_DATA' using process `\fifo.$proc$fifo.v:36$16'.
|
||||
created $dff cell `$procdff$58' with positive edge clock.
|
||||
Creating register for signal `\fifo.$memwr$\data$fifo.v:38$15_EN' using process `\fifo.$proc$fifo.v:36$16'.
|
||||
created $dff cell `$procdff$59' with positive edge clock.
|
||||
Creating register for signal `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000.\addr' using process `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000.$proc$fifo.v:12$32'.
|
||||
created $adff cell `$procdff$60' with positive edge clock and positive level reset.
|
||||
|
||||
yosys> proc_memwr
|
||||
|
||||
13.10. Executing PROC_MEMWR pass (convert process memory writes to cells).
|
||||
|
||||
yosys> proc_clean
|
||||
|
||||
13.11. Executing PROC_CLEAN pass (remove empty switches from decision trees).
|
||||
Removing empty process `fifo.$proc$fifo.v:0$31'.
|
||||
Found and cleaned up 2 empty switches in `\fifo.$proc$fifo.v:62$24'.
|
||||
Removing empty process `fifo.$proc$fifo.v:62$24'.
|
||||
Found and cleaned up 1 empty switch in `\fifo.$proc$fifo.v:36$16'.
|
||||
Removing empty process `fifo.$proc$fifo.v:36$16'.
|
||||
Removing empty process `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000.$proc$fifo.v:0$35'.
|
||||
Found and cleaned up 2 empty switches in `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000.$proc$fifo.v:12$32'.
|
||||
Removing empty process `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000.$proc$fifo.v:12$32'.
|
||||
Cleaned up 5 empty switches.
|
||||
|
||||
yosys> opt_expr -keepdc
|
||||
|
||||
13.12. Executing OPT_EXPR pass (perform const folding).
|
||||
Optimizing module fifo.
|
||||
Optimizing module $paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000.
|
||||
|
||||
yosys> select -set new_cells t:$memrd
|
||||
|
||||
yosys> show -color maroon3 c:fifo_reader -color cornflowerblue @new_cells -notitle -format dot -prefix rdata_proc o:rdata %ci*
|
||||
|
||||
14. Generating Graphviz representation of design.
|
||||
Writing dot description to `rdata_proc.dot'.
|
||||
Dumping selected parts of module fifo to page 1.
|
||||
|
||||
yosys> flatten
|
||||
|
||||
15. Executing FLATTEN pass (flatten design).
|
||||
Deleting now unused module $paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000.
|
||||
<suppressed ~2 debug messages>
|
||||
|
||||
yosys> clean
|
||||
Removed 3 unused cells and 25 unused wires.
|
||||
|
||||
yosys> select -set rdata_path o:rdata %ci*
|
||||
|
||||
yosys> select -set new_cells @rdata_path o:rdata %ci3 %d i:* %d
|
||||
|
||||
yosys> show -color maroon3 @new_cells -notitle -format dot -prefix rdata_flat @rdata_path
|
||||
|
||||
16. Generating Graphviz representation of design.
|
||||
Writing dot description to `rdata_flat.dot'.
|
||||
Dumping selected parts of module fifo to page 1.
|
||||
|
||||
yosys> opt_dff
|
||||
|
||||
17. Executing OPT_DFF pass (perform DFF optimizations).
|
||||
Adding EN signal on $procdff$55 ($adff) from module fifo (D = $0\count[8:0], Q = \count).
|
||||
Adding EN signal on $flatten\fifo_writer.$procdff$60 ($adff) from module fifo (D = $flatten\fifo_writer.$procmux$51_Y, Q = \fifo_writer.addr).
|
||||
Adding EN signal on $flatten\fifo_reader.$procdff$60 ($adff) from module fifo (D = $flatten\fifo_reader.$procmux$51_Y, Q = \fifo_reader.addr).
|
||||
|
||||
yosys> select -set new_cells t:$adffe
|
||||
|
||||
yosys> show -color maroon3 @new_cells -notitle -format dot -prefix rdata_adffe o:rdata %ci*
|
||||
|
||||
18. Generating Graphviz representation of design.
|
||||
Writing dot description to `rdata_adffe.dot'.
|
||||
Dumping selected parts of module fifo to page 1.
|
||||
|
||||
yosys> wreduce
|
||||
|
||||
19. Executing WREDUCE pass (reducing word size of cells).
|
||||
Removed top 31 bits (of 32) from port B of cell fifo.$add$fifo.v:66$27 ($add).
|
||||
Removed top 23 bits (of 32) from port Y of cell fifo.$add$fifo.v:66$27 ($add).
|
||||
Removed top 31 bits (of 32) from port B of cell fifo.$sub$fifo.v:68$30 ($sub).
|
||||
Removed top 23 bits (of 32) from port Y of cell fifo.$sub$fifo.v:68$30 ($sub).
|
||||
Removed top 1 bits (of 2) from port B of cell fifo.$auto$opt_dff.cc:195:make_patterns_logic$66 ($ne).
|
||||
Removed cell fifo.$flatten\fifo_writer.$procmux$53 ($mux).
|
||||
Removed top 31 bits (of 32) from port B of cell fifo.$flatten\fifo_writer.$add$fifo.v:19$34 ($add).
|
||||
Removed top 24 bits (of 32) from port Y of cell fifo.$flatten\fifo_writer.$add$fifo.v:19$34 ($add).
|
||||
Removed cell fifo.$flatten\fifo_reader.$procmux$53 ($mux).
|
||||
Removed top 31 bits (of 32) from port B of cell fifo.$flatten\fifo_reader.$add$fifo.v:19$34 ($add).
|
||||
Removed top 24 bits (of 32) from port Y of cell fifo.$flatten\fifo_reader.$add$fifo.v:19$34 ($add).
|
||||
Removed top 23 bits (of 32) from wire fifo.$add$fifo.v:66$27_Y.
|
||||
Removed top 24 bits (of 32) from wire fifo.$flatten\fifo_reader.$add$fifo.v:19$34_Y.
|
||||
|
||||
yosys> show -notitle -format dot -prefix rdata_wreduce o:rdata %ci*
|
||||
|
||||
20. Generating Graphviz representation of design.
|
||||
Writing dot description to `rdata_wreduce.dot'.
|
||||
Dumping selected parts of module fifo to page 1.
|
||||
|
||||
yosys> opt_clean
|
||||
|
||||
21. Executing OPT_CLEAN pass (remove unused cells and wires).
|
||||
Finding unused cells or wires in module \fifo..
|
||||
Removed 0 unused cells and 4 unused wires.
|
||||
<suppressed ~1 debug messages>
|
||||
|
||||
yosys> memory_dff
|
||||
|
||||
22. Executing MEMORY_DFF pass (merging $dff cells to $memrd).
|
||||
Checking read port `\data'[0] in module `\fifo': merging output FF to cell.
|
||||
Write port 0: non-transparent.
|
||||
|
||||
yosys> select -set new_cells t:$memrd_v2
|
||||
|
||||
yosys> show -color maroon3 @new_cells -notitle -format dot -prefix rdata_memrdv2 o:rdata %ci*
|
||||
|
||||
23. Generating Graphviz representation of design.
|
||||
Writing dot description to `rdata_memrdv2.dot'.
|
||||
Dumping selected parts of module fifo to page 1.
|
||||
|
||||
yosys> alumacc
|
||||
|
||||
24. Executing ALUMACC pass (create $alu and $macc cells).
|
||||
Extracting $alu and $macc cells in module fifo:
|
||||
creating $macc model for $add$fifo.v:66$27 ($add).
|
||||
creating $macc model for $flatten\fifo_reader.$add$fifo.v:19$34 ($add).
|
||||
creating $macc model for $flatten\fifo_writer.$add$fifo.v:19$34 ($add).
|
||||
creating $macc model for $sub$fifo.v:68$30 ($sub).
|
||||
creating $alu model for $macc $sub$fifo.v:68$30.
|
||||
creating $alu model for $macc $flatten\fifo_writer.$add$fifo.v:19$34.
|
||||
creating $alu model for $macc $flatten\fifo_reader.$add$fifo.v:19$34.
|
||||
creating $alu model for $macc $add$fifo.v:66$27.
|
||||
creating $alu cell for $add$fifo.v:66$27: $auto$alumacc.cc:485:replace_alu$80
|
||||
creating $alu cell for $flatten\fifo_reader.$add$fifo.v:19$34: $auto$alumacc.cc:485:replace_alu$83
|
||||
creating $alu cell for $flatten\fifo_writer.$add$fifo.v:19$34: $auto$alumacc.cc:485:replace_alu$86
|
||||
creating $alu cell for $sub$fifo.v:68$30: $auto$alumacc.cc:485:replace_alu$89
|
||||
created 4 $alu and 0 $macc cells.
|
||||
|
||||
yosys> select -set new_cells t:$alu t:$macc
|
||||
|
||||
yosys> show -color maroon3 @new_cells -notitle -format dot -prefix rdata_alumacc o:rdata %ci*
|
||||
|
||||
25. Generating Graphviz representation of design.
|
||||
Writing dot description to `rdata_alumacc.dot'.
|
||||
Dumping selected parts of module fifo to page 1.
|
||||
|
||||
yosys> memory_collect
|
||||
|
||||
26. Executing MEMORY_COLLECT pass (generating $mem cells).
|
||||
|
||||
yosys> select -set new_cells t:$mem_v2
|
||||
|
||||
yosys> select -set rdata_path @new_cells %ci*:-$mem_v2[WR_DATA,WR_ADDR,WR_EN] @new_cells %co* %%
|
||||
|
||||
yosys> show -color maroon3 @new_cells -notitle -format dot -prefix rdata_coarse @rdata_path
|
||||
|
||||
27. Generating Graphviz representation of design.
|
||||
Writing dot description to `rdata_coarse.dot'.
|
||||
Dumping selected parts of module fifo to page 1.
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
|
||||
yosys> stat
|
||||
|
||||
2. Printing statistics.
|
||||
|
||||
=== fifo ===
|
||||
|
||||
Number of wires: 28
|
||||
Number of wire bits: 219
|
||||
Number of public wires: 9
|
||||
Number of public wire bits: 45
|
||||
Number of memories: 1
|
||||
Number of memory bits: 2048
|
||||
Number of processes: 3
|
||||
Number of cells: 9
|
||||
$add 1
|
||||
$logic_and 2
|
||||
$logic_not 2
|
||||
$memrd 1
|
||||
$sub 1
|
||||
addr_gen 2
|
||||
|
||||
=== addr_gen ===
|
||||
|
||||
Number of wires: 8
|
||||
Number of wire bits: 60
|
||||
Number of public wires: 4
|
||||
Number of public wire bits: 11
|
||||
Number of memories: 0
|
||||
Number of memory bits: 0
|
||||
Number of processes: 2
|
||||
Number of cells: 2
|
||||
$add 1
|
||||
$eq 1
|
||||
|
||||
|
||||
yosys> stat -top fifo
|
||||
|
||||
17. Printing statistics.
|
||||
|
||||
=== fifo ===
|
||||
|
||||
Number of wires: 94
|
||||
Number of wire bits: 260
|
||||
Number of public wires: 94
|
||||
Number of public wire bits: 260
|
||||
Number of memories: 0
|
||||
Number of memory bits: 0
|
||||
Number of processes: 0
|
||||
Number of cells: 138
|
||||
$scopeinfo 2
|
||||
SB_CARRY 26
|
||||
SB_DFF 26
|
||||
SB_DFFER 25
|
||||
SB_LUT4 58
|
||||
SB_RAM40_4K 1
|
||||
|
||||
|
|
@ -4,8 +4,10 @@ YOSYS ?= ../../../../$(PROGRAM_PREFIX)yosys
|
|||
|
||||
DOTS = counter_00.dot counter_01.dot counter_02.dot counter_03.dot
|
||||
|
||||
all: dots
|
||||
.PHONY: all dots examples
|
||||
all: dots examples
|
||||
dots: $(DOTS)
|
||||
examples:
|
||||
|
||||
$(DOTS): counter.v counter.ys mycells.lib
|
||||
$(YOSYS) counter.ys
|
||||
|
|
|
|||
|
|
@ -4,8 +4,10 @@ YOSYS ?= ../../../../$(PROGRAM_PREFIX)yosys
|
|||
|
||||
DOTS = macc_simple_xmap.dot macc_xilinx_xmap.dot
|
||||
|
||||
all: dots
|
||||
.PHONY: all dots examples
|
||||
all: dots examples
|
||||
dots: $(DOTS)
|
||||
examples:
|
||||
|
||||
macc_simple_xmap.dot: macc_simple_*.v macc_simple_test.ys
|
||||
$(YOSYS) macc_simple_test.ys
|
||||
|
|
|
|||
|
|
@ -6,14 +6,14 @@ DOT_NAMES = opt_share opt_muxtree opt_merge opt_expr
|
|||
|
||||
DOTS := $(addsuffix .dot,$(DOT_NAMES))
|
||||
|
||||
all: dots
|
||||
.PHONY: all dots examples
|
||||
all: dots examples
|
||||
dots: $(DOTS)
|
||||
examples:
|
||||
|
||||
%_full.dot: %.ys
|
||||
%.dot: %.ys
|
||||
$(YOSYS) $<
|
||||
|
||||
%.dot: %_full.dot
|
||||
gvpack -u $*_full.dot -o $@
|
||||
gvpack -u -o $@ $*_full.dot
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@ PROGRAM_PREFIX :=
|
|||
|
||||
YOSYS ?= ../../../../$(PROGRAM_PREFIX)yosys
|
||||
|
||||
.PHONY: all dots
|
||||
all: dots
|
||||
.PHONY: all dots examples
|
||||
all: dots examples
|
||||
dots: scrambler_p01.dot scrambler_p02.dot
|
||||
examples:
|
||||
|
||||
scrambler_p01.dot scrambler_p02.dot: scrambler.ys scrambler.v
|
||||
$(YOSYS) scrambler.ys
|
||||
|
|
|
|||
|
|
@ -11,14 +11,15 @@ MEMDEMO_DOTS := $(addsuffix .dot,$(MEMDEMO))
|
|||
SUBMOD = submod_00 submod_01 submod_02 submod_03
|
||||
SUBMOD_DOTS := $(addsuffix .dot,$(SUBMOD))
|
||||
|
||||
.PHONY: all dots
|
||||
all: dots
|
||||
.PHONY: all dots examples
|
||||
all: dots examples
|
||||
dots: select.dot $(SUMPROD_DOTS) $(MEMDEMO_DOTS) $(SUBMOD_DOTS)
|
||||
examples: sumprod.out
|
||||
|
||||
select.dot: select.v select.ys
|
||||
$(YOSYS) select.ys
|
||||
|
||||
$(SUMPROD_DOTS): sumprod.v sumprod.ys
|
||||
$(SUMPROD_DOTS) sumprod.out: sumprod.v sumprod.ys
|
||||
$(YOSYS) sumprod.ys
|
||||
|
||||
$(MEMDEMO_DOTS): memdemo.v memdemo.ys
|
||||
|
|
@ -30,3 +31,4 @@ $(SUBMOD_DOTS): memdemo.v submod.ys
|
|||
.PHONY: clean
|
||||
clean:
|
||||
rm -rf *.dot
|
||||
rm -f sumprod.out
|
||||
|
|
|
|||
|
|
@ -1,38 +0,0 @@
|
|||
|
||||
|
||||
attribute \src "sumprod.v:4.21-4.25"
|
||||
wire width 8 output 5 \prod
|
||||
|
||||
attribute \src "sumprod.v:10.17-10.26"
|
||||
cell $mul $mul$sumprod.v:10$4
|
||||
parameter \A_SIGNED 0
|
||||
parameter \A_WIDTH 8
|
||||
parameter \B_SIGNED 0
|
||||
parameter \B_WIDTH 8
|
||||
parameter \Y_WIDTH 8
|
||||
connect \A $mul$sumprod.v:10$3_Y
|
||||
connect \B \c
|
||||
connect \Y \prod
|
||||
end
|
||||
|
||||
|
||||
attribute \src "sumprod.v:10.17-10.22"
|
||||
wire width 8 $mul$sumprod.v:10$3_Y
|
||||
|
||||
attribute \src "sumprod.v:3.21-3.22"
|
||||
wire width 8 input 3 \c
|
||||
|
||||
attribute \src "sumprod.v:4.21-4.25"
|
||||
wire width 8 output 5 \prod
|
||||
|
||||
attribute \src "sumprod.v:10.17-10.26"
|
||||
cell $mul $mul$sumprod.v:10$4
|
||||
parameter \A_SIGNED 0
|
||||
parameter \A_WIDTH 8
|
||||
parameter \B_SIGNED 0
|
||||
parameter \B_WIDTH 8
|
||||
parameter \Y_WIDTH 8
|
||||
connect \A $mul$sumprod.v:10$3_Y
|
||||
connect \B \c
|
||||
connect \Y \prod
|
||||
end
|
||||
|
|
@ -8,9 +8,10 @@ EXAMPLE_DOTS := $(addsuffix .dot,$(EXAMPLE))
|
|||
CMOS = cmos_00 cmos_01
|
||||
CMOS_DOTS := $(addsuffix .dot,$(CMOS))
|
||||
|
||||
.PHONY: all dots
|
||||
all: dots example.out
|
||||
.PHONY: all dots examples
|
||||
all: dots examples
|
||||
dots: splice.dot $(EXAMPLE_DOTS) $(CMOS_DOTS)
|
||||
examples: example.out
|
||||
|
||||
splice.dot: splice.v
|
||||
$(YOSYS) -p 'prep -top splice_demo; show -format dot -prefix splice' splice.v
|
||||
|
|
@ -27,3 +28,4 @@ $(CMOS_DOTS): cmos.v cmos.ys
|
|||
.PHONY: clean
|
||||
clean:
|
||||
rm -rf *.dot
|
||||
rm -f example.out
|
||||
|
|
|
|||
|
|
@ -1,54 +0,0 @@
|
|||
|
||||
-- Executing script file `example_lscd.ys' --
|
||||
|
||||
1. Executing Verilog-2005 frontend: example.v
|
||||
Parsing Verilog input from `example.v' to AST representation.
|
||||
Generating RTLIL representation for module `\example'.
|
||||
Successfully finished Verilog frontend.
|
||||
echo on
|
||||
|
||||
yosys> ls
|
||||
|
||||
1 modules:
|
||||
example
|
||||
|
||||
yosys> cd example
|
||||
|
||||
yosys [example]> ls
|
||||
|
||||
8 wires:
|
||||
$0\y[1:0]
|
||||
$add$example.v:5$2_Y
|
||||
$ternary$example.v:5$3_Y
|
||||
a
|
||||
b
|
||||
c
|
||||
clk
|
||||
y
|
||||
|
||||
2 cells:
|
||||
$add$example.v:5$2
|
||||
$ternary$example.v:5$3
|
||||
|
||||
1 processes:
|
||||
$proc$example.v:3$1
|
||||
|
||||
yosys [example]> dump $2
|
||||
|
||||
|
||||
attribute \src "example.v:5.22-5.27"
|
||||
cell $add $add$example.v:5$2
|
||||
parameter \Y_WIDTH 2
|
||||
parameter \B_WIDTH 1
|
||||
parameter \A_WIDTH 1
|
||||
parameter \B_SIGNED 0
|
||||
parameter \A_SIGNED 0
|
||||
connect \Y $add$example.v:5$2_Y
|
||||
connect \B \b
|
||||
connect \A \a
|
||||
end
|
||||
|
||||
yosys [example]> cd ..
|
||||
|
||||
yosys> echo off
|
||||
echo off
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
.PHONY: all dots
|
||||
all: dots
|
||||
.PHONY: all dots examples
|
||||
all: dots examples
|
||||
dots:
|
||||
examples:
|
||||
|
||||
.PHONY: test
|
||||
test: stubnets.so
|
||||
|
|
|
|||
|
|
@ -9,9 +9,10 @@ YOSYS ?= ../../../../$(PROGRAM_PREFIX)yosys
|
|||
|
||||
DOTS = $(addsuffix .dot,$(DOT_TARGETS))
|
||||
|
||||
.PHONY: all dots
|
||||
all: dots
|
||||
.PHONY: all dots examples
|
||||
all: dots examples
|
||||
dots: $(DOTS)
|
||||
examples:
|
||||
|
||||
%.dot: %.v %.ys
|
||||
$(YOSYS) -p 'script $*.ys; show -notitle -prefix $* -format dot'
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@ PROGRAM_PREFIX :=
|
|||
|
||||
YOSYS ?= ../../../../$(PROGRAM_PREFIX)yosys
|
||||
|
||||
.PHONY: all dots
|
||||
all: dots
|
||||
.PHONY: all dots examples
|
||||
all: dots examples
|
||||
dots: red_or3x1.dot sym_mul.dot mymul.dot mulshift.dot addshift.dot
|
||||
examples:
|
||||
|
||||
red_or3x1.dot: red_or3x1_*
|
||||
$(YOSYS) red_or3x1_test.ys
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import os
|
|||
project = 'YosysHQ Yosys'
|
||||
author = 'YosysHQ GmbH'
|
||||
copyright ='2024 YosysHQ GmbH'
|
||||
yosys_ver = "0.44"
|
||||
yosys_ver = "0.46"
|
||||
|
||||
# select HTML theme
|
||||
html_theme = 'furo'
|
||||
|
|
@ -41,23 +41,44 @@ html_static_path = ['_static', "_images"]
|
|||
pygments_style = 'colorful'
|
||||
highlight_language = 'none'
|
||||
|
||||
extensions = ['sphinx.ext.autosectionlabel', 'sphinxcontrib.bibtex', 'rtds_action']
|
||||
extensions = ['sphinx.ext.autosectionlabel', 'sphinxcontrib.bibtex']
|
||||
|
||||
# rtds_action
|
||||
rtds_action_github_repo = "YosysHQ/yosys"
|
||||
rtds_action_path = "."
|
||||
rtds_action_artifact_prefix = "cmd-ref-"
|
||||
rtds_action_github_token = os.environ["GITHUB_TOKEN"]
|
||||
if os.getenv("READTHEDOCS"):
|
||||
# Use rtds_action if we are building on read the docs and have a github token env var
|
||||
if os.getenv("GITHUB_TOKEN"):
|
||||
extensions += ['rtds_action']
|
||||
rtds_action_github_repo = "YosysHQ/yosys"
|
||||
rtds_action_path = "."
|
||||
rtds_action_artifact_prefix = "cmd-ref-"
|
||||
rtds_action_github_token = os.environ["GITHUB_TOKEN"]
|
||||
else:
|
||||
# We're on read the docs but have no github token, this is probably a PR preview build
|
||||
html_theme_options["announcement"] = 'Missing content? Check <a class="reference internal" href="https://tyrtd--2.org.readthedocs.build/en/2/appendix/building_docs.html#pr-previews-and-limitations">PR preview limitations</a>.'
|
||||
html_theme_options["light_css_variables"]["color-announcement-background"] = "var(--color-admonition-title-background--caution)"
|
||||
html_theme_options["light_css_variables"]["color-announcement-text"] = "var(--color-content-foreground)"
|
||||
|
||||
# Ensure that autosectionlabel will produce unique names
|
||||
autosectionlabel_prefix_document = True
|
||||
autosectionlabel_maxdepth = 1
|
||||
|
||||
# include todos for previews
|
||||
extensions.append('sphinx.ext.todo')
|
||||
|
||||
# set version
|
||||
if os.getenv("READTHEDOCS") and os.getenv("READTHEDOCS_VERSION") == "latest":
|
||||
release = yosys_ver + "-dev"
|
||||
if os.getenv("READTHEDOCS"):
|
||||
rtds_version = os.getenv("READTHEDOCS_VERSION")
|
||||
if rtds_version == "latest":
|
||||
release = yosys_ver + "-dev"
|
||||
todo_include_todos = False
|
||||
elif rtds_version.startswith("docs"):
|
||||
release = rtds_version
|
||||
todo_include_todos = True
|
||||
else:
|
||||
release = yosys_ver
|
||||
todo_include_todos = False
|
||||
else:
|
||||
release = yosys_ver
|
||||
todo_include_todos = True
|
||||
|
||||
# assign figure numbers
|
||||
numfig = True
|
||||
|
|
@ -72,10 +93,6 @@ latex_elements = {
|
|||
'''
|
||||
}
|
||||
|
||||
# include todos during rewrite
|
||||
extensions.append('sphinx.ext.todo')
|
||||
todo_include_todos = False
|
||||
|
||||
# custom cmd-ref parsing/linking
|
||||
sys.path += [os.path.dirname(__file__) + "/../"]
|
||||
extensions.append('util.cmdref')
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ More scripting
|
|||
|
||||
.. todo:: brief overview for the more scripting index
|
||||
|
||||
.. todo:: troubleshooting document(?)
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
Troubleshooting
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
.. todo:: troubleshooting document(?)
|
||||
|
||||
See :doc:`/cmd/bugpoint`
|
||||
|
|
@ -90,8 +90,10 @@ Mapping to hardware
|
|||
For this example, we are using a Liberty file to describe a cell library which
|
||||
our internal cell library will be mapped to:
|
||||
|
||||
.. todo:: find a Liberty pygments style?
|
||||
|
||||
.. literalinclude:: /code_examples/intro/mycells.lib
|
||||
:language: Liberty
|
||||
:language: text
|
||||
:linenos:
|
||||
:name: mycells-lib
|
||||
:caption: :file:`mycells.lib`
|
||||
|
|
|
|||
|
|
@ -81,8 +81,10 @@ The following features, along with their corresponding Yosys build parameters,
|
|||
are required for the Yosys-Verific patch:
|
||||
|
||||
* RTL elaboration with
|
||||
* SystemVerilog with ``ENABLE_VERIFIC_SYSTEMVERILOG``, and/or
|
||||
* VHDL support with ``ENABLE_VERIFIC_VHDL``.
|
||||
|
||||
* SystemVerilog with ``ENABLE_VERIFIC_SYSTEMVERILOG``, and/or
|
||||
* VHDL support with ``ENABLE_VERIFIC_VHDL``.
|
||||
|
||||
* Hierarchy tree support and static elaboration with
|
||||
``ENABLE_VERIFIC_HIER_TREE``.
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,94 @@
|
|||
Writing a new backend using FunctionalIR
|
||||
===========================================
|
||||
|
||||
To simplify the writing of backends for functional languages or similar targets, Yosys provides an alternative intermediate representation called FunctionalIR which maps more directly on those targets.
|
||||
|
||||
FunctionalIR represents the design as a function ``(inputs, current_state) -> (outputs, next_state)``.
|
||||
This function is broken down into a series of assignments to variables.
|
||||
Each assignment is a simple operation, such as an addition.
|
||||
Complex operations are broken up into multiple steps.
|
||||
For example, an RTLIL addition will be translated into a sign/zero extension of the inputs, followed by an addition.
|
||||
|
||||
Like SSA form, each variable is assigned to exactly once.
|
||||
We can thus treat variables and assignments as equivalent and, since this is a graph-like representation, those variables are also called "nodes".
|
||||
Unlike RTLIL's cells and wires representation, this representation is strictly ordered (topologically sorted) with definitions preceding their use.
|
||||
|
||||
Every node has a "sort" (the FunctionalIR term for what might otherwise be called a "type"). The sorts available are
|
||||
|
||||
- ``bit[n]`` for an ``n``-bit bitvector, and
|
||||
- ``memory[n,m]`` for an immutable array of ``2**n`` values of sort ``bit[m]``.
|
||||
|
||||
In terms of actual code, Yosys provides a class ``Functional::IR`` that represents a design in FunctionalIR.
|
||||
``Functional::IR::from_module`` generates an instance from an RTLIL module.
|
||||
The entire design is stored as a whole in an internal data structure.
|
||||
To access the design, the ``Functional::Node`` class provides a reference to a particular node in the design.
|
||||
The ``Functional::IR`` class supports the syntax ``for(auto node : ir)`` to iterate over every node.
|
||||
|
||||
``Functional::IR`` also keeps track of inputs, outputs and states.
|
||||
By a "state" we mean a pair of a "current state" input and a "next state" output.
|
||||
One such pair is created for every register and for every memory.
|
||||
Every input, output and state has a name (equal to their name in RTLIL), a sort and a kind.
|
||||
The kind field usually remains as the default value ``$input``, ``$output`` or ``$state``, however some RTLIL cells such as ``$assert`` or ``$anyseq`` generate auxiliary inputs/outputs/states that are given a different kind to distinguish them from ordinary RTLIL inputs/outputs/states.
|
||||
|
||||
- To access an individual input/output/state, use ``ir.input(name, kind)``, ``ir.output(name, kind)`` or ``ir.state(name, kind)``. ``kind`` defaults to the default kind.
|
||||
- To iterate over all inputs/outputs/states of a certain kind, methods ``ir.inputs``, ``ir.outputs``, ``ir.states`` are provided. Their argument defaults to the default kinds mentioned.
|
||||
- To iterate over inputs/outputs/states of any kind, use ``ir.all_inputs``, ``ir.all_outputs`` and ``ir.all_states``.
|
||||
- Outputs have a node that indicate the value of the output, this can be retrieved via ``output.value()``.
|
||||
- States have a node that indicate the next value of the state, this can be retrieved via ``state.next_value()``.
|
||||
They also have an initial value that is accessed as either ``state.initial_value_signal()`` or ``state.initial_value_memory()``, depending on their sort.
|
||||
|
||||
Each node has a "function", which defines its operation (for a complete list of functions and a specification of their operation, see ``functional.h``).
|
||||
Functions are represented as an enum ``Functional::Fn`` and the function field can be accessed as ``node.fn()``.
|
||||
Since the most common operation is a switch over the function that also accesses the arguments, the ``Node`` class provides a method ``visit`` that implements the visitor pattern.
|
||||
For example, for an addition node ``node`` with arguments ``n1`` and ``n2``, ``node.visit(visitor)`` would call ``visitor.add(node, n1, n2)``.
|
||||
Thus typically one would implement a class with a method for every function.
|
||||
Visitors should inherit from either ``Functional::AbstractVisitor<ReturnType>`` or ``Functional::DefaultVisitor<ReturnType>``.
|
||||
The former will produce a compiler error if a case is unhandled, the latter will call ``default_handler(node)`` instead.
|
||||
Visitor methods should be marked as ``override`` to provide compiler errors if the arguments are wrong.
|
||||
|
||||
Utility classes
|
||||
-----------------
|
||||
|
||||
``functional.h`` also provides utility classes that are independent of the main FunctionalIR representation but are likely to be useful for backends.
|
||||
|
||||
``Functional::Writer`` provides a simple formatting class that wraps a ``std::ostream`` and provides the following methods:
|
||||
|
||||
- ``writer << value`` wraps ``os << value``.
|
||||
- ``writer.print(fmt, value0, value1, value2, ...)`` replaces ``{0}``, ``{1}``, ``{2}``, etc in the string ``fmt`` with ``value0``, ``value1``, ``value2``, resp.
|
||||
Each value is formatted using ``os << value``.
|
||||
It is also possible to write ``{}`` to refer to one past the last index, i.e. ``{1} {} {} {7} {}`` is equivalent to ``{1} {2} {3} {7} {8}``.
|
||||
- ``writer.print_with(fn, fmt, value0, value1, value2, ...)`` functions much the same as ``print`` but it uses ``os << fn(value)`` to print each value and falls back to ``os << value`` if ``fn(value)`` is not legal.
|
||||
|
||||
``Functional::Scope`` keeps track of variable names in a target language.
|
||||
It is used to translate between different sets of legal characters and to avoid accidentally re-defining identifiers.
|
||||
Users should derive a class from ``Scope`` and supply the following:
|
||||
|
||||
- ``Scope<Id>`` takes a template argument that specifies a type that's used to uniquely distinguish variables.
|
||||
Typically this would be ``int`` (if variables are used for ``Functional::IR`` nodes) or ``IdString``.
|
||||
- The derived class should provide a constructor that calls ``reserve`` for every reserved word in the target language.
|
||||
- A method ``bool is_legal_character(char c, int index)`` has to be provided that returns ``true`` iff ``c`` is legal in an identifier at position ``index``.
|
||||
|
||||
Given an instance ``scope`` of the derived class, the following methods are then available:
|
||||
|
||||
- ``scope.reserve(std::string name)`` marks the given name as being in-use
|
||||
- ``scope.unique_name(IdString suggestion)`` generates a previously unused name and attempts to make it similar to ``suggestion``.
|
||||
- ``scope(Id id, IdString suggestion)`` functions similar to ``unique_name``, except that multiple calls with the same ``id`` are guaranteed to retrieve the same name (independent of ``suggestion``).
|
||||
|
||||
``sexpr.h`` provides classes that represent and pretty-print s-expressions.
|
||||
S-expressions can be constructed with ``SExpr::list``, for example ``SExpr expr = SExpr::list("add", "x", SExpr::list("mul", "y", "z"))`` represents ``(add x (mul y z))``
|
||||
(by adding ``using SExprUtil::list`` to the top of the file, ``list`` can be used as shorthand for ``SExpr::list``).
|
||||
For prettyprinting, ``SExprWriter`` wraps an ``std::ostream`` and provides the following methods:
|
||||
|
||||
- ``writer << sexpr`` writes the provided expression to the output, breaking long lines and adding appropriate indentation.
|
||||
- ``writer.open(sexpr)`` is similar to ``writer << sexpr`` but will omit the last closing parenthesis.
|
||||
Further arguments can then be added separately with ``<<`` or ``open``.
|
||||
This allows for printing large s-expressions without needing to construct the whole expression in memory first.
|
||||
- ``writer.open(sexpr, false)`` is similar to ``writer.open(sexpr)`` but further arguments will not be indented.
|
||||
This is used to avoid unlimited indentation on structures with unlimited nesting.
|
||||
- ``writer.close(n = 1)`` closes the last ``n`` open s-expressions.
|
||||
- ``writer.push()`` and ``writer.pop()`` are used to automatically close s-expressions.
|
||||
``writer.pop()`` closes all s-expressions opened since the last call to ``writer.push()``.
|
||||
- ``writer.comment(string)`` writes a comment on a separate-line.
|
||||
``writer.comment(string, true)`` appends a comment to the last printed s-expression.
|
||||
- ``writer.flush()`` flushes any buffering and should be called before any direct access to the underlying ``std::ostream``. It does not close unclosed parentheses.
|
||||
- The destructor calls ``flush`` but also closes all unclosed parentheses.
|
||||
|
|
@ -10,5 +10,6 @@ of interest for developers looking to customise Yosys builds.
|
|||
|
||||
extensions
|
||||
build_verific
|
||||
functional_ir
|
||||
test_suites
|
||||
|
||||
|
|
|
|||
|
|
@ -448,7 +448,7 @@ void AigerReader::parse_xaiger()
|
|||
bool success = ce.eval(o);
|
||||
log_assert(success);
|
||||
log_assert(o.wire == nullptr);
|
||||
lut_mask[gray] = o.data;
|
||||
lut_mask.bits()[gray] = o.data;
|
||||
}
|
||||
RTLIL::Cell *output_cell = module->cell(stringf("$and$aiger%d$%d", aiger_autoidx, rootNodeID));
|
||||
log_assert(output_cell);
|
||||
|
|
|
|||
2
frontends/aiger2/Makefile.inc
Normal file
2
frontends/aiger2/Makefile.inc
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
OBJS += frontends/aiger2/xaiger.o
|
||||
473
frontends/aiger2/xaiger.cc
Normal file
473
frontends/aiger2/xaiger.cc
Normal file
|
|
@ -0,0 +1,473 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) Martin Povišer <povik@cutebit.org>
|
||||
*
|
||||
* 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/register.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
uint32_t read_be32(std::istream &f) {
|
||||
return ((uint32_t) f.get() << 24) |
|
||||
((uint32_t) f.get() << 16) |
|
||||
((uint32_t) f.get() << 8) | (uint32_t) f.get();
|
||||
}
|
||||
|
||||
IdString read_idstring(std::istream &f)
|
||||
{
|
||||
std::string str;
|
||||
std::getline(f, str, '\0');
|
||||
if (!f.good())
|
||||
log_error("failed to read string\n");
|
||||
return RTLIL::escape_id(str);
|
||||
}
|
||||
|
||||
struct Xaiger2Frontend : public Frontend {
|
||||
Xaiger2Frontend() : Frontend("xaiger2", "(experimental) read XAIGER file")
|
||||
{
|
||||
experimental();
|
||||
}
|
||||
|
||||
void help() override
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" read_xaiger2 -sc_mapping [options] <filename>\n");
|
||||
log("\n");
|
||||
log("Read a standard cell mapping from a XAIGER file into an existing module.\n");
|
||||
log("\n");
|
||||
log(" -module_name <name>\n");
|
||||
log(" name of the target module\n");
|
||||
log("\n");
|
||||
log(" -map2 <filename>\n");
|
||||
log(" read file with symbol information\n");
|
||||
log("\n");
|
||||
}
|
||||
|
||||
void read_sc_mapping(std::istream *&f, std::string filename, std::vector<std::string> args, Design *design)
|
||||
{
|
||||
IdString module_name;
|
||||
std::string map_filename;
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 2; argidx < args.size(); argidx++) {
|
||||
std::string arg = args[argidx];
|
||||
if (arg == "-module_name" && argidx + 1 < args.size()) {
|
||||
module_name = RTLIL::escape_id(args[++argidx]);
|
||||
continue;
|
||||
}
|
||||
if (arg == "-map2" && argidx + 1 < args.size()) {
|
||||
map_filename = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(f, filename, args, argidx, true);
|
||||
|
||||
if (map_filename.empty())
|
||||
log_error("A '-map2' argument required\n");
|
||||
if (module_name.empty())
|
||||
log_error("A '-module_name' argument required\n");
|
||||
|
||||
Module *module = design->module(module_name);
|
||||
if (!module)
|
||||
log_error("Module '%s' not found\n", log_id(module_name));
|
||||
|
||||
std::ifstream map_file;
|
||||
map_file.open(map_filename);
|
||||
if (!map_file)
|
||||
log_error("Failed to open map file '%s'\n", map_filename.c_str());
|
||||
|
||||
unsigned int M, I, L, O, A;
|
||||
std::string header;
|
||||
if (!(*f >> header >> M >> I >> L >> O >> A) || header != "aig")
|
||||
log_error("Bad header\n");
|
||||
std::string line;
|
||||
std::getline(*f, line);
|
||||
log_debug("M=%u I=%u L=%u O=%u A=%u\n", M, I, L, O, A);
|
||||
|
||||
if (L != 0)
|
||||
log_error("Latches unsupported\n");
|
||||
if (I + L + A != M)
|
||||
log_error("Inconsistent header\n");
|
||||
|
||||
std::vector<int> outputs;
|
||||
for (int i = 0; i < (int) O; i++) {
|
||||
int po;
|
||||
*f >> po;
|
||||
log_assert(f->get() == '\n');
|
||||
outputs.push_back(po);
|
||||
}
|
||||
|
||||
std::vector<std::pair<Cell *, Module *>> boxes;
|
||||
std::vector<bool> retained_boxes;
|
||||
std::vector<SigBit> bits(2 + 2*M, RTLIL::Sm);
|
||||
bits[0] = RTLIL::S0;
|
||||
bits[1] = RTLIL::S1;
|
||||
|
||||
std::string type;
|
||||
while (map_file >> type) {
|
||||
if (type == "pi") {
|
||||
int pi_idx;
|
||||
int woffset;
|
||||
std::string name;
|
||||
if (!(map_file >> pi_idx >> woffset >> name))
|
||||
log_error("Bad map file (1)\n");
|
||||
int lit = (2 * pi_idx) + 2;
|
||||
if (lit < 0 || lit >= (int) bits.size())
|
||||
log_error("Bad map file (2)\n");
|
||||
Wire *w = module->wire(name);
|
||||
if (!w || woffset < 0 || woffset >= w->width)
|
||||
log_error("Map file references non-existent signal bit %s[%d]\n",
|
||||
name.c_str(), woffset);
|
||||
bits[lit] = SigBit(w, woffset);
|
||||
} else if (type == "box") {
|
||||
int box_seq;
|
||||
std::string name;
|
||||
if (!(map_file >> box_seq >> name))
|
||||
log_error("Bad map file (20)\n");
|
||||
if (box_seq < 0)
|
||||
log_error("Bad map file (21)\n");
|
||||
|
||||
Cell *box = module->cell(RTLIL::escape_id(name));
|
||||
if (!box)
|
||||
log_error("Map file references non-existent box %s\n",
|
||||
name.c_str());
|
||||
|
||||
Module *def = design->module(box->type);
|
||||
if (def && !box->parameters.empty()) {
|
||||
// TODO: This is potentially costly even if a cached derivation exists
|
||||
def = design->module(def->derive(design, box->parameters));
|
||||
log_assert(def);
|
||||
}
|
||||
|
||||
if (!def)
|
||||
log_error("Bad map file (22)\n");
|
||||
|
||||
if (box_seq >= (int) boxes.size()) {
|
||||
boxes.resize(box_seq + 1);
|
||||
retained_boxes.resize(box_seq + 1);
|
||||
}
|
||||
boxes[box_seq] = std::make_pair(box, def);
|
||||
} else {
|
||||
std::string scratch;
|
||||
std::getline(map_file, scratch);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int) A; i++) {
|
||||
while (f->get() & 0x80 && !f->eof());
|
||||
while (f->get() & 0x80 && !f->eof());
|
||||
}
|
||||
|
||||
if (f->get() != 'c')
|
||||
log_error("Missing 'c' ahead of extensions\n");
|
||||
if (f->peek() == '\n')
|
||||
f->get();
|
||||
auto extensions_start = f->tellg();
|
||||
|
||||
log_debug("reading 'h' (first pass)\n");
|
||||
for (int c = f->get(); c != EOF; c = f->get()) {
|
||||
if (c == 'h') {
|
||||
uint32_t len, ci_num, co_num, pi_num, po_num, no_boxes;
|
||||
len = read_be32(*f);
|
||||
read_be32(*f);
|
||||
ci_num = read_be32(*f);
|
||||
co_num = read_be32(*f);
|
||||
pi_num = read_be32(*f);
|
||||
po_num = read_be32(*f);
|
||||
no_boxes = read_be32(*f);
|
||||
|
||||
log_debug("len=%u ci_num=%u co_num=%u pi_num=%u po_nun=%u no_boxes=%u\n",
|
||||
len, ci_num, co_num, pi_num, po_num, no_boxes);
|
||||
|
||||
int ci_counter = 0;
|
||||
for (uint32_t i = 0; i < no_boxes; i++) {
|
||||
uint32_t box_inputs, box_outputs, box_id, box_seq;
|
||||
box_inputs = read_be32(*f);
|
||||
box_outputs = read_be32(*f);
|
||||
box_id = read_be32(*f);
|
||||
box_seq = read_be32(*f);
|
||||
|
||||
log("box_seq=%d boxes.size=%d\n", box_seq, (int) boxes.size());
|
||||
log_assert(box_seq < boxes.size());
|
||||
|
||||
auto [cell, def] = boxes[box_seq];
|
||||
log_assert(cell && def);
|
||||
retained_boxes[box_seq] = true;
|
||||
|
||||
int box_ci_idx = 0;
|
||||
for (auto port_id : def->ports) {
|
||||
Wire *port = def->wire(port_id);
|
||||
if (port->port_output) {
|
||||
if (!cell->hasPort(port_id) || cell->getPort(port_id).size() != port->width)
|
||||
log_error("Malformed design (1)\n");
|
||||
|
||||
SigSpec &conn = cell->connections_[port_id];
|
||||
for (int j = 0; j < port->width; j++) {
|
||||
if (conn[j].wire && conn[j].wire->port_output)
|
||||
conn[j] = module->addWire(module->uniquify(
|
||||
stringf("$box$%s$%s$%d",
|
||||
cell->name.isPublic() ? cell->name.c_str() + 1 : cell->name.c_str(),
|
||||
port_id.isPublic() ? port_id.c_str() + 1 : port_id.c_str(),
|
||||
j)));
|
||||
|
||||
bits[2*(pi_num + ci_counter + box_ci_idx++) + 2] = conn[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log_assert(box_ci_idx == (int) box_outputs);
|
||||
ci_counter += box_ci_idx;
|
||||
}
|
||||
log_assert(pi_num + ci_counter == ci_num);
|
||||
} else if (c == '\n') {
|
||||
break;
|
||||
} else if (c == 'c') {
|
||||
break;
|
||||
} else {
|
||||
uint32_t len = read_be32(*f);
|
||||
f->ignore(len);
|
||||
log_debug(" section '%c' (%d): ignoring %d bytes\n", c, c, len);
|
||||
}
|
||||
}
|
||||
|
||||
log_debug("reading 'M' (second pass)\n");
|
||||
|
||||
f->seekg(extensions_start);
|
||||
bool read_mapping = false;
|
||||
uint32_t no_cells, no_instances;
|
||||
for (int c = f->get(); c != EOF; c = f->get()) {
|
||||
if (c == 'M') {
|
||||
uint32_t len = read_be32(*f);
|
||||
read_mapping = true;
|
||||
|
||||
no_cells = read_be32(*f);
|
||||
no_instances = read_be32(*f);
|
||||
|
||||
log_debug("M: len=%u no_cells=%u no_instances=%u\n", len, no_cells, no_instances);
|
||||
|
||||
struct MappingCell {
|
||||
RTLIL::IdString type;
|
||||
RTLIL::IdString out;
|
||||
std::vector<RTLIL::IdString> ins;
|
||||
};
|
||||
std::vector<MappingCell> cells;
|
||||
cells.resize(no_cells);
|
||||
|
||||
for (unsigned i = 0; i < no_cells; ++i) {
|
||||
auto &cell = cells[i];
|
||||
cell.type = read_idstring(*f);
|
||||
cell.out = read_idstring(*f);
|
||||
uint32_t nins = read_be32(*f);
|
||||
for (uint32_t j = 0; j < nins; j++)
|
||||
cell.ins.push_back(read_idstring(*f));
|
||||
log_debug("M: Cell %s (out %s, ins", log_id(cell.type), log_id(cell.out));
|
||||
for (auto in : cell.ins)
|
||||
log_debug(" %s", log_id(in));
|
||||
log_debug(")\n");
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < no_instances; ++i) {
|
||||
uint32_t cell_id = read_be32(*f);
|
||||
uint32_t out_lit = read_be32(*f);
|
||||
|
||||
log_assert(out_lit < bits.size());
|
||||
log_assert(bits[out_lit] == RTLIL::Sm);
|
||||
log_assert(cell_id < cells.size());
|
||||
auto &cell = cells[cell_id];
|
||||
Cell *instance = module->addCell(module->uniquify(stringf("$sc%d", out_lit)), cell.type);
|
||||
auto out_w = module->addWire(module->uniquify(stringf("$lit%d", out_lit)));
|
||||
instance->setPort(cell.out, out_w);
|
||||
bits[out_lit] = out_w;
|
||||
for (auto in : cell.ins) {
|
||||
uint32_t in_lit = read_be32(*f);
|
||||
log_assert(out_lit < bits.size());
|
||||
log_assert(bits[in_lit] != RTLIL::Sm);
|
||||
instance->setPort(in, bits[in_lit]);
|
||||
}
|
||||
}
|
||||
} else if (c == '\n') {
|
||||
break;
|
||||
} else if (c == 'c') {
|
||||
break;
|
||||
} else {
|
||||
uint32_t len = read_be32(*f);
|
||||
f->ignore(len);
|
||||
log_debug(" section '%c' (%d): ignoring %d bytes\n", c, c, len);
|
||||
}
|
||||
}
|
||||
|
||||
if (!read_mapping)
|
||||
log_error("Missing mapping (no 'M' section)\n");
|
||||
|
||||
log("Read %d instances with cell library of size %d.\n",
|
||||
no_instances, no_cells);
|
||||
|
||||
f->seekg(extensions_start);
|
||||
log_debug("reading 'h' (second pass)\n");
|
||||
int co_counter = 0;
|
||||
for (int c = f->get(); c != EOF; c = f->get()) {
|
||||
if (c == 'h') {
|
||||
uint32_t len, ci_num, co_num, pi_num, po_num, no_boxes;
|
||||
len = read_be32(*f);
|
||||
read_be32(*f);
|
||||
ci_num = read_be32(*f);
|
||||
co_num = read_be32(*f);
|
||||
pi_num = read_be32(*f);
|
||||
po_num = read_be32(*f);
|
||||
no_boxes = read_be32(*f);
|
||||
|
||||
log_debug("len=%u ci_num=%u co_num=%u pi_num=%u po_nun=%u no_boxes=%u\n",
|
||||
len, ci_num, co_num, pi_num, po_num, no_boxes);
|
||||
|
||||
for (uint32_t i = 0; i < no_boxes; i++) {
|
||||
uint32_t box_inputs, box_outputs, box_id, box_seq;
|
||||
box_inputs = read_be32(*f);
|
||||
box_outputs = read_be32(*f);
|
||||
box_id = read_be32(*f);
|
||||
box_seq = read_be32(*f);
|
||||
|
||||
log("box_seq=%d boxes.size=%d\n", box_seq, (int) boxes.size());
|
||||
log_assert(box_seq < boxes.size());
|
||||
|
||||
auto [cell, def] = boxes[box_seq];
|
||||
log_assert(cell && def);
|
||||
|
||||
int box_co_idx = 0;
|
||||
for (auto port_id : def->ports) {
|
||||
Wire *port = def->wire(port_id);
|
||||
SigSpec conn;
|
||||
if (port->port_input) {
|
||||
if (!cell->hasPort(port_id) || cell->getPort(port_id).size() != port->width)
|
||||
log_error("Malformed design (2)\n");
|
||||
|
||||
SigSpec conn;
|
||||
for (int j = 0; j < port->width; j++) {
|
||||
log_assert(co_counter + box_co_idx < (int) outputs.size());
|
||||
int lit = outputs[co_counter + box_co_idx++];
|
||||
log_assert(lit >= 0 && lit < (int) bits.size());
|
||||
SigBit bit = bits[lit];
|
||||
if (bit == RTLIL::Sm)
|
||||
log_error("Malformed mapping (1)\n");
|
||||
conn.append(bit);
|
||||
}
|
||||
cell->setPort(port_id, conn);
|
||||
}
|
||||
}
|
||||
|
||||
log_assert(box_co_idx == (int) box_inputs);
|
||||
co_counter += box_co_idx;
|
||||
}
|
||||
log_assert(po_num + co_counter == co_num);
|
||||
} else if (c == '\n') {
|
||||
break;
|
||||
} else if (c == 'c') {
|
||||
break;
|
||||
} else {
|
||||
uint32_t len = read_be32(*f);
|
||||
f->ignore(len);
|
||||
log_debug(" section '%c' (%d): ignoring %d bytes\n", c, c, len);
|
||||
}
|
||||
}
|
||||
|
||||
while (true) {
|
||||
std::string scratch;
|
||||
std::getline(*f, scratch);
|
||||
if (f->eof())
|
||||
break;
|
||||
log_assert(!f->fail());
|
||||
log("input file: %s\n", scratch.c_str());
|
||||
}
|
||||
|
||||
log_debug("co_counter=%d\n", co_counter);
|
||||
|
||||
// TODO: seek without close/open
|
||||
map_file.close();
|
||||
map_file.open(map_filename);
|
||||
while (map_file >> type) {
|
||||
if (type == "po") {
|
||||
int po_idx;
|
||||
int woffset;
|
||||
std::string name;
|
||||
if (!(map_file >> po_idx >> woffset >> name))
|
||||
log_error("Bad map file (3)\n");
|
||||
po_idx += co_counter;
|
||||
if (po_idx < 0 || po_idx >= (int) outputs.size())
|
||||
log_error("Bad map file (4)\n");
|
||||
int lit = outputs[po_idx];
|
||||
if (lit < 0 || lit >= (int) bits.size())
|
||||
log_error("Bad map file (5)\n");
|
||||
if (bits[lit] == RTLIL::Sm)
|
||||
log_error("Bad map file (6)\n");
|
||||
Wire *w = module->wire(name);
|
||||
if (!w || woffset < 0 || woffset >= w->width)
|
||||
log_error("Map file references non-existent signal bit %s[%d]\n",
|
||||
name.c_str(), woffset);
|
||||
module->connect(SigBit(w, woffset), bits[lit]);
|
||||
} else if (type == "pseudopo") {
|
||||
int po_idx;
|
||||
int poffset;
|
||||
std::string box_name;
|
||||
std::string box_port;
|
||||
if (!(map_file >> po_idx >> poffset >> box_name >> box_port))
|
||||
log_error("Bad map file (7)\n");
|
||||
po_idx += co_counter;
|
||||
if (po_idx < 0 || po_idx >= (int) outputs.size())
|
||||
log_error("Bad map file (8)\n");
|
||||
int lit = outputs[po_idx];
|
||||
if (lit < 0 || lit >= (int) bits.size())
|
||||
log_error("Bad map file (9)\n");
|
||||
if (bits[lit] == RTLIL::Sm)
|
||||
log_error("Bad map file (10)\n");
|
||||
Cell *cell = module->cell(box_name);
|
||||
if (!cell || !cell->hasPort(box_port))
|
||||
log_error("Map file references non-existent box port %s/%s\n",
|
||||
box_name.c_str(), box_port.c_str());
|
||||
SigSpec &port = cell->connections_[box_port];
|
||||
if (poffset < 0 || poffset >= port.size())
|
||||
log_error("Map file references non-existent box port bit %s/%s[%d]\n",
|
||||
box_name.c_str(), box_port.c_str(), poffset);
|
||||
port[poffset] = bits[lit];
|
||||
} else {
|
||||
std::string scratch;
|
||||
std::getline(map_file, scratch);
|
||||
}
|
||||
}
|
||||
|
||||
int box_seq = 0;
|
||||
for (auto [cell, def] : boxes) {
|
||||
if (!retained_boxes[box_seq++])
|
||||
module->remove(cell);
|
||||
}
|
||||
}
|
||||
|
||||
void execute(std::istream *&f, std::string filename, std::vector<std::string> args, Design *design) override
|
||||
{
|
||||
log_header(design, "Executing XAIGER2 frontend.\n");
|
||||
|
||||
if (args.size() > 1 && args[1] == "-sc_mapping") {
|
||||
read_sc_mapping(f, filename, args, design);
|
||||
return;
|
||||
}
|
||||
|
||||
log_cmd_error("Mode '-sc_mapping' must be selected\n");
|
||||
}
|
||||
} Xaiger2Frontend;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
|
|
@ -41,6 +41,8 @@ namespace AST {
|
|||
std::string current_filename;
|
||||
void (*set_line_num)(int) = NULL;
|
||||
int (*get_line_num)() = NULL;
|
||||
unsigned long long astnodes = 0;
|
||||
unsigned long long astnode_count() { return astnodes; }
|
||||
}
|
||||
|
||||
// instantiate global variables (private API)
|
||||
|
|
@ -204,6 +206,7 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch
|
|||
static unsigned int hashidx_count = 123456789;
|
||||
hashidx_count = mkhash_xorshift(hashidx_count);
|
||||
hashidx_ = hashidx_count;
|
||||
astnodes++;
|
||||
|
||||
this->type = type;
|
||||
filename = current_filename;
|
||||
|
|
@ -292,6 +295,7 @@ void AstNode::delete_children()
|
|||
// AstNode destructor
|
||||
AstNode::~AstNode()
|
||||
{
|
||||
astnodes--;
|
||||
delete_children();
|
||||
}
|
||||
|
||||
|
|
@ -474,6 +478,10 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const
|
|||
fprintf(f, ";\n");
|
||||
break;
|
||||
|
||||
case AST_WIRETYPE:
|
||||
fprintf(f, "%s", id2vl(str).c_str());
|
||||
break;
|
||||
|
||||
case AST_MEMORY:
|
||||
fprintf(f, "%s" "memory", indent.c_str());
|
||||
if (is_signed)
|
||||
|
|
@ -690,7 +698,17 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const
|
|||
break;
|
||||
|
||||
case AST_CAST_SIZE:
|
||||
children[0]->dumpVlog(f, "");
|
||||
switch (children[0]->type)
|
||||
{
|
||||
case AST_WIRE:
|
||||
if (children[0]->children.size() > 0)
|
||||
children[0]->children[0]->dumpVlog(f, "");
|
||||
else
|
||||
fprintf(f, "%d'", children[0]->range_left - children[0]->range_right + 1);
|
||||
break;
|
||||
default:
|
||||
children[0]->dumpVlog(f, "");
|
||||
}
|
||||
fprintf(f, "'(");
|
||||
children[1]->dumpVlog(f, "");
|
||||
fprintf(f, ")");
|
||||
|
|
@ -933,15 +951,7 @@ RTLIL::Const AstNode::asAttrConst() const
|
|||
{
|
||||
log_assert(type == AST_CONSTANT);
|
||||
|
||||
RTLIL::Const val;
|
||||
val.bits = bits;
|
||||
|
||||
if (is_string) {
|
||||
val.flags |= RTLIL::CONST_FLAG_STRING;
|
||||
log_assert(val.decode_string() == str);
|
||||
}
|
||||
|
||||
return val;
|
||||
return is_string ? RTLIL::Const(str) : RTLIL::Const(bits);
|
||||
}
|
||||
|
||||
RTLIL::Const AstNode::asParaConst() const
|
||||
|
|
@ -987,7 +997,7 @@ uint64_t AstNode::asInt(bool is_signed)
|
|||
uint64_t ret = 0;
|
||||
|
||||
for (int i = 0; i < 64; i++)
|
||||
if (v.bits.at(i) == RTLIL::State::S1)
|
||||
if (v.at(i) == RTLIL::State::S1)
|
||||
ret |= uint64_t(1) << i;
|
||||
|
||||
return ret;
|
||||
|
|
@ -1005,15 +1015,15 @@ double AstNode::asReal(bool is_signed)
|
|||
{
|
||||
RTLIL::Const val(bits);
|
||||
|
||||
bool is_negative = is_signed && !val.bits.empty() && val.bits.back() == RTLIL::State::S1;
|
||||
bool is_negative = is_signed && !val.empty() && val.back() == RTLIL::State::S1;
|
||||
if (is_negative)
|
||||
val = const_neg(val, val, false, false, val.bits.size());
|
||||
val = const_neg(val, val, false, false, val.size());
|
||||
|
||||
double v = 0;
|
||||
for (size_t i = 0; i < val.bits.size(); i++)
|
||||
for (size_t i = 0; i < val.size(); i++)
|
||||
// IEEE Std 1800-2012 Par 6.12.2: Individual bits that are x or z in
|
||||
// the net or the variable shall be treated as zero upon conversion.
|
||||
if (val.bits.at(i) == RTLIL::State::S1)
|
||||
if (val.at(i) == RTLIL::State::S1)
|
||||
v += exp2(i);
|
||||
if (is_negative)
|
||||
v *= -1;
|
||||
|
|
@ -1036,15 +1046,15 @@ RTLIL::Const AstNode::realAsConst(int width)
|
|||
#else
|
||||
if (!std::isfinite(v)) {
|
||||
#endif
|
||||
result.bits = std::vector<RTLIL::State>(width, RTLIL::State::Sx);
|
||||
result = std::vector<RTLIL::State>(width, RTLIL::State::Sx);
|
||||
} else {
|
||||
bool is_negative = v < 0;
|
||||
if (is_negative)
|
||||
v *= -1;
|
||||
for (int i = 0; i < width; i++, v /= 2)
|
||||
result.bits.push_back((fmod(floor(v), 2) != 0) ? RTLIL::State::S1 : RTLIL::State::S0);
|
||||
result.bits().push_back((fmod(floor(v), 2) != 0) ? RTLIL::State::S1 : RTLIL::State::S0);
|
||||
if (is_negative)
|
||||
result = const_neg(result, result, false, false, result.bits.size());
|
||||
result = const_neg(result, result, false, false, result.size());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
@ -1749,16 +1759,7 @@ static std::string serialize_param_value(const RTLIL::Const &val) {
|
|||
res.push_back('r');
|
||||
res += stringf("%d", GetSize(val));
|
||||
res.push_back('\'');
|
||||
for (int i = GetSize(val) - 1; i >= 0; i--) {
|
||||
switch (val.bits[i]) {
|
||||
case RTLIL::State::S0: res.push_back('0'); break;
|
||||
case RTLIL::State::S1: res.push_back('1'); break;
|
||||
case RTLIL::State::Sx: res.push_back('x'); break;
|
||||
case RTLIL::State::Sz: res.push_back('z'); break;
|
||||
case RTLIL::State::Sa: res.push_back('?'); break;
|
||||
case RTLIL::State::Sm: res.push_back('m'); break;
|
||||
}
|
||||
}
|
||||
res.append(val.as_string("?"));
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
@ -1850,7 +1851,7 @@ std::string AstModule::derive_common(RTLIL::Design *design, const dict<RTLIL::Id
|
|||
} else if ((it->second.flags & RTLIL::CONST_FLAG_STRING) != 0)
|
||||
child->children[0] = AstNode::mkconst_str(it->second.decode_string());
|
||||
else
|
||||
child->children[0] = AstNode::mkconst_bits(it->second.bits, (it->second.flags & RTLIL::CONST_FLAG_SIGNED) != 0);
|
||||
child->children[0] = AstNode::mkconst_bits(it->second.to_bits(), (it->second.flags & RTLIL::CONST_FLAG_SIGNED) != 0);
|
||||
rewritten.insert(it->first);
|
||||
}
|
||||
|
||||
|
|
@ -1863,7 +1864,7 @@ std::string AstModule::derive_common(RTLIL::Design *design, const dict<RTLIL::Id
|
|||
if ((param.second.flags & RTLIL::CONST_FLAG_STRING) != 0)
|
||||
defparam->children.push_back(AstNode::mkconst_str(param.second.decode_string()));
|
||||
else
|
||||
defparam->children.push_back(AstNode::mkconst_bits(param.second.bits, (param.second.flags & RTLIL::CONST_FLAG_SIGNED) != 0));
|
||||
defparam->children.push_back(AstNode::mkconst_bits(param.second.to_bits(), (param.second.flags & RTLIL::CONST_FLAG_SIGNED) != 0));
|
||||
new_ast->children.push_back(defparam);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -410,6 +410,9 @@ namespace AST
|
|||
extern void (*set_line_num)(int);
|
||||
extern int (*get_line_num)();
|
||||
|
||||
// for stats
|
||||
unsigned long long astnode_count();
|
||||
|
||||
// set set_line_num and get_line_num to internal dummy functions (done by simplify() and AstModule::derive
|
||||
// to control the filename and linenum properties of new nodes not generated by a frontend parser)
|
||||
void use_internal_line_num();
|
||||
|
|
|
|||
|
|
@ -735,10 +735,10 @@ struct AST_INTERNAL::ProcessGenerator
|
|||
for (auto sync : proc->syncs) {
|
||||
if (sync->type == RTLIL::STp) {
|
||||
triggers.append(sync->signal);
|
||||
polarity.bits.push_back(RTLIL::S1);
|
||||
polarity.bits().push_back(RTLIL::S1);
|
||||
} else if (sync->type == RTLIL::STn) {
|
||||
triggers.append(sync->signal);
|
||||
polarity.bits.push_back(RTLIL::S0);
|
||||
polarity.bits().push_back(RTLIL::S0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -832,10 +832,10 @@ struct AST_INTERNAL::ProcessGenerator
|
|||
for (auto sync : proc->syncs) {
|
||||
if (sync->type == RTLIL::STp) {
|
||||
triggers.append(sync->signal);
|
||||
polarity.bits.push_back(RTLIL::S1);
|
||||
polarity.bits().push_back(RTLIL::S1);
|
||||
} else if (sync->type == RTLIL::STn) {
|
||||
triggers.append(sync->signal);
|
||||
polarity.bits.push_back(RTLIL::S0);
|
||||
polarity.bits().push_back(RTLIL::S0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -892,7 +892,7 @@ struct AST_INTERNAL::ProcessGenerator
|
|||
RTLIL::Const priority_mask = RTLIL::Const(0, cur_idx);
|
||||
for (int i = 0; i < portid; i++) {
|
||||
int new_bit = port_map[std::make_pair(memid, i)];
|
||||
priority_mask.bits[new_bit] = orig_priority_mask.bits[i];
|
||||
priority_mask.bits()[new_bit] = orig_priority_mask[i];
|
||||
}
|
||||
action.priority_mask = priority_mask;
|
||||
sync->mem_write_actions.push_back(action);
|
||||
|
|
|
|||
|
|
@ -1500,11 +1500,69 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin
|
|||
}
|
||||
break;
|
||||
|
||||
case AST_CAST_SIZE: {
|
||||
int width = 1;
|
||||
AstNode *node;
|
||||
AstNode *child = children[0];
|
||||
|
||||
if (child->type == AST_WIRE) {
|
||||
if (child->children.size() == 0) {
|
||||
// Base type (e.g., int)
|
||||
width = child->range_left - child->range_right +1;
|
||||
node = mkconst_int(width, child->is_signed);
|
||||
} else {
|
||||
// User defined type
|
||||
log_assert(child->children[0]->type == AST_WIRETYPE);
|
||||
|
||||
const std::string &type_name = child->children[0]->str;
|
||||
if (!current_scope.count(type_name))
|
||||
input_error("Unknown identifier `%s' used as type name\n", type_name.c_str());
|
||||
AstNode *resolved_type_node = current_scope.at(type_name);
|
||||
if (resolved_type_node->type != AST_TYPEDEF)
|
||||
input_error("`%s' does not name a type\n", type_name.c_str());
|
||||
log_assert(resolved_type_node->children.size() == 1);
|
||||
AstNode *template_node = resolved_type_node->children[0];
|
||||
|
||||
// Ensure typedef itself is fully simplified
|
||||
while (template_node->simplify(const_fold, stage, width_hint, sign_hint)) {};
|
||||
|
||||
switch (template_node->type)
|
||||
{
|
||||
case AST_WIRE: {
|
||||
if (template_node->children.size() > 0 && template_node->children[0]->type == AST_RANGE)
|
||||
width = range_width(this, template_node->children[0]);
|
||||
child->delete_children();
|
||||
node = mkconst_int(width, true);
|
||||
break;
|
||||
}
|
||||
|
||||
case AST_STRUCT:
|
||||
case AST_UNION: {
|
||||
child->delete_children();
|
||||
width = size_packed_struct(template_node, 0);
|
||||
node = mkconst_int(width, false);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
log_error("Don't know how to translate static cast of type %s\n", type2str(template_node->type).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
delete child;
|
||||
children.erase(children.begin());
|
||||
children.insert(children.begin(), node);
|
||||
}
|
||||
|
||||
detect_width_simple = true;
|
||||
children_are_self_determined = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case AST_TO_BITS:
|
||||
case AST_TO_SIGNED:
|
||||
case AST_TO_UNSIGNED:
|
||||
case AST_SELFSZ:
|
||||
case AST_CAST_SIZE:
|
||||
case AST_CONCAT:
|
||||
case AST_REPLICATE:
|
||||
case AST_REDUCE_AND:
|
||||
|
|
@ -1660,8 +1718,8 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin
|
|||
if (v->type == AST_CONSTANT && v->bits_only_01()) {
|
||||
RTLIL::Const case_item_expr = v->bitsAsConst(width_hint, sign_hint);
|
||||
RTLIL::Const match = const_eq(case_expr, case_item_expr, sign_hint, sign_hint, 1);
|
||||
log_assert(match.bits.size() == 1);
|
||||
if (match.bits.front() == RTLIL::State::S1) {
|
||||
log_assert(match.size() == 1);
|
||||
if (match.front() == RTLIL::State::S1) {
|
||||
while (i+1 < GetSize(children))
|
||||
delete children[++i];
|
||||
goto keep_const_cond;
|
||||
|
|
@ -1963,7 +2021,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin
|
|||
if (children[1]->type != AST_CONSTANT)
|
||||
input_error("Right operand of to_bits expression is not constant!\n");
|
||||
RTLIL::Const new_value = children[1]->bitsAsConst(children[0]->bitsAsConst().as_int(), children[1]->is_signed);
|
||||
newNode = mkconst_bits(new_value.bits, children[1]->is_signed);
|
||||
newNode = mkconst_bits(new_value.to_bits(), children[1]->is_signed);
|
||||
goto apply_newNode;
|
||||
}
|
||||
|
||||
|
|
@ -2126,7 +2184,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin
|
|||
log_file_warning(filename, location.first_line, "converting real value %e to binary %s.\n",
|
||||
children[0]->realvalue, log_signal(constvalue));
|
||||
delete children[0];
|
||||
children[0] = mkconst_bits(constvalue.bits, sign_hint);
|
||||
children[0] = mkconst_bits(constvalue.to_bits(), sign_hint);
|
||||
fixup_hierarchy_flags();
|
||||
did_something = true;
|
||||
}
|
||||
|
|
@ -2135,7 +2193,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin
|
|||
RTLIL::SigSpec sig(children[0]->bits);
|
||||
sig.extend_u0(width, children[0]->is_signed);
|
||||
AstNode *old_child_0 = children[0];
|
||||
children[0] = mkconst_bits(sig.as_const().bits, is_signed);
|
||||
children[0] = mkconst_bits(sig.as_const().to_bits(), is_signed);
|
||||
delete old_child_0;
|
||||
fixup_hierarchy_flags();
|
||||
}
|
||||
|
|
@ -3435,8 +3493,8 @@ skip_dynamic_range_lvalue_expansion:;
|
|||
delete buf;
|
||||
|
||||
uint32_t result = 0;
|
||||
for (size_t i = 0; i < arg_value.bits.size(); i++)
|
||||
if (arg_value.bits.at(i) == RTLIL::State::S1)
|
||||
for (size_t i = 0; i < arg_value.size(); i++)
|
||||
if (arg_value.at(i) == RTLIL::State::S1)
|
||||
result = i + 1;
|
||||
|
||||
newNode = mkconst_int(result, true);
|
||||
|
|
@ -4115,14 +4173,14 @@ replace_fcall_later:;
|
|||
case AST_BIT_NOT:
|
||||
if (children[0]->type == AST_CONSTANT) {
|
||||
RTLIL::Const y = RTLIL::const_not(children[0]->bitsAsConst(width_hint, sign_hint), dummy_arg, sign_hint, false, width_hint);
|
||||
newNode = mkconst_bits(y.bits, sign_hint);
|
||||
newNode = mkconst_bits(y.to_bits(), sign_hint);
|
||||
}
|
||||
break;
|
||||
case AST_TO_SIGNED:
|
||||
case AST_TO_UNSIGNED:
|
||||
if (children[0]->type == AST_CONSTANT) {
|
||||
RTLIL::Const y = children[0]->bitsAsConst(width_hint, sign_hint);
|
||||
newNode = mkconst_bits(y.bits, type == AST_TO_SIGNED);
|
||||
newNode = mkconst_bits(y.to_bits(), type == AST_TO_SIGNED);
|
||||
}
|
||||
break;
|
||||
if (0) { case AST_BIT_AND: const_func = RTLIL::const_and; }
|
||||
|
|
@ -4132,7 +4190,7 @@ replace_fcall_later:;
|
|||
if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) {
|
||||
RTLIL::Const y = const_func(children[0]->bitsAsConst(width_hint, sign_hint),
|
||||
children[1]->bitsAsConst(width_hint, sign_hint), sign_hint, sign_hint, width_hint);
|
||||
newNode = mkconst_bits(y.bits, sign_hint);
|
||||
newNode = mkconst_bits(y.to_bits(), sign_hint);
|
||||
}
|
||||
break;
|
||||
if (0) { case AST_REDUCE_AND: const_func = RTLIL::const_reduce_and; }
|
||||
|
|
@ -4142,13 +4200,13 @@ replace_fcall_later:;
|
|||
if (0) { case AST_REDUCE_BOOL: const_func = RTLIL::const_reduce_bool; }
|
||||
if (children[0]->type == AST_CONSTANT) {
|
||||
RTLIL::Const y = const_func(RTLIL::Const(children[0]->bits), dummy_arg, false, false, -1);
|
||||
newNode = mkconst_bits(y.bits, false);
|
||||
newNode = mkconst_bits(y.to_bits(), false);
|
||||
}
|
||||
break;
|
||||
case AST_LOGIC_NOT:
|
||||
if (children[0]->type == AST_CONSTANT) {
|
||||
RTLIL::Const y = RTLIL::const_logic_not(RTLIL::Const(children[0]->bits), dummy_arg, children[0]->is_signed, false, -1);
|
||||
newNode = mkconst_bits(y.bits, false);
|
||||
newNode = mkconst_bits(y.to_bits(), false);
|
||||
} else
|
||||
if (children[0]->isConst()) {
|
||||
newNode = mkconst_int(children[0]->asReal(sign_hint) == 0, false, 1);
|
||||
|
|
@ -4159,7 +4217,7 @@ replace_fcall_later:;
|
|||
if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) {
|
||||
RTLIL::Const y = const_func(RTLIL::Const(children[0]->bits), RTLIL::Const(children[1]->bits),
|
||||
children[0]->is_signed, children[1]->is_signed, -1);
|
||||
newNode = mkconst_bits(y.bits, false);
|
||||
newNode = mkconst_bits(y.to_bits(), false);
|
||||
} else
|
||||
if (children[0]->isConst() && children[1]->isConst()) {
|
||||
if (type == AST_LOGIC_AND)
|
||||
|
|
@ -4176,7 +4234,7 @@ replace_fcall_later:;
|
|||
if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) {
|
||||
RTLIL::Const y = const_func(children[0]->bitsAsConst(width_hint, sign_hint),
|
||||
RTLIL::Const(children[1]->bits), sign_hint, type == AST_POW ? children[1]->is_signed : false, width_hint);
|
||||
newNode = mkconst_bits(y.bits, sign_hint);
|
||||
newNode = mkconst_bits(y.to_bits(), sign_hint);
|
||||
} else
|
||||
if (type == AST_POW && children[0]->isConst() && children[1]->isConst()) {
|
||||
newNode = new AstNode(AST_REALVALUE);
|
||||
|
|
@ -4196,7 +4254,7 @@ replace_fcall_later:;
|
|||
bool cmp_signed = children[0]->is_signed && children[1]->is_signed;
|
||||
RTLIL::Const y = const_func(children[0]->bitsAsConst(cmp_width, cmp_signed),
|
||||
children[1]->bitsAsConst(cmp_width, cmp_signed), cmp_signed, cmp_signed, 1);
|
||||
newNode = mkconst_bits(y.bits, false);
|
||||
newNode = mkconst_bits(y.to_bits(), false);
|
||||
} else
|
||||
if (children[0]->isConst() && children[1]->isConst()) {
|
||||
bool cmp_signed = (children[0]->type == AST_REALVALUE || children[0]->is_signed) && (children[1]->type == AST_REALVALUE || children[1]->is_signed);
|
||||
|
|
@ -4221,7 +4279,7 @@ replace_fcall_later:;
|
|||
if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) {
|
||||
RTLIL::Const y = const_func(children[0]->bitsAsConst(width_hint, sign_hint),
|
||||
children[1]->bitsAsConst(width_hint, sign_hint), sign_hint, sign_hint, width_hint);
|
||||
newNode = mkconst_bits(y.bits, sign_hint);
|
||||
newNode = mkconst_bits(y.to_bits(), sign_hint);
|
||||
} else
|
||||
if (children[0]->isConst() && children[1]->isConst()) {
|
||||
newNode = new AstNode(AST_REALVALUE);
|
||||
|
|
@ -4240,7 +4298,7 @@ replace_fcall_later:;
|
|||
if (0) { case AST_NEG: const_func = RTLIL::const_neg; }
|
||||
if (children[0]->type == AST_CONSTANT) {
|
||||
RTLIL::Const y = const_func(children[0]->bitsAsConst(width_hint, sign_hint), dummy_arg, sign_hint, false, width_hint);
|
||||
newNode = mkconst_bits(y.bits, sign_hint);
|
||||
newNode = mkconst_bits(y.to_bits(), sign_hint);
|
||||
} else
|
||||
if (children[0]->isConst()) {
|
||||
newNode = new AstNode(AST_REALVALUE);
|
||||
|
|
@ -4268,10 +4326,10 @@ replace_fcall_later:;
|
|||
newNode->realvalue = choice->asReal(sign_hint);
|
||||
} else {
|
||||
RTLIL::Const y = choice->bitsAsConst(width_hint, sign_hint);
|
||||
if (choice->is_string && y.bits.size() % 8 == 0 && sign_hint == false)
|
||||
newNode = mkconst_str(y.bits);
|
||||
if (choice->is_string && y.size() % 8 == 0 && sign_hint == false)
|
||||
newNode = mkconst_str(y.to_bits());
|
||||
else
|
||||
newNode = mkconst_bits(y.bits, sign_hint);
|
||||
newNode = mkconst_bits(y.to_bits(), sign_hint);
|
||||
}
|
||||
} else
|
||||
if (choice->isConst()) {
|
||||
|
|
@ -4280,11 +4338,11 @@ replace_fcall_later:;
|
|||
} else if (children[1]->type == AST_CONSTANT && children[2]->type == AST_CONSTANT) {
|
||||
RTLIL::Const a = children[1]->bitsAsConst(width_hint, sign_hint);
|
||||
RTLIL::Const b = children[2]->bitsAsConst(width_hint, sign_hint);
|
||||
log_assert(a.bits.size() == b.bits.size());
|
||||
for (size_t i = 0; i < a.bits.size(); i++)
|
||||
if (a.bits[i] != b.bits[i])
|
||||
a.bits[i] = RTLIL::State::Sx;
|
||||
newNode = mkconst_bits(a.bits, sign_hint);
|
||||
log_assert(a.size() == b.size());
|
||||
for (size_t i = 0; i < a.size(); i++)
|
||||
if (a[i] != b[i])
|
||||
a.bits()[i] = RTLIL::State::Sx;
|
||||
newNode = mkconst_bits(a.to_bits(), sign_hint);
|
||||
} else if (children[1]->isConst() && children[2]->isConst()) {
|
||||
newNode = new AstNode(AST_REALVALUE);
|
||||
if (children[1]->asReal(sign_hint) == children[2]->asReal(sign_hint))
|
||||
|
|
@ -4305,7 +4363,7 @@ replace_fcall_later:;
|
|||
val = children[1]->bitsAsUnsizedConst(width);
|
||||
else
|
||||
val = children[1]->bitsAsConst(width);
|
||||
newNode = mkconst_bits(val.bits, children[1]->is_signed);
|
||||
newNode = mkconst_bits(val.to_bits(), children[1]->is_signed);
|
||||
}
|
||||
break;
|
||||
case AST_CONCAT:
|
||||
|
|
@ -4890,7 +4948,7 @@ bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod,
|
|||
target->str = str;
|
||||
target->id2ast = id2ast;
|
||||
target->was_checked = true;
|
||||
block->children.push_back(new AstNode(AST_ASSIGN_EQ, target, mkconst_bits(data.extract(i*wordsz + pos, clen).bits, false)));
|
||||
block->children.push_back(new AstNode(AST_ASSIGN_EQ, target, mkconst_bits(data.extract(i*wordsz + pos, clen).to_bits(), false)));
|
||||
pos = epos;
|
||||
}
|
||||
}
|
||||
|
|
@ -5245,7 +5303,7 @@ bool AstNode::is_simple_const_expr()
|
|||
bool AstNode::replace_variables(std::map<std::string, AstNode::varinfo_t> &variables, AstNode *fcall, bool must_succeed)
|
||||
{
|
||||
if (type == AST_IDENTIFIER && variables.count(str)) {
|
||||
int offset = variables.at(str).offset, width = variables.at(str).val.bits.size();
|
||||
int offset = variables.at(str).offset, width = variables.at(str).val.size();
|
||||
if (!children.empty()) {
|
||||
if (children.size() != 1 || children.at(0)->type != AST_RANGE) {
|
||||
if (!must_succeed)
|
||||
|
|
@ -5268,7 +5326,7 @@ bool AstNode::replace_variables(std::map<std::string, AstNode::varinfo_t> &varia
|
|||
offset -= variables.at(str).offset;
|
||||
if (variables.at(str).range_swapped)
|
||||
offset = -offset;
|
||||
std::vector<RTLIL::State> &var_bits = variables.at(str).val.bits;
|
||||
std::vector<RTLIL::State> &var_bits = variables.at(str).val.bits();
|
||||
std::vector<RTLIL::State> new_bits(var_bits.begin() + offset, var_bits.begin() + offset + width);
|
||||
AstNode *newNode = mkconst_bits(new_bits, variables.at(str).is_signed);
|
||||
newNode->cloneInto(this);
|
||||
|
|
@ -5399,7 +5457,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed)
|
|||
}
|
||||
|
||||
if (stmt->children.at(0)->children.empty()) {
|
||||
variables[stmt->children.at(0)->str].val = stmt->children.at(1)->bitsAsConst(variables[stmt->children.at(0)->str].val.bits.size());
|
||||
variables[stmt->children.at(0)->str].val = stmt->children.at(1)->bitsAsConst(variables[stmt->children.at(0)->str].val.size());
|
||||
} else {
|
||||
AstNode *range = stmt->children.at(0)->children.at(0);
|
||||
if (!range->range_valid) {
|
||||
|
|
@ -5410,12 +5468,12 @@ AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed)
|
|||
int offset = min(range->range_left, range->range_right);
|
||||
int width = std::abs(range->range_left - range->range_right) + 1;
|
||||
varinfo_t &v = variables[stmt->children.at(0)->str];
|
||||
RTLIL::Const r = stmt->children.at(1)->bitsAsConst(v.val.bits.size());
|
||||
RTLIL::Const r = stmt->children.at(1)->bitsAsConst(v.val.size());
|
||||
for (int i = 0; i < width; i++) {
|
||||
int index = i + offset - v.offset;
|
||||
if (v.range_swapped)
|
||||
index = -index;
|
||||
v.val.bits.at(index) = r.bits.at(i);
|
||||
v.val.bits().at(index) = r.at(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -5558,7 +5616,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed)
|
|||
log_abort();
|
||||
}
|
||||
|
||||
result = AstNode::mkconst_bits(variables.at(str).val.bits, variables.at(str).is_signed);
|
||||
result = AstNode::mkconst_bits(variables.at(str).val.to_bits(), variables.at(str).is_signed);
|
||||
|
||||
finished:
|
||||
delete block;
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool
|
|||
if (buffer[0] == '.')
|
||||
{
|
||||
if (lutptr) {
|
||||
for (auto &bit : lutptr->bits)
|
||||
for (auto &bit : lutptr->bits())
|
||||
if (bit == RTLIL::State::Sx)
|
||||
bit = lut_default_state;
|
||||
lutptr = NULL;
|
||||
|
|
@ -321,9 +321,9 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool
|
|||
const_v = Const(str);
|
||||
} else {
|
||||
int n = strlen(v);
|
||||
const_v.bits.resize(n);
|
||||
const_v.bits().resize(n);
|
||||
for (int i = 0; i < n; i++)
|
||||
const_v.bits[i] = v[n-i-1] != '0' ? State::S1 : State::S0;
|
||||
const_v.bits()[i] = v[n-i-1] != '0' ? State::S1 : State::S0;
|
||||
}
|
||||
if (!strcmp(cmd, ".attr")) {
|
||||
if (obj_attributes == nullptr) {
|
||||
|
|
@ -566,16 +566,16 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool
|
|||
for (int i = 0; i < input_len; i++)
|
||||
switch (input[i]) {
|
||||
case '0':
|
||||
sopcell->parameters[ID::TABLE].bits.push_back(State::S1);
|
||||
sopcell->parameters[ID::TABLE].bits.push_back(State::S0);
|
||||
sopcell->parameters[ID::TABLE].bits().push_back(State::S1);
|
||||
sopcell->parameters[ID::TABLE].bits().push_back(State::S0);
|
||||
break;
|
||||
case '1':
|
||||
sopcell->parameters[ID::TABLE].bits.push_back(State::S0);
|
||||
sopcell->parameters[ID::TABLE].bits.push_back(State::S1);
|
||||
sopcell->parameters[ID::TABLE].bits().push_back(State::S0);
|
||||
sopcell->parameters[ID::TABLE].bits().push_back(State::S1);
|
||||
break;
|
||||
default:
|
||||
sopcell->parameters[ID::TABLE].bits.push_back(State::S0);
|
||||
sopcell->parameters[ID::TABLE].bits.push_back(State::S0);
|
||||
sopcell->parameters[ID::TABLE].bits().push_back(State::S0);
|
||||
sopcell->parameters[ID::TABLE].bits().push_back(State::S0);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -605,7 +605,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool
|
|||
goto try_next_value;
|
||||
}
|
||||
}
|
||||
lutptr->bits.at(i) = !strcmp(output, "0") ? RTLIL::State::S0 : RTLIL::State::S1;
|
||||
lutptr->bits().at(i) = !strcmp(output, "0") ? RTLIL::State::S0 : RTLIL::State::S1;
|
||||
try_next_value:;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -465,6 +465,9 @@ struct LibertyFrontend : public Frontend {
|
|||
log(" -setattr <attribute_name>\n");
|
||||
log(" set the specified attribute (to the value 1) on all loaded modules\n");
|
||||
log("\n");
|
||||
log(" -unit_delay\n");
|
||||
log(" import combinational timing arcs under the unit delay model\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
|
|
@ -475,6 +478,7 @@ struct LibertyFrontend : public Frontend {
|
|||
bool flag_ignore_miss_func = false;
|
||||
bool flag_ignore_miss_dir = false;
|
||||
bool flag_ignore_miss_data_latch = false;
|
||||
bool flag_unit_delay = false;
|
||||
std::vector<std::string> attributes;
|
||||
|
||||
size_t argidx;
|
||||
|
|
@ -514,6 +518,10 @@ struct LibertyFrontend : public Frontend {
|
|||
attributes.push_back(RTLIL::escape_id(args[++argidx]));
|
||||
continue;
|
||||
}
|
||||
if (arg == "-unit_delay") {
|
||||
flag_unit_delay = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(f, filename, args, argidx);
|
||||
|
|
@ -652,6 +660,7 @@ struct LibertyFrontend : public Frontend {
|
|||
continue;
|
||||
|
||||
RTLIL::Wire *wire = module->wires_.at(RTLIL::escape_id(node->args.at(0)));
|
||||
log_assert(wire);
|
||||
|
||||
if (dir && dir->value == "inout") {
|
||||
wire->port_input = true;
|
||||
|
|
@ -690,6 +699,43 @@ struct LibertyFrontend : public Frontend {
|
|||
}
|
||||
module->connect(RTLIL::SigSig(wire, out_sig));
|
||||
}
|
||||
|
||||
if (flag_unit_delay) {
|
||||
pool<Wire *> done;
|
||||
|
||||
for (auto timing : node->children)
|
||||
if (timing->id == "timing" && timing->args.empty()) {
|
||||
auto type = timing->find("timing_type");
|
||||
auto related_pin = timing->find("related_pin");
|
||||
if (!type || type->value != "combinational" || !related_pin)
|
||||
continue;
|
||||
|
||||
Wire *related = module->wire(RTLIL::escape_id(related_pin->value));
|
||||
if (!related)
|
||||
log_error("Failed to find related pin %s for timing of pin %s on %s\n",
|
||||
related_pin->value.c_str(), log_id(wire), log_id(module));
|
||||
|
||||
if (done.count(related))
|
||||
continue;
|
||||
|
||||
RTLIL::Cell *spec = module->addCell(NEW_ID, ID($specify2));
|
||||
spec->setParam(ID::SRC_WIDTH, 1);
|
||||
spec->setParam(ID::DST_WIDTH, 1);
|
||||
spec->setParam(ID::T_FALL_MAX, 1000);
|
||||
spec->setParam(ID::T_FALL_TYP, 1000);
|
||||
spec->setParam(ID::T_FALL_MIN, 1000);
|
||||
spec->setParam(ID::T_RISE_MAX, 1000);
|
||||
spec->setParam(ID::T_RISE_TYP, 1000);
|
||||
spec->setParam(ID::T_RISE_MIN, 1000);
|
||||
spec->setParam(ID::SRC_DST_POL, false);
|
||||
spec->setParam(ID::SRC_DST_PEN, false);
|
||||
spec->setParam(ID::FULL, false);
|
||||
spec->setPort(ID::EN, Const(1, 1));
|
||||
spec->setPort(ID::SRC, related);
|
||||
spec->setPort(ID::DST, wire);
|
||||
done.insert(related);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -447,7 +447,7 @@ constant:
|
|||
bits.pop_back();
|
||||
$$ = new RTLIL::Const;
|
||||
for (auto it = bits.begin(); it != bits.end(); it++)
|
||||
$$->bits.push_back(*it);
|
||||
$$->bits().push_back(*it);
|
||||
if (is_signed) {
|
||||
$$->flags |= RTLIL::CONST_FLAG_SIGNED;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -236,23 +236,6 @@ RTLIL::IdString VerificImporter::new_verific_id(Verific::DesignObj *obj)
|
|||
return s;
|
||||
}
|
||||
|
||||
RTLIL::Const mkconst_str(const std::string &str)
|
||||
{
|
||||
RTLIL::Const val;
|
||||
std::vector<RTLIL::State> data;
|
||||
data.reserve(str.size() * 8);
|
||||
for (size_t i = 0; i < str.size(); i++) {
|
||||
unsigned char ch = str[str.size() - i - 1];
|
||||
for (int j = 0; j < 8; j++) {
|
||||
data.push_back((ch & 1) ? State::S1 : State::S0);
|
||||
ch = ch >> 1;
|
||||
}
|
||||
}
|
||||
val.bits = data;
|
||||
val.flags |= RTLIL::CONST_FLAG_STRING;
|
||||
return val;
|
||||
}
|
||||
|
||||
static const RTLIL::Const extract_vhdl_boolean(std::string &val)
|
||||
{
|
||||
if (val == "false")
|
||||
|
|
@ -295,7 +278,7 @@ static const RTLIL::Const extract_vhdl_char(std::string &val)
|
|||
|
||||
static const RTLIL::Const extract_real_value(std::string &val)
|
||||
{
|
||||
RTLIL::Const c = mkconst_str(val);
|
||||
RTLIL::Const c(val);
|
||||
c.flags |= RTLIL::CONST_FLAG_REAL;
|
||||
return c;
|
||||
}
|
||||
|
|
@ -333,7 +316,7 @@ static const RTLIL::Const extract_vhdl_const(const char *value, bool output_sig
|
|||
} else if (val == "true") {
|
||||
c = RTLIL::Const::from_string("1");
|
||||
} else {
|
||||
c = mkconst_str(val);
|
||||
c = RTLIL::Const(val);
|
||||
log_warning("encoding value '%s' as string.\n", value);
|
||||
}
|
||||
if (is_signed)
|
||||
|
|
@ -364,7 +347,7 @@ static const RTLIL::Const extract_verilog_const(const char *value, bool allow_s
|
|||
} else if (allow_string) {
|
||||
c = RTLIL::Const(val);
|
||||
} else {
|
||||
c = mkconst_str(val);
|
||||
c = RTLIL::Const(val);
|
||||
log_warning("encoding value '%s' as string.\n", value);
|
||||
}
|
||||
if (is_signed)
|
||||
|
|
@ -450,6 +433,19 @@ void VerificImporter::import_attributes(dict<RTLIL::IdString, RTLIL::Const> &att
|
|||
auto type_range = nl->GetTypeRange(obj->Name());
|
||||
if (!type_range)
|
||||
return;
|
||||
if (type_range->IsTypeScalar()) {
|
||||
const long long bottom_bound = type_range->GetScalarRangeLeftBound();
|
||||
const long long top_bound = type_range->GetScalarRangeRightBound();
|
||||
const unsigned bit_width = type_range->NumElements();
|
||||
RTLIL::Const bottom_const(bottom_bound, bit_width);
|
||||
RTLIL::Const top_const(top_bound, bit_width);
|
||||
if (bottom_bound < 0 || top_bound < 0) {
|
||||
bottom_const.flags |= RTLIL::CONST_FLAG_SIGNED;
|
||||
top_const.flags |= RTLIL::CONST_FLAG_SIGNED;
|
||||
}
|
||||
attributes.emplace(ID(bottom_bound), bottom_const);
|
||||
attributes.emplace(ID(top_bound), top_const);
|
||||
}
|
||||
if (!type_range->IsTypeEnum())
|
||||
return;
|
||||
#ifdef VERIFIC_VHDL_SUPPORT
|
||||
|
|
@ -1621,7 +1617,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
|
|||
if (*ascii_initdata == 0)
|
||||
break;
|
||||
if (*ascii_initdata == '0' || *ascii_initdata == '1') {
|
||||
initval[bit_idx] = (*ascii_initdata == '0') ? State::S0 : State::S1;
|
||||
initval.bits()[bit_idx] = (*ascii_initdata == '0') ? State::S0 : State::S1;
|
||||
initval_valid = true;
|
||||
}
|
||||
ascii_initdata++;
|
||||
|
|
@ -1743,9 +1739,9 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
|
|||
|
||||
if (init_nets.count(net)) {
|
||||
if (init_nets.at(net) == '0')
|
||||
initval.bits.at(bitidx) = State::S0;
|
||||
initval.bits().at(bitidx) = State::S0;
|
||||
if (init_nets.at(net) == '1')
|
||||
initval.bits.at(bitidx) = State::S1;
|
||||
initval.bits().at(bitidx) = State::S1;
|
||||
initval_valid = true;
|
||||
init_nets.erase(net);
|
||||
}
|
||||
|
|
@ -1819,12 +1815,12 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
|
|||
initval = bit.wire->attributes.at(ID::init);
|
||||
|
||||
while (GetSize(initval) < GetSize(bit.wire))
|
||||
initval.bits.push_back(State::Sx);
|
||||
initval.bits().push_back(State::Sx);
|
||||
|
||||
if (it.second == '0')
|
||||
initval.bits.at(bit.offset) = State::S0;
|
||||
initval.bits().at(bit.offset) = State::S0;
|
||||
if (it.second == '1')
|
||||
initval.bits.at(bit.offset) = State::S1;
|
||||
initval.bits().at(bit.offset) = State::S1;
|
||||
|
||||
bit.wire->attributes[ID::init] = initval;
|
||||
}
|
||||
|
|
@ -2011,7 +2007,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
|
|||
}
|
||||
|
||||
Const qx_init = Const(State::S1, width);
|
||||
qx_init.bits.resize(2 * width, State::S0);
|
||||
qx_init.bits().resize(2 * width, State::S0);
|
||||
|
||||
clocking.addDff(new_verific_id(inst), sig_dx, sig_qx, qx_init);
|
||||
module->addXnor(new_verific_id(inst), sig_dx, sig_qx, sig_ox);
|
||||
|
|
@ -2129,13 +2125,12 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
|
|||
if (verific_verbose)
|
||||
log(" assert condition %s.\n", log_signal(cond));
|
||||
|
||||
const char *assume_attr = nullptr; // inst->GetAttValue("assume");
|
||||
|
||||
Cell *cell = nullptr;
|
||||
if (assume_attr != nullptr && !strcmp(assume_attr, "1"))
|
||||
cell = module->addAssume(new_verific_id(inst), cond, State::S1);
|
||||
else
|
||||
cell = module->addAssert(new_verific_id(inst), cond, State::S1);
|
||||
Cell *cell = module->addAssert(new_verific_id(inst), cond, State::S1);
|
||||
// Initialize FF feeding condition to 1, in case it is not
|
||||
// used by rest of design logic, to prevent failing on
|
||||
// initial uninitialized state
|
||||
if (cond.is_wire() && !cond.wire->name.isPublic())
|
||||
cond.wire->attributes[ID::init] = Const(1,1);
|
||||
|
||||
import_attributes(cell->attributes, inst);
|
||||
continue;
|
||||
|
|
@ -2282,7 +2277,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
|
|||
continue;
|
||||
|
||||
if (non_ff_bits.count(SigBit(wire, i)))
|
||||
initval[i] = State::Sx;
|
||||
initval.bits()[i] = State::Sx;
|
||||
}
|
||||
|
||||
if (wire->port_input) {
|
||||
|
|
@ -2469,7 +2464,7 @@ Cell *VerificClocking::addDff(IdString name, SigSpec sig_d, SigSpec sig_q, Const
|
|||
if (c.wire && c.wire->attributes.count(ID::init)) {
|
||||
Const val = c.wire->attributes.at(ID::init);
|
||||
for (int i = 0; i < GetSize(c); i++)
|
||||
initval[offset+i] = val[c.offset+i];
|
||||
initval.bits()[offset+i] = val[c.offset+i];
|
||||
}
|
||||
offset += GetSize(c);
|
||||
}
|
||||
|
|
@ -2540,7 +2535,7 @@ Cell *VerificClocking::addAldff(IdString name, RTLIL::SigSpec sig_aload, RTLIL::
|
|||
if (c.wire && c.wire->attributes.count(ID::init)) {
|
||||
Const val = c.wire->attributes.at(ID::init);
|
||||
for (int i = 0; i < GetSize(c); i++)
|
||||
initval[offset+i] = val[c.offset+i];
|
||||
initval.bits()[offset+i] = val[c.offset+i];
|
||||
}
|
||||
offset += GetSize(c);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -575,7 +575,7 @@ struct SvaFsm
|
|||
|
||||
if (delta_pos >= 0 && i_within_j && j_within_i) {
|
||||
did_something = true;
|
||||
values[i][delta_pos] = State::Sa;
|
||||
values[i].bits()[delta_pos] = State::Sa;
|
||||
values[j] = values.back();
|
||||
values.pop_back();
|
||||
goto next_pair;
|
||||
|
|
|
|||
|
|
@ -464,6 +464,7 @@ static const AstNode *addAsgnBinopStmt(dict<IdString, AstNode*> *attr, AstNode *
|
|||
%%
|
||||
|
||||
input: {
|
||||
(void)frontend_verilog_yynerrs;
|
||||
ast_stack.clear();
|
||||
ast_stack.push_back(current_ast);
|
||||
} design {
|
||||
|
|
@ -3506,6 +3507,12 @@ basic_expr:
|
|||
$$ = new AstNode(AST_CAST_SIZE, $1, $4);
|
||||
SET_AST_NODE_LOC($$, @1, @4);
|
||||
} |
|
||||
typedef_base_type OP_CAST '(' expr ')' {
|
||||
if (!sv_mode)
|
||||
frontend_verilog_yyerror("Static cast is only supported in SystemVerilog mode.");
|
||||
$$ = new AstNode(AST_CAST_SIZE, $1, $4);
|
||||
SET_AST_NODE_LOC($$, @1, @4);
|
||||
} |
|
||||
'(' expr '=' expr ')' {
|
||||
ensureAsgnExprAllowed();
|
||||
AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, $4);
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ struct BitPatternPool
|
|||
bits_t sig2bits(RTLIL::SigSpec sig)
|
||||
{
|
||||
bits_t bits;
|
||||
bits.bitdata = sig.as_const().bits;
|
||||
bits.bitdata = sig.as_const().bits();
|
||||
for (auto &b : bits.bitdata)
|
||||
if (b > RTLIL::State::S1)
|
||||
b = RTLIL::State::Sa;
|
||||
|
|
|
|||
178
kernel/calc.cc
178
kernel/calc.cc
|
|
@ -30,13 +30,13 @@ static void extend_u0(RTLIL::Const &arg, int width, bool is_signed)
|
|||
{
|
||||
RTLIL::State padding = RTLIL::State::S0;
|
||||
|
||||
if (arg.bits.size() > 0 && is_signed)
|
||||
padding = arg.bits.back();
|
||||
if (arg.size() > 0 && is_signed)
|
||||
padding = arg.back();
|
||||
|
||||
while (int(arg.bits.size()) < width)
|
||||
arg.bits.push_back(padding);
|
||||
while (int(arg.size()) < width)
|
||||
arg.bits().push_back(padding);
|
||||
|
||||
arg.bits.resize(width);
|
||||
arg.bits().resize(width);
|
||||
}
|
||||
|
||||
static BigInteger const2big(const RTLIL::Const &val, bool as_signed, int &undef_bit_pos)
|
||||
|
|
@ -45,17 +45,17 @@ static BigInteger const2big(const RTLIL::Const &val, bool as_signed, int &undef_
|
|||
|
||||
BigInteger::Sign sign = BigInteger::positive;
|
||||
State inv_sign_bit = RTLIL::State::S1;
|
||||
size_t num_bits = val.bits.size();
|
||||
size_t num_bits = val.size();
|
||||
|
||||
if (as_signed && num_bits && val.bits[num_bits-1] == RTLIL::State::S1) {
|
||||
if (as_signed && num_bits && val[num_bits-1] == RTLIL::State::S1) {
|
||||
inv_sign_bit = RTLIL::State::S0;
|
||||
sign = BigInteger::negative;
|
||||
num_bits--;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < num_bits; i++)
|
||||
if (val.bits[i] == RTLIL::State::S0 || val.bits[i] == RTLIL::State::S1)
|
||||
mag.setBit(i, val.bits[i] == inv_sign_bit);
|
||||
if (val[i] == RTLIL::State::S0 || val[i] == RTLIL::State::S1)
|
||||
mag.setBit(i, val[i] == inv_sign_bit);
|
||||
else if (undef_bit_pos < 0)
|
||||
undef_bit_pos = i;
|
||||
|
||||
|
|
@ -79,19 +79,19 @@ static RTLIL::Const big2const(const BigInteger &val, int result_len, int undef_b
|
|||
{
|
||||
mag--;
|
||||
for (int i = 0; i < result_len; i++)
|
||||
result.bits[i] = mag.getBit(i) ? RTLIL::State::S0 : RTLIL::State::S1;
|
||||
result.bits()[i] = mag.getBit(i) ? RTLIL::State::S0 : RTLIL::State::S1;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < result_len; i++)
|
||||
result.bits[i] = mag.getBit(i) ? RTLIL::State::S1 : RTLIL::State::S0;
|
||||
result.bits()[i] = mag.getBit(i) ? RTLIL::State::S1 : RTLIL::State::S0;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (undef_bit_pos >= 0)
|
||||
for (int i = undef_bit_pos; i < result_len; i++)
|
||||
result.bits[i] = RTLIL::State::Sx;
|
||||
result[i] = RTLIL::State::Sx;
|
||||
#endif
|
||||
|
||||
return result;
|
||||
|
|
@ -132,19 +132,19 @@ static RTLIL::State logic_xnor(RTLIL::State a, RTLIL::State b)
|
|||
RTLIL::Const RTLIL::const_not(const RTLIL::Const &arg1, const RTLIL::Const&, bool signed1, bool, int result_len)
|
||||
{
|
||||
if (result_len < 0)
|
||||
result_len = arg1.bits.size();
|
||||
result_len = arg1.size();
|
||||
|
||||
RTLIL::Const arg1_ext = arg1;
|
||||
extend_u0(arg1_ext, result_len, signed1);
|
||||
|
||||
RTLIL::Const result(RTLIL::State::Sx, result_len);
|
||||
for (size_t i = 0; i < size_t(result_len); i++) {
|
||||
if (i >= arg1_ext.bits.size())
|
||||
result.bits[i] = RTLIL::State::S0;
|
||||
else if (arg1_ext.bits[i] == RTLIL::State::S0)
|
||||
result.bits[i] = RTLIL::State::S1;
|
||||
else if (arg1_ext.bits[i] == RTLIL::State::S1)
|
||||
result.bits[i] = RTLIL::State::S0;
|
||||
if (i >= arg1_ext.size())
|
||||
result.bits()[i] = RTLIL::State::S0;
|
||||
else if (arg1_ext.bits()[i] == RTLIL::State::S0)
|
||||
result.bits()[i] = RTLIL::State::S1;
|
||||
else if (arg1_ext.bits()[i] == RTLIL::State::S1)
|
||||
result.bits()[i] = RTLIL::State::S0;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
@ -154,16 +154,16 @@ static RTLIL::Const logic_wrapper(RTLIL::State(*logic_func)(RTLIL::State, RTLIL:
|
|||
RTLIL::Const arg1, RTLIL::Const arg2, bool signed1, bool signed2, int result_len = -1)
|
||||
{
|
||||
if (result_len < 0)
|
||||
result_len = max(arg1.bits.size(), arg2.bits.size());
|
||||
result_len = max(arg1.size(), arg2.size());
|
||||
|
||||
extend_u0(arg1, result_len, signed1);
|
||||
extend_u0(arg2, result_len, signed2);
|
||||
|
||||
RTLIL::Const result(RTLIL::State::Sx, result_len);
|
||||
for (size_t i = 0; i < size_t(result_len); i++) {
|
||||
RTLIL::State a = i < arg1.bits.size() ? arg1.bits[i] : RTLIL::State::S0;
|
||||
RTLIL::State b = i < arg2.bits.size() ? arg2.bits[i] : RTLIL::State::S0;
|
||||
result.bits[i] = logic_func(a, b);
|
||||
RTLIL::State a = i < arg1.size() ? arg1.bits()[i] : RTLIL::State::S0;
|
||||
RTLIL::State b = i < arg2.size() ? arg2.bits()[i] : RTLIL::State::S0;
|
||||
result.bits()[i] = logic_func(a, b);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
@ -193,12 +193,12 @@ static RTLIL::Const logic_reduce_wrapper(RTLIL::State initial, RTLIL::State(*log
|
|||
{
|
||||
RTLIL::State temp = initial;
|
||||
|
||||
for (size_t i = 0; i < arg1.bits.size(); i++)
|
||||
temp = logic_func(temp, arg1.bits[i]);
|
||||
for (size_t i = 0; i < arg1.size(); i++)
|
||||
temp = logic_func(temp, arg1[i]);
|
||||
|
||||
RTLIL::Const result(temp);
|
||||
while (int(result.bits.size()) < result_len)
|
||||
result.bits.push_back(RTLIL::State::S0);
|
||||
while (int(result.size()) < result_len)
|
||||
result.bits().push_back(RTLIL::State::S0);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -220,11 +220,11 @@ RTLIL::Const RTLIL::const_reduce_xor(const RTLIL::Const &arg1, const RTLIL::Cons
|
|||
RTLIL::Const RTLIL::const_reduce_xnor(const RTLIL::Const &arg1, const RTLIL::Const&, bool, bool, int result_len)
|
||||
{
|
||||
RTLIL::Const buffer = logic_reduce_wrapper(RTLIL::State::S0, logic_xor, arg1, result_len);
|
||||
if (!buffer.bits.empty()) {
|
||||
if (buffer.bits.front() == RTLIL::State::S0)
|
||||
buffer.bits.front() = RTLIL::State::S1;
|
||||
else if (buffer.bits.front() == RTLIL::State::S1)
|
||||
buffer.bits.front() = RTLIL::State::S0;
|
||||
if (!buffer.empty()) {
|
||||
if (buffer.front() == RTLIL::State::S0)
|
||||
buffer.bits().front() = RTLIL::State::S1;
|
||||
else if (buffer.front() == RTLIL::State::S1)
|
||||
buffer.bits().front() = RTLIL::State::S0;
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
|
@ -240,8 +240,8 @@ RTLIL::Const RTLIL::const_logic_not(const RTLIL::Const &arg1, const RTLIL::Const
|
|||
BigInteger a = const2big(arg1, signed1, undef_bit_pos_a);
|
||||
RTLIL::Const result(a.isZero() ? undef_bit_pos_a >= 0 ? RTLIL::State::Sx : RTLIL::State::S1 : RTLIL::State::S0);
|
||||
|
||||
while (int(result.bits.size()) < result_len)
|
||||
result.bits.push_back(RTLIL::State::S0);
|
||||
while (int(result.size()) < result_len)
|
||||
result.bits().push_back(RTLIL::State::S0);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -255,8 +255,8 @@ RTLIL::Const RTLIL::const_logic_and(const RTLIL::Const &arg1, const RTLIL::Const
|
|||
RTLIL::State bit_b = b.isZero() ? undef_bit_pos_b >= 0 ? RTLIL::State::Sx : RTLIL::State::S0 : RTLIL::State::S1;
|
||||
RTLIL::Const result(logic_and(bit_a, bit_b));
|
||||
|
||||
while (int(result.bits.size()) < result_len)
|
||||
result.bits.push_back(RTLIL::State::S0);
|
||||
while (int(result.size()) < result_len)
|
||||
result.bits().push_back(RTLIL::State::S0);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -270,8 +270,8 @@ RTLIL::Const RTLIL::const_logic_or(const RTLIL::Const &arg1, const RTLIL::Const
|
|||
RTLIL::State bit_b = b.isZero() ? undef_bit_pos_b >= 0 ? RTLIL::State::Sx : RTLIL::State::S0 : RTLIL::State::S1;
|
||||
RTLIL::Const result(logic_or(bit_a, bit_b));
|
||||
|
||||
while (int(result.bits.size()) < result_len)
|
||||
result.bits.push_back(RTLIL::State::S0);
|
||||
while (int(result.size()) < result_len)
|
||||
result.bits().push_back(RTLIL::State::S0);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -286,7 +286,7 @@ static RTLIL::Const const_shift_worker(const RTLIL::Const &arg1, const RTLIL::Co
|
|||
BigInteger offset = const2big(arg2, signed2, undef_bit_pos) * direction;
|
||||
|
||||
if (result_len < 0)
|
||||
result_len = arg1.bits.size();
|
||||
result_len = arg1.size();
|
||||
|
||||
RTLIL::Const result(RTLIL::State::Sx, result_len);
|
||||
if (undef_bit_pos >= 0)
|
||||
|
|
@ -295,11 +295,11 @@ static RTLIL::Const const_shift_worker(const RTLIL::Const &arg1, const RTLIL::Co
|
|||
for (int i = 0; i < result_len; i++) {
|
||||
BigInteger pos = BigInteger(i) + offset;
|
||||
if (pos < 0)
|
||||
result.bits[i] = vacant_bits;
|
||||
else if (pos >= BigInteger(int(arg1.bits.size())))
|
||||
result.bits[i] = sign_ext ? arg1.bits.back() : vacant_bits;
|
||||
result.bits()[i] = vacant_bits;
|
||||
else if (pos >= BigInteger(int(arg1.size())))
|
||||
result.bits()[i] = sign_ext ? arg1.back() : vacant_bits;
|
||||
else
|
||||
result.bits[i] = arg1.bits[pos.toInt()];
|
||||
result.bits()[i] = arg1[pos.toInt()];
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
@ -347,8 +347,8 @@ RTLIL::Const RTLIL::const_lt(const RTLIL::Const &arg1, const RTLIL::Const &arg2,
|
|||
bool y = const2big(arg1, signed1, undef_bit_pos) < const2big(arg2, signed2, undef_bit_pos);
|
||||
RTLIL::Const result(undef_bit_pos >= 0 ? RTLIL::State::Sx : y ? RTLIL::State::S1 : RTLIL::State::S0);
|
||||
|
||||
while (int(result.bits.size()) < result_len)
|
||||
result.bits.push_back(RTLIL::State::S0);
|
||||
while (int(result.size()) < result_len)
|
||||
result.bits().push_back(RTLIL::State::S0);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -358,8 +358,8 @@ RTLIL::Const RTLIL::const_le(const RTLIL::Const &arg1, const RTLIL::Const &arg2,
|
|||
bool y = const2big(arg1, signed1, undef_bit_pos) <= const2big(arg2, signed2, undef_bit_pos);
|
||||
RTLIL::Const result(undef_bit_pos >= 0 ? RTLIL::State::Sx : y ? RTLIL::State::S1 : RTLIL::State::S0);
|
||||
|
||||
while (int(result.bits.size()) < result_len)
|
||||
result.bits.push_back(RTLIL::State::S0);
|
||||
while (int(result.size()) < result_len)
|
||||
result.bits().push_back(RTLIL::State::S0);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -369,31 +369,31 @@ RTLIL::Const RTLIL::const_eq(const RTLIL::Const &arg1, const RTLIL::Const &arg2,
|
|||
RTLIL::Const arg2_ext = arg2;
|
||||
RTLIL::Const result(RTLIL::State::S0, result_len);
|
||||
|
||||
int width = max(arg1_ext.bits.size(), arg2_ext.bits.size());
|
||||
int width = max(arg1_ext.size(), arg2_ext.size());
|
||||
extend_u0(arg1_ext, width, signed1 && signed2);
|
||||
extend_u0(arg2_ext, width, signed1 && signed2);
|
||||
|
||||
RTLIL::State matched_status = RTLIL::State::S1;
|
||||
for (size_t i = 0; i < arg1_ext.bits.size(); i++) {
|
||||
if (arg1_ext.bits.at(i) == RTLIL::State::S0 && arg2_ext.bits.at(i) == RTLIL::State::S1)
|
||||
for (size_t i = 0; i < arg1_ext.size(); i++) {
|
||||
if (arg1_ext.at(i) == RTLIL::State::S0 && arg2_ext.at(i) == RTLIL::State::S1)
|
||||
return result;
|
||||
if (arg1_ext.bits.at(i) == RTLIL::State::S1 && arg2_ext.bits.at(i) == RTLIL::State::S0)
|
||||
if (arg1_ext.at(i) == RTLIL::State::S1 && arg2_ext.at(i) == RTLIL::State::S0)
|
||||
return result;
|
||||
if (arg1_ext.bits.at(i) > RTLIL::State::S1 || arg2_ext.bits.at(i) > RTLIL::State::S1)
|
||||
if (arg1_ext.at(i) > RTLIL::State::S1 || arg2_ext.at(i) > RTLIL::State::S1)
|
||||
matched_status = RTLIL::State::Sx;
|
||||
}
|
||||
|
||||
result.bits.front() = matched_status;
|
||||
result.bits().front() = matched_status;
|
||||
return result;
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_ne(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
|
||||
{
|
||||
RTLIL::Const result = RTLIL::const_eq(arg1, arg2, signed1, signed2, result_len);
|
||||
if (result.bits.front() == RTLIL::State::S0)
|
||||
result.bits.front() = RTLIL::State::S1;
|
||||
else if (result.bits.front() == RTLIL::State::S1)
|
||||
result.bits.front() = RTLIL::State::S0;
|
||||
if (result.front() == RTLIL::State::S0)
|
||||
result.bits().front() = RTLIL::State::S1;
|
||||
else if (result.front() == RTLIL::State::S1)
|
||||
result.bits().front() = RTLIL::State::S0;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -403,26 +403,26 @@ RTLIL::Const RTLIL::const_eqx(const RTLIL::Const &arg1, const RTLIL::Const &arg2
|
|||
RTLIL::Const arg2_ext = arg2;
|
||||
RTLIL::Const result(RTLIL::State::S0, result_len);
|
||||
|
||||
int width = max(arg1_ext.bits.size(), arg2_ext.bits.size());
|
||||
int width = max(arg1_ext.size(), arg2_ext.size());
|
||||
extend_u0(arg1_ext, width, signed1 && signed2);
|
||||
extend_u0(arg2_ext, width, signed1 && signed2);
|
||||
|
||||
for (size_t i = 0; i < arg1_ext.bits.size(); i++) {
|
||||
if (arg1_ext.bits.at(i) != arg2_ext.bits.at(i))
|
||||
for (size_t i = 0; i < arg1_ext.size(); i++) {
|
||||
if (arg1_ext.at(i) != arg2_ext.at(i))
|
||||
return result;
|
||||
}
|
||||
|
||||
result.bits.front() = RTLIL::State::S1;
|
||||
result.bits().front() = RTLIL::State::S1;
|
||||
return result;
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_nex(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
|
||||
{
|
||||
RTLIL::Const result = RTLIL::const_eqx(arg1, arg2, signed1, signed2, result_len);
|
||||
if (result.bits.front() == RTLIL::State::S0)
|
||||
result.bits.front() = RTLIL::State::S1;
|
||||
else if (result.bits.front() == RTLIL::State::S1)
|
||||
result.bits.front() = RTLIL::State::S0;
|
||||
if (result.front() == RTLIL::State::S0)
|
||||
result.bits().front() = RTLIL::State::S1;
|
||||
else if (result.front() == RTLIL::State::S1)
|
||||
result.bits().front() = RTLIL::State::S0;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -432,8 +432,8 @@ RTLIL::Const RTLIL::const_ge(const RTLIL::Const &arg1, const RTLIL::Const &arg2,
|
|||
bool y = const2big(arg1, signed1, undef_bit_pos) >= const2big(arg2, signed2, undef_bit_pos);
|
||||
RTLIL::Const result(undef_bit_pos >= 0 ? RTLIL::State::Sx : y ? RTLIL::State::S1 : RTLIL::State::S0);
|
||||
|
||||
while (int(result.bits.size()) < result_len)
|
||||
result.bits.push_back(RTLIL::State::S0);
|
||||
while (int(result.size()) < result_len)
|
||||
result.bits().push_back(RTLIL::State::S0);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -443,8 +443,8 @@ RTLIL::Const RTLIL::const_gt(const RTLIL::Const &arg1, const RTLIL::Const &arg2,
|
|||
bool y = const2big(arg1, signed1, undef_bit_pos) > const2big(arg2, signed2, undef_bit_pos);
|
||||
RTLIL::Const result(undef_bit_pos >= 0 ? RTLIL::State::Sx : y ? RTLIL::State::S1 : RTLIL::State::S0);
|
||||
|
||||
while (int(result.bits.size()) < result_len)
|
||||
result.bits.push_back(RTLIL::State::S0);
|
||||
while (int(result.size()) < result_len)
|
||||
result.bits().push_back(RTLIL::State::S0);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -452,21 +452,21 @@ RTLIL::Const RTLIL::const_add(const RTLIL::Const &arg1, const RTLIL::Const &arg2
|
|||
{
|
||||
int undef_bit_pos = -1;
|
||||
BigInteger y = const2big(arg1, signed1, undef_bit_pos) + const2big(arg2, signed2, undef_bit_pos);
|
||||
return big2const(y, result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), undef_bit_pos);
|
||||
return big2const(y, result_len >= 0 ? result_len : max(arg1.size(), arg2.size()), undef_bit_pos);
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_sub(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
|
||||
{
|
||||
int undef_bit_pos = -1;
|
||||
BigInteger y = const2big(arg1, signed1, undef_bit_pos) - const2big(arg2, signed2, undef_bit_pos);
|
||||
return big2const(y, result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), undef_bit_pos);
|
||||
return big2const(y, result_len >= 0 ? result_len : max(arg1.size(), arg2.size()), undef_bit_pos);
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_mul(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
|
||||
{
|
||||
int undef_bit_pos = -1;
|
||||
BigInteger y = const2big(arg1, signed1, undef_bit_pos) * const2big(arg2, signed2, undef_bit_pos);
|
||||
return big2const(y, result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), min(undef_bit_pos, 0));
|
||||
return big2const(y, result_len >= 0 ? result_len : max(arg1.size(), arg2.size()), min(undef_bit_pos, 0));
|
||||
}
|
||||
|
||||
// truncating division
|
||||
|
|
@ -480,7 +480,7 @@ RTLIL::Const RTLIL::const_div(const RTLIL::Const &arg1, const RTLIL::Const &arg2
|
|||
bool result_neg = (a.getSign() == BigInteger::negative) != (b.getSign() == BigInteger::negative);
|
||||
a = a.getSign() == BigInteger::negative ? -a : a;
|
||||
b = b.getSign() == BigInteger::negative ? -b : b;
|
||||
return big2const(result_neg ? -(a / b) : (a / b), result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), min(undef_bit_pos, 0));
|
||||
return big2const(result_neg ? -(a / b) : (a / b), result_len >= 0 ? result_len : max(arg1.size(), arg2.size()), min(undef_bit_pos, 0));
|
||||
}
|
||||
|
||||
// truncating modulo
|
||||
|
|
@ -494,7 +494,7 @@ RTLIL::Const RTLIL::const_mod(const RTLIL::Const &arg1, const RTLIL::Const &arg2
|
|||
bool result_neg = a.getSign() == BigInteger::negative;
|
||||
a = a.getSign() == BigInteger::negative ? -a : a;
|
||||
b = b.getSign() == BigInteger::negative ? -b : b;
|
||||
return big2const(result_neg ? -(a % b) : (a % b), result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), min(undef_bit_pos, 0));
|
||||
return big2const(result_neg ? -(a % b) : (a % b), result_len >= 0 ? result_len : max(arg1.size(), arg2.size()), min(undef_bit_pos, 0));
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_divfloor(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
|
||||
|
|
@ -516,7 +516,7 @@ RTLIL::Const RTLIL::const_divfloor(const RTLIL::Const &arg1, const RTLIL::Const
|
|||
// bigint division with negative numbers is wonky, make sure we only negate at the very end
|
||||
result = -((a + b - 1) / b);
|
||||
}
|
||||
return big2const(result, result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), min(undef_bit_pos, 0));
|
||||
return big2const(result, result_len >= 0 ? result_len : max(arg1.size(), arg2.size()), min(undef_bit_pos, 0));
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_modfloor(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
|
||||
|
|
@ -539,7 +539,7 @@ RTLIL::Const RTLIL::const_modfloor(const RTLIL::Const &arg1, const RTLIL::Const
|
|||
} else {
|
||||
modulo = b_sign == BigInteger::negative ? truncated - b : truncated + b;
|
||||
}
|
||||
return big2const(modulo, result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), min(undef_bit_pos, 0));
|
||||
return big2const(modulo, result_len >= 0 ? result_len : max(arg1.size(), arg2.size()), min(undef_bit_pos, 0));
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_pow(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
|
||||
|
|
@ -590,7 +590,7 @@ RTLIL::Const RTLIL::const_pow(const RTLIL::Const &arg1, const RTLIL::Const &arg2
|
|||
y *= -1;
|
||||
}
|
||||
|
||||
return big2const(y, result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), min(undef_bit_pos, 0));
|
||||
return big2const(y, result_len >= 0 ? result_len : max(arg1.size(), arg2.size()), min(undef_bit_pos, 0));
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_pos(const RTLIL::Const &arg1, const RTLIL::Const&, bool signed1, bool, int result_len)
|
||||
|
|
@ -601,6 +601,14 @@ RTLIL::Const RTLIL::const_pos(const RTLIL::Const &arg1, const RTLIL::Const&, boo
|
|||
return arg1_ext;
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_buf(const RTLIL::Const &arg1, const RTLIL::Const&, bool signed1, bool, int result_len)
|
||||
{
|
||||
RTLIL::Const arg1_ext = arg1;
|
||||
extend_u0(arg1_ext, result_len, signed1);
|
||||
|
||||
return arg1_ext;
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_neg(const RTLIL::Const &arg1, const RTLIL::Const&, bool signed1, bool, int result_len)
|
||||
{
|
||||
RTLIL::Const arg1_ext = arg1;
|
||||
|
|
@ -620,7 +628,7 @@ RTLIL::Const RTLIL::const_mux(const RTLIL::Const &arg1, const RTLIL::Const &arg2
|
|||
RTLIL::Const ret = arg1;
|
||||
for (int i = 0; i < ret.size(); i++)
|
||||
if (ret[i] != arg2[i])
|
||||
ret[i] = State::Sx;
|
||||
ret.bits()[i] = State::Sx;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -634,18 +642,18 @@ RTLIL::Const RTLIL::const_pmux(const RTLIL::Const &arg1, const RTLIL::Const &arg
|
|||
|
||||
for (int i = 0; i < arg3.size(); i++)
|
||||
if (arg3[i] == State::S1)
|
||||
return RTLIL::Const(std::vector<RTLIL::State>(arg2.bits.begin() + i*arg1.bits.size(), arg2.bits.begin() + (i+1)*arg1.bits.size()));
|
||||
return RTLIL::Const(std::vector<RTLIL::State>(arg2.begin() + i*arg1.size(), arg2.begin() + (i+1)*arg1.size()));
|
||||
|
||||
log_abort(); // unreachable
|
||||
}
|
||||
|
||||
RTLIL::Const RTLIL::const_bmux(const RTLIL::Const &arg1, const RTLIL::Const &arg2)
|
||||
{
|
||||
std::vector<RTLIL::State> t = arg1.bits;
|
||||
std::vector<State> t = arg1.to_bits();
|
||||
|
||||
for (int i = GetSize(arg2)-1; i >= 0; i--)
|
||||
{
|
||||
RTLIL::State sel = arg2.bits.at(i);
|
||||
RTLIL::State sel = arg2.at(i);
|
||||
std::vector<RTLIL::State> new_t;
|
||||
if (sel == State::S0)
|
||||
new_t = std::vector<RTLIL::State>(t.begin(), t.begin() + GetSize(t)/2);
|
||||
|
|
@ -681,10 +689,10 @@ RTLIL::Const RTLIL::const_demux(const RTLIL::Const &arg1, const RTLIL::Const &ar
|
|||
res.push_back(State::S0);
|
||||
} else if (x) {
|
||||
for (int j = 0; j < width; j++)
|
||||
res.push_back(arg1.bits[j] == State::S0 ? State::S0 : State::Sx);
|
||||
res.push_back(arg1[j] == State::S0 ? State::S0 : State::Sx);
|
||||
} else {
|
||||
for (int j = 0; j < width; j++)
|
||||
res.push_back(arg1.bits[j]);
|
||||
res.push_back(arg1[j]);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
|
|
@ -695,7 +703,7 @@ RTLIL::Const RTLIL::const_bweqx(const RTLIL::Const &arg1, const RTLIL::Const &ar
|
|||
log_assert(arg2.size() == arg1.size());
|
||||
RTLIL::Const result(RTLIL::State::S0, arg1.size());
|
||||
for (int i = 0; i < arg1.size(); i++)
|
||||
result[i] = arg1[i] == arg2[i] ? State::S1 : State::S0;
|
||||
result.bits()[i] = arg1[i] == arg2[i] ? State::S1 : State::S0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
@ -707,7 +715,7 @@ RTLIL::Const RTLIL::const_bwmux(const RTLIL::Const &arg1, const RTLIL::Const &ar
|
|||
RTLIL::Const result(RTLIL::State::Sx, arg1.size());
|
||||
for (int i = 0; i < arg1.size(); i++) {
|
||||
if (arg3[i] != State::Sx || arg1[i] == arg2[i])
|
||||
result[i] = arg3[i] == State::S1 ? arg2[i] : arg1[i];
|
||||
result.bits()[i] = arg3[i] == State::S1 ? arg2[i] : arg1[i];
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -290,7 +290,7 @@ Aig::Aig(Cell *cell)
|
|||
}
|
||||
}
|
||||
|
||||
if (cell->type.in(ID($not), ID($_NOT_), ID($pos), ID($_BUF_)))
|
||||
if (cell->type.in(ID($not), ID($_NOT_), ID($pos), ID($buf), ID($_BUF_)))
|
||||
{
|
||||
for (int i = 0; i < GetSize(cell->getPort(ID::Y)); i++) {
|
||||
int A = mk.inport(ID::A, i);
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
|
||||
void bitwise_unary_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell)
|
||||
{
|
||||
bool is_signed = cell->getParam(ID::A_SIGNED).as_bool();
|
||||
bool is_signed = (cell->type != ID($buf)) && cell->getParam(ID::A_SIGNED).as_bool();
|
||||
int a_width = GetSize(cell->getPort(ID::A));
|
||||
int y_width = GetSize(cell->getPort(ID::Y));
|
||||
|
||||
|
|
@ -392,7 +392,7 @@ PRIVATE_NAMESPACE_END
|
|||
|
||||
bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL::Cell *cell)
|
||||
{
|
||||
if (cell->type.in(ID($not), ID($pos))) {
|
||||
if (cell->type.in(ID($not), ID($pos), ID($buf))) {
|
||||
bitwise_unary_op(this, cell);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ struct CellTypes
|
|||
void setup_internals_eval()
|
||||
{
|
||||
std::vector<RTLIL::IdString> unary_ops = {
|
||||
ID($not), ID($pos), ID($neg),
|
||||
ID($not), ID($pos), ID($buf), ID($neg),
|
||||
ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool),
|
||||
ID($logic_not), ID($slice), ID($lut), ID($sop)
|
||||
};
|
||||
|
|
@ -325,7 +325,7 @@ struct CellTypes
|
|||
|
||||
static RTLIL::Const eval_not(RTLIL::Const v)
|
||||
{
|
||||
for (auto &bit : v.bits)
|
||||
for (auto &bit : v.bits())
|
||||
if (bit == State::S0) bit = State::S1;
|
||||
else if (bit == State::S1) bit = State::S0;
|
||||
return v;
|
||||
|
|
@ -339,7 +339,7 @@ struct CellTypes
|
|||
type = ID($shl);
|
||||
|
||||
if (type != ID($sshr) && type != ID($sshl) && type != ID($shr) && type != ID($shl) && type != ID($shift) && type != ID($shiftx) &&
|
||||
type != ID($pos) && type != ID($neg) && type != ID($not)) {
|
||||
type != ID($pos) && type != ID($buf) && type != ID($neg) && type != ID($not)) {
|
||||
if (!signed1 || !signed2)
|
||||
signed1 = false, signed2 = false;
|
||||
}
|
||||
|
|
@ -384,7 +384,7 @@ struct CellTypes
|
|||
HANDLE_CELL_TYPE(neg)
|
||||
#undef HANDLE_CELL_TYPE
|
||||
|
||||
if (type == ID($_BUF_))
|
||||
if (type.in(ID($_BUF_), ID($buf)))
|
||||
return arg1;
|
||||
if (type == ID($_NOT_))
|
||||
return eval_not(arg1);
|
||||
|
|
@ -419,13 +419,13 @@ struct CellTypes
|
|||
RTLIL::Const ret;
|
||||
int width = cell->parameters.at(ID::Y_WIDTH).as_int();
|
||||
int offset = cell->parameters.at(ID::OFFSET).as_int();
|
||||
ret.bits.insert(ret.bits.end(), arg1.bits.begin()+offset, arg1.bits.begin()+offset+width);
|
||||
ret.bits().insert(ret.bits().end(), arg1.begin()+offset, arg1.begin()+offset+width);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (cell->type == ID($concat)) {
|
||||
RTLIL::Const ret = arg1;
|
||||
ret.bits.insert(ret.bits.end(), arg2.bits.begin(), arg2.bits.end());
|
||||
ret.bits().insert(ret.bits().end(), arg2.begin(), arg2.end());
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -448,7 +448,7 @@ struct CellTypes
|
|||
{
|
||||
int width = cell->parameters.at(ID::WIDTH).as_int();
|
||||
|
||||
std::vector<RTLIL::State> t = cell->parameters.at(ID::LUT).bits;
|
||||
std::vector<RTLIL::State> t = cell->parameters.at(ID::LUT).to_bits();
|
||||
while (GetSize(t) < (1 << width))
|
||||
t.push_back(State::S0);
|
||||
t.resize(1 << width);
|
||||
|
|
@ -460,7 +460,7 @@ struct CellTypes
|
|||
{
|
||||
int width = cell->parameters.at(ID::WIDTH).as_int();
|
||||
int depth = cell->parameters.at(ID::DEPTH).as_int();
|
||||
std::vector<RTLIL::State> t = cell->parameters.at(ID::TABLE).bits;
|
||||
std::vector<RTLIL::State> t = cell->parameters.at(ID::TABLE).to_bits();
|
||||
|
||||
while (GetSize(t) < width*depth*2)
|
||||
t.push_back(State::S0);
|
||||
|
|
@ -473,7 +473,7 @@ struct CellTypes
|
|||
bool match_x = true;
|
||||
|
||||
for (int j = 0; j < width; j++) {
|
||||
RTLIL::State a = arg1.bits.at(j);
|
||||
RTLIL::State a = arg1.at(j);
|
||||
if (t.at(2*width*i + 2*j + 0) == State::S1) {
|
||||
if (a == State::S1) match_x = false;
|
||||
if (a != State::S0) match = false;
|
||||
|
|
@ -513,7 +513,7 @@ struct CellTypes
|
|||
if (cell->type == ID($_OAI3_))
|
||||
return eval_not(const_and(const_or(arg1, arg2, false, false, 1), arg3, false, false, 1));
|
||||
|
||||
log_assert(arg3.bits.size() == 0);
|
||||
log_assert(arg3.size() == 0);
|
||||
return eval(cell, arg1, arg2, errp);
|
||||
}
|
||||
|
||||
|
|
@ -524,7 +524,7 @@ struct CellTypes
|
|||
if (cell->type == ID($_OAI4_))
|
||||
return eval_not(const_and(const_or(arg1, arg2, false, false, 1), const_or(arg3, arg4, false, false, 1), false, false, 1));
|
||||
|
||||
log_assert(arg4.bits.size() == 0);
|
||||
log_assert(arg4.size() == 0);
|
||||
return eval(cell, arg1, arg2, arg3, errp);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
403
kernel/compute_graph.h
Normal file
403
kernel/compute_graph.h
Normal file
|
|
@ -0,0 +1,403 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2024 Jannis Harder <jix@yosyshq.com> <me@jix.one>
|
||||
*
|
||||
* 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 COMPUTE_GRAPH_H
|
||||
#define COMPUTE_GRAPH_H
|
||||
|
||||
#include <tuple>
|
||||
#include "kernel/yosys.h"
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
template<
|
||||
typename Fn, // Function type (deduplicated across whole graph)
|
||||
typename Attr = std::tuple<>, // Call attributes (present in every node)
|
||||
typename SparseAttr = std::tuple<>, // Sparse call attributes (optional per node)
|
||||
typename Key = std::tuple<> // Stable keys to refer to nodes
|
||||
>
|
||||
struct ComputeGraph
|
||||
{
|
||||
struct Ref;
|
||||
private:
|
||||
|
||||
// Functions are deduplicated by assigning unique ids
|
||||
idict<Fn> functions;
|
||||
|
||||
struct Node {
|
||||
int fn_index;
|
||||
int arg_offset;
|
||||
int arg_count;
|
||||
Attr attr;
|
||||
|
||||
Node(int fn_index, Attr &&attr, int arg_offset, int arg_count = 0)
|
||||
: fn_index(fn_index), arg_offset(arg_offset), arg_count(arg_count), attr(std::move(attr)) {}
|
||||
|
||||
Node(int fn_index, Attr const &attr, int arg_offset, int arg_count = 0)
|
||||
: fn_index(fn_index), arg_offset(arg_offset), arg_count(arg_count), attr(attr) {}
|
||||
};
|
||||
|
||||
|
||||
std::vector<Node> nodes;
|
||||
std::vector<int> args;
|
||||
dict<Key, int> keys_;
|
||||
dict<int, SparseAttr> sparse_attrs;
|
||||
|
||||
public:
|
||||
template<typename Graph>
|
||||
struct BaseRef
|
||||
{
|
||||
protected:
|
||||
friend struct ComputeGraph;
|
||||
Graph *graph_;
|
||||
int index_;
|
||||
BaseRef(Graph *graph, int index) : graph_(graph), index_(index) {
|
||||
log_assert(index_ >= 0);
|
||||
check();
|
||||
}
|
||||
|
||||
void check() const { log_assert(index_ < graph_->size()); }
|
||||
|
||||
Node const &deref() const { check(); return graph_->nodes[index_]; }
|
||||
|
||||
public:
|
||||
ComputeGraph const &graph() const { return graph_; }
|
||||
int index() const { return index_; }
|
||||
|
||||
int size() const { return deref().arg_count; }
|
||||
|
||||
BaseRef arg(int n) const
|
||||
{
|
||||
Node const &node = deref();
|
||||
log_assert(n >= 0 && n < node.arg_count);
|
||||
return BaseRef(graph_, graph_->args[node.arg_offset + n]);
|
||||
}
|
||||
|
||||
std::vector<int>::const_iterator arg_indices_cbegin() const
|
||||
{
|
||||
Node const &node = deref();
|
||||
return graph_->args.cbegin() + node.arg_offset;
|
||||
}
|
||||
|
||||
std::vector<int>::const_iterator arg_indices_cend() const
|
||||
{
|
||||
Node const &node = deref();
|
||||
return graph_->args.cbegin() + node.arg_offset + node.arg_count;
|
||||
}
|
||||
|
||||
Fn const &function() const { return graph_->functions[deref().fn_index]; }
|
||||
Attr const &attr() const { return deref().attr; }
|
||||
|
||||
bool has_sparse_attr() const { return graph_->sparse_attrs.count(index_); }
|
||||
|
||||
SparseAttr const &sparse_attr() const
|
||||
{
|
||||
auto found = graph_->sparse_attrs.find(index_);
|
||||
log_assert(found != graph_->sparse_attrs.end());
|
||||
return found->second;
|
||||
}
|
||||
};
|
||||
|
||||
using ConstRef = BaseRef<ComputeGraph const>;
|
||||
|
||||
struct Ref : public BaseRef<ComputeGraph>
|
||||
{
|
||||
private:
|
||||
friend struct ComputeGraph;
|
||||
Ref(ComputeGraph *graph, int index) : BaseRef<ComputeGraph>(graph, index) {}
|
||||
Node &deref() const { this->check(); return this->graph_->nodes[this->index_]; }
|
||||
|
||||
public:
|
||||
Ref(BaseRef<ComputeGraph> ref) : Ref(ref.graph_, ref.index_) {}
|
||||
|
||||
void set_function(Fn const &function) const
|
||||
{
|
||||
deref().fn_index = this->graph_->functions(function);
|
||||
}
|
||||
|
||||
Attr &attr() const { return deref().attr; }
|
||||
|
||||
void append_arg(ConstRef arg) const
|
||||
{
|
||||
log_assert(arg.graph_ == this->graph_);
|
||||
append_arg(arg.index());
|
||||
}
|
||||
|
||||
void append_arg(int arg) const
|
||||
{
|
||||
log_assert(arg >= 0 && arg < this->graph_->size());
|
||||
Node &node = deref();
|
||||
if (node.arg_offset + node.arg_count != GetSize(this->graph_->args))
|
||||
move_args(node);
|
||||
this->graph_->args.push_back(arg);
|
||||
node.arg_count++;
|
||||
}
|
||||
|
||||
operator ConstRef() const
|
||||
{
|
||||
return ConstRef(this->graph_, this->index_);
|
||||
}
|
||||
|
||||
SparseAttr &sparse_attr() const
|
||||
{
|
||||
return this->graph_->sparse_attrs[this->index_];
|
||||
}
|
||||
|
||||
void clear_sparse_attr() const
|
||||
{
|
||||
this->graph_->sparse_attrs.erase(this->index_);
|
||||
}
|
||||
|
||||
void assign_key(Key const &key) const
|
||||
{
|
||||
this->graph_->keys_.emplace(key, this->index_);
|
||||
}
|
||||
|
||||
private:
|
||||
void move_args(Node &node) const
|
||||
{
|
||||
auto &args = this->graph_->args;
|
||||
int old_offset = node.arg_offset;
|
||||
node.arg_offset = GetSize(args);
|
||||
for (int i = 0; i != node.arg_count; ++i)
|
||||
args.push_back(args[old_offset + i]);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
bool has_key(Key const &key) const
|
||||
{
|
||||
return keys_.count(key);
|
||||
}
|
||||
|
||||
dict<Key, int> const &keys() const
|
||||
{
|
||||
return keys_;
|
||||
}
|
||||
|
||||
ConstRef operator()(Key const &key) const
|
||||
{
|
||||
auto it = keys_.find(key);
|
||||
log_assert(it != keys_.end());
|
||||
return (*this)[it->second];
|
||||
}
|
||||
|
||||
Ref operator()(Key const &key)
|
||||
{
|
||||
auto it = keys_.find(key);
|
||||
log_assert(it != keys_.end());
|
||||
return (*this)[it->second];
|
||||
}
|
||||
|
||||
int size() const { return GetSize(nodes); }
|
||||
|
||||
ConstRef operator[](int index) const { return ConstRef(this, index); }
|
||||
Ref operator[](int index) { return Ref(this, index); }
|
||||
|
||||
Ref add(Fn const &function, Attr &&attr)
|
||||
{
|
||||
int index = GetSize(nodes);
|
||||
int fn_index = functions(function);
|
||||
nodes.emplace_back(fn_index, std::move(attr), GetSize(args));
|
||||
return Ref(this, index);
|
||||
}
|
||||
|
||||
Ref add(Fn const &function, Attr const &attr)
|
||||
{
|
||||
int index = GetSize(nodes);
|
||||
int fn_index = functions(function);
|
||||
nodes.emplace_back(fn_index, attr, GetSize(args));
|
||||
return Ref(this, index);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Ref add(Fn const &function, Attr const &attr, T &&args)
|
||||
{
|
||||
Ref added = add(function, attr);
|
||||
for (auto arg : args)
|
||||
added.append_arg(arg);
|
||||
return added;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Ref add(Fn const &function, Attr &&attr, T &&args)
|
||||
{
|
||||
Ref added = add(function, std::move(attr));
|
||||
for (auto arg : args)
|
||||
added.append_arg(arg);
|
||||
return added;
|
||||
}
|
||||
|
||||
Ref add(Fn const &function, Attr const &attr, std::initializer_list<Ref> args)
|
||||
{
|
||||
Ref added = add(function, attr);
|
||||
for (auto arg : args)
|
||||
added.append_arg(arg);
|
||||
return added;
|
||||
}
|
||||
|
||||
Ref add(Fn const &function, Attr &&attr, std::initializer_list<Ref> args)
|
||||
{
|
||||
Ref added = add(function, std::move(attr));
|
||||
for (auto arg : args)
|
||||
added.append_arg(arg);
|
||||
return added;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Ref add(Fn const &function, Attr const &attr, T begin, T end)
|
||||
{
|
||||
Ref added = add(function, attr);
|
||||
for (; begin != end; ++begin)
|
||||
added.append_arg(*begin);
|
||||
return added;
|
||||
}
|
||||
|
||||
void compact_args()
|
||||
{
|
||||
std::vector<int> new_args;
|
||||
for (auto &node : nodes)
|
||||
{
|
||||
int new_offset = GetSize(new_args);
|
||||
for (int i = 0; i < node.arg_count; i++)
|
||||
new_args.push_back(args[node.arg_offset + i]);
|
||||
node.arg_offset = new_offset;
|
||||
}
|
||||
std::swap(args, new_args);
|
||||
}
|
||||
|
||||
void permute(std::vector<int> const &perm)
|
||||
{
|
||||
log_assert(perm.size() <= nodes.size());
|
||||
std::vector<int> inv_perm;
|
||||
inv_perm.resize(nodes.size(), -1);
|
||||
for (int i = 0; i < GetSize(perm); ++i)
|
||||
{
|
||||
int j = perm[i];
|
||||
log_assert(j >= 0 && j < GetSize(nodes));
|
||||
log_assert(inv_perm[j] == -1);
|
||||
inv_perm[j] = i;
|
||||
}
|
||||
permute(perm, inv_perm);
|
||||
}
|
||||
|
||||
void permute(std::vector<int> const &perm, std::vector<int> const &inv_perm)
|
||||
{
|
||||
log_assert(inv_perm.size() == nodes.size());
|
||||
std::vector<Node> new_nodes;
|
||||
new_nodes.reserve(perm.size());
|
||||
dict<int, SparseAttr> new_sparse_attrs;
|
||||
for (int i : perm)
|
||||
{
|
||||
int j = GetSize(new_nodes);
|
||||
new_nodes.emplace_back(std::move(nodes[i]));
|
||||
auto found = sparse_attrs.find(i);
|
||||
if (found != sparse_attrs.end())
|
||||
new_sparse_attrs.emplace(j, std::move(found->second));
|
||||
}
|
||||
|
||||
std::swap(nodes, new_nodes);
|
||||
std::swap(sparse_attrs, new_sparse_attrs);
|
||||
|
||||
compact_args();
|
||||
for (int &arg : args)
|
||||
{
|
||||
log_assert(arg < GetSize(inv_perm));
|
||||
log_assert(inv_perm[arg] >= 0);
|
||||
arg = inv_perm[arg];
|
||||
}
|
||||
|
||||
for (auto &key : keys_)
|
||||
{
|
||||
log_assert(key.second < GetSize(inv_perm));
|
||||
log_assert(inv_perm[key.second] >= 0);
|
||||
key.second = inv_perm[key.second];
|
||||
}
|
||||
}
|
||||
|
||||
struct SccAdaptor
|
||||
{
|
||||
private:
|
||||
ComputeGraph const &graph_;
|
||||
std::vector<int> indices_;
|
||||
public:
|
||||
SccAdaptor(ComputeGraph const &graph) : graph_(graph)
|
||||
{
|
||||
indices_.resize(graph.size(), -1);
|
||||
}
|
||||
|
||||
|
||||
typedef int node_type;
|
||||
|
||||
struct node_enumerator {
|
||||
private:
|
||||
friend struct SccAdaptor;
|
||||
int current, end;
|
||||
node_enumerator(int current, int end) : current(current), end(end) {}
|
||||
|
||||
public:
|
||||
|
||||
bool finished() const { return current == end; }
|
||||
node_type next() {
|
||||
log_assert(!finished());
|
||||
node_type result = current;
|
||||
++current;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
node_enumerator enumerate_nodes() {
|
||||
return node_enumerator(0, GetSize(indices_));
|
||||
}
|
||||
|
||||
|
||||
struct successor_enumerator {
|
||||
private:
|
||||
friend struct SccAdaptor;
|
||||
std::vector<int>::const_iterator current, end;
|
||||
successor_enumerator(std::vector<int>::const_iterator current, std::vector<int>::const_iterator end) :
|
||||
current(current), end(end) {}
|
||||
|
||||
public:
|
||||
bool finished() const { return current == end; }
|
||||
node_type next() {
|
||||
log_assert(!finished());
|
||||
node_type result = *current;
|
||||
++current;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
successor_enumerator enumerate_successors(int index) const {
|
||||
auto const &ref = graph_[index];
|
||||
return successor_enumerator(ref.arg_indices_cbegin(), ref.arg_indices_cend());
|
||||
}
|
||||
|
||||
int &dfs_index(node_type const &node) { return indices_[node]; }
|
||||
|
||||
std::vector<int> const &dfs_indices() { return indices_; }
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -76,7 +76,7 @@ struct ConstEval
|
|||
#ifndef NDEBUG
|
||||
RTLIL::SigSpec current_val = values_map(sig);
|
||||
for (int i = 0; i < GetSize(current_val); i++)
|
||||
log_assert(current_val[i].wire != NULL || current_val[i] == value.bits[i]);
|
||||
log_assert(current_val[i].wire != NULL || current_val[i] == value[i]);
|
||||
#endif
|
||||
values_map.add(sig, RTLIL::SigSpec(value));
|
||||
}
|
||||
|
|
@ -115,7 +115,7 @@ struct ConstEval
|
|||
|
||||
for (int i = 0; i < GetSize(coval); i++) {
|
||||
carry = (sig_g[i] == State::S1) || (sig_p[i] == RTLIL::S1 && carry);
|
||||
coval.bits[i] = carry ? State::S1 : State::S0;
|
||||
coval.bits()[i] = carry ? State::S1 : State::S0;
|
||||
}
|
||||
|
||||
set(sig_co, coval);
|
||||
|
|
@ -153,7 +153,7 @@ struct ConstEval
|
|||
|
||||
for (int i = 0; i < sig_s.size(); i++)
|
||||
{
|
||||
RTLIL::State s_bit = sig_s.extract(i, 1).as_const().bits.at(0);
|
||||
RTLIL::State s_bit = sig_s.extract(i, 1).as_const().at(0);
|
||||
RTLIL::SigSpec b_slice = sig_b.extract(sig_y.size()*i, sig_y.size());
|
||||
|
||||
if (s_bit == RTLIL::State::Sx || s_bit == RTLIL::State::S1)
|
||||
|
|
@ -180,10 +180,10 @@ struct ConstEval
|
|||
|
||||
if (y_values.size() > 1)
|
||||
{
|
||||
std::vector<RTLIL::State> master_bits = y_values.at(0).bits;
|
||||
std::vector<RTLIL::State> master_bits = y_values.at(0).to_bits();
|
||||
|
||||
for (size_t i = 1; i < y_values.size(); i++) {
|
||||
std::vector<RTLIL::State> &slave_bits = y_values.at(i).bits;
|
||||
std::vector<RTLIL::State> slave_bits = y_values.at(i).to_bits();
|
||||
log_assert(master_bits.size() == slave_bits.size());
|
||||
for (size_t j = 0; j < master_bits.size(); j++)
|
||||
if (master_bits[j] != slave_bits[j])
|
||||
|
|
@ -248,8 +248,8 @@ struct ConstEval
|
|||
RTLIL::Const val_x = const_or(t2, t3, false, false, width);
|
||||
|
||||
for (int i = 0; i < GetSize(val_y); i++)
|
||||
if (val_y.bits[i] == RTLIL::Sx)
|
||||
val_x.bits[i] = RTLIL::Sx;
|
||||
if (val_y[i] == RTLIL::Sx)
|
||||
val_x.bits()[i] = RTLIL::Sx;
|
||||
|
||||
set(sig_y, val_y);
|
||||
set(sig_x, val_x);
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ X(CE_OVER_SRST)
|
|||
X(CFG_ABITS)
|
||||
X(CFG_DBITS)
|
||||
X(CFG_INIT)
|
||||
X(chain)
|
||||
X(CI)
|
||||
X(CLK)
|
||||
X(clkbuf_driver)
|
||||
|
|
|
|||
562
kernel/driver.cc
562
kernel/driver.cc
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
#include "kernel/yosys.h"
|
||||
#include "libs/sha1/sha1.h"
|
||||
#include "libs/cxxopts/include/cxxopts.hpp"
|
||||
#include <iostream>
|
||||
|
||||
#ifdef YOSYS_ENABLE_READLINE
|
||||
# include <readline/readline.h>
|
||||
|
|
@ -55,55 +57,6 @@
|
|||
|
||||
USING_YOSYS_NAMESPACE
|
||||
|
||||
char *optarg;
|
||||
int optind = 1, optcur = 1, optopt = 0;
|
||||
int getopt(int argc, char **argv, const char *optstring)
|
||||
{
|
||||
if (optind >= argc)
|
||||
return -1;
|
||||
|
||||
if (argv[optind][0] != '-' || argv[optind][1] == 0) {
|
||||
optopt = 1;
|
||||
optarg = argv[optind++];
|
||||
return optopt;
|
||||
}
|
||||
|
||||
bool takes_arg = false;
|
||||
optopt = argv[optind][optcur];
|
||||
|
||||
if (optopt == '-') {
|
||||
++optind;
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int i = 0; optstring[i]; i++)
|
||||
if (optopt == optstring[i] && optstring[i + 1] == ':')
|
||||
takes_arg = true;
|
||||
|
||||
if (!takes_arg) {
|
||||
if (argv[optind][++optcur] == 0)
|
||||
optind++, optcur = 1;
|
||||
return optopt;
|
||||
}
|
||||
|
||||
if (argv[optind][++optcur]) {
|
||||
optarg = argv[optind++] + optcur;
|
||||
optcur = 1;
|
||||
return optopt;
|
||||
}
|
||||
|
||||
if (++optind >= argc) {
|
||||
fprintf(stderr, "%s: option '-%c' expects an argument\n", argv[0], optopt);
|
||||
optopt = '?';
|
||||
return optopt;
|
||||
}
|
||||
|
||||
optarg = argv[optind];
|
||||
optind++, optcur = 1;
|
||||
|
||||
return optopt;
|
||||
}
|
||||
|
||||
#ifdef EMSCRIPTEN
|
||||
# include <sys/stat.h>
|
||||
# include <sys/types.h>
|
||||
|
|
@ -235,12 +188,14 @@ int main(int argc, char **argv)
|
|||
std::vector<std::string> passes_commands;
|
||||
std::vector<std::string> frontend_files;
|
||||
std::vector<std::string> plugin_filenames;
|
||||
std::vector<std::string> special_args;
|
||||
std::string output_filename = "";
|
||||
std::string scriptfile = "";
|
||||
std::string depsfile = "";
|
||||
std::string topmodule = "";
|
||||
std::string perffile = "";
|
||||
bool scriptfile_tcl = false;
|
||||
bool scriptfile_python = false;
|
||||
bool print_banner = true;
|
||||
bool print_stats = true;
|
||||
bool call_abort = false;
|
||||
|
|
@ -250,292 +205,243 @@ int main(int argc, char **argv)
|
|||
bool mode_v = false;
|
||||
bool mode_q = false;
|
||||
|
||||
if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "-help") || !strcmp(argv[1], "--help")))
|
||||
{
|
||||
printf("\n");
|
||||
printf("Usage: %s [options] [<infile> [..]]\n", argv[0]);
|
||||
printf("\n");
|
||||
printf(" -Q\n");
|
||||
printf(" suppress printing of banner (copyright, disclaimer, version)\n");
|
||||
printf("\n");
|
||||
printf(" -T\n");
|
||||
printf(" suppress printing of footer (log hash, version, timing statistics)\n");
|
||||
printf("\n");
|
||||
printf(" -q\n");
|
||||
printf(" quiet operation. only write warnings and error messages to console\n");
|
||||
printf(" use this option twice to also quiet warning messages\n");
|
||||
printf("\n");
|
||||
printf(" -v <level>\n");
|
||||
printf(" print log headers up to level <level> to the console. (this\n");
|
||||
printf(" implies -q for everything except the 'End of script.' message.)\n");
|
||||
printf("\n");
|
||||
printf(" -t\n");
|
||||
printf(" annotate all log messages with a time stamp\n");
|
||||
printf("\n");
|
||||
printf(" -d\n");
|
||||
printf(" print more detailed timing stats at exit\n");
|
||||
printf("\n");
|
||||
printf(" -l logfile\n");
|
||||
printf(" write log messages to the specified file\n");
|
||||
printf("\n");
|
||||
printf(" -L logfile\n");
|
||||
printf(" like -l but open log file in line buffered mode\n");
|
||||
printf("\n");
|
||||
printf(" -o outfile\n");
|
||||
printf(" write the design to the specified file on exit\n");
|
||||
printf("\n");
|
||||
printf(" -b backend\n");
|
||||
printf(" use this backend for the output file specified on the command line\n");
|
||||
printf("\n");
|
||||
printf(" -f frontend\n");
|
||||
printf(" use the specified frontend for the input files on the command line\n");
|
||||
printf("\n");
|
||||
printf(" -H\n");
|
||||
printf(" print the command list\n");
|
||||
printf("\n");
|
||||
printf(" -h command\n");
|
||||
printf(" print the help message for the specified command\n");
|
||||
printf("\n");
|
||||
printf(" -s scriptfile\n");
|
||||
printf(" execute the commands in the script file\n");
|
||||
cxxopts::Options options(argv[0], "Yosys Open SYnthesis Suite");
|
||||
options.set_width(SIZE_MAX);
|
||||
|
||||
options.add_options("operation")
|
||||
("b,backend", "use <backend> for the output file specified on the command line",
|
||||
cxxopts::value<std::string>(), "<backend>")
|
||||
("f,frontend", "use <frontend> for the input files on the command line",
|
||||
cxxopts::value<std::string>(), "<frontend>")
|
||||
("s,scriptfile", "execute the commands in <scriptfile>",
|
||||
cxxopts::value<std::string>(), "<scriptfile>")
|
||||
#ifdef YOSYS_ENABLE_TCL
|
||||
printf("\n");
|
||||
printf(" -c tcl_scriptfile\n");
|
||||
printf(" execute the commands in the tcl script file (see 'help tcl' for details)\n");
|
||||
printf("\n");
|
||||
printf(" -C\n");
|
||||
printf(" enters TCL interatcive shell mode\n");
|
||||
#endif
|
||||
printf("\n");
|
||||
printf(" -p command\n");
|
||||
printf(" execute the commands (to chain commands, separate them with semicolon + whitespace: 'cmd1; cmd2')\n");
|
||||
printf("\n");
|
||||
printf(" -m module_file\n");
|
||||
printf(" load the specified module (aka plugin)\n");
|
||||
printf("\n");
|
||||
printf(" -X\n");
|
||||
printf(" enable tracing of core data structure changes. for debugging\n");
|
||||
printf("\n");
|
||||
printf(" -M\n");
|
||||
printf(" will slightly randomize allocated pointer addresses. for debugging\n");
|
||||
printf("\n");
|
||||
printf(" -A\n");
|
||||
printf(" will call abort() at the end of the script. for debugging\n");
|
||||
printf("\n");
|
||||
printf(" -r <module_name>\n");
|
||||
printf(" elaborate command line arguments using the specified top module\n");
|
||||
printf("\n");
|
||||
printf(" -D <macro>[=<value>]\n");
|
||||
printf(" set the specified Verilog define (via \"read -define\")\n");
|
||||
printf("\n");
|
||||
printf(" -P <header_id>[:<filename>]\n");
|
||||
printf(" dump the design when printing the specified log header to a file.\n");
|
||||
printf(" yosys_dump_<header_id>.il is used as filename if none is specified.\n");
|
||||
printf(" Use 'ALL' as <header_id> to dump at every header.\n");
|
||||
printf("\n");
|
||||
printf(" -W regex\n");
|
||||
printf(" print a warning for all log messages matching the regex.\n");
|
||||
printf("\n");
|
||||
printf(" -w regex\n");
|
||||
printf(" if a warning message matches the regex, it is printed as regular\n");
|
||||
printf(" message instead.\n");
|
||||
printf("\n");
|
||||
printf(" -e regex\n");
|
||||
printf(" if a warning message matches the regex, it is printed as error\n");
|
||||
printf(" message instead and the tool terminates with a nonzero return code.\n");
|
||||
printf("\n");
|
||||
printf(" -E <depsfile>\n");
|
||||
printf(" write a Makefile dependencies file with in- and output file names\n");
|
||||
printf("\n");
|
||||
printf(" -x <feature>\n");
|
||||
printf(" do not print warnings for the specified experimental feature\n");
|
||||
printf("\n");
|
||||
printf(" -g\n");
|
||||
printf(" globally enable debug log messages\n");
|
||||
printf("\n");
|
||||
printf(" -V\n");
|
||||
printf(" print version information and exit\n");
|
||||
printf("\n");
|
||||
printf("The option -S is a shortcut for calling the \"synth\" command, a default\n");
|
||||
printf("script for transforming the Verilog input to a gate-level netlist. For example:\n");
|
||||
printf("\n");
|
||||
printf(" yosys -o output.blif -S input.v\n");
|
||||
printf("\n");
|
||||
printf("For more complex synthesis jobs it is recommended to use the read_* and write_*\n");
|
||||
printf("commands in a script file instead of specifying input and output files on the\n");
|
||||
printf("command line.\n");
|
||||
printf("\n");
|
||||
printf("When no commands, script files or input files are specified on the command\n");
|
||||
printf("line, yosys automatically enters the interactive command mode. Use the 'help'\n");
|
||||
printf("command to get information on the individual commands.\n");
|
||||
printf("\n");
|
||||
("c,tcl-scriptfile", "execute the commands in the TCL <tcl_scriptfile> (see 'help tcl' for details)",
|
||||
cxxopts::value<std::string>(),"<tcl_scriptfile>")
|
||||
("C,tcl-interactive", "enters TCL interactive shell mode")
|
||||
#endif // YOSYS_ENABLE_TCL
|
||||
#ifdef WITH_PYTHON
|
||||
("y,py-scriptfile", "execute the Python <script>",
|
||||
cxxopts::value<std::vector<std::string>>(), "<script>")
|
||||
#endif // WITH_PYTHON
|
||||
("p,commands", "execute <commands> (to chain commands, separate them with semicolon + whitespace: 'cmd1; cmd2')",
|
||||
cxxopts::value<std::vector<std::string>>(), "<commands>")
|
||||
("r,top", "elaborate the specified HDL <top> module",
|
||||
cxxopts::value<std::string>(), "<top>")
|
||||
("m,plugin", "load the specified <plugin> module",
|
||||
cxxopts::value<std::vector<std::string>>(), "<plugin>")
|
||||
("D,define", "set the specified Verilog define to <value> if supplied via command \"read -define\"",
|
||||
cxxopts::value<std::vector<std::string>>(), "<define>[=<value>]")
|
||||
("S,synth", "shortcut for calling the \"synth\" command, a default script for transforming " \
|
||||
"the Verilog input to a gate-level netlist. For example: " \
|
||||
"yosys -o output.blif -S input.v " \
|
||||
"For more complex synthesis jobs it is recommended to use the read_* and write_* " \
|
||||
"commands in a script file instead of specifying input and output files on the " \
|
||||
"command line.")
|
||||
("H", "print the command list")
|
||||
("h,help", "print this help message. If given, print help for <command>.",
|
||||
cxxopts::value<std::string>(), "[<command>]")
|
||||
("V,version", "print version information and exit")
|
||||
("infile", "input files", cxxopts::value<std::vector<std::string>>())
|
||||
;
|
||||
options.add_options("logging")
|
||||
("Q", "suppress printing of banner (copyright, disclaimer, version)")
|
||||
("T", "suppress printing of footer (log hash, version, timing statistics)")
|
||||
("q,quiet", "quiet operation. Only write warnings and error messages to console. " \
|
||||
"Use this option twice to also quiet warning messages")
|
||||
("v,verbose", "print log headers up to <level> to the console. " \
|
||||
"Implies -q for everything except the 'End of script.' message.",
|
||||
cxxopts::value<int>(), "<level>")
|
||||
("t,timestamp", "annotate all log messages with a time stamp")
|
||||
("d,detailed-timing", "print more detailed timing stats at exit")
|
||||
("l,logfile", "write log messages to <logfile>",
|
||||
cxxopts::value<std::vector<std::string>>(), "<logfile>")
|
||||
("L,line-buffered-logfile", "like -l but open <logfile> in line buffered mode",
|
||||
cxxopts::value<std::vector<std::string>>(), "<logfile>")
|
||||
("o,outfile", "write the design to <outfile> on exit",
|
||||
cxxopts::value<std::string>(), "<outfile>")
|
||||
("P,dump-design", "dump the design when printing the specified log header to a file. " \
|
||||
"yosys_dump_<header_id>.il is used as filename if none is specified. " \
|
||||
"Use 'ALL' as <header_id> to dump at every header.",
|
||||
cxxopts::value<std::vector<std::string>>(), "<header_id>[:<filename>]")
|
||||
("W,warning-as-warning", "print a warning for all log messages matching <regex>",
|
||||
cxxopts::value<std::vector<std::string>>(), "<regex>")
|
||||
("w,warning-as-message", "if a warning message matches <regex>, it is printed as regular message instead",
|
||||
cxxopts::value<std::vector<std::string>>(), "<regex>")
|
||||
("e,warning-as-error", "if a warning message matches <regex>, it is printed as error message instead",
|
||||
cxxopts::value<std::vector<std::string>>(), "<regex>")
|
||||
("E,deps-file", "write a Makefile dependencies file <depsfile> with input and output file names",
|
||||
cxxopts::value<std::string>(), "<depsfile>")
|
||||
;
|
||||
options.add_options("developer")
|
||||
("X,trace", "enable tracing of core data structure changes. for debugging")
|
||||
("M,randomize-pointers", "will slightly randomize allocated pointer addresses. for debugging")
|
||||
("A,abort", "will call abort() at the end of the script. for debugging")
|
||||
("x,experimental", "do not print warnings for the experimental <feature>",
|
||||
cxxopts::value<std::vector<std::string>>(), "<feature>")
|
||||
("g,debug", "globally enable debug log messages")
|
||||
("perffile", "write a JSON performance log to <perffile>", cxxopts::value<std::string>(), "<perffile>")
|
||||
;
|
||||
|
||||
options.parse_positional({"infile"});
|
||||
options.positional_help("[<infile> [..]]");
|
||||
|
||||
// We can't have -h optionally require an argument
|
||||
// cxxopts does have an implit argument concept but that doesn't work for us
|
||||
// cxxopts is therefore instructed allowed to only handle the command help case
|
||||
if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "-help") || !strcmp(argv[1], "--help"))) {
|
||||
std::cout << options.help() << std::endl;
|
||||
exit(0);
|
||||
}
|
||||
try {
|
||||
// Check for "--" in arguments
|
||||
auto it = std::find(argv, argv + argc, std::string("--"));
|
||||
if (it != argv + argc) {
|
||||
special_args.assign(it + 1, argv + argc);
|
||||
// Remove these arguments from cxxopts parsing
|
||||
argc = std::distance(argv, it);
|
||||
}
|
||||
|
||||
if (argc == 2 && (!strcmp(argv[1], "-V") || !strcmp(argv[1], "-version") || !strcmp(argv[1], "--version")))
|
||||
{
|
||||
printf("%s\n", yosys_version_str);
|
||||
exit(0);
|
||||
}
|
||||
auto result = options.parse(argc, argv);
|
||||
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "MXAQTVCSgm:f:Hh:b:o:p:l:L:qv:tds:c:W:w:e:r:D:P:E:x:B:")) != -1)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
case 'M':
|
||||
memhasher_on();
|
||||
break;
|
||||
case 'X':
|
||||
yosys_xtrace++;
|
||||
break;
|
||||
case 'A':
|
||||
call_abort = true;
|
||||
break;
|
||||
case 'Q':
|
||||
print_banner = false;
|
||||
break;
|
||||
case 'T':
|
||||
print_stats = false;
|
||||
break;
|
||||
case 'V':
|
||||
printf("%s\n", yosys_version_str);
|
||||
if (result.count("M")) memhasher_on();
|
||||
if (result.count("X")) yosys_xtrace++;
|
||||
if (result.count("A")) call_abort = true;
|
||||
if (result.count("Q")) print_banner = false;
|
||||
if (result.count("T")) print_stats = false;
|
||||
if (result.count("V")) {
|
||||
std::cout << yosys_version_str << std::endl;
|
||||
exit(0);
|
||||
case 'S':
|
||||
}
|
||||
if (result.count("S")) {
|
||||
passes_commands.push_back("synth");
|
||||
run_shell = false;
|
||||
break;
|
||||
case 'g':
|
||||
log_force_debug++;
|
||||
break;
|
||||
case 'm':
|
||||
plugin_filenames.push_back(optarg);
|
||||
break;
|
||||
case 'f':
|
||||
frontend_command = optarg;
|
||||
break;
|
||||
case 'H':
|
||||
}
|
||||
if (result.count("C")) run_tcl_shell = true;
|
||||
if (result.count("g")) log_force_debug++;
|
||||
if (result.count("m")) plugin_filenames = result["m"].as<std::vector<std::string>>();
|
||||
if (result.count("f")) frontend_command = result["f"].as<std::string>();
|
||||
if (result.count("H")) {
|
||||
passes_commands.push_back("help");
|
||||
run_shell = false;
|
||||
break;
|
||||
case 'h':
|
||||
passes_commands.push_back(stringf("help %s", optarg));
|
||||
}
|
||||
if (result.count("h")) {
|
||||
std::string res = result["h"].as<std::string>();
|
||||
passes_commands.push_back("help " + res);
|
||||
run_shell = false;
|
||||
break;
|
||||
case 'b':
|
||||
backend_command = optarg;
|
||||
}
|
||||
if (result.count("b")) {
|
||||
backend_command = result["b"].as<std::string>();
|
||||
run_shell = false;
|
||||
break;
|
||||
case 'p':
|
||||
passes_commands.push_back(optarg);
|
||||
}
|
||||
if (result.count("p")) {
|
||||
auto cmds = result["p"].as<std::vector<std::string>>();
|
||||
passes_commands.insert(passes_commands.end(), cmds.begin(), cmds.end());
|
||||
run_shell = false;
|
||||
break;
|
||||
case 'o':
|
||||
output_filename = optarg;
|
||||
}
|
||||
if (result.count("o")) {
|
||||
output_filename = result["o"].as<std::string>();
|
||||
run_shell = false;
|
||||
break;
|
||||
case 'l':
|
||||
case 'L':
|
||||
log_files.push_back(fopen(optarg, "wt"));
|
||||
if (log_files.back() == NULL) {
|
||||
fprintf(stderr, "Can't open log file `%s' for writing!\n", optarg);
|
||||
exit(1);
|
||||
}
|
||||
for (const auto& key : {"l", "L"}) {
|
||||
if (result.count(key)) {
|
||||
for (const auto& filename : result[key].as<std::vector<std::string>>()) {
|
||||
if (FILE* f = fopen(filename.c_str(), "wt")) {
|
||||
log_files.push_back(f);
|
||||
if (key[0] == 'L') setvbuf(f, NULL, _IOLBF, 0);
|
||||
} else {
|
||||
std::cerr << "Can't open log file `" << filename << "' for writing!\n";
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (opt == 'L')
|
||||
setvbuf(log_files.back(), NULL, _IOLBF, 0);
|
||||
break;
|
||||
case 'q':
|
||||
}
|
||||
if (result.count("q")) {
|
||||
mode_q = true;
|
||||
if (log_errfile == stderr)
|
||||
log_quiet_warnings = true;
|
||||
if (log_errfile == stderr) log_quiet_warnings = true;
|
||||
log_errfile = stderr;
|
||||
break;
|
||||
case 'v':
|
||||
}
|
||||
if (result.count("v")) {
|
||||
mode_v = true;
|
||||
log_errfile = stderr;
|
||||
log_verbose_level = atoi(optarg);
|
||||
break;
|
||||
case 't':
|
||||
log_time = true;
|
||||
break;
|
||||
case 'd':
|
||||
timing_details = true;
|
||||
break;
|
||||
case 's':
|
||||
scriptfile = optarg;
|
||||
scriptfile_tcl = false;
|
||||
log_verbose_level = result["v"].as<int>();
|
||||
}
|
||||
if (result.count("t")) log_time = true;
|
||||
if (result.count("d")) timing_details = true;
|
||||
for (const auto& key : {"s", "c"}) {
|
||||
if (result.count(key)) {
|
||||
scriptfile = result[key].as<std::string>();
|
||||
scriptfile_tcl = std::string(key) == "c";
|
||||
run_shell = false;
|
||||
}
|
||||
}
|
||||
if (result.count("y")) {
|
||||
scriptfile = result["y"].as<std::string>();
|
||||
scriptfile_python = true;
|
||||
run_shell = false;
|
||||
break;
|
||||
case 'c':
|
||||
scriptfile = optarg;
|
||||
scriptfile_tcl = true;
|
||||
run_shell = false;
|
||||
break;
|
||||
case 'W':
|
||||
log_warn_regexes.push_back(YS_REGEX_COMPILE(optarg));
|
||||
break;
|
||||
case 'w':
|
||||
log_nowarn_regexes.push_back(YS_REGEX_COMPILE(optarg));
|
||||
break;
|
||||
case 'e':
|
||||
log_werror_regexes.push_back(YS_REGEX_COMPILE(optarg));
|
||||
break;
|
||||
case 'r':
|
||||
topmodule = optarg;
|
||||
break;
|
||||
case 'D':
|
||||
vlog_defines.push_back(optarg);
|
||||
break;
|
||||
case 'P':
|
||||
{
|
||||
auto args = split_tokens(optarg, ":");
|
||||
if (!args.empty() && args[0] == "ALL") {
|
||||
if (GetSize(args) != 1) {
|
||||
fprintf(stderr, "Invalid number of tokens in -D ALL.\n");
|
||||
}
|
||||
for (const auto& key : {"W", "w", "e"}) {
|
||||
if (result.count(key)) {
|
||||
auto regexes = result[key].as<std::vector<std::string>>();
|
||||
for (const auto& regex : regexes) {
|
||||
if (std::string(key) == "W")
|
||||
log_warn_regexes.push_back(std::regex(regex));
|
||||
if (std::string(key) == "w")
|
||||
log_nowarn_regexes.push_back(std::regex(regex));
|
||||
if (std::string(key) == "e")
|
||||
log_werror_regexes.push_back(std::regex(regex));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (result.count("r")) topmodule = result["r"].as<std::string>();
|
||||
if (result.count("D")) vlog_defines = result["D"].as<std::vector<std::string>>();
|
||||
if (result.count("P")) {
|
||||
auto dump_args = result["P"].as<std::vector<std::string>>();
|
||||
for (const auto& arg : dump_args) {
|
||||
auto tokens = split_tokens(arg, ":");
|
||||
if (!tokens.empty() && tokens[0] == "ALL") {
|
||||
if (tokens.size() != 1) {
|
||||
std::cerr << "Invalid number of tokens in -P ALL." << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
log_hdump_all = true;
|
||||
} else {
|
||||
if (!args.empty() && !args[0].empty() && args[0].back() == '.')
|
||||
args[0].pop_back();
|
||||
if (GetSize(args) == 1)
|
||||
args.push_back("yosys_dump_" + args[0] + ".il");
|
||||
if (GetSize(args) != 2) {
|
||||
fprintf(stderr, "Invalid number of tokens in -D.\n");
|
||||
if (!tokens.empty() && !tokens[0].empty() && tokens[0].back() == '.')
|
||||
tokens[0].pop_back();
|
||||
if (tokens.size() == 1)
|
||||
tokens.push_back("yosys_dump_" + tokens[0] + ".il");
|
||||
if (tokens.size() != 2) {
|
||||
std::cerr << "Invalid number of tokens in -P." << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
log_hdump[args[0]].insert(args[1]);
|
||||
log_hdump[tokens[0]].insert(tokens[1]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'E':
|
||||
depsfile = optarg;
|
||||
break;
|
||||
case 'x':
|
||||
log_experimentals_ignored.insert(optarg);
|
||||
break;
|
||||
case 'B':
|
||||
perffile = optarg;
|
||||
break;
|
||||
case 'C':
|
||||
run_tcl_shell = true;
|
||||
break;
|
||||
case '\001':
|
||||
frontend_files.push_back(optarg);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Run '%s -h' for help.\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if (result.count("E")) depsfile = result["E"].as<std::string>();
|
||||
if (result.count("x")) {
|
||||
auto ignores = result["x"].as<std::vector<std::string>>();
|
||||
log_experimentals_ignored.insert(ignores.begin(), ignores.end());
|
||||
}
|
||||
if (result.count("perffile")) perffile = result["perffile"].as<std::string>();
|
||||
if (result.count("infile")) {
|
||||
frontend_files = result["infile"].as<std::vector<std::string>>();
|
||||
}
|
||||
|
||||
if (log_errfile == NULL) {
|
||||
log_files.push_back(stdout);
|
||||
log_error_stderr = true;
|
||||
}
|
||||
if (log_errfile == NULL) {
|
||||
log_files.push_back(stdout);
|
||||
log_error_stderr = true;
|
||||
}
|
||||
|
||||
if (print_banner)
|
||||
yosys_banner();
|
||||
if (print_banner)
|
||||
yosys_banner();
|
||||
|
||||
}
|
||||
catch (const cxxopts::exceptions::parsing& e) {
|
||||
std::cerr << "Error parsing options: " << e.what() << std::endl;
|
||||
std::cerr << "Run '" << argv[0] << " --help' for help." << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE)
|
||||
std::string state_dir;
|
||||
|
|
@ -607,10 +513,11 @@ int main(int argc, char **argv)
|
|||
run_pass(vdef_cmd);
|
||||
}
|
||||
|
||||
if (scriptfile.empty() || !scriptfile_tcl) {
|
||||
// Without a TCL script, arguments following '--' are also treated as frontend files
|
||||
for (int i = optind; i < argc; ++i)
|
||||
frontend_files.push_back(argv[i]);
|
||||
if (scriptfile.empty() || (!scriptfile_tcl && !scriptfile_python)) {
|
||||
// Without a TCL or Python script, arguments following '--'
|
||||
// are also treated as frontend files
|
||||
for (auto special_arg : special_args)
|
||||
frontend_files.push_back(special_arg);
|
||||
}
|
||||
|
||||
for (auto it = frontend_files.begin(); it != frontend_files.end(); ++it) {
|
||||
|
|
@ -636,7 +543,36 @@ int main(int argc, char **argv)
|
|||
if (Tcl_EvalFile(interp, scriptfile.c_str()) != TCL_OK)
|
||||
log_error("TCL interpreter returned an error: %s\n", Tcl_GetStringResult(yosys_get_tcl_interp()));
|
||||
#else
|
||||
log_error("Can't exectue TCL script: this version of yosys is not built with TCL support enabled.\n");
|
||||
log_error("Can't execute TCL script: this version of yosys is not built with TCL support enabled.\n");
|
||||
#endif
|
||||
} else if (scriptfile_python) {
|
||||
#ifdef WITH_PYTHON
|
||||
PyObject *sys = PyImport_ImportModule("sys");
|
||||
PyObject *new_argv = PyList_New(argc - optind + 1);
|
||||
PyList_SetItem(new_argv, 0, PyUnicode_FromString(scriptfile.c_str()));
|
||||
for (int i = optind; i < argc; ++i)
|
||||
PyList_SetItem(new_argv, i - optind + 1, PyUnicode_FromString(argv[i]));
|
||||
|
||||
PyObject *old_argv = PyObject_GetAttrString(sys, "argv");
|
||||
PyObject_SetAttrString(sys, "argv", new_argv);
|
||||
Py_DECREF(old_argv);
|
||||
|
||||
PyObject *py_path = PyUnicode_FromString(scriptfile.c_str());
|
||||
PyObject_SetAttrString(sys, "_yosys_script_path", py_path);
|
||||
Py_DECREF(py_path);
|
||||
PyRun_SimpleString("import os, sys; sys.path.insert(0, os.path.dirname(os.path.abspath(sys._yosys_script_path)))");
|
||||
|
||||
FILE *scriptfp = fopen(scriptfile.c_str(), "r");
|
||||
if (scriptfp == nullptr) {
|
||||
log_error("Failed to open file '%s' for reading.\n", scriptfile.c_str());
|
||||
}
|
||||
if (PyRun_SimpleFile(scriptfp, scriptfile.c_str()) != 0) {
|
||||
log_flush();
|
||||
PyErr_Print();
|
||||
log_error("Python interpreter encountered an exception.");
|
||||
}
|
||||
#else
|
||||
log_error("Can't execute Python script: this version of yosys is not built with Python support enabled.\n");
|
||||
#endif
|
||||
} else
|
||||
run_frontend(scriptfile, "script");
|
||||
|
|
@ -785,10 +721,10 @@ int main(int argc, char **argv)
|
|||
for (auto it = timedat.rbegin(); it != timedat.rend(); it++) {
|
||||
if (!first)
|
||||
fprintf(f, ",");
|
||||
fprintf(f, "\n \"%s\": {\n", std::get<2>(*it).c_str());
|
||||
fprintf(f, " \"runtime_ns\": %" PRIu64 ",\n", std::get<0>(*it));
|
||||
fprintf(f, " \"num_calls\": %u\n", std::get<1>(*it));
|
||||
fprintf(f, " }");
|
||||
fprintf(f, "\n \"%s\": {\n", std::get<2>(*it).c_str());
|
||||
fprintf(f, " \"runtime_ns\": %" PRIu64 ",\n", std::get<0>(*it));
|
||||
fprintf(f, " \"num_calls\": %u\n", std::get<1>(*it));
|
||||
fprintf(f, " }");
|
||||
first = false;
|
||||
}
|
||||
fprintf(f, "\n }\n}\n");
|
||||
|
|
|
|||
949
kernel/drivertools.cc
Normal file
949
kernel/drivertools.cc
Normal file
|
|
@ -0,0 +1,949 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2024 Jannis Harder <jix@yosyshq.com> <me@jix.one>
|
||||
*
|
||||
* 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/drivertools.h"
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
DriveBit::DriveBit(SigBit const &bit)
|
||||
{
|
||||
if (bit.is_wire())
|
||||
*this = DriveBitWire(bit.wire, bit.offset);
|
||||
else
|
||||
*this = bit.data;
|
||||
}
|
||||
|
||||
void DriveBit::merge(DriveBit const &other)
|
||||
{
|
||||
if (other.type_ == DriveType::NONE)
|
||||
return;
|
||||
if (type_ == DriveType::NONE) {
|
||||
*this = other;
|
||||
return;
|
||||
}
|
||||
if (type_ != DriveType::MULTIPLE) {
|
||||
DriveBitMultiple multi(std::move(*this));
|
||||
*this = std::move(multi);
|
||||
}
|
||||
multiple().merge(other);
|
||||
}
|
||||
|
||||
|
||||
void DriveBitMultiple::merge(DriveBit const &single)
|
||||
{
|
||||
if (single.type() == DriveType::NONE)
|
||||
return;
|
||||
if (single.type() == DriveType::MULTIPLE) {
|
||||
merge(single.multiple());
|
||||
return;
|
||||
}
|
||||
multiple_.emplace(single);
|
||||
}
|
||||
|
||||
void DriveBitMultiple::merge(DriveBit &&single)
|
||||
{
|
||||
if (single.type() == DriveType::NONE)
|
||||
return;
|
||||
if (single.type() == DriveType::MULTIPLE) {
|
||||
merge(std::move(single.multiple()));
|
||||
return;
|
||||
}
|
||||
multiple_.emplace(std::move(single));
|
||||
}
|
||||
|
||||
DriveBitMultiple DriveChunkMultiple::operator[](int i) const
|
||||
{
|
||||
DriveBitMultiple result;
|
||||
for (auto const &single : multiple_)
|
||||
result.merge(single[i]);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool DriveChunkWire::can_append(DriveBitWire const &bit) const
|
||||
{
|
||||
return bit.wire == wire && bit.offset == offset + width;
|
||||
}
|
||||
|
||||
bool DriveChunkWire::try_append(DriveBitWire const &bit)
|
||||
{
|
||||
if (!can_append(bit))
|
||||
return false;
|
||||
width += 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DriveChunkWire::try_append(DriveChunkWire const &chunk)
|
||||
{
|
||||
if (chunk.wire != wire || chunk.offset != offset + width)
|
||||
return false;
|
||||
width += chunk.width;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DriveChunkPort::can_append(DriveBitPort const &bit) const
|
||||
{
|
||||
return bit.cell == cell && bit.port == port && bit.offset == offset + width;
|
||||
}
|
||||
|
||||
bool DriveChunkPort::try_append(DriveBitPort const &bit)
|
||||
{
|
||||
if (!can_append(bit))
|
||||
return false;
|
||||
width += 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DriveChunkPort::try_append(DriveChunkPort const &chunk)
|
||||
{
|
||||
if (chunk.cell != cell || chunk.port != port || chunk.offset != offset + width)
|
||||
return false;
|
||||
width += chunk.width;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DriveChunkMarker::can_append(DriveBitMarker const &bit) const
|
||||
{
|
||||
return bit.marker == marker && bit.offset == offset + width;
|
||||
}
|
||||
|
||||
bool DriveChunkMarker::try_append(DriveBitMarker const &bit)
|
||||
{
|
||||
if (!can_append(bit))
|
||||
return false;
|
||||
width += 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DriveChunkMarker::try_append(DriveChunkMarker const &chunk)
|
||||
{
|
||||
if (chunk.marker != marker || chunk.offset != offset + width)
|
||||
return false;
|
||||
width += chunk.width;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool DriveChunkMultiple::can_append(DriveBitMultiple const &bit) const
|
||||
{
|
||||
if (bit.multiple().size() != multiple_.size())
|
||||
return false;
|
||||
|
||||
int const_drivers = 0;
|
||||
for (DriveChunk const &single : multiple_)
|
||||
if (single.is_constant())
|
||||
const_drivers += 1;
|
||||
|
||||
if (const_drivers > 1)
|
||||
return false;
|
||||
|
||||
for (DriveBit const &single : bit.multiple())
|
||||
if (single.is_constant())
|
||||
const_drivers -= 1;
|
||||
|
||||
if (const_drivers != 0)
|
||||
return false;
|
||||
|
||||
for (DriveChunk const &single : multiple_)
|
||||
{
|
||||
switch (single.type())
|
||||
{
|
||||
case DriveType::CONSTANT: {
|
||||
} break;
|
||||
case DriveType::WIRE: {
|
||||
auto const &wire = single.wire();
|
||||
DriveBit next = DriveBitWire(wire.wire, wire.offset + wire.width);
|
||||
if (!bit.multiple().count(next))
|
||||
return false;
|
||||
} break;
|
||||
case DriveType::PORT: {
|
||||
auto const &port = single.port();
|
||||
DriveBit next = DriveBitPort(port.cell, port.port, port.offset + port.width);
|
||||
if (!bit.multiple().count(next))
|
||||
return false;
|
||||
} break;
|
||||
case DriveType::MARKER: {
|
||||
auto const &marker = single.marker();
|
||||
DriveBit next = DriveBitMarker(marker.marker, marker.offset + marker.width);
|
||||
if (!bit.multiple().count(next))
|
||||
return false;
|
||||
} break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DriveChunkMultiple::can_append(DriveChunkMultiple const &chunk) const
|
||||
{
|
||||
if (chunk.multiple().size() != multiple_.size())
|
||||
return false;
|
||||
|
||||
int const_drivers = 0;
|
||||
for (DriveChunk const &single : multiple_)
|
||||
if (single.is_constant())
|
||||
const_drivers += 1;
|
||||
|
||||
if (const_drivers > 1)
|
||||
return false;
|
||||
|
||||
for (DriveChunk const &single : chunk.multiple())
|
||||
if (single.is_constant())
|
||||
const_drivers -= 1;
|
||||
|
||||
if (const_drivers != 0)
|
||||
return false;
|
||||
|
||||
for (DriveChunk const &single : multiple_)
|
||||
{
|
||||
switch (single.type())
|
||||
{
|
||||
case DriveType::CONSTANT: {
|
||||
} break;
|
||||
case DriveType::WIRE: {
|
||||
auto const &wire = single.wire();
|
||||
DriveChunk next = DriveChunkWire(wire.wire, wire.offset + wire.width, chunk.size());
|
||||
if (!chunk.multiple().count(next))
|
||||
return false;
|
||||
} break;
|
||||
case DriveType::PORT: {
|
||||
auto const &port = single.port();
|
||||
DriveChunk next = DriveChunkPort(port.cell, port.port, port.offset + port.width, chunk.size());
|
||||
if (!chunk.multiple().count(next))
|
||||
return false;
|
||||
} break;
|
||||
case DriveType::MARKER: {
|
||||
auto const &marker = single.marker();
|
||||
DriveChunk next = DriveChunkMarker(marker.marker, marker.offset + marker.width, chunk.size());
|
||||
if (!chunk.multiple().count(next))
|
||||
return false;
|
||||
} break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DriveChunkMultiple::try_append(DriveBitMultiple const &bit)
|
||||
{
|
||||
if (!can_append(bit))
|
||||
return false;
|
||||
width_ += 1;
|
||||
State constant;
|
||||
|
||||
for (DriveBit const &single : bit.multiple())
|
||||
if (single.is_constant())
|
||||
constant = single.constant();
|
||||
|
||||
for (DriveChunk &single : multiple_)
|
||||
{
|
||||
switch (single.type())
|
||||
{
|
||||
case DriveType::CONSTANT: {
|
||||
single.constant().bits().push_back(constant);
|
||||
} break;
|
||||
case DriveType::WIRE: {
|
||||
single.wire().width += 1;
|
||||
} break;
|
||||
case DriveType::PORT: {
|
||||
single.port().width += 1;
|
||||
} break;
|
||||
case DriveType::MARKER: {
|
||||
single.marker().width += 1;
|
||||
} break;
|
||||
default:
|
||||
log_abort();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DriveChunkMultiple::try_append(DriveChunkMultiple const &chunk)
|
||||
{
|
||||
if (!can_append(chunk))
|
||||
return false;
|
||||
int width = chunk.size();
|
||||
width_ += width;
|
||||
Const constant;
|
||||
|
||||
for (DriveChunk const &single : chunk.multiple())
|
||||
if (single.is_constant())
|
||||
constant = single.constant();
|
||||
|
||||
for (DriveChunk &single : multiple_)
|
||||
{
|
||||
switch (single.type())
|
||||
{
|
||||
case DriveType::CONSTANT: {
|
||||
auto &bits = single.constant().bits();
|
||||
bits.insert(bits.end(), constant.bits().begin(), constant.bits().end());
|
||||
} break;
|
||||
case DriveType::WIRE: {
|
||||
single.wire().width += width;
|
||||
} break;
|
||||
case DriveType::PORT: {
|
||||
single.port().width += width;
|
||||
} break;
|
||||
case DriveType::MARKER: {
|
||||
single.marker().width += width;
|
||||
} break;
|
||||
default:
|
||||
log_abort();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DriveChunk::can_append(DriveBit const &bit) const
|
||||
{
|
||||
if (size() == 0)
|
||||
return true;
|
||||
if (bit.type() != type_)
|
||||
return false;
|
||||
switch (type_)
|
||||
{
|
||||
case DriveType::NONE:
|
||||
return true;
|
||||
case DriveType::CONSTANT:
|
||||
return true;
|
||||
case DriveType::WIRE:
|
||||
return wire_.can_append(bit.wire());
|
||||
case DriveType::PORT:
|
||||
return port_.can_append(bit.port());
|
||||
case DriveType::MULTIPLE:
|
||||
return multiple_.can_append(bit.multiple());
|
||||
default:
|
||||
log_abort();
|
||||
}
|
||||
}
|
||||
|
||||
bool DriveChunk::try_append(DriveBit const &bit)
|
||||
{
|
||||
if (size() == 0)
|
||||
*this = bit;
|
||||
if (bit.type() != type_)
|
||||
return false;
|
||||
switch (type_)
|
||||
{
|
||||
case DriveType::NONE:
|
||||
none_ += 1;
|
||||
return true;
|
||||
case DriveType::CONSTANT:
|
||||
constant_.bits().push_back(bit.constant());
|
||||
return true;
|
||||
case DriveType::WIRE:
|
||||
return wire_.try_append(bit.wire());
|
||||
case DriveType::PORT:
|
||||
return port_.try_append(bit.port());
|
||||
case DriveType::MULTIPLE:
|
||||
return multiple_.try_append(bit.multiple());
|
||||
default:
|
||||
log_abort();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool DriveChunk::try_append(DriveChunk const &chunk)
|
||||
{
|
||||
if (size() == 0)
|
||||
*this = chunk;
|
||||
if (chunk.type_ != type_)
|
||||
return false;
|
||||
switch (type_)
|
||||
{
|
||||
case DriveType::NONE:
|
||||
none_ += chunk.none_;
|
||||
return true;
|
||||
case DriveType::CONSTANT:
|
||||
constant_.bits().insert(constant_.bits().end(), chunk.constant_.begin(), chunk.constant_.end());
|
||||
return true;
|
||||
case DriveType::WIRE:
|
||||
return wire_.try_append(chunk.wire());
|
||||
case DriveType::PORT:
|
||||
return port_.try_append(chunk.port());
|
||||
case DriveType::MARKER:
|
||||
return marker_.try_append(chunk.marker());
|
||||
case DriveType::MULTIPLE:
|
||||
return multiple_.try_append(chunk.multiple());
|
||||
}
|
||||
log_abort();
|
||||
}
|
||||
|
||||
void DriveSpec::append(DriveBit const &bit)
|
||||
{
|
||||
hash_ = 0;
|
||||
if (!packed()) {
|
||||
bits_.push_back(bit);
|
||||
width_ += 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (chunks_.empty() || !chunks_.back().try_append(bit))
|
||||
chunks_.emplace_back(bit);
|
||||
width_ += 1;
|
||||
}
|
||||
|
||||
void DriveSpec::append(DriveChunk const &chunk)
|
||||
{
|
||||
hash_ = 0;
|
||||
pack();
|
||||
if (chunks_.empty() || !chunks_.back().try_append(chunk))
|
||||
chunks_.emplace_back(chunk);
|
||||
width_ += chunk.size();
|
||||
}
|
||||
|
||||
void DriveSpec::pack() const {
|
||||
if (bits_.empty())
|
||||
return;
|
||||
std::vector<DriveBit> bits(std::move(bits_));
|
||||
for (auto &bit : bits)
|
||||
if (chunks_.empty() || !chunks_.back().try_append(bit))
|
||||
chunks_.emplace_back(std::move(bit));
|
||||
}
|
||||
|
||||
void DriveSpec::unpack() const {
|
||||
if (chunks_.empty())
|
||||
return;
|
||||
for (auto &chunk : chunks_)
|
||||
{
|
||||
for (int i = 0, width = chunk.size(); i != width; ++i)
|
||||
{
|
||||
bits_.emplace_back(chunk[i]);
|
||||
}
|
||||
}
|
||||
chunks_.clear();
|
||||
}
|
||||
|
||||
void DriveSpec::compute_width()
|
||||
{
|
||||
width_ = 0;
|
||||
for (auto const &chunk : chunks_)
|
||||
width_ += chunk.size();
|
||||
}
|
||||
|
||||
void DriverMap::DriveBitGraph::add_edge(DriveBitId src, DriveBitId dst)
|
||||
{
|
||||
if (first_edges.emplace(src, dst).first->second == dst)
|
||||
return;
|
||||
if (second_edges.emplace(src, dst).first->second == dst)
|
||||
return;
|
||||
more_edges[src].emplace(dst);
|
||||
}
|
||||
|
||||
DriverMap::DriveBitId DriverMap::DriveBitGraph::pop_edge(DriveBitId src)
|
||||
{
|
||||
// TODO unused I think?
|
||||
auto found_more = more_edges.find(src);
|
||||
if (found_more != more_edges.end()) {
|
||||
auto result = found_more->second.pop();
|
||||
if (found_more->second.empty())
|
||||
more_edges.erase(found_more);
|
||||
return result;
|
||||
}
|
||||
|
||||
auto found_second = second_edges.find(src);
|
||||
if (found_second != second_edges.end()) {
|
||||
auto result = found_second->second;
|
||||
second_edges.erase(found_second);
|
||||
return result;
|
||||
}
|
||||
|
||||
auto found_first = first_edges.find(src);
|
||||
if (found_first != first_edges.end()) {
|
||||
auto result = found_first->second;
|
||||
first_edges.erase(found_first);
|
||||
return result;
|
||||
}
|
||||
|
||||
return DriveBitId();
|
||||
}
|
||||
|
||||
void DriverMap::DriveBitGraph::clear(DriveBitId src)
|
||||
{
|
||||
first_edges.erase(src);
|
||||
second_edges.erase(src);
|
||||
more_edges.erase(src);
|
||||
}
|
||||
|
||||
bool DriverMap::DriveBitGraph::contains(DriveBitId src)
|
||||
{
|
||||
return first_edges.count(src);
|
||||
}
|
||||
|
||||
int DriverMap::DriveBitGraph::count(DriveBitId src)
|
||||
{
|
||||
if (!first_edges.count(src))
|
||||
return 0;
|
||||
if (!second_edges.count(src))
|
||||
return 1;
|
||||
auto found = more_edges.find(src);
|
||||
if (found == more_edges.end())
|
||||
return 2;
|
||||
return GetSize(found->second) + 2;
|
||||
}
|
||||
|
||||
DriverMap::DriveBitId DriverMap::DriveBitGraph::at(DriveBitId src, int index)
|
||||
{
|
||||
if (index == 0)
|
||||
return first_edges.at(src);
|
||||
else if (index == 1)
|
||||
return second_edges.at(src);
|
||||
else
|
||||
return *more_edges.at(src).element(index - 2);
|
||||
}
|
||||
|
||||
|
||||
DriverMap::BitMode DriverMap::bit_mode(DriveBit const &bit)
|
||||
{
|
||||
switch (bit.type())
|
||||
{
|
||||
case DriveType::NONE:
|
||||
return BitMode::NONE;
|
||||
case DriveType::CONSTANT:
|
||||
// TODO how to handle Sx here?
|
||||
return bit.constant() == State::Sz ? BitMode::NONE : BitMode::DRIVER;
|
||||
case DriveType::WIRE: {
|
||||
auto const &wire = bit.wire();
|
||||
bool driver = wire.wire->port_input;
|
||||
bool driven = wire.wire->port_output;
|
||||
|
||||
if (driver && !driven)
|
||||
return BitMode::DRIVER;
|
||||
else if (driven && !driver)
|
||||
return BitMode::DRIVEN;
|
||||
else if (driver && driven)
|
||||
return BitMode::TRISTATE;
|
||||
else
|
||||
return keep_wire(bit.wire().wire) ? BitMode::KEEP : BitMode::NONE;
|
||||
}
|
||||
case DriveType::PORT: {
|
||||
auto const &port = bit.port();
|
||||
bool driver = celltypes.cell_output(port.cell->type, port.port);
|
||||
bool driven = celltypes.cell_input(port.cell->type, port.port);
|
||||
if (driver && !driven)
|
||||
return BitMode::DRIVER;
|
||||
else if (driven && !driver)
|
||||
return BitMode::DRIVEN_UNIQUE;
|
||||
else
|
||||
return BitMode::TRISTATE;
|
||||
}
|
||||
case DriveType::MARKER: {
|
||||
// TODO user supplied classification
|
||||
log_abort();
|
||||
}
|
||||
default:
|
||||
log_abort();
|
||||
}
|
||||
}
|
||||
|
||||
DriverMap::DriveBitId DriverMap::id_from_drive_bit(DriveBit const &bit)
|
||||
{
|
||||
switch (bit.type())
|
||||
{
|
||||
case DriveType::NONE:
|
||||
return -1;
|
||||
case DriveType::CONSTANT:
|
||||
return (int)bit.constant();
|
||||
case DriveType::WIRE: {
|
||||
auto const &wire_bit = bit.wire();
|
||||
int offset = next_offset;
|
||||
auto insertion = wire_offsets.emplace(wire_bit.wire, offset);
|
||||
if (insertion.second) {
|
||||
if (wire_bit.wire->width == 1) {
|
||||
log_assert(wire_bit.offset == 0);
|
||||
isolated_drive_bits.emplace(offset, bit);
|
||||
} else
|
||||
drive_bits.emplace(offset, DriveBitWire(wire_bit.wire, 0));
|
||||
next_offset += wire_bit.wire->width;
|
||||
}
|
||||
return insertion.first->second.id + wire_bit.offset;
|
||||
}
|
||||
case DriveType::PORT: {
|
||||
auto const &port_bit = bit.port();
|
||||
auto key = std::make_pair(port_bit.cell, port_bit.port);
|
||||
int offset = next_offset;
|
||||
auto insertion = port_offsets.emplace(key, offset);
|
||||
if (insertion.second) {
|
||||
int width = port_bit.cell->connections().at(port_bit.port).size();
|
||||
if (width == 1 && offset == 0) {
|
||||
log_assert(port_bit.offset == 0);
|
||||
isolated_drive_bits.emplace(offset, bit);
|
||||
} else
|
||||
drive_bits.emplace(offset, DriveBitPort(port_bit.cell, port_bit.port, 0));
|
||||
next_offset += width;
|
||||
}
|
||||
return insertion.first->second.id + port_bit.offset;
|
||||
}
|
||||
default:
|
||||
log_assert(false && "unsupported DriveType in DriverMap");
|
||||
}
|
||||
log_abort();
|
||||
}
|
||||
|
||||
DriveBit DriverMap::drive_bit_from_id(DriveBitId id)
|
||||
{
|
||||
auto found_isolated = isolated_drive_bits.find(id);
|
||||
if (found_isolated != isolated_drive_bits.end())
|
||||
return found_isolated->second;
|
||||
|
||||
auto found = drive_bits.upper_bound(id);
|
||||
if (found == drive_bits.begin()) {
|
||||
return id < 0 ? DriveBit() : DriveBit((State) id.id);
|
||||
}
|
||||
--found;
|
||||
DriveBit result = found->second;
|
||||
if (result.is_wire()) {
|
||||
result.wire().offset += id.id - found->first.id;
|
||||
} else {
|
||||
log_assert(result.is_port());
|
||||
result.port().offset += id.id - found->first.id;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void DriverMap::connect_directed_merge(DriveBitId driven_id, DriveBitId driver_id)
|
||||
{
|
||||
if (driven_id == driver_id)
|
||||
return;
|
||||
|
||||
same_driver.merge(driven_id, driver_id);
|
||||
|
||||
for (int i = 0, end = connected_drivers.count(driven_id); i != end; ++i)
|
||||
connected_drivers.add_edge(driver_id, connected_drivers.at(driven_id, i));
|
||||
|
||||
connected_drivers.clear(driven_id);
|
||||
|
||||
for (int i = 0, end = connected_undirected.count(driven_id); i != end; ++i)
|
||||
connected_undirected.add_edge(driver_id, connected_undirected.at(driven_id, i));
|
||||
|
||||
connected_undirected.clear(driven_id);
|
||||
}
|
||||
|
||||
void DriverMap::connect_directed_buffer(DriveBitId driven_id, DriveBitId driver_id)
|
||||
{
|
||||
connected_drivers.add_edge(driven_id, driver_id);
|
||||
}
|
||||
|
||||
void DriverMap::connect_undirected(DriveBitId a_id, DriveBitId b_id)
|
||||
{
|
||||
connected_undirected.add_edge(a_id, b_id);
|
||||
connected_undirected.add_edge(b_id, a_id);
|
||||
}
|
||||
|
||||
void DriverMap::add(Module *module)
|
||||
{
|
||||
for (auto const &conn : module->connections())
|
||||
add(conn.first, conn.second);
|
||||
|
||||
for (auto cell : module->cells())
|
||||
for (auto const &conn : cell->connections())
|
||||
add_port(cell, conn.first, conn.second);
|
||||
}
|
||||
|
||||
// Add a single bit connection to the driver map.
|
||||
void DriverMap::add(DriveBit const &a, DriveBit const &b)
|
||||
{
|
||||
DriveBitId a_id = id_from_drive_bit(a);
|
||||
DriveBitId b_id = id_from_drive_bit(b);
|
||||
|
||||
DriveBitId orig_a_id = a_id;
|
||||
DriveBitId orig_b_id = b_id;
|
||||
|
||||
a_id = same_driver.find(a_id);
|
||||
b_id = same_driver.find(b_id);
|
||||
|
||||
if (a_id == b_id)
|
||||
return;
|
||||
|
||||
BitMode a_mode = bit_mode(orig_a_id == a_id ? a : drive_bit_from_id(a_id));
|
||||
BitMode b_mode = bit_mode(orig_b_id == b_id ? b : drive_bit_from_id(b_id));
|
||||
|
||||
// If either bit is just a wire that we don't need to keep, merge and
|
||||
// use the other end as representative bit.
|
||||
if (a_mode == BitMode::NONE && !(b_mode == BitMode::DRIVEN_UNIQUE || b_mode == BitMode::DRIVEN))
|
||||
connect_directed_merge(a_id, b_id);
|
||||
else if (b_mode == BitMode::NONE && !(a_mode == BitMode::DRIVEN_UNIQUE || a_mode == BitMode::DRIVEN))
|
||||
connect_directed_merge(b_id, a_id);
|
||||
// If either bit requires a driven value and has a unique driver, merge
|
||||
// and use the other end as representative bit.
|
||||
else if (a_mode == BitMode::DRIVEN_UNIQUE && !(b_mode == BitMode::DRIVEN_UNIQUE || b_mode == BitMode::DRIVEN))
|
||||
connect_directed_buffer(a_id, b_id);
|
||||
else if (b_mode == BitMode::DRIVEN_UNIQUE && !(a_mode == BitMode::DRIVEN_UNIQUE || a_mode == BitMode::DRIVEN))
|
||||
connect_directed_buffer(b_id, a_id);
|
||||
// If either bit only drives a value, store a directed connection from
|
||||
// it to the other bit.
|
||||
else if (a_mode == BitMode::DRIVER)
|
||||
connect_directed_buffer(b_id, a_id);
|
||||
else if (b_mode == BitMode::DRIVER)
|
||||
connect_directed_buffer(a_id, b_id);
|
||||
// Otherwise we store an undirected connection which we will resolve
|
||||
// during querying.
|
||||
else
|
||||
connect_undirected(a_id, b_id);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Specialized version that avoids unpacking
|
||||
void DriverMap::add(SigSpec const &a, SigSpec const &b)
|
||||
{
|
||||
log_assert(a.size() == b.size());
|
||||
auto const &a_chunks = a.chunks();
|
||||
auto const &b_chunks = b.chunks();
|
||||
|
||||
auto a_chunk = a_chunks.begin();
|
||||
auto a_end = a_chunks.end();
|
||||
int a_offset = 0;
|
||||
|
||||
auto b_chunk = b_chunks.begin();
|
||||
int b_offset = 0;
|
||||
|
||||
SigChunk tmp_a, tmp_b;
|
||||
while (a_chunk != a_end) {
|
||||
int a_width = a_chunk->width - a_offset;
|
||||
if (a_width == 0) {
|
||||
a_offset = 0;
|
||||
++a_chunk;
|
||||
continue;
|
||||
}
|
||||
int b_width = b_chunk->width - b_offset;
|
||||
if (b_width == 0) {
|
||||
b_offset = 0;
|
||||
++b_chunk;
|
||||
continue;
|
||||
}
|
||||
int width = std::min(a_width, b_width);
|
||||
log_assert(width > 0);
|
||||
|
||||
SigChunk const &a_subchunk =
|
||||
a_offset == 0 && a_width == width ? *a_chunk : a_chunk->extract(a_offset, width);
|
||||
SigChunk const &b_subchunk =
|
||||
b_offset == 0 && b_width == width ? *b_chunk : b_chunk->extract(b_offset, width);
|
||||
|
||||
add(a_subchunk, b_subchunk);
|
||||
|
||||
a_offset += width;
|
||||
b_offset += width;
|
||||
}
|
||||
}
|
||||
|
||||
void DriverMap::add_port(Cell *cell, IdString const &port, SigSpec const &b)
|
||||
{
|
||||
int offset = 0;
|
||||
for (auto const &chunk : b.chunks()) {
|
||||
add(chunk, DriveChunkPort(cell, port, offset, chunk.width));
|
||||
offset += chunk.size();
|
||||
}
|
||||
}
|
||||
|
||||
void DriverMap::orient_undirected(DriveBitId id)
|
||||
{
|
||||
pool<DriveBitId> &seen = orient_undirected_seen;
|
||||
pool<DriveBitId> &drivers = orient_undirected_drivers;
|
||||
dict<DriveBitId, int> &distance = orient_undirected_distance;
|
||||
seen.clear();
|
||||
drivers.clear();
|
||||
|
||||
seen.emplace(id);
|
||||
|
||||
for (int pos = 0; pos < GetSize(seen); ++pos) {
|
||||
DriveBitId current = *seen.element(seen.size() - 1 - pos);
|
||||
DriveBit bit = drive_bit_from_id(current);
|
||||
|
||||
BitMode mode = bit_mode(bit);
|
||||
|
||||
if (mode == BitMode::DRIVER || mode == BitMode::TRISTATE)
|
||||
drivers.emplace(current);
|
||||
|
||||
if (connected_drivers.contains(current))
|
||||
drivers.emplace(current);
|
||||
|
||||
int undirected_driver_count = connected_undirected.count(current);
|
||||
|
||||
for (int i = 0; i != undirected_driver_count; ++i)
|
||||
seen.emplace(same_driver.find(connected_undirected.at(current, i)));
|
||||
}
|
||||
|
||||
if (drivers.empty())
|
||||
for (auto seen_id : seen)
|
||||
drivers.emplace(seen_id);
|
||||
|
||||
for (auto driver : drivers)
|
||||
{
|
||||
distance.clear();
|
||||
distance.emplace(driver, 0);
|
||||
|
||||
for (int pos = 0; pos < GetSize(distance); ++pos) {
|
||||
auto current_it = distance.element(distance.size() - 1 - pos);
|
||||
|
||||
DriveBitId current = current_it->first;
|
||||
int undirected_driver_count = connected_undirected.count(current);
|
||||
|
||||
for (int i = 0; i != undirected_driver_count; ++i)
|
||||
{
|
||||
DriveBitId next = same_driver.find(connected_undirected.at(current, i));
|
||||
auto emplaced = distance.emplace(next, current_it->second + 1);
|
||||
if (emplaced.first->second == current_it->second + 1)
|
||||
connected_oriented.add_edge(next, current);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto seen_id : seen)
|
||||
oriented_present.emplace(seen_id);
|
||||
}
|
||||
|
||||
DriveBit DriverMap::operator()(DriveBit const &bit)
|
||||
{
|
||||
if (bit.type() == DriveType::MARKER || bit.type() == DriveType::NONE)
|
||||
return bit;
|
||||
if (bit.type() == DriveType::MULTIPLE)
|
||||
{
|
||||
DriveBit result;
|
||||
for (auto const &inner : bit.multiple().multiple())
|
||||
result.merge((*this)(inner));
|
||||
return result;
|
||||
}
|
||||
|
||||
DriveBitId bit_id = id_from_drive_bit(bit);
|
||||
|
||||
DriveBitId bit_repr_id = same_driver.find(bit_id);
|
||||
|
||||
DriveBit bit_repr = drive_bit_from_id(bit_repr_id);
|
||||
|
||||
BitMode mode = bit_mode(bit_repr);
|
||||
|
||||
if (mode == BitMode::KEEP && bit_repr_id != bit_id)
|
||||
return bit_repr;
|
||||
|
||||
int implicit_driver_count = connected_drivers.count(bit_repr_id);
|
||||
if (connected_undirected.contains(bit_repr_id) && !oriented_present.count(bit_repr_id))
|
||||
orient_undirected(bit_repr_id);
|
||||
|
||||
DriveBit driver;
|
||||
|
||||
if (mode == BitMode::DRIVER || mode == BitMode::TRISTATE)
|
||||
driver = bit_repr;
|
||||
|
||||
for (int i = 0; i != implicit_driver_count; ++i)
|
||||
driver.merge(drive_bit_from_id(connected_drivers.at(bit_repr_id, i)));
|
||||
|
||||
int oriented_driver_count = connected_oriented.count(bit_repr_id);
|
||||
for (int i = 0; i != oriented_driver_count; ++i)
|
||||
driver.merge(drive_bit_from_id(connected_oriented.at(bit_repr_id, i)));
|
||||
|
||||
return driver;
|
||||
}
|
||||
|
||||
DriveSpec DriverMap::operator()(DriveSpec spec)
|
||||
{
|
||||
DriveSpec result;
|
||||
|
||||
for (int i = 0, width = spec.size(); i != width; ++i)
|
||||
result.append((*this)(spec[i]));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const char *log_signal(DriveChunkWire const &chunk)
|
||||
{
|
||||
const char *id = log_id(chunk.wire->name);
|
||||
if (chunk.is_whole())
|
||||
return id;
|
||||
if (chunk.width == 1)
|
||||
return log_str(stringf("%s [%d]", id, chunk.offset));
|
||||
return log_str(stringf("%s [%d:%d]", id, chunk.offset + chunk.width - 1, chunk.offset));
|
||||
}
|
||||
|
||||
|
||||
const char *log_signal(DriveChunkPort const &chunk)
|
||||
{
|
||||
const char *cell_id = log_id(chunk.cell->name);
|
||||
const char *port_id = log_id(chunk.port);
|
||||
if (chunk.is_whole())
|
||||
return log_str(stringf("%s <%s>", cell_id, port_id));
|
||||
if (chunk.width == 1)
|
||||
return log_str(stringf("%s <%s> [%d]", cell_id, port_id, chunk.offset));
|
||||
return log_str(stringf("%s <%s> [%d:%d]", cell_id, port_id, chunk.offset + chunk.width - 1, chunk.offset));
|
||||
}
|
||||
|
||||
const char *log_signal(DriveChunkMarker const &chunk)
|
||||
{
|
||||
if (chunk.width == 1)
|
||||
return log_str(stringf("<marker %d> [%d]", chunk.marker, chunk.offset));
|
||||
return log_str(stringf("<marker %d> [%d:%d]", chunk.marker, chunk.offset + chunk.width - 1, chunk.offset));
|
||||
}
|
||||
|
||||
const char *log_signal(DriveChunk const &chunk)
|
||||
{
|
||||
switch (chunk.type())
|
||||
{
|
||||
case DriveType::NONE:
|
||||
return log_str(stringf("<none x%d>", chunk.size()));
|
||||
case DriveType::CONSTANT:
|
||||
return log_const(chunk.constant());
|
||||
case DriveType::WIRE:
|
||||
return log_signal(chunk.wire());
|
||||
case DriveType::PORT:
|
||||
return log_signal(chunk.port());
|
||||
case DriveType::MARKER:
|
||||
return log_signal(chunk.marker());
|
||||
case DriveType::MULTIPLE: {
|
||||
std::string str = "<multiple";
|
||||
const char *sep = " ";
|
||||
for (auto const &single : chunk.multiple().multiple()) {
|
||||
str += sep;
|
||||
sep = ", ";
|
||||
str += log_signal(single);
|
||||
}
|
||||
str += ">";
|
||||
return log_str(str);
|
||||
}
|
||||
default:
|
||||
log_abort();
|
||||
}
|
||||
}
|
||||
|
||||
const char *log_signal(DriveSpec const &spec)
|
||||
{
|
||||
auto &chunks = spec.chunks();
|
||||
if (chunks.empty())
|
||||
return "{}";
|
||||
if (chunks.size() == 1)
|
||||
return log_signal(chunks[0]);
|
||||
|
||||
std::string str;
|
||||
const char *sep = "{ ";
|
||||
|
||||
for (auto i = chunks.rbegin(), end = chunks.rend(); i != end; ++i)
|
||||
{
|
||||
str += sep;
|
||||
sep = " ";
|
||||
str += log_signal(*i);
|
||||
}
|
||||
str += " }";
|
||||
|
||||
return log_str(str);
|
||||
}
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
1332
kernel/drivertools.h
Normal file
1332
kernel/drivertools.h
Normal file
File diff suppressed because it is too large
Load diff
14
kernel/ff.cc
14
kernel/ff.cc
|
|
@ -298,11 +298,11 @@ FfData FfData::slice(const std::vector<int> &bits) {
|
|||
res.sig_set.append(sig_set[i]);
|
||||
}
|
||||
if (has_arst)
|
||||
res.val_arst.bits.push_back(val_arst[i]);
|
||||
res.val_arst.bits().push_back(val_arst[i]);
|
||||
if (has_srst)
|
||||
res.val_srst.bits.push_back(val_srst[i]);
|
||||
res.val_srst.bits().push_back(val_srst[i]);
|
||||
if (initvals)
|
||||
res.val_init.bits.push_back(val_init[i]);
|
||||
res.val_init.bits().push_back(val_init[i]);
|
||||
}
|
||||
res.width = GetSize(res.sig_q);
|
||||
return res;
|
||||
|
|
@ -688,10 +688,10 @@ void FfData::flip_rst_bits(const pool<int> &bits) {
|
|||
|
||||
for (auto bit: bits) {
|
||||
if (has_arst)
|
||||
val_arst[bit] = invert(val_arst[bit]);
|
||||
val_arst.bits()[bit] = invert(val_arst[bit]);
|
||||
if (has_srst)
|
||||
val_srst[bit] = invert(val_srst[bit]);
|
||||
val_init[bit] = invert(val_init[bit]);
|
||||
val_srst.bits()[bit] = invert(val_srst[bit]);
|
||||
val_init.bits()[bit] = invert(val_init[bit]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -760,7 +760,7 @@ void FfData::flip_bits(const pool<int> &bits) {
|
|||
|
||||
Const mask = Const(State::S0, width);
|
||||
for (auto bit: bits)
|
||||
mask.bits[bit] = State::S1;
|
||||
mask.bits()[bit] = State::S1;
|
||||
|
||||
if (has_clk || has_gclk)
|
||||
sig_d = module->Xor(NEW_ID, sig_d, mask);
|
||||
|
|
|
|||
|
|
@ -131,9 +131,11 @@ struct FfData {
|
|||
bool is_fine;
|
||||
// True if this FF is an $anyinit cell. Depends on has_gclk.
|
||||
bool is_anyinit;
|
||||
// Polarities, corresponding to sig_*. True means active-high, false
|
||||
// means active-low.
|
||||
// Polarities, corresponding to sig_*.
|
||||
// True means rising edge, false means falling edge.
|
||||
bool pol_clk;
|
||||
// True means active-high, false
|
||||
// means active-low.
|
||||
bool pol_ce;
|
||||
bool pol_aload;
|
||||
bool pol_arst;
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ struct FfInitVals
|
|||
{
|
||||
RTLIL::Const res;
|
||||
for (auto bit : sig)
|
||||
res.bits.push_back((*this)(bit));
|
||||
res.bits().push_back((*this)(bit));
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
@ -93,12 +93,12 @@ struct FfInitVals
|
|||
initbits[mbit] = std::make_pair(val,abit);
|
||||
auto it2 = abit.wire->attributes.find(ID::init);
|
||||
if (it2 != abit.wire->attributes.end()) {
|
||||
it2->second[abit.offset] = val;
|
||||
it2->second.bits()[abit.offset] = val;
|
||||
if (it2->second.is_fully_undef())
|
||||
abit.wire->attributes.erase(it2);
|
||||
} else if (val != State::Sx) {
|
||||
Const cval(State::Sx, GetSize(abit.wire));
|
||||
cval[abit.offset] = val;
|
||||
cval.bits()[abit.offset] = val;
|
||||
abit.wire->attributes[ID::init] = cval;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,9 +42,9 @@ bool FfMergeHelper::find_output_ff(RTLIL::SigSpec sig, FfData &ff, pool<std::pai
|
|||
ff.sig_d.append(bit);
|
||||
ff.sig_clr.append(State::Sx);
|
||||
ff.sig_set.append(State::Sx);
|
||||
ff.val_init.bits.push_back(State::Sx);
|
||||
ff.val_srst.bits.push_back(State::Sx);
|
||||
ff.val_arst.bits.push_back(State::Sx);
|
||||
ff.val_init.bits().push_back(State::Sx);
|
||||
ff.val_srst.bits().push_back(State::Sx);
|
||||
ff.val_arst.bits().push_back(State::Sx);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -147,9 +147,9 @@ bool FfMergeHelper::find_output_ff(RTLIL::SigSpec sig, FfData &ff, pool<std::pai
|
|||
ff.sig_q.append(cur_ff.sig_q[idx]);
|
||||
ff.sig_clr.append(ff.has_sr ? cur_ff.sig_clr[idx] : State::S0);
|
||||
ff.sig_set.append(ff.has_sr ? cur_ff.sig_set[idx] : State::S0);
|
||||
ff.val_arst.bits.push_back(ff.has_arst ? cur_ff.val_arst[idx] : State::Sx);
|
||||
ff.val_srst.bits.push_back(ff.has_srst ? cur_ff.val_srst[idx] : State::Sx);
|
||||
ff.val_init.bits.push_back(cur_ff.val_init[idx]);
|
||||
ff.val_arst.bits().push_back(ff.has_arst ? cur_ff.val_arst[idx] : State::Sx);
|
||||
ff.val_srst.bits().push_back(ff.has_srst ? cur_ff.val_srst[idx] : State::Sx);
|
||||
ff.val_init.bits().push_back(cur_ff.val_init[idx]);
|
||||
found = true;
|
||||
}
|
||||
|
||||
|
|
@ -174,9 +174,9 @@ bool FfMergeHelper::find_input_ff(RTLIL::SigSpec sig, FfData &ff, pool<std::pair
|
|||
// These two will be fixed up later.
|
||||
ff.sig_clr.append(State::Sx);
|
||||
ff.sig_set.append(State::Sx);
|
||||
ff.val_init.bits.push_back(bit.data);
|
||||
ff.val_srst.bits.push_back(bit.data);
|
||||
ff.val_arst.bits.push_back(bit.data);
|
||||
ff.val_init.bits().push_back(bit.data);
|
||||
ff.val_srst.bits().push_back(bit.data);
|
||||
ff.val_arst.bits().push_back(bit.data);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -274,9 +274,9 @@ bool FfMergeHelper::find_input_ff(RTLIL::SigSpec sig, FfData &ff, pool<std::pair
|
|||
ff.sig_q.append(cur_ff.sig_q[idx]);
|
||||
ff.sig_clr.append(ff.has_sr ? cur_ff.sig_clr[idx] : State::S0);
|
||||
ff.sig_set.append(ff.has_sr ? cur_ff.sig_set[idx] : State::S0);
|
||||
ff.val_arst.bits.push_back(ff.has_arst ? cur_ff.val_arst[idx] : State::Sx);
|
||||
ff.val_srst.bits.push_back(ff.has_srst ? cur_ff.val_srst[idx] : State::Sx);
|
||||
ff.val_init.bits.push_back(cur_ff.val_init[idx]);
|
||||
ff.val_arst.bits().push_back(ff.has_arst ? cur_ff.val_arst[idx] : State::Sx);
|
||||
ff.val_srst.bits().push_back(ff.has_srst ? cur_ff.val_srst[idx] : State::Sx);
|
||||
ff.val_init.bits().push_back(cur_ff.val_init[idx]);
|
||||
found = true;
|
||||
}
|
||||
|
||||
|
|
|
|||
853
kernel/functional.cc
Normal file
853
kernel/functional.cc
Normal file
|
|
@ -0,0 +1,853 @@
|
|||
/*
|
||||
* 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/topo_scc.h"
|
||||
#include "ff.h"
|
||||
#include "ffinit.h"
|
||||
#include <deque>
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
namespace Functional {
|
||||
|
||||
const char *fn_to_string(Fn fn) {
|
||||
switch(fn) {
|
||||
case Fn::invalid: return "invalid";
|
||||
case Fn::buf: return "buf";
|
||||
case Fn::slice: return "slice";
|
||||
case Fn::zero_extend: return "zero_extend";
|
||||
case Fn::sign_extend: return "sign_extend";
|
||||
case Fn::concat: return "concat";
|
||||
case Fn::add: return "add";
|
||||
case Fn::sub: return "sub";
|
||||
case Fn::mul: return "mul";
|
||||
case Fn::unsigned_div: return "unsigned_div";
|
||||
case Fn::unsigned_mod: return "unsigned_mod";
|
||||
case Fn::bitwise_and: return "bitwise_and";
|
||||
case Fn::bitwise_or: return "bitwise_or";
|
||||
case Fn::bitwise_xor: return "bitwise_xor";
|
||||
case Fn::bitwise_not: return "bitwise_not";
|
||||
case Fn::reduce_and: return "reduce_and";
|
||||
case Fn::reduce_or: return "reduce_or";
|
||||
case Fn::reduce_xor: return "reduce_xor";
|
||||
case Fn::unary_minus: return "unary_minus";
|
||||
case Fn::equal: return "equal";
|
||||
case Fn::not_equal: return "not_equal";
|
||||
case Fn::signed_greater_than: return "signed_greater_than";
|
||||
case Fn::signed_greater_equal: return "signed_greater_equal";
|
||||
case Fn::unsigned_greater_than: return "unsigned_greater_than";
|
||||
case Fn::unsigned_greater_equal: return "unsigned_greater_equal";
|
||||
case Fn::logical_shift_left: return "logical_shift_left";
|
||||
case Fn::logical_shift_right: return "logical_shift_right";
|
||||
case Fn::arithmetic_shift_right: return "arithmetic_shift_right";
|
||||
case Fn::mux: return "mux";
|
||||
case Fn::constant: return "constant";
|
||||
case Fn::input: return "input";
|
||||
case Fn::state: return "state";
|
||||
case Fn::memory_read: return "memory_read";
|
||||
case Fn::memory_write: return "memory_write";
|
||||
}
|
||||
log_error("fn_to_string: unknown Functional::Fn value %d", (int)fn);
|
||||
}
|
||||
|
||||
vector<IRInput const*> IR::inputs(IdString kind) const {
|
||||
vector<IRInput const*> ret;
|
||||
for (const auto &[name, input] : _inputs)
|
||||
if(input.kind == kind)
|
||||
ret.push_back(&input);
|
||||
return ret;
|
||||
}
|
||||
|
||||
vector<IROutput const*> IR::outputs(IdString kind) const {
|
||||
vector<IROutput const*> ret;
|
||||
for (const auto &[name, output] : _outputs)
|
||||
if(output.kind == kind)
|
||||
ret.push_back(&output);
|
||||
return ret;
|
||||
}
|
||||
|
||||
vector<IRState const*> IR::states(IdString kind) const {
|
||||
vector<IRState const*> ret;
|
||||
for (const auto &[name, state] : _states)
|
||||
if(state.kind == kind)
|
||||
ret.push_back(&state);
|
||||
return ret;
|
||||
}
|
||||
|
||||
vector<IRInput const*> IR::all_inputs() const {
|
||||
vector<IRInput const*> ret;
|
||||
for (const auto &[name, input] : _inputs)
|
||||
ret.push_back(&input);
|
||||
return ret;
|
||||
}
|
||||
|
||||
vector<IROutput const*> IR::all_outputs() const {
|
||||
vector<IROutput const*> ret;
|
||||
for (const auto &[name, output] : _outputs)
|
||||
ret.push_back(&output);
|
||||
return ret;
|
||||
}
|
||||
|
||||
vector<IRState const*> IR::all_states() const {
|
||||
vector<IRState const*> ret;
|
||||
for (const auto &[name, state] : _states)
|
||||
ret.push_back(&state);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct PrintVisitor : DefaultVisitor<std::string> {
|
||||
std::function<std::string(Node)> np;
|
||||
PrintVisitor(std::function<std::string(Node)> np) : np(np) { }
|
||||
// as a general rule the default handler is good enough iff the only arguments are of type Node
|
||||
std::string slice(Node, Node a, int offset, int out_width) override { return "slice(" + np(a) + ", " + std::to_string(offset) + ", " + std::to_string(out_width) + ")"; }
|
||||
std::string zero_extend(Node, Node a, int out_width) override { return "zero_extend(" + np(a) + ", " + std::to_string(out_width) + ")"; }
|
||||
std::string sign_extend(Node, Node a, int out_width) override { return "sign_extend(" + np(a) + ", " + std::to_string(out_width) + ")"; }
|
||||
std::string constant(Node, RTLIL::Const const& value) override { return "constant(" + value.as_string() + ")"; }
|
||||
std::string input(Node, IdString name, IdString kind) override { return "input(" + name.str() + ", " + kind.str() + ")"; }
|
||||
std::string state(Node, IdString name, IdString kind) override { return "state(" + name.str() + ", " + kind.str() + ")"; }
|
||||
std::string default_handler(Node self) override {
|
||||
std::string ret = fn_to_string(self.fn());
|
||||
ret += "(";
|
||||
for(size_t i = 0; i < self.arg_count(); i++) {
|
||||
if(i > 0) ret += ", ";
|
||||
ret += np(self.arg(i));
|
||||
}
|
||||
ret += ")";
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
std::string Node::to_string()
|
||||
{
|
||||
return to_string([](Node n) { return RTLIL::unescape_id(n.name()); });
|
||||
}
|
||||
|
||||
std::string Node::to_string(std::function<std::string(Node)> np)
|
||||
{
|
||||
return visit(PrintVisitor(np));
|
||||
}
|
||||
|
||||
class CellSimplifier {
|
||||
Factory &factory;
|
||||
Node sign(Node a) {
|
||||
return factory.slice(a, a.width() - 1, 1);
|
||||
}
|
||||
Node neg_if(Node a, Node s) {
|
||||
return factory.mux(a, factory.unary_minus(a), s);
|
||||
}
|
||||
Node abs(Node a) {
|
||||
return neg_if(a, sign(a));
|
||||
}
|
||||
Node handle_shift(Node a, Node b, bool is_right, bool is_signed) {
|
||||
// to prevent new_width == 0, we handle this case separately
|
||||
if(a.width() == 1) {
|
||||
if(!is_signed)
|
||||
return factory.bitwise_and(a, factory.bitwise_not(factory.reduce_or(b)));
|
||||
else
|
||||
return a;
|
||||
}
|
||||
int new_width = ceil_log2(a.width());
|
||||
Node b_truncated = factory.extend(b, new_width, false);
|
||||
Node y =
|
||||
!is_right ? factory.logical_shift_left(a, b_truncated) :
|
||||
!is_signed ? factory.logical_shift_right(a, b_truncated) :
|
||||
factory.arithmetic_shift_right(a, b_truncated);
|
||||
if(b.width() <= new_width)
|
||||
return y;
|
||||
Node overflow = factory.unsigned_greater_equal(b, factory.constant(RTLIL::Const(a.width(), b.width())));
|
||||
Node y_if_overflow = is_signed ? factory.extend(sign(a), a.width(), true) : factory.constant(RTLIL::Const(State::S0, a.width()));
|
||||
return factory.mux(y, y_if_overflow, overflow);
|
||||
}
|
||||
public:
|
||||
Node logical_shift_left(Node a, Node b) { return handle_shift(a, b, false, false); }
|
||||
Node logical_shift_right(Node a, Node b) { return handle_shift(a, b, true, false); }
|
||||
Node arithmetic_shift_right(Node a, Node b) { return handle_shift(a, b, true, true); }
|
||||
Node bitwise_mux(Node a, Node b, Node s) {
|
||||
Node aa = factory.bitwise_and(a, factory.bitwise_not(s));
|
||||
Node bb = factory.bitwise_and(b, s);
|
||||
return factory.bitwise_or(aa, bb);
|
||||
}
|
||||
CellSimplifier(Factory &f) : factory(f) {}
|
||||
private:
|
||||
Node handle_pow(Node a0, Node b, int y_width, bool is_signed) {
|
||||
Node a = factory.extend(a0, y_width, is_signed);
|
||||
Node r = factory.constant(Const(1, y_width));
|
||||
for(int i = 0; i < b.width(); i++) {
|
||||
Node b_bit = factory.slice(b, i, 1);
|
||||
r = factory.mux(r, factory.mul(r, a), b_bit);
|
||||
a = factory.mul(a, a);
|
||||
}
|
||||
if (is_signed) {
|
||||
Node a_ge_1 = factory.unsigned_greater_than(abs(a0), factory.constant(Const(1, a0.width())));
|
||||
Node zero_result = factory.bitwise_and(a_ge_1, sign(b));
|
||||
r = factory.mux(r, factory.constant(Const(0, y_width)), zero_result);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
Node handle_bmux(Node a, Node s, int a_offset, int width, int sn) {
|
||||
if(sn < 1)
|
||||
return factory.slice(a, a_offset, width);
|
||||
else {
|
||||
Node y0 = handle_bmux(a, s, a_offset, width, sn - 1);
|
||||
Node y1 = handle_bmux(a, s, a_offset + (width << (sn - 1)), width, sn - 1);
|
||||
return factory.mux(y0, y1, factory.slice(s, sn - 1, 1));
|
||||
}
|
||||
}
|
||||
Node handle_pmux(Node a, Node b, Node s) {
|
||||
// TODO : what to do about multiple b bits set ?
|
||||
log_assert(b.width() == a.width() * s.width());
|
||||
Node y = a;
|
||||
for(int i = 0; i < s.width(); i++)
|
||||
y = factory.mux(y, factory.slice(b, a.width() * i, a.width()), factory.slice(s, i, 1));
|
||||
return y;
|
||||
}
|
||||
dict<IdString, Node> handle_fa(Node a, Node b, Node c) {
|
||||
Node t1 = factory.bitwise_xor(a, b);
|
||||
Node t2 = factory.bitwise_and(a, b);
|
||||
Node t3 = factory.bitwise_and(c, t1);
|
||||
Node y = factory.bitwise_xor(c, t1);
|
||||
Node x = factory.bitwise_or(t2, t3);
|
||||
return {{ID(X), x}, {ID(Y), y}};
|
||||
}
|
||||
dict<IdString, Node> handle_alu(Node a_in, Node b_in, int y_width, bool is_signed, Node ci, Node bi) {
|
||||
Node a = factory.extend(a_in, y_width, is_signed);
|
||||
Node b_uninverted = factory.extend(b_in, y_width, is_signed);
|
||||
Node b = factory.mux(b_uninverted, factory.bitwise_not(b_uninverted), bi);
|
||||
Node x = factory.bitwise_xor(a, b);
|
||||
// we can compute the carry into each bit using (a+b+c)^a^b. since we want the carry out,
|
||||
// i.e. the carry into the next bit, we have to add an extra bit to a and b, and
|
||||
// then slice off the bottom bit of the result.
|
||||
Node a_extra = factory.extend(a, y_width + 1, false);
|
||||
Node b_extra = factory.extend(b, y_width + 1, false);
|
||||
Node y_extra = factory.add(factory.add(a_extra, b_extra), factory.extend(ci, a.width() + 1, false));
|
||||
Node y = factory.slice(y_extra, 0, y_width);
|
||||
Node carries = factory.bitwise_xor(y_extra, factory.bitwise_xor(a_extra, b_extra));
|
||||
Node co = factory.slice(carries, 1, y_width);
|
||||
return {{ID(X), x}, {ID(Y), y}, {ID(CO), co}};
|
||||
}
|
||||
Node handle_lcu(Node p, Node g, Node ci) {
|
||||
return handle_alu(g, factory.bitwise_or(p, g), g.width(), false, ci, factory.constant(Const(State::S0, 1))).at(ID(CO));
|
||||
}
|
||||
public:
|
||||
std::variant<dict<IdString, Node>, Node> handle(IdString cellName, IdString cellType, dict<IdString, Const> parameters, dict<IdString, Node> inputs)
|
||||
{
|
||||
int a_width = parameters.at(ID(A_WIDTH), Const(-1)).as_int();
|
||||
int b_width = parameters.at(ID(B_WIDTH), Const(-1)).as_int();
|
||||
int y_width = parameters.at(ID(Y_WIDTH), Const(-1)).as_int();
|
||||
bool a_signed = parameters.at(ID(A_SIGNED), Const(0)).as_bool();
|
||||
bool b_signed = parameters.at(ID(B_SIGNED), Const(0)).as_bool();
|
||||
if(cellType.in({ID($add), ID($sub), ID($and), ID($or), ID($xor), ID($xnor), ID($mul)})){
|
||||
bool is_signed = a_signed && b_signed;
|
||||
Node a = factory.extend(inputs.at(ID(A)), y_width, is_signed);
|
||||
Node b = factory.extend(inputs.at(ID(B)), y_width, is_signed);
|
||||
if(cellType == ID($add))
|
||||
return factory.add(a, b);
|
||||
else if(cellType == ID($sub))
|
||||
return factory.sub(a, b);
|
||||
else if(cellType == ID($mul))
|
||||
return factory.mul(a, b);
|
||||
else if(cellType == ID($and))
|
||||
return factory.bitwise_and(a, b);
|
||||
else if(cellType == ID($or))
|
||||
return factory.bitwise_or(a, b);
|
||||
else if(cellType == ID($xor))
|
||||
return factory.bitwise_xor(a, b);
|
||||
else if(cellType == ID($xnor))
|
||||
return factory.bitwise_not(factory.bitwise_xor(a, b));
|
||||
else
|
||||
log_abort();
|
||||
}else if(cellType.in({ID($eq), ID($ne), ID($eqx), ID($nex), ID($le), ID($lt), ID($ge), ID($gt)})){
|
||||
bool is_signed = a_signed && b_signed;
|
||||
int width = max(a_width, b_width);
|
||||
Node a = factory.extend(inputs.at(ID(A)), width, is_signed);
|
||||
Node b = factory.extend(inputs.at(ID(B)), width, is_signed);
|
||||
if(cellType.in({ID($eq), ID($eqx)}))
|
||||
return factory.extend(factory.equal(a, b), y_width, false);
|
||||
else if(cellType.in({ID($ne), ID($nex)}))
|
||||
return factory.extend(factory.not_equal(a, b), y_width, false);
|
||||
else if(cellType == ID($lt))
|
||||
return factory.extend(is_signed ? factory.signed_greater_than(b, a) : factory.unsigned_greater_than(b, a), y_width, false);
|
||||
else if(cellType == ID($le))
|
||||
return factory.extend(is_signed ? factory.signed_greater_equal(b, a) : factory.unsigned_greater_equal(b, a), y_width, false);
|
||||
else if(cellType == ID($gt))
|
||||
return factory.extend(is_signed ? factory.signed_greater_than(a, b) : factory.unsigned_greater_than(a, b), y_width, false);
|
||||
else if(cellType == ID($ge))
|
||||
return factory.extend(is_signed ? factory.signed_greater_equal(a, b) : factory.unsigned_greater_equal(a, b), y_width, false);
|
||||
else
|
||||
log_abort();
|
||||
}else if(cellType.in({ID($logic_or), ID($logic_and)})){
|
||||
Node a = factory.reduce_or(inputs.at(ID(A)));
|
||||
Node b = factory.reduce_or(inputs.at(ID(B)));
|
||||
Node y = cellType == ID($logic_and) ? factory.bitwise_and(a, b) : factory.bitwise_or(a, b);
|
||||
return factory.extend(y, y_width, false);
|
||||
}else if(cellType == ID($not)){
|
||||
Node a = factory.extend(inputs.at(ID(A)), y_width, a_signed);
|
||||
return factory.bitwise_not(a);
|
||||
}else if(cellType == ID($pos)){
|
||||
return factory.extend(inputs.at(ID(A)), y_width, a_signed);
|
||||
}else if(cellType == ID($neg)){
|
||||
Node a = factory.extend(inputs.at(ID(A)), y_width, a_signed);
|
||||
return factory.unary_minus(a);
|
||||
}else if(cellType == ID($logic_not)){
|
||||
Node a = factory.reduce_or(inputs.at(ID(A)));
|
||||
Node y = factory.bitwise_not(a);
|
||||
return factory.extend(y, y_width, false);
|
||||
}else if(cellType.in({ID($reduce_or), ID($reduce_bool)})){
|
||||
Node a = factory.reduce_or(inputs.at(ID(A)));
|
||||
return factory.extend(a, y_width, false);
|
||||
}else if(cellType == ID($reduce_and)){
|
||||
Node a = factory.reduce_and(inputs.at(ID(A)));
|
||||
return factory.extend(a, y_width, false);
|
||||
}else if(cellType.in({ID($reduce_xor), ID($reduce_xnor)})){
|
||||
Node a = factory.reduce_xor(inputs.at(ID(A)));
|
||||
Node y = cellType == ID($reduce_xnor) ? factory.bitwise_not(a) : a;
|
||||
return factory.extend(y, y_width, false);
|
||||
}else if(cellType == ID($shl) || cellType == ID($sshl)){
|
||||
Node a = factory.extend(inputs.at(ID(A)), y_width, a_signed);
|
||||
Node b = inputs.at(ID(B));
|
||||
return logical_shift_left(a, b);
|
||||
}else if(cellType == ID($shr) || cellType == ID($sshr)){
|
||||
int width = max(a_width, y_width);
|
||||
Node a = factory.extend(inputs.at(ID(A)), width, a_signed);
|
||||
Node b = inputs.at(ID(B));
|
||||
Node y = a_signed && cellType == ID($sshr) ?
|
||||
arithmetic_shift_right(a, b) :
|
||||
logical_shift_right(a, b);
|
||||
return factory.extend(y, y_width, a_signed);
|
||||
}else if(cellType == ID($shiftx) || cellType == ID($shift)){
|
||||
int width = max(a_width, y_width);
|
||||
Node a = factory.extend(inputs.at(ID(A)), width, cellType == ID($shift) && a_signed);
|
||||
Node b = inputs.at(ID(B));
|
||||
Node shr = logical_shift_right(a, b);
|
||||
if(b_signed) {
|
||||
Node shl = logical_shift_left(a, factory.unary_minus(b));
|
||||
Node y = factory.mux(shr, shl, sign(b));
|
||||
return factory.extend(y, y_width, false);
|
||||
} else {
|
||||
return factory.extend(shr, y_width, false);
|
||||
}
|
||||
}else if(cellType == ID($mux)){
|
||||
return factory.mux(inputs.at(ID(A)), inputs.at(ID(B)), inputs.at(ID(S)));
|
||||
}else if(cellType == ID($pmux)){
|
||||
return handle_pmux(inputs.at(ID(A)), inputs.at(ID(B)), inputs.at(ID(S)));
|
||||
}else if(cellType == ID($concat)){
|
||||
Node a = inputs.at(ID(A));
|
||||
Node b = inputs.at(ID(B));
|
||||
return factory.concat(a, b);
|
||||
}else if(cellType == ID($slice)){
|
||||
int offset = parameters.at(ID(OFFSET)).as_int();
|
||||
Node a = inputs.at(ID(A));
|
||||
return factory.slice(a, offset, y_width);
|
||||
}else if(cellType.in({ID($div), ID($mod), ID($divfloor), ID($modfloor)})) {
|
||||
int width = max(a_width, b_width);
|
||||
bool is_signed = a_signed && b_signed;
|
||||
Node a = factory.extend(inputs.at(ID(A)), width, is_signed);
|
||||
Node b = factory.extend(inputs.at(ID(B)), width, is_signed);
|
||||
if(is_signed) {
|
||||
if(cellType == ID($div)) {
|
||||
// divide absolute values, then flip the sign if input signs differ
|
||||
// but extend the width first, to handle the case (most negative value) / (-1)
|
||||
Node abs_y = factory.unsigned_div(abs(a), abs(b));
|
||||
Node out_sign = factory.not_equal(sign(a), sign(b));
|
||||
return neg_if(factory.extend(abs_y, y_width, false), out_sign);
|
||||
} else if(cellType == ID($mod)) {
|
||||
// similar to division but output sign == divisor sign
|
||||
Node abs_y = factory.unsigned_mod(abs(a), abs(b));
|
||||
return neg_if(factory.extend(abs_y, y_width, false), sign(a));
|
||||
} else if(cellType == ID($divfloor)) {
|
||||
// if b is negative, flip both signs so that b is positive
|
||||
Node b_sign = sign(b);
|
||||
Node a1 = neg_if(a, b_sign);
|
||||
Node b1 = neg_if(b, b_sign);
|
||||
// if a is now negative, calculate ~((~a) / b) = -((-a - 1) / b + 1)
|
||||
// which equals the negative of (-a) / b with rounding up rather than down
|
||||
// note that to handle the case where a = most negative value properly,
|
||||
// we have to calculate a1_sign from the original values rather than using sign(a1)
|
||||
Node a1_sign = factory.bitwise_and(factory.not_equal(sign(a), sign(b)), factory.reduce_or(a));
|
||||
Node a2 = factory.mux(a1, factory.bitwise_not(a1), a1_sign);
|
||||
Node y1 = factory.unsigned_div(a2, b1);
|
||||
Node y2 = factory.extend(y1, y_width, false);
|
||||
return factory.mux(y2, factory.bitwise_not(y2), a1_sign);
|
||||
} else if(cellType == ID($modfloor)) {
|
||||
// calculate |a| % |b| and then subtract from |b| if input signs differ and the remainder is non-zero
|
||||
Node abs_b = abs(b);
|
||||
Node abs_y = factory.unsigned_mod(abs(a), abs_b);
|
||||
Node flip_y = factory.bitwise_and(factory.bitwise_xor(sign(a), sign(b)), factory.reduce_or(abs_y));
|
||||
Node y_flipped = factory.mux(abs_y, factory.sub(abs_b, abs_y), flip_y);
|
||||
// since y_flipped is strictly less than |b|, the top bit is always 0 and we can just sign extend the flipped result
|
||||
Node y = neg_if(y_flipped, sign(b));
|
||||
return factory.extend(y, y_width, true);
|
||||
} else
|
||||
log_error("unhandled cell in CellSimplifier %s\n", cellType.c_str());
|
||||
} else {
|
||||
if(cellType.in({ID($mod), ID($modfloor)}))
|
||||
return factory.extend(factory.unsigned_mod(a, b), y_width, false);
|
||||
else
|
||||
return factory.extend(factory.unsigned_div(a, b), y_width, false);
|
||||
}
|
||||
} else if(cellType == ID($pow)) {
|
||||
return handle_pow(inputs.at(ID(A)), inputs.at(ID(B)), y_width, a_signed && b_signed);
|
||||
} else if (cellType == ID($lut)) {
|
||||
int width = parameters.at(ID(WIDTH)).as_int();
|
||||
Const lut_table = parameters.at(ID(LUT));
|
||||
lut_table.extu(1 << width);
|
||||
return handle_bmux(factory.constant(lut_table), inputs.at(ID(A)), 0, 1, width);
|
||||
} else if (cellType == ID($bwmux)) {
|
||||
Node a = inputs.at(ID(A));
|
||||
Node b = inputs.at(ID(B));
|
||||
Node s = inputs.at(ID(S));
|
||||
return factory.bitwise_or(
|
||||
factory.bitwise_and(a, factory.bitwise_not(s)),
|
||||
factory.bitwise_and(b, s));
|
||||
} else if (cellType == ID($bweqx)) {
|
||||
Node a = inputs.at(ID(A));
|
||||
Node b = inputs.at(ID(B));
|
||||
return factory.bitwise_not(factory.bitwise_xor(a, b));
|
||||
} else if(cellType == ID($bmux)) {
|
||||
int width = parameters.at(ID(WIDTH)).as_int();
|
||||
int s_width = parameters.at(ID(S_WIDTH)).as_int();
|
||||
return handle_bmux(inputs.at(ID(A)), inputs.at(ID(S)), 0, width, s_width);
|
||||
} else if(cellType == ID($demux)) {
|
||||
int width = parameters.at(ID(WIDTH)).as_int();
|
||||
int s_width = parameters.at(ID(S_WIDTH)).as_int();
|
||||
int y_width = width << s_width;
|
||||
int b_width = ceil_log2(y_width);
|
||||
Node a = factory.extend(inputs.at(ID(A)), y_width, false);
|
||||
Node s = factory.extend(inputs.at(ID(S)), b_width, false);
|
||||
Node b = factory.mul(s, factory.constant(Const(width, b_width)));
|
||||
return factory.logical_shift_left(a, b);
|
||||
} else if(cellType == ID($fa)) {
|
||||
return handle_fa(inputs.at(ID(A)), inputs.at(ID(B)), inputs.at(ID(C)));
|
||||
} else if(cellType == ID($lcu)) {
|
||||
return handle_lcu(inputs.at(ID(P)), inputs.at(ID(G)), inputs.at(ID(CI)));
|
||||
} else if(cellType == ID($alu)) {
|
||||
return handle_alu(inputs.at(ID(A)), inputs.at(ID(B)), y_width, a_signed && b_signed, inputs.at(ID(CI)), inputs.at(ID(BI)));
|
||||
} else if(cellType.in({ID($assert), ID($assume), ID($live), ID($fair), ID($cover)})) {
|
||||
Node a = factory.mux(factory.constant(Const(State::S1, 1)), inputs.at(ID(A)), inputs.at(ID(EN)));
|
||||
auto &output = factory.add_output(cellName, cellType, Sort(1));
|
||||
output.set_value(a);
|
||||
return {};
|
||||
} else if(cellType.in({ID($anyconst), ID($allconst), ID($anyseq), ID($allseq)})) {
|
||||
int width = parameters.at(ID(WIDTH)).as_int();
|
||||
auto &input = factory.add_input(cellName, cellType, Sort(width));
|
||||
return factory.value(input);
|
||||
} else if(cellType == ID($initstate)) {
|
||||
if(factory.ir().has_state(ID($initstate), ID($state)))
|
||||
return factory.value(factory.ir().state(ID($initstate)));
|
||||
else {
|
||||
auto &state = factory.add_state(ID($initstate), ID($state), Sort(1));
|
||||
state.set_initial_value(RTLIL::Const(State::S1, 1));
|
||||
state.set_next_value(factory.constant(RTLIL::Const(State::S0, 1)));
|
||||
return factory.value(state);
|
||||
}
|
||||
} else if(cellType == ID($check)) {
|
||||
log_error("The design contains a $check cell `%s'. This is not supported by the functional backend. Call `chformal -lower' to avoid this error.\n", cellName.c_str());
|
||||
} else {
|
||||
log_error("`%s' cells are not supported by the functional backend\n", cellType.c_str());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class FunctionalIRConstruction {
|
||||
std::deque<std::variant<DriveSpec, Cell *>> queue;
|
||||
dict<DriveSpec, Node> graph_nodes;
|
||||
dict<std::pair<Cell *, IdString>, Node> cell_outputs;
|
||||
DriverMap driver_map;
|
||||
Factory& factory;
|
||||
CellSimplifier simplifier;
|
||||
vector<Mem> memories_vector;
|
||||
dict<Cell*, Mem*> memories;
|
||||
SigMap sig_map; // TODO: this is only for FfInitVals, remove this once FfInitVals supports DriverMap
|
||||
FfInitVals ff_initvals;
|
||||
|
||||
Node enqueue(DriveSpec const &spec)
|
||||
{
|
||||
auto it = graph_nodes.find(spec);
|
||||
if(it == graph_nodes.end()){
|
||||
auto node = factory.create_pending(spec.size());
|
||||
graph_nodes.insert({spec, node});
|
||||
queue.emplace_back(spec);
|
||||
return node;
|
||||
}else
|
||||
return it->second;
|
||||
}
|
||||
Node enqueue_cell(Cell *cell, IdString port_name)
|
||||
{
|
||||
auto it = cell_outputs.find({cell, port_name});
|
||||
if(it == cell_outputs.end()) {
|
||||
queue.emplace_back(cell);
|
||||
std::optional<Node> rv;
|
||||
for(auto const &[name, sigspec] : cell->connections())
|
||||
if(driver_map.celltypes.cell_output(cell->type, name)) {
|
||||
auto node = factory.create_pending(sigspec.size());
|
||||
factory.suggest_name(node, cell->name.str() + "$" + name.str());
|
||||
cell_outputs.emplace({cell, name}, node);
|
||||
if(name == port_name)
|
||||
rv = node;
|
||||
}
|
||||
return *rv;
|
||||
} else
|
||||
return it->second;
|
||||
}
|
||||
public:
|
||||
FunctionalIRConstruction(Module *module, Factory &f)
|
||||
: factory(f)
|
||||
, simplifier(f)
|
||||
, sig_map(module)
|
||||
, ff_initvals(&sig_map, module)
|
||||
{
|
||||
driver_map.add(module);
|
||||
for (auto cell : module->cells()) {
|
||||
if (cell->type.in(ID($assert), ID($assume), ID($live), ID($fair), ID($cover), ID($check)))
|
||||
queue.emplace_back(cell);
|
||||
}
|
||||
for (auto wire : module->wires()) {
|
||||
if (wire->port_input)
|
||||
factory.add_input(wire->name, ID($input), Sort(wire->width));
|
||||
if (wire->port_output) {
|
||||
auto &output = factory.add_output(wire->name, ID($output), Sort(wire->width));
|
||||
output.set_value(enqueue(DriveChunk(DriveChunkWire(wire, 0, wire->width))));
|
||||
}
|
||||
}
|
||||
memories_vector = Mem::get_all_memories(module);
|
||||
for (auto &mem : memories_vector) {
|
||||
if (mem.cell != nullptr)
|
||||
memories[mem.cell] = &mem;
|
||||
}
|
||||
}
|
||||
private:
|
||||
Node concatenate_read_results(Mem *mem, vector<Node> results)
|
||||
{
|
||||
// sanity check: all read ports concatenated should equal to the RD_DATA port
|
||||
const SigSpec &rd_data = mem->cell->connections().at(ID(RD_DATA));
|
||||
int current = 0;
|
||||
for(size_t i = 0; i < mem->rd_ports.size(); i++) {
|
||||
int width = mem->width << mem->rd_ports[i].wide_log2;
|
||||
log_assert (results[i].width() == width);
|
||||
log_assert (mem->rd_ports[i].data == rd_data.extract(current, width));
|
||||
current += width;
|
||||
}
|
||||
log_assert (current == rd_data.size());
|
||||
log_assert (!results.empty());
|
||||
Node node = results[0];
|
||||
for(size_t i = 1; i < results.size(); i++)
|
||||
node = factory.concat(node, results[i]);
|
||||
return node;
|
||||
}
|
||||
Node handle_memory(Mem *mem)
|
||||
{
|
||||
// To simplify memory handling, the functional backend makes the following assumptions:
|
||||
// - Since async2sync or clk2fflogic must be run to use the functional backend,
|
||||
// we can assume that all ports are asynchronous.
|
||||
// - Async rd/wr are always transparent and so we must do reads after writes,
|
||||
// but we can ignore transparency_mask.
|
||||
// - We ignore collision_x_mask because x is a dont care value for us anyway.
|
||||
// - Since wr port j can only have priority over wr port i if j > i, if we do writes in
|
||||
// ascending index order the result will obey the priorty relation.
|
||||
vector<Node> read_results;
|
||||
auto &state = factory.add_state(mem->cell->name, ID($state), Sort(ceil_log2(mem->size), mem->width));
|
||||
state.set_initial_value(MemContents(mem));
|
||||
Node node = factory.value(state);
|
||||
for (size_t i = 0; i < mem->wr_ports.size(); i++) {
|
||||
const auto &wr = mem->wr_ports[i];
|
||||
if (wr.clk_enable)
|
||||
log_error("Write port %zd of memory %s.%s is clocked. This is not supported by the functional backend. "
|
||||
"Call async2sync or clk2fflogic to avoid this error.\n", i, log_id(mem->module), log_id(mem->memid));
|
||||
Node en = enqueue(driver_map(DriveSpec(wr.en)));
|
||||
Node addr = enqueue(driver_map(DriveSpec(wr.addr)));
|
||||
Node new_data = enqueue(driver_map(DriveSpec(wr.data)));
|
||||
Node old_data = factory.memory_read(node, addr);
|
||||
Node wr_data = simplifier.bitwise_mux(old_data, new_data, en);
|
||||
node = factory.memory_write(node, addr, wr_data);
|
||||
}
|
||||
if (mem->rd_ports.empty())
|
||||
log_error("Memory %s.%s has no read ports. This is not supported by the functional backend. "
|
||||
"Call opt_clean to remove it.", log_id(mem->module), log_id(mem->memid));
|
||||
for (size_t i = 0; i < mem->rd_ports.size(); i++) {
|
||||
const auto &rd = mem->rd_ports[i];
|
||||
if (rd.clk_enable)
|
||||
log_error("Read port %zd of memory %s.%s is clocked. This is not supported by the functional backend. "
|
||||
"Call memory_nordff to avoid this error.\n", i, log_id(mem->module), log_id(mem->memid));
|
||||
Node addr = enqueue(driver_map(DriveSpec(rd.addr)));
|
||||
read_results.push_back(factory.memory_read(node, addr));
|
||||
}
|
||||
state.set_next_value(node);
|
||||
return concatenate_read_results(mem, read_results);
|
||||
}
|
||||
void process_cell(Cell *cell)
|
||||
{
|
||||
if (cell->is_mem_cell()) {
|
||||
Mem *mem = memories.at(cell, nullptr);
|
||||
if (mem == nullptr) {
|
||||
log_assert(cell->has_memid());
|
||||
log_error("The design contains an unpacked memory at %s. This is not supported by the functional backend. "
|
||||
"Call memory_collect to avoid this error.\n", log_const(cell->parameters.at(ID(MEMID))));
|
||||
}
|
||||
Node node = handle_memory(mem);
|
||||
factory.update_pending(cell_outputs.at({cell, ID(RD_DATA)}), node);
|
||||
} else if (RTLIL::builtin_ff_cell_types().count(cell->type)) {
|
||||
FfData ff(&ff_initvals, cell);
|
||||
if (!ff.has_gclk)
|
||||
log_error("The design contains a %s flip-flop at %s. This is not supported by the functional backend. "
|
||||
"Call async2sync or clk2fflogic to avoid this error.\n", log_id(cell->type), log_id(cell));
|
||||
auto &state = factory.add_state(ff.name, ID($state), Sort(ff.width));
|
||||
Node q_value = factory.value(state);
|
||||
factory.suggest_name(q_value, ff.name);
|
||||
factory.update_pending(cell_outputs.at({cell, ID(Q)}), q_value);
|
||||
state.set_next_value(enqueue(ff.sig_d));
|
||||
state.set_initial_value(ff.val_init);
|
||||
} else {
|
||||
dict<IdString, Node> connections;
|
||||
IdString output_name; // for the single output case
|
||||
int n_outputs = 0;
|
||||
for(auto const &[name, sigspec] : cell->connections()) {
|
||||
if(driver_map.celltypes.cell_input(cell->type, name) && sigspec.size() > 0)
|
||||
connections.insert({ name, enqueue(DriveChunkPort(cell, {name, sigspec})) });
|
||||
if(driver_map.celltypes.cell_output(cell->type, name)) {
|
||||
output_name = name;
|
||||
n_outputs++;
|
||||
}
|
||||
}
|
||||
std::variant<dict<IdString, Node>, Node> outputs = simplifier.handle(cell->name, cell->type, cell->parameters, connections);
|
||||
if(auto *nodep = std::get_if<Node>(&outputs); nodep != nullptr) {
|
||||
log_assert(n_outputs == 1);
|
||||
factory.update_pending(cell_outputs.at({cell, output_name}), *nodep);
|
||||
} else {
|
||||
for(auto [name, node] : std::get<dict<IdString, Node>>(outputs))
|
||||
factory.update_pending(cell_outputs.at({cell, name}), node);
|
||||
}
|
||||
}
|
||||
}
|
||||
void undriven(const char *name) {
|
||||
log_error("The design contains an undriven signal %s. This is not supported by the functional backend. "
|
||||
"Call setundef with appropriate options to avoid this error.\n", name);
|
||||
}
|
||||
// we perform this check separately to give better error messages that include the wire or port name
|
||||
void check_undriven(DriveSpec const& spec, std::string const& name) {
|
||||
for(auto const &chunk : spec.chunks())
|
||||
if(chunk.is_none())
|
||||
undriven(name.c_str());
|
||||
}
|
||||
public:
|
||||
void process_queue()
|
||||
{
|
||||
for (; !queue.empty(); queue.pop_front()) {
|
||||
if(auto p = std::get_if<Cell *>(&queue.front()); p != nullptr) {
|
||||
process_cell(*p);
|
||||
continue;
|
||||
}
|
||||
|
||||
DriveSpec spec = std::get<DriveSpec>(queue.front());
|
||||
Node pending = graph_nodes.at(spec);
|
||||
|
||||
if (spec.chunks().size() > 1) {
|
||||
auto chunks = spec.chunks();
|
||||
Node node = enqueue(chunks[0]);
|
||||
for(size_t i = 1; i < chunks.size(); i++)
|
||||
node = factory.concat(node, enqueue(chunks[i]));
|
||||
factory.update_pending(pending, node);
|
||||
} else if (spec.chunks().size() == 1) {
|
||||
DriveChunk chunk = spec.chunks()[0];
|
||||
if (chunk.is_wire()) {
|
||||
DriveChunkWire wire_chunk = chunk.wire();
|
||||
if (wire_chunk.is_whole()) {
|
||||
if (wire_chunk.wire->port_input) {
|
||||
Node node = factory.value(factory.ir().input(wire_chunk.wire->name));
|
||||
factory.suggest_name(node, wire_chunk.wire->name);
|
||||
factory.update_pending(pending, node);
|
||||
} else {
|
||||
DriveSpec driver = driver_map(DriveSpec(wire_chunk));
|
||||
check_undriven(driver, RTLIL::unescape_id(wire_chunk.wire->name));
|
||||
Node node = enqueue(driver);
|
||||
factory.suggest_name(node, wire_chunk.wire->name);
|
||||
factory.update_pending(pending, node);
|
||||
}
|
||||
} else {
|
||||
DriveChunkWire whole_wire(wire_chunk.wire, 0, wire_chunk.wire->width);
|
||||
Node node = factory.slice(enqueue(whole_wire), wire_chunk.offset, wire_chunk.width);
|
||||
factory.update_pending(pending, node);
|
||||
}
|
||||
} else if (chunk.is_port()) {
|
||||
DriveChunkPort port_chunk = chunk.port();
|
||||
if (port_chunk.is_whole()) {
|
||||
if (driver_map.celltypes.cell_output(port_chunk.cell->type, port_chunk.port)) {
|
||||
Node node = enqueue_cell(port_chunk.cell, port_chunk.port);
|
||||
factory.update_pending(pending, node);
|
||||
} else {
|
||||
DriveSpec driver = driver_map(DriveSpec(port_chunk));
|
||||
check_undriven(driver, RTLIL::unescape_id(port_chunk.cell->name) + " port " + RTLIL::unescape_id(port_chunk.port));
|
||||
factory.update_pending(pending, enqueue(driver));
|
||||
}
|
||||
} else {
|
||||
DriveChunkPort whole_port(port_chunk.cell, port_chunk.port, 0, GetSize(port_chunk.cell->connections().at(port_chunk.port)));
|
||||
Node node = factory.slice(enqueue(whole_port), port_chunk.offset, port_chunk.width);
|
||||
factory.update_pending(pending, node);
|
||||
}
|
||||
} else if (chunk.is_constant()) {
|
||||
Node node = factory.constant(chunk.constant());
|
||||
factory.suggest_name(node, "$const" + std::to_string(chunk.size()) + "b" + chunk.constant().as_string());
|
||||
factory.update_pending(pending, node);
|
||||
} else if (chunk.is_multiple()) {
|
||||
log_error("Signal %s has multiple drivers. This is not supported by the functional backend. "
|
||||
"If tristate drivers are used, call tristate -formal to avoid this error.\n", log_signal(chunk));
|
||||
} else if (chunk.is_none()) {
|
||||
undriven(log_signal(chunk));
|
||||
} else {
|
||||
log_error("unhandled drivespec: %s\n", log_signal(chunk));
|
||||
log_abort();
|
||||
}
|
||||
} else {
|
||||
log_abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
IR IR::from_module(Module *module) {
|
||||
IR ir;
|
||||
auto factory = ir.factory();
|
||||
FunctionalIRConstruction ctor(module, factory);
|
||||
ctor.process_queue();
|
||||
ir.topological_sort();
|
||||
ir.forward_buf();
|
||||
return ir;
|
||||
}
|
||||
|
||||
void IR::topological_sort() {
|
||||
Graph::SccAdaptor compute_graph_scc(_graph);
|
||||
bool scc = false;
|
||||
std::vector<int> perm;
|
||||
TopoSortedSccs toposort(compute_graph_scc, [&](int *begin, int *end) {
|
||||
perm.insert(perm.end(), begin, end);
|
||||
if (end > begin + 1)
|
||||
{
|
||||
log_warning("Combinational loop:\n");
|
||||
for (int *i = begin; i != end; ++i) {
|
||||
Node node(_graph[*i]);
|
||||
log("- %s = %s\n", RTLIL::unescape_id(node.name()).c_str(), node.to_string().c_str());
|
||||
}
|
||||
log("\n");
|
||||
scc = true;
|
||||
}
|
||||
});
|
||||
for(const auto &[name, state]: _states)
|
||||
if(state.has_next_value())
|
||||
toposort.process(state.next_value().id());
|
||||
for(const auto &[name, output]: _outputs)
|
||||
if(output.has_value())
|
||||
toposort.process(output.value().id());
|
||||
// any nodes untouched by this point are dead code and will be removed by permute
|
||||
_graph.permute(perm);
|
||||
if(scc) log_error("The design contains combinational loops. This is not supported by the functional backend. "
|
||||
"Try `scc -select; simplemap; select -clear` to avoid this error.\n");
|
||||
}
|
||||
|
||||
static IdString merge_name(IdString a, IdString b) {
|
||||
if(a[0] == '$' && b[0] == '\\')
|
||||
return b;
|
||||
else
|
||||
return a;
|
||||
}
|
||||
|
||||
void IR::forward_buf() {
|
||||
std::vector<int> perm, alias;
|
||||
perm.clear();
|
||||
|
||||
for (int i = 0; i < _graph.size(); ++i)
|
||||
{
|
||||
auto node = _graph[i];
|
||||
if (node.function().fn() == Fn::buf && node.arg(0).index() < i)
|
||||
{
|
||||
int target_index = alias[node.arg(0).index()];
|
||||
auto target_node = _graph[perm[target_index]];
|
||||
if(node.has_sparse_attr()) {
|
||||
if(target_node.has_sparse_attr()) {
|
||||
IdString id = merge_name(node.sparse_attr(), target_node.sparse_attr());
|
||||
target_node.sparse_attr() = id;
|
||||
} else {
|
||||
IdString id = node.sparse_attr();
|
||||
target_node.sparse_attr() = id;
|
||||
}
|
||||
}
|
||||
alias.push_back(target_index);
|
||||
}
|
||||
else
|
||||
{
|
||||
alias.push_back(GetSize(perm));
|
||||
perm.push_back(i);
|
||||
}
|
||||
}
|
||||
_graph.permute(perm, alias);
|
||||
}
|
||||
|
||||
// Quoting routine to make error messages nicer
|
||||
static std::string quote_fmt(const char *fmt)
|
||||
{
|
||||
std::string r;
|
||||
for(const char *p = fmt; *p != 0; p++) {
|
||||
switch(*p) {
|
||||
case '\n': r += "\\n"; break;
|
||||
case '\t': r += "\\t"; break;
|
||||
case '"': r += "\\\""; break;
|
||||
case '\\': r += "\\\\"; break;
|
||||
default: r += *p; break;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
void Writer::print_impl(const char *fmt, vector<std::function<void()>> &fns)
|
||||
{
|
||||
size_t next_index = 0;
|
||||
for(const char *p = fmt; *p != 0; p++)
|
||||
switch(*p) {
|
||||
case '{':
|
||||
if(*++p == '{') {
|
||||
*os << '{';
|
||||
} else {
|
||||
char *pe;
|
||||
size_t index = strtoul(p, &pe, 10);
|
||||
if(*pe != '}')
|
||||
log_error("invalid format string: expected {<number>}, {} or {{, got \"%s\": \"%s\"\n",
|
||||
quote_fmt(std::string(p - 1, pe - p + 2).c_str()).c_str(),
|
||||
quote_fmt(fmt).c_str());
|
||||
if(p == pe)
|
||||
index = next_index;
|
||||
else
|
||||
p = pe;
|
||||
if(index >= fns.size())
|
||||
log_error("invalid format string: index %zu out of bounds (%zu): \"%s\"\n", index, fns.size(), quote_fmt(fmt).c_str());
|
||||
fns[index]();
|
||||
next_index = index + 1;
|
||||
}
|
||||
break;
|
||||
case '}':
|
||||
p++;
|
||||
if(*p != '}')
|
||||
log_error("invalid format string: unescaped }: \"%s\"\n", quote_fmt(fmt).c_str());
|
||||
*os << '}';
|
||||
break;
|
||||
default:
|
||||
*os << *p;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
YOSYS_NAMESPACE_END
|
||||
643
kernel/functional.h
Normal file
643
kernel/functional.h
Normal file
|
|
@ -0,0 +1,643 @@
|
|||
/*
|
||||
* 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 FUNCTIONAL_H
|
||||
#define FUNCTIONAL_H
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/compute_graph.h"
|
||||
#include "kernel/drivertools.h"
|
||||
#include "kernel/mem.h"
|
||||
#include "kernel/utils.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
namespace Functional {
|
||||
// each function is documented with a short pseudocode declaration or definition
|
||||
// standard C/Verilog operators are used to describe the result
|
||||
//
|
||||
// the sorts used in this are:
|
||||
// - bit[N]: a bitvector of N bits
|
||||
// bit[N] can be indicated as signed or unsigned. this is not tracked by the functional backend
|
||||
// but is meant to indicate how the value is interpreted
|
||||
// if a bit[N] is marked as neither signed nor unsigned, this means the result should be valid with *either* interpretation
|
||||
// - memory[N, M]: a memory with N address and M data bits
|
||||
// - int: C++ int
|
||||
// - Const[N]: yosys RTLIL::Const (with size() == N)
|
||||
// - IdString: yosys IdString
|
||||
// - any: used in documentation to indicate that the sort is unconstrained
|
||||
//
|
||||
// nodes in the functional backend are either of sort bit[N] or memory[N,M] (for some N, M: int)
|
||||
// additionally, they can carry a constant of sort int, Const[N] or IdString
|
||||
// each node has a 'sort' field that stores the sort of the node
|
||||
// slice, zero_extend, sign_extend use the sort field to store out_width
|
||||
enum class Fn {
|
||||
// invalid() = known-invalid/shouldn't happen value
|
||||
// TODO: maybe remove this and use e.g. std::optional instead?
|
||||
invalid,
|
||||
// buf(a: any): any = a
|
||||
// no-op operation
|
||||
// when constructing the compute graph we generate invalid buf() nodes as a placeholder
|
||||
// and later insert the argument
|
||||
buf,
|
||||
// slice(a: bit[in_width], offset: int, out_width: int): bit[out_width] = a[offset +: out_width]
|
||||
// required: offset + out_width <= in_width
|
||||
slice,
|
||||
// zero_extend(a: unsigned bit[in_width], out_width: int): unsigned bit[out_width] = a (zero extended)
|
||||
// required: out_width > in_width
|
||||
zero_extend,
|
||||
// sign_extend(a: signed bit[in_width], out_width: int): signed bit[out_width] = a (sign extended)
|
||||
// required: out_width > in_width
|
||||
sign_extend,
|
||||
// concat(a: bit[N], b: bit[M]): bit[N+M] = {b, a} (verilog syntax)
|
||||
// concatenates two bitvectors, with a in the least significant position and b in the more significant position
|
||||
concat,
|
||||
// add(a: bit[N], b: bit[N]): bit[N] = a + b
|
||||
add,
|
||||
// sub(a: bit[N], b: bit[N]): bit[N] = a - b
|
||||
sub,
|
||||
// mul(a: bit[N], b: bit[N]): bit[N] = a * b
|
||||
mul,
|
||||
// unsigned_div(a: unsigned bit[N], b: unsigned bit[N]): bit[N] = a / b
|
||||
unsigned_div,
|
||||
// unsigned_mod(a: signed bit[N], b: signed bit[N]): bit[N] = a % b
|
||||
unsigned_mod,
|
||||
// bitwise_and(a: bit[N], b: bit[N]): bit[N] = a & b
|
||||
bitwise_and,
|
||||
// bitwise_or(a: bit[N], b: bit[N]): bit[N] = a | b
|
||||
bitwise_or,
|
||||
// bitwise_xor(a: bit[N], b: bit[N]): bit[N] = a ^ b
|
||||
bitwise_xor,
|
||||
// bitwise_not(a: bit[N]): bit[N] = ~a
|
||||
bitwise_not,
|
||||
// reduce_and(a: bit[N]): bit[1] = &a
|
||||
reduce_and,
|
||||
// reduce_or(a: bit[N]): bit[1] = |a
|
||||
reduce_or,
|
||||
// reduce_xor(a: bit[N]): bit[1] = ^a
|
||||
reduce_xor,
|
||||
// unary_minus(a: bit[N]): bit[N] = -a
|
||||
unary_minus,
|
||||
// equal(a: bit[N], b: bit[N]): bit[1] = (a == b)
|
||||
equal,
|
||||
// not_equal(a: bit[N], b: bit[N]): bit[1] = (a != b)
|
||||
not_equal,
|
||||
// signed_greater_than(a: signed bit[N], b: signed bit[N]): bit[1] = (a > b)
|
||||
signed_greater_than,
|
||||
// signed_greater_equal(a: signed bit[N], b: signed bit[N]): bit[1] = (a >= b)
|
||||
signed_greater_equal,
|
||||
// unsigned_greater_than(a: unsigned bit[N], b: unsigned bit[N]): bit[1] = (a > b)
|
||||
unsigned_greater_than,
|
||||
// unsigned_greater_equal(a: unsigned bit[N], b: unsigned bit[N]): bit[1] = (a >= b)
|
||||
unsigned_greater_equal,
|
||||
// logical_shift_left(a: bit[N], b: unsigned bit[M]): bit[N] = a << b
|
||||
// required: M == clog2(N)
|
||||
logical_shift_left,
|
||||
// logical_shift_right(a: unsigned bit[N], b: unsigned bit[M]): unsigned bit[N] = a >> b
|
||||
// required: M == clog2(N)
|
||||
logical_shift_right,
|
||||
// arithmetic_shift_right(a: signed bit[N], b: unsigned bit[M]): signed bit[N] = a >> b
|
||||
// required: M == clog2(N)
|
||||
arithmetic_shift_right,
|
||||
// mux(a: bit[N], b: bit[N], s: bit[1]): bit[N] = s ? b : a
|
||||
mux,
|
||||
// constant(a: Const[N]): bit[N] = a
|
||||
constant,
|
||||
// input(a: IdString): any
|
||||
// returns the current value of the input with the specified name
|
||||
input,
|
||||
// state(a: IdString): any
|
||||
// returns the current value of the state variable with the specified name
|
||||
state,
|
||||
// memory_read(memory: memory[addr_width, data_width], addr: bit[addr_width]): bit[data_width] = memory[addr]
|
||||
memory_read,
|
||||
// memory_write(memory: memory[addr_width, data_width], addr: bit[addr_width], data: bit[data_width]): memory[addr_width, data_width]
|
||||
// returns a copy of `memory` but with the value at `addr` changed to `data`
|
||||
memory_write
|
||||
};
|
||||
// returns the name of a Fn value, as a string literal
|
||||
const char *fn_to_string(Fn);
|
||||
// Sort represents the sort or type of a node
|
||||
// currently the only two sorts are signal/bit and memory
|
||||
class Sort {
|
||||
std::variant<int, std::pair<int, int>> _v;
|
||||
public:
|
||||
explicit Sort(int width) : _v(width) { }
|
||||
Sort(int addr_width, int data_width) : _v(std::make_pair(addr_width, data_width)) { }
|
||||
bool is_signal() const { return _v.index() == 0; }
|
||||
bool is_memory() const { return _v.index() == 1; }
|
||||
// returns the width of a bitvector sort, errors out for other sorts
|
||||
int width() const { return std::get<0>(_v); }
|
||||
// returns the address width of a bitvector sort, errors out for other sorts
|
||||
int addr_width() const { return std::get<1>(_v).first; }
|
||||
// returns the data width of a bitvector sort, errors out for other sorts
|
||||
int data_width() const { return std::get<1>(_v).second; }
|
||||
bool operator==(Sort const& other) const { return _v == other._v; }
|
||||
unsigned int hash() const { return mkhash(_v); }
|
||||
};
|
||||
class IR;
|
||||
class Factory;
|
||||
class Node;
|
||||
class IRInput {
|
||||
friend class Factory;
|
||||
public:
|
||||
IdString name;
|
||||
IdString kind;
|
||||
Sort sort;
|
||||
private:
|
||||
IRInput(IR &, IdString name, IdString kind, Sort sort)
|
||||
: name(name), kind(kind), sort(std::move(sort)) {}
|
||||
};
|
||||
class IROutput {
|
||||
friend class Factory;
|
||||
IR &_ir;
|
||||
public:
|
||||
IdString name;
|
||||
IdString kind;
|
||||
Sort sort;
|
||||
private:
|
||||
IROutput(IR &ir, IdString name, IdString kind, Sort sort)
|
||||
: _ir(ir), name(name), kind(kind), sort(std::move(sort)) {}
|
||||
public:
|
||||
Node value() const;
|
||||
bool has_value() const;
|
||||
void set_value(Node value);
|
||||
};
|
||||
class IRState {
|
||||
friend class Factory;
|
||||
IR &_ir;
|
||||
public:
|
||||
IdString name;
|
||||
IdString kind;
|
||||
Sort sort;
|
||||
private:
|
||||
std::variant<RTLIL::Const, MemContents> _initial;
|
||||
IRState(IR &ir, IdString name, IdString kind, Sort sort)
|
||||
: _ir(ir), name(name), kind(kind), sort(std::move(sort)) {}
|
||||
public:
|
||||
Node next_value() const;
|
||||
bool has_next_value() const;
|
||||
RTLIL::Const const& initial_value_signal() const { return std::get<RTLIL::Const>(_initial); }
|
||||
MemContents const& initial_value_memory() const { return std::get<MemContents>(_initial); }
|
||||
void set_next_value(Node value);
|
||||
void set_initial_value(RTLIL::Const value) { value.extu(sort.width()); _initial = std::move(value); }
|
||||
void set_initial_value(MemContents value) { log_assert(Sort(value.addr_width(), value.data_width()) == sort); _initial = std::move(value); }
|
||||
};
|
||||
class IR {
|
||||
friend class Factory;
|
||||
friend class Node;
|
||||
friend class IRInput;
|
||||
friend class IROutput;
|
||||
friend class IRState;
|
||||
// one NodeData is stored per Node, containing the function and non-node arguments
|
||||
// note that NodeData is deduplicated by ComputeGraph
|
||||
class NodeData {
|
||||
Fn _fn;
|
||||
std::variant<
|
||||
std::monostate,
|
||||
RTLIL::Const,
|
||||
std::pair<IdString, IdString>,
|
||||
int
|
||||
> _extra;
|
||||
public:
|
||||
NodeData() : _fn(Fn::invalid) {}
|
||||
NodeData(Fn fn) : _fn(fn) {}
|
||||
template<class T> NodeData(Fn fn, T &&extra) : _fn(fn), _extra(std::forward<T>(extra)) {}
|
||||
Fn fn() const { return _fn; }
|
||||
const RTLIL::Const &as_const() const { return std::get<RTLIL::Const>(_extra); }
|
||||
std::pair<IdString, IdString> as_idstring_pair() const { return std::get<std::pair<IdString, IdString>>(_extra); }
|
||||
int as_int() const { return std::get<int>(_extra); }
|
||||
int hash() const {
|
||||
return mkhash((unsigned int) _fn, mkhash(_extra));
|
||||
}
|
||||
bool operator==(NodeData const &other) const {
|
||||
return _fn == other._fn && _extra == other._extra;
|
||||
}
|
||||
};
|
||||
// Attr contains all the information about a note that should not be deduplicated
|
||||
struct Attr {
|
||||
Sort sort;
|
||||
};
|
||||
// our specialised version of ComputeGraph
|
||||
// the sparse_attr IdString stores a naming suggestion, retrieved with name()
|
||||
// the key is currently used to identify the nodes that represent output and next state values
|
||||
// the bool is true for next state values
|
||||
using Graph = ComputeGraph<NodeData, Attr, IdString, std::tuple<IdString, IdString, bool>>;
|
||||
Graph _graph;
|
||||
dict<std::pair<IdString, IdString>, IRInput> _inputs;
|
||||
dict<std::pair<IdString, IdString>, IROutput> _outputs;
|
||||
dict<std::pair<IdString, IdString>, IRState> _states;
|
||||
IR::Graph::Ref mutate(Node n);
|
||||
public:
|
||||
static IR from_module(Module *module);
|
||||
Factory factory();
|
||||
int size() const { return _graph.size(); }
|
||||
Node operator[](int i);
|
||||
void topological_sort();
|
||||
void forward_buf();
|
||||
IRInput const& input(IdString name, IdString kind) const { return _inputs.at({name, kind}); }
|
||||
IRInput const& input(IdString name) const { return input(name, ID($input)); }
|
||||
IROutput const& output(IdString name, IdString kind) const { return _outputs.at({name, kind}); }
|
||||
IROutput const& output(IdString name) const { return output(name, ID($output)); }
|
||||
IRState const& state(IdString name, IdString kind) const { return _states.at({name, kind}); }
|
||||
IRState const& state(IdString name) const { return state(name, ID($state)); }
|
||||
bool has_input(IdString name, IdString kind) const { return _inputs.count({name, kind}); }
|
||||
bool has_output(IdString name, IdString kind) const { return _outputs.count({name, kind}); }
|
||||
bool has_state(IdString name, IdString kind) const { return _states.count({name, kind}); }
|
||||
vector<IRInput const*> inputs(IdString kind) const;
|
||||
vector<IRInput const*> inputs() const { return inputs(ID($input)); }
|
||||
vector<IROutput const*> outputs(IdString kind) const;
|
||||
vector<IROutput const*> outputs() const { return outputs(ID($output)); }
|
||||
vector<IRState const*> states(IdString kind) const;
|
||||
vector<IRState const*> states() const { return states(ID($state)); }
|
||||
vector<IRInput const*> all_inputs() const;
|
||||
vector<IROutput const*> all_outputs() const;
|
||||
vector<IRState const*> all_states() const;
|
||||
class iterator {
|
||||
friend class IR;
|
||||
IR *_ir;
|
||||
int _index;
|
||||
iterator(IR *ir, int index) : _ir(ir), _index(index) {}
|
||||
public:
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using value_type = Node;
|
||||
using pointer = arrow_proxy<Node>;
|
||||
using reference = Node;
|
||||
using difference_type = ptrdiff_t;
|
||||
Node operator*();
|
||||
iterator &operator++() { _index++; return *this; }
|
||||
bool operator!=(iterator const &other) const { return _ir != other._ir || _index != other._index; }
|
||||
bool operator==(iterator const &other) const { return !(*this != other); }
|
||||
pointer operator->();
|
||||
};
|
||||
iterator begin() { return iterator(this, 0); }
|
||||
iterator end() { return iterator(this, _graph.size()); }
|
||||
};
|
||||
// Node is an immutable reference to a FunctionalIR node
|
||||
class Node {
|
||||
friend class Factory;
|
||||
friend class IR;
|
||||
friend class IRInput;
|
||||
friend class IROutput;
|
||||
friend class IRState;
|
||||
IR::Graph::ConstRef _ref;
|
||||
explicit Node(IR::Graph::ConstRef ref) : _ref(ref) { }
|
||||
explicit operator IR::Graph::ConstRef() { return _ref; }
|
||||
public:
|
||||
// the node's index. may change if nodes are added or removed
|
||||
int id() const { return _ref.index(); }
|
||||
// a name suggestion for the node, which need not be unique
|
||||
IdString name() const {
|
||||
if(_ref.has_sparse_attr())
|
||||
return _ref.sparse_attr();
|
||||
else
|
||||
return std::string("\\n") + std::to_string(id());
|
||||
}
|
||||
Fn fn() const { return _ref.function().fn(); }
|
||||
Sort sort() const { return _ref.attr().sort; }
|
||||
// returns the width of a bitvector node, errors out for other nodes
|
||||
int width() const { return sort().width(); }
|
||||
size_t arg_count() const { return _ref.size(); }
|
||||
Node arg(int n) const { return Node(_ref.arg(n)); }
|
||||
// visit calls the appropriate visitor method depending on the type of the node
|
||||
template<class Visitor> auto visit(Visitor v) const
|
||||
{
|
||||
// currently templated but could be switched to AbstractVisitor &
|
||||
switch(_ref.function().fn()) {
|
||||
case Fn::invalid: log_error("invalid node in visit"); break;
|
||||
case Fn::buf: return v.buf(*this, arg(0)); break;
|
||||
case Fn::slice: return v.slice(*this, arg(0), _ref.function().as_int(), sort().width()); break;
|
||||
case Fn::zero_extend: return v.zero_extend(*this, arg(0), width()); break;
|
||||
case Fn::sign_extend: return v.sign_extend(*this, arg(0), width()); break;
|
||||
case Fn::concat: return v.concat(*this, arg(0), arg(1)); break;
|
||||
case Fn::add: return v.add(*this, arg(0), arg(1)); break;
|
||||
case Fn::sub: return v.sub(*this, arg(0), arg(1)); break;
|
||||
case Fn::mul: return v.mul(*this, arg(0), arg(1)); break;
|
||||
case Fn::unsigned_div: return v.unsigned_div(*this, arg(0), arg(1)); break;
|
||||
case Fn::unsigned_mod: return v.unsigned_mod(*this, arg(0), arg(1)); break;
|
||||
case Fn::bitwise_and: return v.bitwise_and(*this, arg(0), arg(1)); break;
|
||||
case Fn::bitwise_or: return v.bitwise_or(*this, arg(0), arg(1)); break;
|
||||
case Fn::bitwise_xor: return v.bitwise_xor(*this, arg(0), arg(1)); break;
|
||||
case Fn::bitwise_not: return v.bitwise_not(*this, arg(0)); break;
|
||||
case Fn::unary_minus: return v.unary_minus(*this, arg(0)); break;
|
||||
case Fn::reduce_and: return v.reduce_and(*this, arg(0)); break;
|
||||
case Fn::reduce_or: return v.reduce_or(*this, arg(0)); break;
|
||||
case Fn::reduce_xor: return v.reduce_xor(*this, arg(0)); break;
|
||||
case Fn::equal: return v.equal(*this, arg(0), arg(1)); break;
|
||||
case Fn::not_equal: return v.not_equal(*this, arg(0), arg(1)); break;
|
||||
case Fn::signed_greater_than: return v.signed_greater_than(*this, arg(0), arg(1)); break;
|
||||
case Fn::signed_greater_equal: return v.signed_greater_equal(*this, arg(0), arg(1)); break;
|
||||
case Fn::unsigned_greater_than: return v.unsigned_greater_than(*this, arg(0), arg(1)); break;
|
||||
case Fn::unsigned_greater_equal: return v.unsigned_greater_equal(*this, arg(0), arg(1)); break;
|
||||
case Fn::logical_shift_left: return v.logical_shift_left(*this, arg(0), arg(1)); break;
|
||||
case Fn::logical_shift_right: return v.logical_shift_right(*this, arg(0), arg(1)); break;
|
||||
case Fn::arithmetic_shift_right: return v.arithmetic_shift_right(*this, arg(0), arg(1)); break;
|
||||
case Fn::mux: return v.mux(*this, arg(0), arg(1), arg(2)); break;
|
||||
case Fn::constant: return v.constant(*this, _ref.function().as_const()); break;
|
||||
case Fn::input: return v.input(*this, _ref.function().as_idstring_pair().first, _ref.function().as_idstring_pair().second); break;
|
||||
case Fn::state: return v.state(*this, _ref.function().as_idstring_pair().first, _ref.function().as_idstring_pair().second); break;
|
||||
case Fn::memory_read: return v.memory_read(*this, arg(0), arg(1)); break;
|
||||
case Fn::memory_write: return v.memory_write(*this, arg(0), arg(1), arg(2)); break;
|
||||
}
|
||||
log_abort();
|
||||
}
|
||||
std::string to_string();
|
||||
std::string to_string(std::function<std::string(Node)>);
|
||||
};
|
||||
inline IR::Graph::Ref IR::mutate(Node n) { return _graph[n._ref.index()]; }
|
||||
inline Node IR::operator[](int i) { return Node(_graph[i]); }
|
||||
inline Node IROutput::value() const { return Node(_ir._graph({name, kind, false})); }
|
||||
inline bool IROutput::has_value() const { return _ir._graph.has_key({name, kind, false}); }
|
||||
inline void IROutput::set_value(Node value) { log_assert(sort == value.sort()); _ir.mutate(value).assign_key({name, kind, false}); }
|
||||
inline Node IRState::next_value() const { return Node(_ir._graph({name, kind, true})); }
|
||||
inline bool IRState::has_next_value() const { return _ir._graph.has_key({name, kind, true}); }
|
||||
inline void IRState::set_next_value(Node value) { log_assert(sort == value.sort()); _ir.mutate(value).assign_key({name, kind, true}); }
|
||||
inline Node IR::iterator::operator*() { return Node(_ir->_graph[_index]); }
|
||||
inline arrow_proxy<Node> IR::iterator::operator->() { return arrow_proxy<Node>(**this); }
|
||||
// AbstractVisitor provides an abstract base class for visitors
|
||||
template<class T> struct AbstractVisitor {
|
||||
virtual T buf(Node self, Node n) = 0;
|
||||
virtual T slice(Node self, Node a, int offset, int out_width) = 0;
|
||||
virtual T zero_extend(Node self, Node a, int out_width) = 0;
|
||||
virtual T sign_extend(Node self, Node a, int out_width) = 0;
|
||||
virtual T concat(Node self, Node a, Node b) = 0;
|
||||
virtual T add(Node self, Node a, Node b) = 0;
|
||||
virtual T sub(Node self, Node a, Node b) = 0;
|
||||
virtual T mul(Node self, Node a, Node b) = 0;
|
||||
virtual T unsigned_div(Node self, Node a, Node b) = 0;
|
||||
virtual T unsigned_mod(Node self, Node a, Node b) = 0;
|
||||
virtual T bitwise_and(Node self, Node a, Node b) = 0;
|
||||
virtual T bitwise_or(Node self, Node a, Node b) = 0;
|
||||
virtual T bitwise_xor(Node self, Node a, Node b) = 0;
|
||||
virtual T bitwise_not(Node self, Node a) = 0;
|
||||
virtual T unary_minus(Node self, Node a) = 0;
|
||||
virtual T reduce_and(Node self, Node a) = 0;
|
||||
virtual T reduce_or(Node self, Node a) = 0;
|
||||
virtual T reduce_xor(Node self, Node a) = 0;
|
||||
virtual T equal(Node self, Node a, Node b) = 0;
|
||||
virtual T not_equal(Node self, Node a, Node b) = 0;
|
||||
virtual T signed_greater_than(Node self, Node a, Node b) = 0;
|
||||
virtual T signed_greater_equal(Node self, Node a, Node b) = 0;
|
||||
virtual T unsigned_greater_than(Node self, Node a, Node b) = 0;
|
||||
virtual T unsigned_greater_equal(Node self, Node a, Node b) = 0;
|
||||
virtual T logical_shift_left(Node self, Node a, Node b) = 0;
|
||||
virtual T logical_shift_right(Node self, Node a, Node b) = 0;
|
||||
virtual T arithmetic_shift_right(Node self, Node a, Node b) = 0;
|
||||
virtual T mux(Node self, Node a, Node b, Node s) = 0;
|
||||
virtual T constant(Node self, RTLIL::Const const & value) = 0;
|
||||
virtual T input(Node self, IdString name, IdString kind) = 0;
|
||||
virtual T state(Node self, IdString name, IdString kind) = 0;
|
||||
virtual T memory_read(Node self, Node mem, Node addr) = 0;
|
||||
virtual T memory_write(Node self, Node mem, Node addr, Node data) = 0;
|
||||
};
|
||||
// DefaultVisitor provides defaults for all visitor methods which just calls default_handler
|
||||
template<class T> struct DefaultVisitor : public AbstractVisitor<T> {
|
||||
virtual T default_handler(Node self) = 0;
|
||||
T buf(Node self, Node) override { return default_handler(self); }
|
||||
T slice(Node self, Node, int, int) override { return default_handler(self); }
|
||||
T zero_extend(Node self, Node, int) override { return default_handler(self); }
|
||||
T sign_extend(Node self, Node, int) override { return default_handler(self); }
|
||||
T concat(Node self, Node, Node) override { return default_handler(self); }
|
||||
T add(Node self, Node, Node) override { return default_handler(self); }
|
||||
T sub(Node self, Node, Node) override { return default_handler(self); }
|
||||
T mul(Node self, Node, Node) override { return default_handler(self); }
|
||||
T unsigned_div(Node self, Node, Node) override { return default_handler(self); }
|
||||
T unsigned_mod(Node self, Node, Node) override { return default_handler(self); }
|
||||
T bitwise_and(Node self, Node, Node) override { return default_handler(self); }
|
||||
T bitwise_or(Node self, Node, Node) override { return default_handler(self); }
|
||||
T bitwise_xor(Node self, Node, Node) override { return default_handler(self); }
|
||||
T bitwise_not(Node self, Node) override { return default_handler(self); }
|
||||
T unary_minus(Node self, Node) override { return default_handler(self); }
|
||||
T reduce_and(Node self, Node) override { return default_handler(self); }
|
||||
T reduce_or(Node self, Node) override { return default_handler(self); }
|
||||
T reduce_xor(Node self, Node) override { return default_handler(self); }
|
||||
T equal(Node self, Node, Node) override { return default_handler(self); }
|
||||
T not_equal(Node self, Node, Node) override { return default_handler(self); }
|
||||
T signed_greater_than(Node self, Node, Node) override { return default_handler(self); }
|
||||
T signed_greater_equal(Node self, Node, Node) override { return default_handler(self); }
|
||||
T unsigned_greater_than(Node self, Node, Node) override { return default_handler(self); }
|
||||
T unsigned_greater_equal(Node self, Node, Node) override { return default_handler(self); }
|
||||
T logical_shift_left(Node self, Node, Node) override { return default_handler(self); }
|
||||
T logical_shift_right(Node self, Node, Node) override { return default_handler(self); }
|
||||
T arithmetic_shift_right(Node self, Node, Node) override { return default_handler(self); }
|
||||
T mux(Node self, Node, Node, Node) override { return default_handler(self); }
|
||||
T constant(Node self, RTLIL::Const const &) override { return default_handler(self); }
|
||||
T input(Node self, IdString, IdString) override { return default_handler(self); }
|
||||
T state(Node self, IdString, IdString) override { return default_handler(self); }
|
||||
T memory_read(Node self, Node, Node) override { return default_handler(self); }
|
||||
T memory_write(Node self, Node, Node, Node) override { return default_handler(self); }
|
||||
};
|
||||
// a factory is used to modify a FunctionalIR. it creates new nodes and allows for some modification of existing nodes.
|
||||
class Factory {
|
||||
friend class IR;
|
||||
IR &_ir;
|
||||
explicit Factory(IR &ir) : _ir(ir) {}
|
||||
Node add(IR::NodeData &&fn, Sort const &sort, std::initializer_list<Node> args) {
|
||||
log_assert(!sort.is_signal() || sort.width() > 0);
|
||||
log_assert(!sort.is_memory() || (sort.addr_width() > 0 && sort.data_width() > 0));
|
||||
IR::Graph::Ref ref = _ir._graph.add(std::move(fn), {std::move(sort)});
|
||||
for (auto arg : args)
|
||||
ref.append_arg(IR::Graph::ConstRef(arg));
|
||||
return Node(ref);
|
||||
}
|
||||
void check_basic_binary(Node const &a, Node const &b) { log_assert(a.sort().is_signal() && a.sort() == b.sort()); }
|
||||
void check_shift(Node const &a, Node const &b) { log_assert(a.sort().is_signal() && b.sort().is_signal() && b.width() == ceil_log2(a.width())); }
|
||||
void check_unary(Node const &a) { log_assert(a.sort().is_signal()); }
|
||||
public:
|
||||
IR &ir() { return _ir; }
|
||||
Node slice(Node a, int offset, int out_width) {
|
||||
log_assert(a.sort().is_signal() && offset + out_width <= a.sort().width());
|
||||
if(offset == 0 && out_width == a.width())
|
||||
return a;
|
||||
return add(IR::NodeData(Fn::slice, offset), Sort(out_width), {a});
|
||||
}
|
||||
// extend will either extend or truncate the provided value to reach the desired width
|
||||
Node extend(Node a, int out_width, bool is_signed) {
|
||||
int in_width = a.sort().width();
|
||||
log_assert(a.sort().is_signal());
|
||||
if(in_width == out_width)
|
||||
return a;
|
||||
if(in_width > out_width)
|
||||
return slice(a, 0, out_width);
|
||||
if(is_signed)
|
||||
return add(Fn::sign_extend, Sort(out_width), {a});
|
||||
else
|
||||
return add(Fn::zero_extend, Sort(out_width), {a});
|
||||
}
|
||||
Node concat(Node a, Node b) {
|
||||
log_assert(a.sort().is_signal() && b.sort().is_signal());
|
||||
return add(Fn::concat, Sort(a.sort().width() + b.sort().width()), {a, b});
|
||||
}
|
||||
Node add(Node a, Node b) { check_basic_binary(a, b); return add(Fn::add, a.sort(), {a, b}); }
|
||||
Node sub(Node a, Node b) { check_basic_binary(a, b); return add(Fn::sub, a.sort(), {a, b}); }
|
||||
Node mul(Node a, Node b) { check_basic_binary(a, b); return add(Fn::mul, a.sort(), {a, b}); }
|
||||
Node unsigned_div(Node a, Node b) { check_basic_binary(a, b); return add(Fn::unsigned_div, a.sort(), {a, b}); }
|
||||
Node unsigned_mod(Node a, Node b) { check_basic_binary(a, b); return add(Fn::unsigned_mod, a.sort(), {a, b}); }
|
||||
Node bitwise_and(Node a, Node b) { check_basic_binary(a, b); return add(Fn::bitwise_and, a.sort(), {a, b}); }
|
||||
Node bitwise_or(Node a, Node b) { check_basic_binary(a, b); return add(Fn::bitwise_or, a.sort(), {a, b}); }
|
||||
Node bitwise_xor(Node a, Node b) { check_basic_binary(a, b); return add(Fn::bitwise_xor, a.sort(), {a, b}); }
|
||||
Node bitwise_not(Node a) { check_unary(a); return add(Fn::bitwise_not, a.sort(), {a}); }
|
||||
Node unary_minus(Node a) { check_unary(a); return add(Fn::unary_minus, a.sort(), {a}); }
|
||||
Node reduce_and(Node a) {
|
||||
check_unary(a);
|
||||
if(a.width() == 1)
|
||||
return a;
|
||||
return add(Fn::reduce_and, Sort(1), {a});
|
||||
}
|
||||
Node reduce_or(Node a) {
|
||||
check_unary(a);
|
||||
if(a.width() == 1)
|
||||
return a;
|
||||
return add(Fn::reduce_or, Sort(1), {a});
|
||||
}
|
||||
Node reduce_xor(Node a) {
|
||||
check_unary(a);
|
||||
if(a.width() == 1)
|
||||
return a;
|
||||
return add(Fn::reduce_xor, Sort(1), {a});
|
||||
}
|
||||
Node equal(Node a, Node b) { check_basic_binary(a, b); return add(Fn::equal, Sort(1), {a, b}); }
|
||||
Node not_equal(Node a, Node b) { check_basic_binary(a, b); return add(Fn::not_equal, Sort(1), {a, b}); }
|
||||
Node signed_greater_than(Node a, Node b) { check_basic_binary(a, b); return add(Fn::signed_greater_than, Sort(1), {a, b}); }
|
||||
Node signed_greater_equal(Node a, Node b) { check_basic_binary(a, b); return add(Fn::signed_greater_equal, Sort(1), {a, b}); }
|
||||
Node unsigned_greater_than(Node a, Node b) { check_basic_binary(a, b); return add(Fn::unsigned_greater_than, Sort(1), {a, b}); }
|
||||
Node unsigned_greater_equal(Node a, Node b) { check_basic_binary(a, b); return add(Fn::unsigned_greater_equal, Sort(1), {a, b}); }
|
||||
Node logical_shift_left(Node a, Node b) { check_shift(a, b); return add(Fn::logical_shift_left, a.sort(), {a, b}); }
|
||||
Node logical_shift_right(Node a, Node b) { check_shift(a, b); return add(Fn::logical_shift_right, a.sort(), {a, b}); }
|
||||
Node arithmetic_shift_right(Node a, Node b) { check_shift(a, b); return add(Fn::arithmetic_shift_right, a.sort(), {a, b}); }
|
||||
Node mux(Node a, Node b, Node s) {
|
||||
log_assert(a.sort().is_signal() && a.sort() == b.sort() && s.sort() == Sort(1));
|
||||
return add(Fn::mux, a.sort(), {a, b, s});
|
||||
}
|
||||
Node memory_read(Node mem, Node addr) {
|
||||
log_assert(mem.sort().is_memory() && addr.sort().is_signal() && mem.sort().addr_width() == addr.sort().width());
|
||||
return add(Fn::memory_read, Sort(mem.sort().data_width()), {mem, addr});
|
||||
}
|
||||
Node memory_write(Node mem, Node addr, Node data) {
|
||||
log_assert(mem.sort().is_memory() && addr.sort().is_signal() && data.sort().is_signal() &&
|
||||
mem.sort().addr_width() == addr.sort().width() && mem.sort().data_width() == data.sort().width());
|
||||
return add(Fn::memory_write, mem.sort(), {mem, addr, data});
|
||||
}
|
||||
Node constant(RTLIL::Const value) {
|
||||
int s = value.size();
|
||||
return add(IR::NodeData(Fn::constant, std::move(value)), Sort(s), {});
|
||||
}
|
||||
Node create_pending(int width) {
|
||||
return add(Fn::buf, Sort(width), {});
|
||||
}
|
||||
void update_pending(Node node, Node value) {
|
||||
log_assert(node._ref.function() == Fn::buf && node._ref.size() == 0);
|
||||
log_assert(node.sort() == value.sort());
|
||||
_ir.mutate(node).append_arg(value._ref);
|
||||
}
|
||||
IRInput &add_input(IdString name, IdString kind, Sort sort) {
|
||||
auto [it, inserted] = _ir._inputs.emplace({name, kind}, IRInput(_ir, name, kind, std::move(sort)));
|
||||
if (!inserted) log_error("input `%s` was re-defined", name.c_str());
|
||||
return it->second;
|
||||
}
|
||||
IROutput &add_output(IdString name, IdString kind, Sort sort) {
|
||||
auto [it, inserted] = _ir._outputs.emplace({name, kind}, IROutput(_ir, name, kind, std::move(sort)));
|
||||
if (!inserted) log_error("output `%s` was re-defined", name.c_str());
|
||||
return it->second;
|
||||
}
|
||||
IRState &add_state(IdString name, IdString kind, Sort sort) {
|
||||
auto [it, inserted] = _ir._states.emplace({name, kind}, IRState(_ir, name, kind, std::move(sort)));
|
||||
if (!inserted) log_error("state `%s` was re-defined", name.c_str());
|
||||
return it->second;
|
||||
}
|
||||
Node value(IRInput const& input) {
|
||||
return add(IR::NodeData(Fn::input, std::pair(input.name, input.kind)), input.sort, {});
|
||||
}
|
||||
Node value(IRState const& state) {
|
||||
return add(IR::NodeData(Fn::state, std::pair(state.name, state.kind)), state.sort, {});
|
||||
}
|
||||
void suggest_name(Node node, IdString name) {
|
||||
_ir.mutate(node).sparse_attr() = name;
|
||||
}
|
||||
};
|
||||
inline Factory IR::factory() { return Factory(*this); }
|
||||
template<class Id> class Scope {
|
||||
protected:
|
||||
char substitution_character = '_';
|
||||
virtual bool is_character_legal(char, int) = 0;
|
||||
private:
|
||||
pool<std::string> _used_names;
|
||||
dict<Id, std::string> _by_id;
|
||||
public:
|
||||
void reserve(std::string name) {
|
||||
_used_names.insert(std::move(name));
|
||||
}
|
||||
std::string unique_name(IdString suggestion) {
|
||||
std::string str = RTLIL::unescape_id(suggestion);
|
||||
for(size_t i = 0; i < str.size(); i++)
|
||||
if(!is_character_legal(str[i], i))
|
||||
str[i] = substitution_character;
|
||||
if(_used_names.count(str) == 0) {
|
||||
_used_names.insert(str);
|
||||
return str;
|
||||
}
|
||||
for (int idx = 0 ; ; idx++){
|
||||
std::string suffixed = str + "_" + std::to_string(idx);
|
||||
if(_used_names.count(suffixed) == 0) {
|
||||
_used_names.insert(suffixed);
|
||||
return suffixed;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::string operator()(Id id, IdString suggestion) {
|
||||
auto it = _by_id.find(id);
|
||||
if(it != _by_id.end())
|
||||
return it->second;
|
||||
std::string str = unique_name(suggestion);
|
||||
_by_id.insert({id, str});
|
||||
return str;
|
||||
}
|
||||
};
|
||||
class Writer {
|
||||
std::ostream *os;
|
||||
void print_impl(const char *fmt, vector<std::function<void()>>& fns);
|
||||
public:
|
||||
Writer(std::ostream &os) : os(&os) {}
|
||||
template<class T> Writer& operator <<(T&& arg) { *os << std::forward<T>(arg); return *this; }
|
||||
template<typename... Args>
|
||||
void print(const char *fmt, Args&&... args)
|
||||
{
|
||||
vector<std::function<void()>> fns { [&]() { *this << args; }... };
|
||||
print_impl(fmt, fns);
|
||||
}
|
||||
template<typename Fn, typename... Args>
|
||||
void print_with(Fn fn, const char *fmt, Args&&... args)
|
||||
{
|
||||
vector<std::function<void()>> fns { [&]() {
|
||||
if constexpr (std::is_invocable_v<Fn, Args>)
|
||||
*this << fn(args);
|
||||
else
|
||||
*this << args; }...
|
||||
};
|
||||
print_impl(fmt, fns);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
|
@ -15,6 +15,7 @@
|
|||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include <stdint.h>
|
||||
|
|
@ -186,6 +187,37 @@ inline unsigned int mkhash(const T &v) {
|
|||
return hash_ops<T>().hash(v);
|
||||
}
|
||||
|
||||
template<> struct hash_ops<std::monostate> {
|
||||
static inline bool cmp(std::monostate a, std::monostate b) {
|
||||
return a == b;
|
||||
}
|
||||
static inline unsigned int hash(std::monostate) {
|
||||
return mkhash_init;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... T> struct hash_ops<std::variant<T...>> {
|
||||
static inline bool cmp(std::variant<T...> a, std::variant<T...> b) {
|
||||
return a == b;
|
||||
}
|
||||
static inline unsigned int hash(std::variant<T...> a) {
|
||||
unsigned int h = std::visit([](const auto &v) { return mkhash(v); }, a);
|
||||
return mkhash(a.index(), h);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> struct hash_ops<std::optional<T>> {
|
||||
static inline bool cmp(std::optional<T> a, std::optional<T> b) {
|
||||
return a == b;
|
||||
}
|
||||
static inline unsigned int hash(std::optional<T> a) {
|
||||
if(a.has_value())
|
||||
return mkhash(*a);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
inline int hashtable_size(int min_size)
|
||||
{
|
||||
// Primes as generated by https://oeis.org/A175953
|
||||
|
|
|
|||
|
|
@ -459,8 +459,21 @@ void log_cmd_error(const char *format, ...)
|
|||
|
||||
if (log_cmd_error_throw) {
|
||||
log_last_error = vstringf(format, ap);
|
||||
|
||||
// Make sure the error message gets through any selective silencing
|
||||
// of log output
|
||||
bool pop_errfile = false;
|
||||
if (log_errfile != NULL) {
|
||||
log_files.push_back(log_errfile);
|
||||
pop_errfile = true;
|
||||
}
|
||||
|
||||
log("ERROR: %s", log_last_error.c_str());
|
||||
log_flush();
|
||||
|
||||
if (pop_errfile)
|
||||
log_files.pop_back();
|
||||
|
||||
throw log_cmd_error_exception();
|
||||
}
|
||||
|
||||
|
|
@ -662,6 +675,16 @@ const char *log_id(const RTLIL::IdString &str)
|
|||
return p+1;
|
||||
}
|
||||
|
||||
const char *log_str(const char *str)
|
||||
{
|
||||
log_id_cache.push_back(strdup(str));
|
||||
return log_id_cache.back();
|
||||
}
|
||||
|
||||
const char *log_str(std::string const &str) {
|
||||
return log_str(str.c_str());
|
||||
}
|
||||
|
||||
void log_module(RTLIL::Module *module, std::string indent)
|
||||
{
|
||||
std::stringstream buf;
|
||||
|
|
|
|||
|
|
@ -206,6 +206,8 @@ void log_check_expected();
|
|||
const char *log_signal(const RTLIL::SigSpec &sig, bool autoint = true);
|
||||
const char *log_const(const RTLIL::Const &value, bool autoint = true);
|
||||
const char *log_id(const RTLIL::IdString &id);
|
||||
const char *log_str(const char *str);
|
||||
const char *log_str(std::string const &str);
|
||||
|
||||
template<typename T> static inline const char *log_id(T *obj, const char *nullstr = nullptr) {
|
||||
if (nullstr && obj == nullptr)
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ struct Macc
|
|||
ports.clear();
|
||||
bit_ports = cell->getPort(ID::B);
|
||||
|
||||
std::vector<RTLIL::State> config_bits = cell->getParam(ID::CONFIG).bits;
|
||||
auto config_bits = cell->getParam(ID::CONFIG);
|
||||
int config_cursor = 0;
|
||||
|
||||
int config_width = cell->getParam(ID::CONFIG_WIDTH).as_int();
|
||||
|
|
@ -199,7 +199,7 @@ struct Macc
|
|||
|
||||
bool eval(RTLIL::Const &result) const
|
||||
{
|
||||
for (auto &bit : result.bits)
|
||||
for (auto &bit : result.bits())
|
||||
bit = State::S0;
|
||||
|
||||
for (auto &port : ports)
|
||||
|
|
|
|||
254
kernel/mem.cc
254
kernel/mem.cc
|
|
@ -157,10 +157,10 @@ void Mem::emit() {
|
|||
}
|
||||
for (int sub = 0; sub < (1 << port.wide_log2); sub++)
|
||||
{
|
||||
rd_wide_continuation.bits.push_back(State(sub != 0));
|
||||
rd_clk_enable.bits.push_back(State(port.clk_enable));
|
||||
rd_clk_polarity.bits.push_back(State(port.clk_polarity));
|
||||
rd_ce_over_srst.bits.push_back(State(port.ce_over_srst));
|
||||
rd_wide_continuation.bits().push_back(State(sub != 0));
|
||||
rd_clk_enable.bits().push_back(State(port.clk_enable));
|
||||
rd_clk_polarity.bits().push_back(State(port.clk_polarity));
|
||||
rd_ce_over_srst.bits().push_back(State(port.ce_over_srst));
|
||||
rd_clk.append(port.clk);
|
||||
rd_arst.append(port.arst);
|
||||
rd_srst.append(port.srst);
|
||||
|
|
@ -170,17 +170,17 @@ void Mem::emit() {
|
|||
rd_addr.append(addr);
|
||||
log_assert(GetSize(addr) == abits);
|
||||
for (auto idx : wr_port_xlat) {
|
||||
rd_transparency_mask.bits.push_back(State(bool(port.transparency_mask[idx])));
|
||||
rd_collision_x_mask.bits.push_back(State(bool(port.collision_x_mask[idx])));
|
||||
rd_transparency_mask.bits().push_back(State(bool(port.transparency_mask[idx])));
|
||||
rd_collision_x_mask.bits().push_back(State(bool(port.collision_x_mask[idx])));
|
||||
}
|
||||
}
|
||||
rd_data.append(port.data);
|
||||
for (auto &bit : port.arst_value)
|
||||
rd_arst_value.bits.push_back(bit);
|
||||
for (auto &bit : port.srst_value)
|
||||
rd_srst_value.bits.push_back(bit);
|
||||
for (auto &bit : port.init_value)
|
||||
rd_init_value.bits.push_back(bit);
|
||||
for (auto bit : port.arst_value)
|
||||
rd_arst_value.bits().push_back(bit);
|
||||
for (auto bit : port.srst_value)
|
||||
rd_srst_value.bits().push_back(bit);
|
||||
for (auto bit : port.init_value)
|
||||
rd_init_value.bits().push_back(bit);
|
||||
}
|
||||
if (rd_ports.empty()) {
|
||||
rd_wide_continuation = State::S0;
|
||||
|
|
@ -222,12 +222,12 @@ void Mem::emit() {
|
|||
}
|
||||
for (int sub = 0; sub < (1 << port.wide_log2); sub++)
|
||||
{
|
||||
wr_wide_continuation.bits.push_back(State(sub != 0));
|
||||
wr_clk_enable.bits.push_back(State(port.clk_enable));
|
||||
wr_clk_polarity.bits.push_back(State(port.clk_polarity));
|
||||
wr_wide_continuation.bits().push_back(State(sub != 0));
|
||||
wr_clk_enable.bits().push_back(State(port.clk_enable));
|
||||
wr_clk_polarity.bits().push_back(State(port.clk_polarity));
|
||||
wr_clk.append(port.clk);
|
||||
for (auto idx : wr_port_xlat)
|
||||
wr_priority_mask.bits.push_back(State(bool(port.priority_mask[idx])));
|
||||
wr_priority_mask.bits().push_back(State(bool(port.priority_mask[idx])));
|
||||
SigSpec addr = port.sub_addr(sub);
|
||||
addr.extend_u0(abits, false);
|
||||
wr_addr.append(addr);
|
||||
|
|
@ -414,7 +414,7 @@ void Mem::coalesce_inits() {
|
|||
if (!init.en.is_fully_ones()) {
|
||||
for (int i = 0; i < GetSize(init.data); i++)
|
||||
if (init.en[i % width] != State::S1)
|
||||
init.data[i] = State::Sx;
|
||||
init.data.bits()[i] = State::Sx;
|
||||
init.en = Const(State::S1, width);
|
||||
}
|
||||
continue;
|
||||
|
|
@ -427,7 +427,7 @@ void Mem::coalesce_inits() {
|
|||
log_assert(offset + GetSize(init.data) <= GetSize(cdata));
|
||||
for (int i = 0; i < GetSize(init.data); i++)
|
||||
if (init.en[i % width] == State::S1)
|
||||
cdata.bits[i+offset] = init.data.bits[i];
|
||||
cdata.bits()[i+offset] = init.data[i];
|
||||
init.removed = true;
|
||||
}
|
||||
MemInit new_init;
|
||||
|
|
@ -446,7 +446,7 @@ Const Mem::get_init_data() const {
|
|||
int offset = (init.addr.as_int() - start_offset) * width;
|
||||
for (int i = 0; i < GetSize(init.data); i++)
|
||||
if (0 <= i+offset && i+offset < GetSize(init_data) && init.en[i % width] == State::S1)
|
||||
init_data.bits[i+offset] = init.data.bits[i];
|
||||
init_data.bits()[i+offset] = init.data[i];
|
||||
}
|
||||
return init_data;
|
||||
}
|
||||
|
|
@ -1679,3 +1679,219 @@ SigSpec MemWr::decompress_en(const std::vector<int> &swizzle, SigSpec sig) {
|
|||
res.append(sig[i]);
|
||||
return res;
|
||||
}
|
||||
|
||||
using addr_t = MemContents::addr_t;
|
||||
|
||||
MemContents::MemContents(Mem *mem) :
|
||||
MemContents(ceil_log2(mem->size), mem->width)
|
||||
{
|
||||
for(const auto &init : mem->inits) {
|
||||
if(init.en.is_fully_zero()) continue;
|
||||
log_assert(init.en.size() == _data_width);
|
||||
if(init.en.is_fully_ones())
|
||||
insert_concatenated(init.addr.as_int(), init.data);
|
||||
else {
|
||||
// TODO: this case could be handled more efficiently by adding
|
||||
// a flag to reserve_range that tells it to preserve
|
||||
// previous contents
|
||||
addr_t addr = init.addr.as_int();
|
||||
addr_t words = init.data.size() / _data_width;
|
||||
RTLIL::Const data = init.data;
|
||||
log_assert(data.size() % _data_width == 0);
|
||||
for(addr_t i = 0; i < words; i++) {
|
||||
RTLIL::Const previous = (*this)[addr + i];
|
||||
for(int j = 0; j < _data_width; j++)
|
||||
if(init.en[j] != State::S1)
|
||||
data.bits()[_data_width * i + j] = previous[j];
|
||||
}
|
||||
insert_concatenated(init.addr.as_int(), data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MemContents::iterator & MemContents::iterator::operator++() {
|
||||
auto it = _memory->_values.upper_bound(_addr);
|
||||
if(it == _memory->_values.end()) {
|
||||
_memory = nullptr;
|
||||
_addr = ~(addr_t) 0;
|
||||
} else
|
||||
_addr = it->first;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void MemContents::check() {
|
||||
log_assert(_addr_width > 0 && _addr_width < (int)sizeof(addr_t) * 8);
|
||||
log_assert(_data_width > 0);
|
||||
log_assert(_default_value.size() == _data_width);
|
||||
if(_values.empty()) return;
|
||||
auto it = _values.begin();
|
||||
for(;;) {
|
||||
log_assert(!it->second.empty());
|
||||
log_assert(it->second.size() % _data_width == 0);
|
||||
auto end1 = _range_end(it);
|
||||
log_assert(_range_begin(it) < (addr_t)(1<<_addr_width));
|
||||
log_assert(end1 <= (addr_t)(1<<_addr_width));
|
||||
if(++it == _values.end())
|
||||
break;
|
||||
// check that ranges neither overlap nor touch
|
||||
log_assert(_range_begin(it) > end1);
|
||||
}
|
||||
}
|
||||
|
||||
bool MemContents::_range_contains(std::map<addr_t, RTLIL::Const>::iterator it, addr_t addr) const {
|
||||
// if addr < begin, the subtraction will overflow, and the comparison will always fail
|
||||
// (since we have an invariant that begin + size <= 2^(addr_t bits))
|
||||
return it != _values.end() && addr - _range_begin(it) < _range_size(it);
|
||||
}
|
||||
|
||||
|
||||
bool MemContents::_range_contains(std::map<addr_t, RTLIL::Const>::iterator it, addr_t begin_addr, addr_t end_addr) const {
|
||||
// note that we assume begin_addr <= end_addr
|
||||
return it != _values.end() && _range_begin(it) <= begin_addr && end_addr - _range_begin(it) <= _range_size(it);
|
||||
}
|
||||
|
||||
bool MemContents::_range_overlaps(std::map<addr_t, RTLIL::Const>::iterator it, addr_t begin_addr, addr_t end_addr) const {
|
||||
if(it == _values.end() || begin_addr >= end_addr)
|
||||
return false;
|
||||
auto top1 = _range_end(it) - 1;
|
||||
auto top2 = end_addr - 1;
|
||||
return !(top1 < begin_addr || top2 < _range_begin(it));
|
||||
}
|
||||
|
||||
std::map<addr_t, RTLIL::Const>::iterator MemContents::_range_at(addr_t addr) const {
|
||||
// allow addr == 1<<_addr_width (which will just return end())
|
||||
log_assert(addr <= (addr_t)(1<<_addr_width));
|
||||
// get the first range with base > addr
|
||||
// (we use const_cast since map::iterators are only passed around internally and not exposed to the user
|
||||
// and using map::iterator in both the const and non-const case simplifies the code a little,
|
||||
// at the cost of having to be a little careful when implementing const methods)
|
||||
auto it = const_cast<std::map<addr_t, RTLIL::Const> &>(_values).upper_bound(addr);
|
||||
// if we get the very first range, all ranges are past the addr, so return the first one
|
||||
if(it == _values.begin())
|
||||
return it;
|
||||
// otherwise, go back to the previous interval
|
||||
// this must be the last interval with base <= addr
|
||||
auto it_prev = std::next(it, -1);
|
||||
if(_range_contains(it_prev, addr))
|
||||
return it_prev;
|
||||
else
|
||||
return it;
|
||||
}
|
||||
|
||||
RTLIL::Const MemContents::operator[](addr_t addr) const {
|
||||
auto it = _range_at(addr);
|
||||
if(_range_contains(it, addr))
|
||||
return it->second.extract(_range_offset(it, addr), _data_width);
|
||||
else
|
||||
return _default_value;
|
||||
}
|
||||
|
||||
addr_t MemContents::count_range(addr_t begin_addr, addr_t end_addr) const {
|
||||
addr_t count = 0;
|
||||
for(auto it = _range_at(begin_addr); _range_overlaps(it, begin_addr, end_addr); it++) {
|
||||
auto first = std::max(_range_begin(it), begin_addr);
|
||||
auto last = std::min(_range_end(it), end_addr);
|
||||
count += last - first;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void MemContents::clear_range(addr_t begin_addr, addr_t end_addr) {
|
||||
if(begin_addr >= end_addr) return;
|
||||
// identify which ranges are affected by this operation
|
||||
// the first iterator affected is the first one containing any addr >= begin_addr
|
||||
auto begin_it = _range_at(begin_addr);
|
||||
// the first iterator *not* affected is the first one with base addr > end_addr - 1
|
||||
auto end_it = _values.upper_bound(end_addr - 1);
|
||||
if(begin_it == end_it)
|
||||
return; // nothing to do
|
||||
// the last iterator affected is one before the first one not affected
|
||||
auto last_it = std::next(end_it, -1);
|
||||
// the first and last range may need to be truncated, the rest can just be deleted
|
||||
// to handle the begin_it == last_it case correctly, do the end case first by inserting a new range past the end
|
||||
if(_range_contains(last_it, end_addr - 1)) {
|
||||
auto new_begin = end_addr;
|
||||
auto end = _range_end(last_it);
|
||||
// if there is data past the end address, preserve it by creating a new range
|
||||
if(new_begin != end)
|
||||
end_it = _values.emplace_hint(last_it, new_begin, last_it->second.extract(_range_offset(last_it, new_begin), (_range_end(last_it) - new_begin) * _data_width));
|
||||
// the original range will either be truncated in the next if() block or deleted in the erase, so we can leave it untruncated
|
||||
}
|
||||
if(_range_contains(begin_it, begin_addr)) {
|
||||
auto new_end = begin_addr;
|
||||
// if there is data before the start address, truncate but don't delete
|
||||
if(new_end != begin_it->first) {
|
||||
begin_it->second.extu(_range_offset(begin_it, new_end));
|
||||
++begin_it;
|
||||
}
|
||||
// else: begin_it will be deleted
|
||||
}
|
||||
_values.erase(begin_it, end_it);
|
||||
}
|
||||
|
||||
std::map<addr_t, RTLIL::Const>::iterator MemContents::_reserve_range(addr_t begin_addr, addr_t end_addr) {
|
||||
if(begin_addr >= end_addr)
|
||||
return _values.end(); // need a dummy value to return, end() is cheap
|
||||
// find the first range containing any addr >= begin_addr - 1
|
||||
auto lower_it = begin_addr == 0 ? _values.begin() : _range_at(begin_addr - 1);
|
||||
// check if our range is already covered by a single range
|
||||
// note that since ranges are not allowed to touch, if any range contains begin_addr, lower_it equals that range
|
||||
if (_range_contains(lower_it, begin_addr, end_addr))
|
||||
return lower_it;
|
||||
// find the first range containing any addr >= end_addr
|
||||
auto upper_it = _range_at(end_addr);
|
||||
// check if either of the two ranges we just found touch our range
|
||||
bool lower_touch = begin_addr > 0 && _range_contains(lower_it, begin_addr - 1);
|
||||
bool upper_touch = _range_contains(upper_it, end_addr);
|
||||
if (lower_touch && upper_touch) {
|
||||
log_assert (lower_it != upper_it); // lower_it == upper_it should be excluded by the check above
|
||||
// we have two different ranges touching at either end, we need to merge them
|
||||
auto upper_end = _range_end(upper_it);
|
||||
// make range bigger (maybe reserve here instead of resize?)
|
||||
lower_it->second.bits().resize(_range_offset(lower_it, upper_end), State::Sx);
|
||||
// copy only the data beyond our range
|
||||
std::copy(_range_data(upper_it, end_addr), _range_data(upper_it, upper_end), _range_data(lower_it, end_addr));
|
||||
// keep lower_it, but delete upper_it
|
||||
_values.erase(std::next(lower_it), std::next(upper_it));
|
||||
return lower_it;
|
||||
} else if (lower_touch) {
|
||||
// we have a range to the left, just make it bigger and delete any other that may exist.
|
||||
lower_it->second.bits().resize(_range_offset(lower_it, end_addr), State::Sx);
|
||||
// keep lower_it and upper_it
|
||||
_values.erase(std::next(lower_it), upper_it);
|
||||
return lower_it;
|
||||
} else if (upper_touch) {
|
||||
// we have a range to the right, we need to expand it
|
||||
// since we need to erase and reinsert to a new address, steal the data
|
||||
RTLIL::Const data = std::move(upper_it->second);
|
||||
// note that begin_addr is not in upper_it, otherwise the whole range covered check would have tripped
|
||||
data.bits().insert(data.bits().begin(), (_range_begin(upper_it) - begin_addr) * _data_width, State::Sx);
|
||||
// delete lower_it and upper_it, then reinsert
|
||||
_values.erase(lower_it, std::next(upper_it));
|
||||
return _values.emplace(begin_addr, std::move(data)).first;
|
||||
} else {
|
||||
// no ranges are touching, so just delete all ranges in our range and allocate a new one
|
||||
// could try to resize an existing range but not sure if that actually helps
|
||||
_values.erase(lower_it, upper_it);
|
||||
return _values.emplace(begin_addr, RTLIL::Const(State::Sx, (end_addr - begin_addr) * _data_width)).first;
|
||||
}
|
||||
}
|
||||
|
||||
void MemContents::insert_concatenated(addr_t addr, RTLIL::Const const &values) {
|
||||
addr_t words = (values.size() + _data_width - 1) / _data_width;
|
||||
log_assert(addr < (addr_t)(1<<_addr_width));
|
||||
log_assert(words <= (addr_t)(1<<_addr_width) - addr);
|
||||
auto it = _reserve_range(addr, addr + words);
|
||||
auto to_begin = _range_data(it, addr);
|
||||
std::copy(values.begin(), values.end(), to_begin);
|
||||
// if values is not word-aligned, fill any missing bits with 0
|
||||
std::fill(to_begin + values.size(), to_begin + words * _data_width, State::S0);
|
||||
}
|
||||
|
||||
std::vector<State>::iterator MemContents::_range_write(std::vector<State>::iterator it, RTLIL::Const const &word) {
|
||||
auto from_end = word.size() <= _data_width ? word.end() : word.begin() + _data_width;
|
||||
auto to_end = std::copy(word.begin(), from_end, it);
|
||||
auto it_next = std::next(it, _data_width);
|
||||
std::fill(to_end, it_next, State::S0);
|
||||
return it_next;
|
||||
}
|
||||
109
kernel/mem.h
109
kernel/mem.h
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/ffinit.h"
|
||||
#include "kernel/utils.h"
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
|
|
@ -224,6 +225,114 @@ struct Mem : RTLIL::AttrObject {
|
|||
Mem(Module *module, IdString memid, int width, int start_offset, int size) : module(module), memid(memid), packed(false), mem(nullptr), cell(nullptr), width(width), start_offset(start_offset), size(size) {}
|
||||
};
|
||||
|
||||
// MemContents efficiently represents the contents of a potentially sparse memory by storing only those segments that are actually defined
|
||||
class MemContents {
|
||||
public:
|
||||
class range; class iterator;
|
||||
using addr_t = uint32_t;
|
||||
private:
|
||||
// we ban _addr_width == sizeof(addr_t) * 8 because it adds too many cornercases
|
||||
int _addr_width;
|
||||
int _data_width;
|
||||
RTLIL::Const _default_value;
|
||||
// for each range, store the concatenation of the words at the start address
|
||||
// invariants:
|
||||
// - no overlapping or adjacent ranges
|
||||
// - no empty ranges
|
||||
// - all Consts are a multiple of the word size
|
||||
std::map<addr_t, RTLIL::Const> _values;
|
||||
// returns an iterator to the range containing addr, if it exists, or the first range past addr
|
||||
std::map<addr_t, RTLIL::Const>::iterator _range_at(addr_t addr) const;
|
||||
addr_t _range_size(std::map<addr_t, RTLIL::Const>::iterator it) const { return it->second.size() / _data_width; }
|
||||
addr_t _range_begin(std::map<addr_t, RTLIL::Const>::iterator it) const { return it->first; }
|
||||
addr_t _range_end(std::map<addr_t, RTLIL::Const>::iterator it) const { return _range_begin(it) + _range_size(it); }
|
||||
// check if the iterator points to a range containing addr
|
||||
bool _range_contains(std::map<addr_t, RTLIL::Const>::iterator it, addr_t addr) const;
|
||||
// check if the iterator points to a range containing [begin_addr, end_addr). assumes end_addr >= begin_addr.
|
||||
bool _range_contains(std::map<addr_t, RTLIL::Const>::iterator it, addr_t begin_addr, addr_t end_addr) const;
|
||||
// check if the iterator points to a range overlapping with [begin_addr, end_addr)
|
||||
bool _range_overlaps(std::map<addr_t, RTLIL::Const>::iterator it, addr_t begin_addr, addr_t end_addr) const;
|
||||
// return the offset the addr would have in the range at `it`
|
||||
size_t _range_offset(std::map<addr_t, RTLIL::Const>::iterator it, addr_t addr) const { return (addr - it->first) * _data_width; }
|
||||
// assuming _range_contains(it, addr), return an iterator pointing to the data at addr
|
||||
std::vector<State>::iterator _range_data(std::map<addr_t, RTLIL::Const>::iterator it, addr_t addr) { return it->second.bits().begin() + _range_offset(it, addr); }
|
||||
// internal version of reserve_range that returns an iterator to the range
|
||||
std::map<addr_t, RTLIL::Const>::iterator _reserve_range(addr_t begin_addr, addr_t end_addr);
|
||||
// write a single word at addr, return iterator to next word
|
||||
std::vector<State>::iterator _range_write(std::vector<State>::iterator it, RTLIL::Const const &data);
|
||||
public:
|
||||
class range {
|
||||
int _data_width;
|
||||
addr_t _base;
|
||||
RTLIL::Const const &_values;
|
||||
friend class iterator;
|
||||
range(int data_width, addr_t base, RTLIL::Const const &values)
|
||||
: _data_width(data_width), _base(base), _values(values) {}
|
||||
public:
|
||||
addr_t base() const { return _base; }
|
||||
addr_t size() const { return ((addr_t) _values.size()) / _data_width; }
|
||||
addr_t limit() const { return _base + size(); }
|
||||
RTLIL::Const const &concatenated() const { return _values; }
|
||||
RTLIL::Const operator[](addr_t addr) const {
|
||||
log_assert(addr - _base < size());
|
||||
return _values.extract((addr - _base) * _data_width, _data_width);
|
||||
}
|
||||
RTLIL::Const at_offset(addr_t offset) const { return (*this)[_base + offset]; }
|
||||
};
|
||||
class iterator {
|
||||
MemContents const *_memory;
|
||||
// storing addr instead of an iterator gives more well-defined behaviour under insertions/deletions
|
||||
// use ~0 for end so that all end iterators compare the same
|
||||
addr_t _addr;
|
||||
friend class MemContents;
|
||||
iterator(MemContents const *memory, addr_t addr) : _memory(memory), _addr(addr) {}
|
||||
public:
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using value_type = range;
|
||||
using pointer = arrow_proxy<range>;
|
||||
using reference = range;
|
||||
using difference_type = addr_t;
|
||||
reference operator *() const { return range(_memory->_data_width, _addr, _memory->_values.at(_addr)); }
|
||||
pointer operator->() const { return arrow_proxy<range>(**this); }
|
||||
bool operator !=(iterator const &other) const { return _memory != other._memory || _addr != other._addr; }
|
||||
bool operator ==(iterator const &other) const { return !(*this != other); }
|
||||
iterator &operator++();
|
||||
};
|
||||
MemContents(int addr_width, int data_width, RTLIL::Const default_value)
|
||||
: _addr_width(addr_width), _data_width(data_width)
|
||||
, _default_value((default_value.extu(data_width), std::move(default_value)))
|
||||
{ log_assert(_addr_width > 0 && _addr_width < (int)sizeof(addr_t) * 8); log_assert(_data_width > 0); }
|
||||
MemContents(int addr_width, int data_width) : MemContents(addr_width, data_width, RTLIL::Const(State::Sx, data_width)) {}
|
||||
explicit MemContents(Mem *mem);
|
||||
int addr_width() const { return _addr_width; }
|
||||
int data_width() const { return _data_width; }
|
||||
RTLIL::Const const &default_value() const { return _default_value; }
|
||||
// return the value at the address if it exists, the default_value of the memory otherwise. address must not exceed 2**addr_width.
|
||||
RTLIL::Const operator [](addr_t addr) const;
|
||||
// return the number of defined words in the range [begin_addr, end_addr)
|
||||
addr_t count_range(addr_t begin_addr, addr_t end_addr) const;
|
||||
// allocate memory for the range [begin_addr, end_addr), but leave the contents undefined.
|
||||
void reserve_range(addr_t begin_addr, addr_t end_addr) { _reserve_range(begin_addr, end_addr); }
|
||||
// insert multiple words (provided as a single concatenated RTLIL::Const) at the given address, overriding any previous assignment.
|
||||
void insert_concatenated(addr_t addr, RTLIL::Const const &values);
|
||||
// insert multiple words at the given address, overriding any previous assignment.
|
||||
template<typename Iterator> void insert_range(addr_t addr, Iterator begin, Iterator end) {
|
||||
auto words = end - begin;
|
||||
log_assert(addr < (addr_t)(1<<_addr_width)); log_assert(words <= (addr_t)(1<<_addr_width) - addr);
|
||||
auto range = _reserve_range(addr, addr + words);
|
||||
auto it = _range_data(range, addr);
|
||||
for(; begin != end; ++begin)
|
||||
it = _range_write(it, *begin);
|
||||
}
|
||||
// undefine all words in the range [begin_addr, end_addr)
|
||||
void clear_range(addr_t begin_addr, addr_t end_addr);
|
||||
// check invariants, abort if invariants failed
|
||||
void check();
|
||||
iterator end() const { return iterator(nullptr, ~(addr_t) 0); }
|
||||
iterator begin() const { return _values.empty() ? end() : iterator(this, _values.begin()->first); }
|
||||
bool empty() const { return _values.empty(); }
|
||||
};
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue