Merge branch 'master' into krys/docs

This commit is contained in:
Krystine Sherwin 2024-03-05 05:48:46 +13:00
commit 1455941ab9
No known key found for this signature in database
152 changed files with 3892 additions and 1578 deletions

View File

@ -14,10 +14,10 @@ jobs:
run: sudo apt-get install bison flex libreadline-dev tcl-dev libffi-dev run: sudo apt-get install bison flex libreadline-dev tcl-dev libffi-dev
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v2 uses: github/codeql-action/init@v3
with: with:
languages: cpp languages: cpp
queries: security-extended,security-and-quality queries: security-extended,security-and-quality
@ -26,4 +26,4 @@ jobs:
run: make yosys -j6 run: make yosys -j6
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2 uses: github/codeql-action/analyze@v3

View File

@ -6,13 +6,13 @@ jobs:
emcc: emcc:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: mymindstorm/setup-emsdk@v11 - uses: mymindstorm/setup-emsdk@v14
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Build - name: Build
run: | run: |
make config-emcc make config-emcc
make YOSYS_VER=latest make YOSYS_VER=latest
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v4
with: with:
name: yosysjs name: yosysjs
path: yosysjs-latest.zip path: yosysjs-latest.zip

View File

@ -79,19 +79,18 @@ jobs:
$CXX --version $CXX --version
- name: Checkout Yosys - name: Checkout Yosys
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: Get iverilog - name: Get iverilog
shell: bash shell: bash
run: | run: |
git clone https://github.com/steveicarus/iverilog.git git clone https://github.com/steveicarus/iverilog.git
cd iverilog cd iverilog
git checkout ${{ vars.IVERILOG_VERSION }}
echo "IVERILOG_GIT=$(git rev-parse HEAD)" >> $GITHUB_ENV echo "IVERILOG_GIT=$(git rev-parse HEAD)" >> $GITHUB_ENV
- name: Cache iverilog - name: Cache iverilog
id: cache-iverilog id: cache-iverilog
uses: actions/cache@v3 uses: actions/cache@v4
with: with:
path: .local/ path: .local/
key: ${{ matrix.os.id }}-${{ env.IVERILOG_GIT }} key: ${{ matrix.os.id }}-${{ env.IVERILOG_GIT }}
@ -111,7 +110,7 @@ jobs:
shell: bash shell: bash
run: | run: |
make config-${CC%%-*} make config-${CC%%-*}
make -j${{ env.procs }} CCXXSTD=${{ matrix.cpp_std }} CC=$CC CXX=$CC LD=$CC make -j${{ env.procs }} CXXSTD=${{ matrix.cpp_std }} CC=$CC CXX=$CC LD=$CC
- name: Store build artifact - name: Store build artifact
if: (matrix.cpp_std == 'c++11') && (matrix.compiler == 'gcc-11') if: (matrix.cpp_std == 'c++11') && (matrix.compiler == 'gcc-11')
@ -125,3 +124,7 @@ jobs:
shell: bash shell: bash
run: | run: |
make -j${{ env.procs }} test CXXSTD=${{ matrix.cpp_std }} CC=$CC CXX=$CC LD=$CC make -j${{ env.procs }} test CXXSTD=${{ matrix.cpp_std }} CC=$CC CXX=$CC LD=$CC
- name: Log yosys-config output
run: |
./yosys-config || true

View File

@ -35,19 +35,18 @@ jobs:
cc --version cc --version
- name: Checkout Yosys - name: Checkout Yosys
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: Get iverilog - name: Get iverilog
shell: bash shell: bash
run: | run: |
git clone https://github.com/steveicarus/iverilog.git git clone https://github.com/steveicarus/iverilog.git
cd iverilog cd iverilog
git checkout ${{ vars.IVERILOG_VERSION }}
echo "IVERILOG_GIT=$(git rev-parse HEAD)" >> $GITHUB_ENV echo "IVERILOG_GIT=$(git rev-parse HEAD)" >> $GITHUB_ENV
- name: Cache iverilog - name: Cache iverilog
id: cache-iverilog id: cache-iverilog
uses: actions/cache@v3 uses: actions/cache@v4
with: with:
path: .local/ path: .local/
key: ${{ matrix.os.id }}-${{ env.IVERILOG_GIT }} key: ${{ matrix.os.id }}-${{ env.IVERILOG_GIT }}

View File

@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Take last commit - name: Take last commit

View File

@ -6,10 +6,10 @@ jobs:
yosys-vcxsrc: yosys-vcxsrc:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Build - name: Build
run: make vcxsrc YOSYS_VER=latest run: make vcxsrc YOSYS_VER=latest
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v4
with: with:
name: vcxsrc name: vcxsrc
path: yosys-win32-vcxsrc-latest.zip path: yosys-win32-vcxsrc-latest.zip
@ -18,7 +18,7 @@ jobs:
runs-on: windows-2019 runs-on: windows-2019
needs: yosys-vcxsrc needs: yosys-vcxsrc
steps: steps:
- uses: actions/download-artifact@v3 - uses: actions/download-artifact@v4
with: with:
name: vcxsrc name: vcxsrc
path: . path: .

View File

@ -6,7 +6,7 @@ jobs:
wasi: wasi:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Build - name: Build
run: | run: |
WASI_SDK=wasi-sdk-19.0 WASI_SDK=wasi-sdk-19.0

View File

@ -2,9 +2,28 @@
List of major changes and improvements between releases List of major changes and improvements between releases
======================================================= =======================================================
Yosys 0.37 .. Yosys 0.38-dev Yosys 0.38 .. Yosys 0.39-dev
-------------------------- --------------------------
Yosys 0.37 .. Yosys 0.38
--------------------------
* New commands and options
- Added option "-tech" to "opt_lut" pass.
- Added option "-nokeep_prints" to "hierarchy" pass.
- Added option "-nolower" to "async2sync" and "clk2fflogic" pass.
- Added option "-lower" to "chformal" pass.
* Various
- Added $check cell to represent assertions with messages.
- Allow capturing $print cell output in CXXRTL.
- Added API to overwrite existing pass from plugin.
- Follow the XDG Base Directory Specification for storing history files.
- Without a known top module, derive all deferred modules (hierarchy pass).
- Detect and error out on combinational loops in write_aiger.
* Verific support
- Added option "-no-split-complex-ports" to "verific -import".
Yosys 0.36 .. Yosys 0.37 Yosys 0.36 .. Yosys 0.37
-------------------------- --------------------------
* New commands and options * New commands and options

184
Makefile
View File

@ -92,14 +92,14 @@ VPATH := $(YOSYS_SRC)
CXXSTD ?= c++11 CXXSTD ?= c++11
CXXFLAGS := $(CXXFLAGS) -Wall -Wextra -ggdb -I. -I"$(YOSYS_SRC)" -MD -MP -D_YOSYS_ -fPIC -I$(PREFIX)/include CXXFLAGS := $(CXXFLAGS) -Wall -Wextra -ggdb -I. -I"$(YOSYS_SRC)" -MD -MP -D_YOSYS_ -fPIC -I$(PREFIX)/include
LDLIBS := $(LDLIBS) -lstdc++ -lm LIBS := $(LIBS) -lstdc++ -lm
PLUGIN_LDFLAGS := PLUGIN_LINKFLAGS :=
PLUGIN_LDLIBS := PLUGIN_LIBS :=
EXE_LDFLAGS := EXE_LINKFLAGS :=
ifeq ($(OS), MINGW) ifeq ($(OS), MINGW)
EXE_LDFLAGS := -Wl,--export-all-symbols -Wl,--out-implib,libyosys_exe.a EXE_LINKFLAGS := -Wl,--export-all-symbols -Wl,--out-implib,libyosys_exe.a
PLUGIN_LDFLAGS += -L"$(LIBDIR)" PLUGIN_LINKFLAGS += -L"$(LIBDIR)"
PLUGIN_LDLIBS := -lyosys_exe PLUGIN_LIBS := -lyosys_exe
endif endif
PKG_CONFIG ?= pkg-config PKG_CONFIG ?= pkg-config
@ -109,7 +109,7 @@ STRIP ?= strip
AWK ?= awk AWK ?= awk
ifeq ($(OS), Darwin) ifeq ($(OS), Darwin)
PLUGIN_LDFLAGS += -undefined dynamic_lookup PLUGIN_LINKFLAGS += -undefined dynamic_lookup
# homebrew search paths # homebrew search paths
ifneq ($(shell :; command -v brew),) ifneq ($(shell :; command -v brew),)
@ -117,10 +117,10 @@ BREW_PREFIX := $(shell brew --prefix)/opt
$(info $$BREW_PREFIX is [${BREW_PREFIX}]) $(info $$BREW_PREFIX is [${BREW_PREFIX}])
ifeq ($(ENABLE_PYOSYS),1) ifeq ($(ENABLE_PYOSYS),1)
CXXFLAGS += -I$(BREW_PREFIX)/boost/include/boost CXXFLAGS += -I$(BREW_PREFIX)/boost/include/boost
LDFLAGS += -L$(BREW_PREFIX)/boost/lib LINKFLAGS += -L$(BREW_PREFIX)/boost/lib
endif endif
CXXFLAGS += -I$(BREW_PREFIX)/readline/include CXXFLAGS += -I$(BREW_PREFIX)/readline/include
LDFLAGS += -L$(BREW_PREFIX)/readline/lib LINKFLAGS += -L$(BREW_PREFIX)/readline/lib
PKG_CONFIG_PATH := $(BREW_PREFIX)/libffi/lib/pkgconfig:$(PKG_CONFIG_PATH) PKG_CONFIG_PATH := $(BREW_PREFIX)/libffi/lib/pkgconfig:$(PKG_CONFIG_PATH)
PKG_CONFIG_PATH := $(BREW_PREFIX)/tcl-tk/lib/pkgconfig:$(PKG_CONFIG_PATH) PKG_CONFIG_PATH := $(BREW_PREFIX)/tcl-tk/lib/pkgconfig:$(PKG_CONFIG_PATH)
export PATH := $(BREW_PREFIX)/bison/bin:$(BREW_PREFIX)/gettext/bin:$(BREW_PREFIX)/flex/bin:$(PATH) export PATH := $(BREW_PREFIX)/bison/bin:$(BREW_PREFIX)/gettext/bin:$(BREW_PREFIX)/flex/bin:$(PATH)
@ -129,19 +129,19 @@ export PATH := $(BREW_PREFIX)/bison/bin:$(BREW_PREFIX)/gettext/bin:$(BREW_PREFIX
else ifneq ($(shell :; command -v port),) else ifneq ($(shell :; command -v port),)
PORT_PREFIX := $(patsubst %/bin/port,%,$(shell :; command -v port)) PORT_PREFIX := $(patsubst %/bin/port,%,$(shell :; command -v port))
CXXFLAGS += -I$(PORT_PREFIX)/include CXXFLAGS += -I$(PORT_PREFIX)/include
LDFLAGS += -L$(PORT_PREFIX)/lib LINKFLAGS += -L$(PORT_PREFIX)/lib
PKG_CONFIG_PATH := $(PORT_PREFIX)/lib/pkgconfig:$(PKG_CONFIG_PATH) PKG_CONFIG_PATH := $(PORT_PREFIX)/lib/pkgconfig:$(PKG_CONFIG_PATH)
export PATH := $(PORT_PREFIX)/bin:$(PATH) export PATH := $(PORT_PREFIX)/bin:$(PATH)
endif endif
else else
LDFLAGS += -rdynamic LINKFLAGS += -rdynamic
ifneq ($(OS), OpenBSD) ifneq ($(OS), OpenBSD)
LDLIBS += -lrt LIBS += -lrt
endif endif
endif endif
YOSYS_VER := 0.37+27 YOSYS_VER := 0.38+113
# Note: We arrange for .gitcommit to contain the (short) commit hash in # Note: We arrange for .gitcommit to contain the (short) commit hash in
# tarballs generated with git-archive(1) using .gitattributes. The git repo # tarballs generated with git-archive(1) using .gitattributes. The git repo
@ -157,7 +157,7 @@ endif
OBJS = kernel/version_$(GIT_REV).o OBJS = kernel/version_$(GIT_REV).o
bumpversion: bumpversion:
sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline a5c7f69.. | wc -l`/;" Makefile sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 543faed.. | wc -l`/;" Makefile
# set 'ABCREV = default' to use abc/ as it is # set 'ABCREV = default' to use abc/ as it is
# #
@ -215,41 +215,38 @@ ABC_ARCHFLAGS += "-DABC_NO_RLIMIT"
endif endif
ifeq ($(CONFIG),clang) ifeq ($(CONFIG),clang)
CXX = clang CXX = clang++
LD = clang++
CXXFLAGS += -std=$(CXXSTD) -Os CXXFLAGS += -std=$(CXXSTD) -Os
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -Wno-c++11-narrowing $(ABC_ARCHFLAGS)" ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -Wno-c++11-narrowing $(ABC_ARCHFLAGS)"
ifneq ($(SANITIZER),) ifneq ($(SANITIZER),)
$(info [Clang Sanitizer] $(SANITIZER)) $(info [Clang Sanitizer] $(SANITIZER))
CXXFLAGS += -g -O1 -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=$(SANITIZER) CXXFLAGS += -g -O1 -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=$(SANITIZER)
LDFLAGS += -g -fsanitize=$(SANITIZER) LINKFLAGS += -g -fsanitize=$(SANITIZER)
ifneq ($(findstring address,$(SANITIZER)),) ifneq ($(findstring address,$(SANITIZER)),)
ENABLE_COVER := 0 ENABLE_COVER := 0
endif endif
ifneq ($(findstring memory,$(SANITIZER)),) ifneq ($(findstring memory,$(SANITIZER)),)
CXXFLAGS += -fPIE -fsanitize-memory-track-origins CXXFLAGS += -fPIE -fsanitize-memory-track-origins
LDFLAGS += -fPIE -fsanitize-memory-track-origins LINKFLAGS += -fPIE -fsanitize-memory-track-origins
endif endif
ifneq ($(findstring cfi,$(SANITIZER)),) ifneq ($(findstring cfi,$(SANITIZER)),)
CXXFLAGS += -flto CXXFLAGS += -flto
LDFLAGS += -flto LINKFLAGS += -flto
endif endif
endif endif
else ifeq ($(CONFIG),gcc) else ifeq ($(CONFIG),gcc)
CXX = gcc CXX = g++
LD = gcc
CXXFLAGS += -std=$(CXXSTD) -Os CXXFLAGS += -std=$(CXXSTD) -Os
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H $(ABC_ARCHFLAGS)" ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H $(ABC_ARCHFLAGS)"
else ifeq ($(CONFIG),gcc-static) else ifeq ($(CONFIG),gcc-static)
LD = $(CXX) LINKFLAGS := $(filter-out -rdynamic,$(LINKFLAGS)) -static
LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -static LIBS := $(filter-out -lrt,$(LIBS))
LDLIBS := $(filter-out -lrt,$(LDLIBS))
CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS)) CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS))
CXXFLAGS += -std=$(CXXSTD) -Os CXXFLAGS += -std=$(CXXSTD) -Os
ABCMKARGS = CC="$(CC)" CXX="$(CXX)" LD="$(LD)" ABC_USE_LIBSTDCXX=1 LIBS="-lm -lpthread -static" OPTFLAGS="-O" \ ABCMKARGS = CC="$(CC)" CXX="$(CXX)" LD="$(CXX)" ABC_USE_LIBSTDCXX=1 LIBS="-lm -lpthread -static" OPTFLAGS="-O" \
ARCHFLAGS="-DABC_USE_STDINT_H -DABC_NO_DYNAMIC_LINKING=1 -Wno-unused-but-set-variable $(ARCHFLAGS)" ABC_USE_NO_READLINE=1 ARCHFLAGS="-DABC_USE_STDINT_H -DABC_NO_DYNAMIC_LINKING=1 -Wno-unused-but-set-variable $(ARCHFLAGS)" ABC_USE_NO_READLINE=1
ifeq ($(DISABLE_ABC_THREADS),1) ifeq ($(DISABLE_ABC_THREADS),1)
ABCMKARGS += "ABC_USE_NO_PTHREADS=1" ABCMKARGS += "ABC_USE_NO_PTHREADS=1"
@ -257,31 +254,28 @@ endif
else ifeq ($(CONFIG),afl-gcc) else ifeq ($(CONFIG),afl-gcc)
CXX = AFL_QUIET=1 AFL_HARDEN=1 afl-gcc CXX = AFL_QUIET=1 AFL_HARDEN=1 afl-gcc
LD = AFL_QUIET=1 AFL_HARDEN=1 afl-gcc
CXXFLAGS += -std=$(CXXSTD) -Os CXXFLAGS += -std=$(CXXSTD) -Os
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H" ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H"
else ifeq ($(CONFIG),cygwin) else ifeq ($(CONFIG),cygwin)
CXX = gcc CXX = g++
LD = gcc
CXXFLAGS += -std=gnu++11 -Os CXXFLAGS += -std=gnu++11 -Os
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H" ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H"
else ifeq ($(CONFIG),emcc) else ifeq ($(CONFIG),emcc)
CXX = emcc CXX = emcc
LD = emcc
CXXFLAGS := -std=$(CXXSTD) $(filter-out -fPIC -ggdb,$(CXXFLAGS)) CXXFLAGS := -std=$(CXXSTD) $(filter-out -fPIC -ggdb,$(CXXFLAGS))
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DABC_MEMALIGN=8 -Wno-c++11-narrowing" ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DABC_MEMALIGN=8 -Wno-c++11-narrowing"
EMCC_CXXFLAGS := -Os -Wno-warn-absolute-paths EMCC_CXXFLAGS := -Os -Wno-warn-absolute-paths
EMCC_LDFLAGS := --memory-init-file 0 --embed-file share EMCC_LINKFLAGS := --embed-file share
EMCC_LDFLAGS += -s NO_EXIT_RUNTIME=1 EMCC_LINKFLAGS += -s NO_EXIT_RUNTIME=1
EMCC_LDFLAGS += -s EXPORTED_FUNCTIONS="['_main','_run','_prompt','_errmsg','_memset']" EMCC_LINKFLAGS += -s EXPORTED_FUNCTIONS="['_main','_run','_prompt','_errmsg','_memset']"
EMCC_LDFLAGS += -s TOTAL_MEMORY=134217728 EMCC_LINKFLAGS += -s TOTAL_MEMORY=134217728
EMCC_LDFLAGS += -s EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]' EMCC_LINKFLAGS += -s EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]'
# https://github.com/kripken/emscripten/blob/master/src/settings.js # https://github.com/kripken/emscripten/blob/master/src/settings.js
CXXFLAGS += $(EMCC_CXXFLAGS) CXXFLAGS += $(EMCC_CXXFLAGS)
LDFLAGS += $(EMCC_LDFLAGS) LINKFLAGS += $(EMCC_LINKFLAGS)
LDLIBS = LIBS =
EXE = .js EXE = .js
DISABLE_SPAWN := 1 DISABLE_SPAWN := 1
@ -309,21 +303,19 @@ yosys.html: misc/yosys.html
else ifeq ($(CONFIG),wasi) else ifeq ($(CONFIG),wasi)
ifeq ($(WASI_SDK),) ifeq ($(WASI_SDK),)
CXX = clang CXX = clang++
LD = clang++
AR = llvm-ar AR = llvm-ar
RANLIB = llvm-ranlib RANLIB = llvm-ranlib
WASIFLAGS := -target wasm32-wasi --sysroot $(WASI_SYSROOT) $(WASIFLAGS) WASIFLAGS := -target wasm32-wasi --sysroot $(WASI_SYSROOT) $(WASIFLAGS)
else else
CXX = $(WASI_SDK)/bin/clang CXX = $(WASI_SDK)/bin/clang++
LD = $(WASI_SDK)/bin/clang++
AR = $(WASI_SDK)/bin/ar AR = $(WASI_SDK)/bin/ar
RANLIB = $(WASI_SDK)/bin/ranlib RANLIB = $(WASI_SDK)/bin/ranlib
WASIFLAGS := --sysroot $(WASI_SDK)/share/wasi-sysroot $(WASIFLAGS) WASIFLAGS := --sysroot $(WASI_SDK)/share/wasi-sysroot $(WASIFLAGS)
endif endif
CXXFLAGS := $(WASIFLAGS) -std=$(CXXSTD) -Os -D_WASI_EMULATED_PROCESS_CLOCKS $(filter-out -fPIC,$(CXXFLAGS)) CXXFLAGS := $(WASIFLAGS) -std=$(CXXSTD) -Os -D_WASI_EMULATED_PROCESS_CLOCKS $(filter-out -fPIC,$(CXXFLAGS))
LDFLAGS := $(WASIFLAGS) -Wl,-z,stack-size=1048576 $(filter-out -rdynamic,$(LDFLAGS)) LINKFLAGS := $(WASIFLAGS) -Wl,-z,stack-size=1048576 $(filter-out -rdynamic,$(LINKFLAGS))
LDLIBS := -lwasi-emulated-process-clocks $(filter-out -lrt,$(LDLIBS)) LIBS := -lwasi-emulated-process-clocks $(filter-out -lrt,$(LIBS))
ABCMKARGS += AR="$(AR)" RANLIB="$(RANLIB)" ABCMKARGS += AR="$(AR)" RANLIB="$(RANLIB)"
ABCMKARGS += ARCHFLAGS="$(WASIFLAGS) -D_WASI_EMULATED_PROCESS_CLOCKS -DABC_USE_STDINT_H -DABC_NO_DYNAMIC_LINKING -DABC_NO_RLIMIT -Wno-c++11-narrowing" ABCMKARGS += ARCHFLAGS="$(WASIFLAGS) -D_WASI_EMULATED_PROCESS_CLOCKS -DABC_USE_STDINT_H -DABC_NO_DYNAMIC_LINKING -DABC_NO_RLIMIT -Wno-c++11-narrowing"
ABCMKARGS += OPTFLAGS="-Os" ABCMKARGS += OPTFLAGS="-Os"
@ -339,34 +331,31 @@ endif
else ifeq ($(CONFIG),mxe) else ifeq ($(CONFIG),mxe)
PKG_CONFIG = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-pkg-config PKG_CONFIG = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-pkg-config
CXX = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-g++ CXX = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-g++
LD = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-g++
CXXFLAGS += -std=$(CXXSTD) -Os -D_POSIX_SOURCE -DYOSYS_MXE_HACKS -Wno-attributes CXXFLAGS += -std=$(CXXSTD) -Os -D_POSIX_SOURCE -DYOSYS_MXE_HACKS -Wno-attributes
CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS)) CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS))
LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -s LINKFLAGS := $(filter-out -rdynamic,$(LINKFLAGS)) -s
LDLIBS := $(filter-out -lrt,$(LDLIBS)) LIBS := $(filter-out -lrt,$(LIBS))
ABCMKARGS += ARCHFLAGS="-DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -fpermissive -w" ABCMKARGS += ARCHFLAGS="-DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -fpermissive -w"
# TODO: Try to solve pthread linking issue in more appropriate way # TODO: Try to solve pthread linking issue in more appropriate way
ABCMKARGS += LIBS="lib/x86/pthreadVC2.lib -s" LDFLAGS="-Wl,--allow-multiple-definition" ABC_USE_NO_READLINE=1 CC="/usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-gcc" ABCMKARGS += LIBS="lib/x86/pthreadVC2.lib -s" LINKFLAGS="-Wl,--allow-multiple-definition" ABC_USE_NO_READLINE=1 CC="/usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-gcc"
EXE = .exe EXE = .exe
else ifeq ($(CONFIG),msys2-32) else ifeq ($(CONFIG),msys2-32)
CXX = i686-w64-mingw32-g++ CXX = i686-w64-mingw32-g++
LD = i686-w64-mingw32-g++
CXXFLAGS += -std=$(CXXSTD) -Os -D_POSIX_SOURCE -DYOSYS_WIN32_UNIX_DIR CXXFLAGS += -std=$(CXXSTD) -Os -D_POSIX_SOURCE -DYOSYS_WIN32_UNIX_DIR
CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS)) CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS))
LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -s LINKFLAGS := $(filter-out -rdynamic,$(LINKFLAGS)) -s
LDLIBS := $(filter-out -lrt,$(LDLIBS)) LIBS := $(filter-out -lrt,$(LIBS))
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -fpermissive -w" ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -fpermissive -w"
ABCMKARGS += LIBS="-lpthread -lshlwapi -s" ABC_USE_NO_READLINE=0 CC="i686-w64-mingw32-gcc" CXX="$(CXX)" ABCMKARGS += LIBS="-lpthread -lshlwapi -s" ABC_USE_NO_READLINE=0 CC="i686-w64-mingw32-gcc" CXX="$(CXX)"
EXE = .exe EXE = .exe
else ifeq ($(CONFIG),msys2-64) else ifeq ($(CONFIG),msys2-64)
CXX = x86_64-w64-mingw32-g++ CXX = x86_64-w64-mingw32-g++
LD = x86_64-w64-mingw32-g++
CXXFLAGS += -std=$(CXXSTD) -Os -D_POSIX_SOURCE -DYOSYS_WIN32_UNIX_DIR CXXFLAGS += -std=$(CXXSTD) -Os -D_POSIX_SOURCE -DYOSYS_WIN32_UNIX_DIR
CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS)) CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS))
LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -s LINKFLAGS := $(filter-out -rdynamic,$(LINKFLAGS)) -s
LDLIBS := $(filter-out -lrt,$(LDLIBS)) LIBS := $(filter-out -lrt,$(LIBS))
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -fpermissive -w" ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -fpermissive -w"
ABCMKARGS += LIBS="-lpthread -lshlwapi -s" ABC_USE_NO_READLINE=0 CC="x86_64-w64-mingw32-gcc" CXX="$(CXX)" ABCMKARGS += LIBS="-lpthread -lshlwapi -s" ABC_USE_NO_READLINE=0 CC="x86_64-w64-mingw32-gcc" CXX="$(CXX)"
EXE = .exe EXE = .exe
@ -393,9 +382,9 @@ ifeq ($(BOOST_PYTHON_LIB),)
$(error BOOST_PYTHON_LIB could not be detected. Please define manually) $(error BOOST_PYTHON_LIB could not be detected. Please define manually)
endif endif
LDLIBS += $(shell $(PYTHON_CONFIG) --libs) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem LIBS += $(shell $(PYTHON_CONFIG) --libs) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
# python-config --ldflags includes LDLIBS for some reason # python-config --ldflags includes LIBS for some reason
LDFLAGS += $(filter-out -l%,$(shell $(PYTHON_CONFIG) --ldflags)) LINKFLAGS += $(filter-out -l%,$(shell $(PYTHON_CONFIG) --ldflags))
CXXFLAGS += $(shell $(PYTHON_CONFIG) --includes) -DWITH_PYTHON CXXFLAGS += $(shell $(PYTHON_CONFIG) --includes) -DWITH_PYTHON
PY_WRAPPER_FILE = kernel/python_wrappers PY_WRAPPER_FILE = kernel/python_wrappers
@ -409,22 +398,22 @@ CXXFLAGS += -DYOSYS_ENABLE_READLINE
ifeq ($(OS), $(filter $(OS),FreeBSD OpenBSD NetBSD)) ifeq ($(OS), $(filter $(OS),FreeBSD OpenBSD NetBSD))
CXXFLAGS += -I/usr/local/include CXXFLAGS += -I/usr/local/include
endif endif
LDLIBS += -lreadline LIBS += -lreadline
ifeq ($(LINK_CURSES),1) ifeq ($(LINK_CURSES),1)
LDLIBS += -lcurses LIBS += -lcurses
ABCMKARGS += "ABC_READLINE_LIBRARIES=-lcurses -lreadline" ABCMKARGS += "ABC_READLINE_LIBRARIES=-lcurses -lreadline"
endif endif
ifeq ($(LINK_TERMCAP),1) ifeq ($(LINK_TERMCAP),1)
LDLIBS += -ltermcap LIBS += -ltermcap
ABCMKARGS += "ABC_READLINE_LIBRARIES=-lreadline -ltermcap" ABCMKARGS += "ABC_READLINE_LIBRARIES=-lreadline -ltermcap"
endif endif
ifeq ($(CONFIG),mxe) ifeq ($(CONFIG),mxe)
LDLIBS += -ltermcap LIBS += -ltermcap
endif endif
else else
ifeq ($(ENABLE_EDITLINE),1) ifeq ($(ENABLE_EDITLINE),1)
CXXFLAGS += -DYOSYS_ENABLE_EDITLINE CXXFLAGS += -DYOSYS_ENABLE_EDITLINE
LDLIBS += -ledit -ltinfo -lbsd LIBS += -ledit -ltinfo -lbsd
else else
ABCMKARGS += "ABC_USE_NO_READLINE=1" ABCMKARGS += "ABC_USE_NO_READLINE=1"
endif endif
@ -443,9 +432,9 @@ CXXFLAGS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-e
ifeq ($(OS), MINGW) ifeq ($(OS), MINGW)
CXXFLAGS += -Ilibs/dlfcn-win32 CXXFLAGS += -Ilibs/dlfcn-win32
endif endif
LDLIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --libs libffi || echo -lffi) LIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --libs libffi || echo -lffi)
ifneq ($(OS), $(filter $(OS),FreeBSD OpenBSD NetBSD MINGW)) ifneq ($(OS), $(filter $(OS),FreeBSD OpenBSD NetBSD MINGW))
LDLIBS += -ldl LIBS += -ldl
endif endif
endif endif
@ -455,7 +444,7 @@ endif
ifeq ($(ENABLE_ZLIB),1) ifeq ($(ENABLE_ZLIB),1)
CXXFLAGS += -DYOSYS_ENABLE_ZLIB CXXFLAGS += -DYOSYS_ENABLE_ZLIB
LDLIBS += -lz LIBS += -lz
endif endif
@ -472,21 +461,21 @@ endif
ifeq ($(CONFIG),mxe) ifeq ($(CONFIG),mxe)
CXXFLAGS += -DYOSYS_ENABLE_TCL CXXFLAGS += -DYOSYS_ENABLE_TCL
LDLIBS += -ltcl86 -lwsock32 -lws2_32 -lnetapi32 -lz -luserenv LIBS += -ltcl86 -lwsock32 -lws2_32 -lnetapi32 -lz -luserenv
else else
CXXFLAGS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --cflags tcl || echo -I$(TCL_INCLUDE)) -DYOSYS_ENABLE_TCL CXXFLAGS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --cflags tcl || echo -I$(TCL_INCLUDE)) -DYOSYS_ENABLE_TCL
LDLIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --libs tcl || echo $(TCL_LIBS)) LIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --libs tcl || echo $(TCL_LIBS))
endif endif
endif endif
ifeq ($(ENABLE_GCOV),1) ifeq ($(ENABLE_GCOV),1)
CXXFLAGS += --coverage CXXFLAGS += --coverage
LDFLAGS += --coverage LINKFLAGS += --coverage
endif endif
ifeq ($(ENABLE_GPROF),1) ifeq ($(ENABLE_GPROF),1)
CXXFLAGS += -pg CXXFLAGS += -pg
LDFLAGS += -pg LINKFLAGS += -pg
endif endif
ifeq ($(ENABLE_NDEBUG),1) ifeq ($(ENABLE_NDEBUG),1)
@ -506,7 +495,7 @@ CXXFLAGS += -DYOSYS_ENABLE_ABC
ifeq ($(LINK_ABC),1) ifeq ($(LINK_ABC),1)
CXXFLAGS += -DYOSYS_LINK_ABC CXXFLAGS += -DYOSYS_LINK_ABC
ifeq ($(DISABLE_ABC_THREADS),0) ifeq ($(DISABLE_ABC_THREADS),0)
LDLIBS += -lpthread LIBS += -lpthread
endif endif
else else
ifeq ($(ABCEXTERNAL),) ifeq ($(ABCEXTERNAL),)
@ -520,10 +509,10 @@ GHDL_PREFIX ?= $(PREFIX)
GHDL_INCLUDE_DIR ?= $(GHDL_PREFIX)/include GHDL_INCLUDE_DIR ?= $(GHDL_PREFIX)/include
GHDL_LIB_DIR ?= $(GHDL_PREFIX)/lib GHDL_LIB_DIR ?= $(GHDL_PREFIX)/lib
CXXFLAGS += -I$(GHDL_INCLUDE_DIR) -DYOSYS_ENABLE_GHDL CXXFLAGS += -I$(GHDL_INCLUDE_DIR) -DYOSYS_ENABLE_GHDL
LDLIBS += $(GHDL_LIB_DIR)/libghdl.a $(file <$(GHDL_LIB_DIR)/libghdl.link) LIBS += $(GHDL_LIB_DIR)/libghdl.a $(file <$(GHDL_LIB_DIR)/libghdl.link)
endif endif
LDLIBS_VERIFIC = LIBS_VERIFIC =
ifeq ($(ENABLE_VERIFIC),1) ifeq ($(ENABLE_VERIFIC),1)
VERIFIC_DIR ?= /usr/local/src/verific_lib VERIFIC_DIR ?= /usr/local/src/verific_lib
VERIFIC_COMPONENTS ?= verilog database util containers hier_tree VERIFIC_COMPONENTS ?= verilog database util containers hier_tree
@ -549,9 +538,9 @@ CXXFLAGS += -DYOSYSHQ_VERIFIC_EXTENSIONS
endif endif
CXXFLAGS += $(patsubst %,-I$(VERIFIC_DIR)/%,$(VERIFIC_COMPONENTS)) -DYOSYS_ENABLE_VERIFIC CXXFLAGS += $(patsubst %,-I$(VERIFIC_DIR)/%,$(VERIFIC_COMPONENTS)) -DYOSYS_ENABLE_VERIFIC
ifeq ($(OS), Darwin) ifeq ($(OS), Darwin)
LDLIBS_VERIFIC += $(foreach comp,$(patsubst %,$(VERIFIC_DIR)/%/*-mac.a,$(VERIFIC_COMPONENTS)),-Wl,-force_load $(comp)) -lz LIBS_VERIFIC += $(foreach comp,$(patsubst %,$(VERIFIC_DIR)/%/*-mac.a,$(VERIFIC_COMPONENTS)),-Wl,-force_load $(comp)) -lz
else else
LDLIBS_VERIFIC += -Wl,--whole-archive $(patsubst %,$(VERIFIC_DIR)/%/*-linux.a,$(VERIFIC_COMPONENTS)) -Wl,--no-whole-archive -lz LIBS_VERIFIC += -Wl,--whole-archive $(patsubst %,$(VERIFIC_DIR)/%/*-linux.a,$(VERIFIC_COMPONENTS)) -Wl,--no-whole-archive -lz
endif endif
endif endif
@ -630,6 +619,7 @@ $(eval $(call add_include_file,kernel/qcsat.h))
$(eval $(call add_include_file,kernel/register.h)) $(eval $(call add_include_file,kernel/register.h))
$(eval $(call add_include_file,kernel/rtlil.h)) $(eval $(call add_include_file,kernel/rtlil.h))
$(eval $(call add_include_file,kernel/satgen.h)) $(eval $(call add_include_file,kernel/satgen.h))
$(eval $(call add_include_file,kernel/scopeinfo.h))
$(eval $(call add_include_file,kernel/sigtools.h)) $(eval $(call add_include_file,kernel/sigtools.h))
$(eval $(call add_include_file,kernel/timinginfo.h)) $(eval $(call add_include_file,kernel/timinginfo.h))
$(eval $(call add_include_file,kernel/utils.h)) $(eval $(call add_include_file,kernel/utils.h))
@ -647,16 +637,10 @@ $(eval $(call add_include_file,frontends/ast/ast.h))
$(eval $(call add_include_file,frontends/ast/ast_binding.h)) $(eval $(call add_include_file,frontends/ast/ast_binding.h))
$(eval $(call add_include_file,frontends/blif/blifparse.h)) $(eval $(call add_include_file,frontends/blif/blifparse.h))
$(eval $(call add_include_file,backends/rtlil/rtlil_backend.h)) $(eval $(call add_include_file,backends/rtlil/rtlil_backend.h))
$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/cxxrtl.h))
$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/cxxrtl_vcd.h))
$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi.cc))
$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi.h))
$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi_vcd.cc))
$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi_vcd.h))
OBJS += kernel/driver.o kernel/register.o kernel/rtlil.o kernel/log.o kernel/calc.o kernel/yosys.o 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/binding.o
OBJS += kernel/cellaigs.o kernel/celledges.o kernel/satgen.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/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
ifeq ($(ENABLE_ZLIB),1) ifeq ($(ENABLE_ZLIB),1)
OBJS += kernel/fstdata.o OBJS += kernel/fstdata.o
endif endif
@ -679,12 +663,8 @@ OBJS += libs/bigint/BigUnsigned.o libs/bigint/BigUnsignedInABase.o
OBJS += libs/sha1/sha1.o OBJS += libs/sha1/sha1.o
ifneq ($(SMALL),1)
OBJS += libs/json11/json11.o OBJS += libs/json11/json11.o
OBJS += libs/subcircuit/subcircuit.o
OBJS += libs/ezsat/ezsat.o OBJS += libs/ezsat/ezsat.o
OBJS += libs/ezsat/ezminisat.o OBJS += libs/ezsat/ezminisat.o
@ -699,6 +679,10 @@ OBJS += libs/fst/fastlz.o
OBJS += libs/fst/lz4.o OBJS += libs/fst/lz4.o
endif endif
ifneq ($(SMALL),1)
OBJS += libs/subcircuit/subcircuit.o
include $(YOSYS_SRC)/frontends/*/Makefile.inc include $(YOSYS_SRC)/frontends/*/Makefile.inc
include $(YOSYS_SRC)/passes/*/Makefile.inc include $(YOSYS_SRC)/passes/*/Makefile.inc
include $(YOSYS_SRC)/backends/*/Makefile.inc include $(YOSYS_SRC)/backends/*/Makefile.inc
@ -707,6 +691,9 @@ include $(YOSYS_SRC)/techlibs/*/Makefile.inc
else else
include $(YOSYS_SRC)/frontends/verilog/Makefile.inc include $(YOSYS_SRC)/frontends/verilog/Makefile.inc
ifeq ($(ENABLE_VERIFIC),1)
include $(YOSYS_SRC)/frontends/verific/Makefile.inc
endif
include $(YOSYS_SRC)/frontends/rtlil/Makefile.inc include $(YOSYS_SRC)/frontends/rtlil/Makefile.inc
include $(YOSYS_SRC)/frontends/ast/Makefile.inc include $(YOSYS_SRC)/frontends/ast/Makefile.inc
include $(YOSYS_SRC)/frontends/blif/Makefile.inc include $(YOSYS_SRC)/frontends/blif/Makefile.inc
@ -748,13 +735,13 @@ yosys.js: $(filter-out yosysjs-$(YOSYS_VER).zip,$(EXTRA_TARGETS))
endif endif
$(PROGRAM_PREFIX)yosys$(EXE): $(OBJS) $(PROGRAM_PREFIX)yosys$(EXE): $(OBJS)
$(P) $(LD) -o $(PROGRAM_PREFIX)yosys$(EXE) $(EXE_LDFLAGS) $(LDFLAGS) $(OBJS) $(LDLIBS) $(LDLIBS_VERIFIC) $(P) $(CXX) -o $(PROGRAM_PREFIX)yosys$(EXE) $(EXE_LINKFLAGS) $(LINKFLAGS) $(OBJS) $(LIBS) $(LIBS_VERIFIC)
libyosys.so: $(filter-out kernel/driver.o,$(OBJS)) libyosys.so: $(filter-out kernel/driver.o,$(OBJS))
ifeq ($(OS), Darwin) ifeq ($(OS), Darwin)
$(P) $(LD) -o libyosys.so -shared -Wl,-install_name,$(LIBDIR)/libyosys.so $(LDFLAGS) $^ $(LDLIBS) $(LDLIBS_VERIFIC) $(P) $(CXX) -o libyosys.so -shared -Wl,-install_name,$(LIBDIR)/libyosys.so $(LINKFLAGS) $^ $(LIBS) $(LIBS_VERIFIC)
else else
$(P) $(LD) -o libyosys.so -shared -Wl,-soname,$(LIBDIR)/libyosys.so $(LDFLAGS) $^ $(LDLIBS) $(LDLIBS_VERIFIC) $(P) $(CXX) -o libyosys.so -shared -Wl,-soname,$(LIBDIR)/libyosys.so $(LINKFLAGS) $^ $(LIBS) $(LIBS_VERIFIC)
endif endif
%.o: %.cc %.o: %.cc
@ -763,7 +750,7 @@ endif
%.pyh: %.h %.pyh: %.h
$(Q) mkdir -p $(dir $@) $(Q) mkdir -p $(dir $@)
$(P) cat $< | grep -E -v "#[ ]*(include|error)" | $(LD) $(CXXFLAGS) -x c++ -o $@ -E -P - $(P) cat $< | grep -E -v "#[ ]*(include|error)" | $(CXX) $(CXXFLAGS) -x c++ -o $@ -E -P -
ifeq ($(ENABLE_PYOSYS),1) ifeq ($(ENABLE_PYOSYS),1)
$(PY_WRAPPER_FILE).cc: misc/$(PY_GEN_SCRIPT).py $(PY_WRAP_INCLUDES) $(PY_WRAPPER_FILE).cc: misc/$(PY_GEN_SCRIPT).py $(PY_WRAP_INCLUDES)
@ -784,15 +771,15 @@ kernel/version_$(GIT_REV).cc: $(YOSYS_SRC)/Makefile
ifeq ($(ENABLE_VERIFIC),1) ifeq ($(ENABLE_VERIFIC),1)
CXXFLAGS_NOVERIFIC = $(foreach v,$(CXXFLAGS),$(if $(findstring $(VERIFIC_DIR),$(v)),,$(v))) CXXFLAGS_NOVERIFIC = $(foreach v,$(CXXFLAGS),$(if $(findstring $(VERIFIC_DIR),$(v)),,$(v)))
LDLIBS_NOVERIFIC = $(foreach v,$(LDLIBS),$(if $(findstring $(VERIFIC_DIR),$(v)),,$(v))) LIBS_NOVERIFIC = $(foreach v,$(LIBS),$(if $(findstring $(VERIFIC_DIR),$(v)),,$(v)))
else else
CXXFLAGS_NOVERIFIC = $(CXXFLAGS) CXXFLAGS_NOVERIFIC = $(CXXFLAGS)
LDLIBS_NOVERIFIC = $(LDLIBS) LIBS_NOVERIFIC = $(LIBS)
endif endif
$(PROGRAM_PREFIX)yosys-config: misc/yosys-config.in $(PROGRAM_PREFIX)yosys-config: misc/yosys-config.in
$(P) $(SED) -e 's#@CXXFLAGS@#$(subst -Ilibs/dlfcn-win32,,$(subst -I. -I"$(YOSYS_SRC)",-I"$(DATDIR)/include",$(strip $(CXXFLAGS_NOVERIFIC))))#;' \ $(P) $(SED) -e 's#@CXXFLAGS@#$(subst -Ilibs/dlfcn-win32,,$(subst -I. -I"$(YOSYS_SRC)",-I"$(DATDIR)/include",$(strip $(CXXFLAGS_NOVERIFIC))))#;' \
-e 's#@CXX@#$(strip $(CXX))#;' -e 's#@LDFLAGS@#$(strip $(LDFLAGS) $(PLUGIN_LDFLAGS))#;' -e 's#@LDLIBS@#$(strip $(LDLIBS_NOVERIFIC) $(PLUGIN_LDLIBS))#;' \ -e 's#@CXX@#$(strip $(CXX))#;' -e 's#@LINKFLAGS@#$(strip $(LINKFLAGS) $(PLUGIN_LINKFLAGS))#;' -e 's#@LIBS@#$(strip $(LIBS_NOVERIFIC) $(PLUGIN_LIBS))#;' \
-e 's#@BINDIR@#$(strip $(BINDIR))#;' -e 's#@DATDIR@#$(strip $(DATDIR))#;' < $< > $(PROGRAM_PREFIX)yosys-config -e 's#@BINDIR@#$(strip $(BINDIR))#;' -e 's#@DATDIR@#$(strip $(DATDIR))#;' < $< > $(PROGRAM_PREFIX)yosys-config
$(Q) chmod +x $(PROGRAM_PREFIX)yosys-config $(Q) chmod +x $(PROGRAM_PREFIX)yosys-config
@ -844,9 +831,22 @@ else
ABCOPT="" ABCOPT=""
endif endif
# When YOSYS_NOVERIFIC is set as a make variable, also export it to the
# enviornment, so that `YOSYS_NOVERIFIC=1 make test` _and_
# `make test YOSYS_NOVERIFIC=1` will run with verific disabled.
ifeq ($(YOSYS_NOVERIFIC),1)
export YOSYS_NOVERIFIC
endif
test: $(TARGETS) $(EXTRA_TARGETS) test: $(TARGETS) $(EXTRA_TARGETS)
ifeq ($(ENABLE_VERIFIC),1) ifeq ($(ENABLE_VERIFIC),1)
ifeq ($(YOSYS_NOVERIFIC),1)
@echo
@echo "Running tests without verific support due to YOSYS_NOVERIFIC=1"
@echo
else
+cd tests/verific && bash run-test.sh $(SEEDOPT) +cd tests/verific && bash run-test.sh $(SEEDOPT)
endif
endif endif
+cd tests/simple && bash run-test.sh $(SEEDOPT) +cd tests/simple && bash run-test.sh $(SEEDOPT)
+cd tests/simple_abc9 && bash run-test.sh $(SEEDOPT) +cd tests/simple_abc9 && bash run-test.sh $(SEEDOPT)
@ -918,7 +918,7 @@ ystests: $(TARGETS) $(EXTRA_TARGETS)
# Unit test # Unit test
unit-test: libyosys.so unit-test: libyosys.so
@$(MAKE) -C $(UNITESTPATH) CXX="$(CXX)" CPPFLAGS="$(CPPFLAGS)" \ @$(MAKE) -C $(UNITESTPATH) CXX="$(CXX)" CPPFLAGS="$(CPPFLAGS)" \
CXXFLAGS="$(CXXFLAGS)" LDLIBS="$(LDLIBS)" ROOTPATH="$(CURDIR)" CXXFLAGS="$(CXXFLAGS)" LIBS="$(LIBS)" ROOTPATH="$(CURDIR)"
clean-unit-test: clean-unit-test:
@$(MAKE) -C $(UNITESTPATH) clean @$(MAKE) -C $(UNITESTPATH) clean

View File

@ -1,7 +1,7 @@
``` ```
yosys -- Yosys Open SYnthesis Suite yosys -- Yosys Open SYnthesis Suite
Copyright (C) 2012 - 2020 Claire Xenia Wolf <claire@yosyshq.com> Copyright (C) 2012 - 2024 Claire Xenia Wolf <claire@yosyshq.com>
Permission to use, copy, modify, and/or distribute this software for any Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above purpose with or without fee is hereby granted, provided that the above
@ -587,7 +587,13 @@ from SystemVerilog:
- enums are supported (including inside packages) - enums are supported (including inside packages)
- but are currently not strongly typed - but are currently not strongly typed
- packed structs and unions are supported. - packed structs and unions are supported
- arrays of packed structs/unions are currently not supported
- structure literals are currently not supported
- multidimensional arrays are supported
- array assignment of unpacked arrays is currently not supported
- array literals are currently not supported
- SystemVerilog interfaces (SVIs) are supported. Modports for specifying whether - SystemVerilog interfaces (SVIs) are supported. Modports for specifying whether
ports are inputs or outputs are supported. ports are inputs or outputs are supported.

View File

@ -54,6 +54,8 @@ struct AigerWriter
vector<pair<int, int>> aig_gates; vector<pair<int, int>> aig_gates;
vector<int> aig_latchin, aig_latchinit, aig_outputs; vector<int> aig_latchin, aig_latchinit, aig_outputs;
vector<SigBit> bit2aig_stack;
size_t next_loop_check = 1024;
int aig_m = 0, aig_i = 0, aig_l = 0, aig_o = 0, aig_a = 0; int aig_m = 0, aig_i = 0, aig_l = 0, aig_o = 0, aig_a = 0;
int aig_b = 0, aig_c = 0, aig_j = 0, aig_f = 0; int aig_b = 0, aig_c = 0, aig_j = 0, aig_f = 0;
@ -81,6 +83,23 @@ struct AigerWriter
return it->second; return it->second;
} }
if (bit2aig_stack.size() == next_loop_check) {
for (size_t i = 0; i < next_loop_check; ++i)
{
SigBit report_bit = bit2aig_stack[i];
if (report_bit != bit)
continue;
for (size_t j = i; j < next_loop_check; ++j) {
report_bit = bit2aig_stack[j];
if (report_bit.is_wire() && report_bit.wire->name.isPublic())
break;
}
log_error("Found combinational logic loop while processing signal %s.\n", log_signal(report_bit));
}
next_loop_check *= 2;
}
bit2aig_stack.push_back(bit);
// NB: Cannot use iterator returned from aig_map.insert() // NB: Cannot use iterator returned from aig_map.insert()
// since this function is called recursively // since this function is called recursively
@ -101,6 +120,8 @@ struct AigerWriter
a = initstate_ff; a = initstate_ff;
} }
bit2aig_stack.pop_back();
if (bit == State::Sx || bit == State::Sz) if (bit == State::Sx || bit == State::Sz)
log_error("Design contains 'x' or 'z' bits. Use 'setundef' to replace those constants.\n"); log_error("Design contains 'x' or 'z' bits. Use 'setundef' to replace those constants.\n");
@ -299,6 +320,9 @@ struct AigerWriter
continue; continue;
} }
if (cell->type == ID($scopeinfo))
continue;
log_error("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell)); log_error("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell));
} }

View File

@ -226,6 +226,9 @@ struct BlifDumper
for (auto cell : module->cells()) for (auto cell : module->cells())
{ {
if (cell->type == ID($scopeinfo))
continue;
if (config->unbuf_types.count(cell->type)) { if (config->unbuf_types.count(cell->type)) {
auto portnames = config->unbuf_types.at(cell->type); auto portnames = config->unbuf_types.at(cell->type);
f << stringf(".names %s %s\n1 1\n", f << stringf(".names %s %s\n1 1\n",

View File

@ -1,2 +1,11 @@
OBJS += backends/cxxrtl/cxxrtl_backend.o OBJS += backends/cxxrtl/cxxrtl_backend.o
$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/cxxrtl.h))
$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/cxxrtl_vcd.h))
$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/cxxrtl_time.h))
$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/cxxrtl_replay.h))
$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi.cc))
$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi.h))
$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi_vcd.cc))
$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi_vcd.h))

View File

@ -25,6 +25,7 @@
#include "kernel/mem.h" #include "kernel/mem.h"
#include "kernel/log.h" #include "kernel/log.h"
#include "kernel/fmt.h" #include "kernel/fmt.h"
#include "kernel/scopeinfo.h"
USING_YOSYS_NAMESPACE USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN PRIVATE_NAMESPACE_BEGIN
@ -218,7 +219,7 @@ bool is_internal_cell(RTLIL::IdString type)
bool is_effectful_cell(RTLIL::IdString type) bool is_effectful_cell(RTLIL::IdString type)
{ {
return type.isPublic() || type == ID($print); return type.in(ID($print), ID($check));
} }
bool is_cxxrtl_blackbox_cell(const RTLIL::Cell *cell) bool is_cxxrtl_blackbox_cell(const RTLIL::Cell *cell)
@ -282,7 +283,7 @@ struct FlowGraph {
CONNECT, CONNECT,
CELL_SYNC, CELL_SYNC,
CELL_EVAL, CELL_EVAL,
PRINT_SYNC, EFFECT_SYNC,
PROCESS_SYNC, PROCESS_SYNC,
PROCESS_CASE, PROCESS_CASE,
MEM_RDPORT, MEM_RDPORT,
@ -292,7 +293,7 @@ struct FlowGraph {
Type type; Type type;
RTLIL::SigSig connect = {}; RTLIL::SigSig connect = {};
const RTLIL::Cell *cell = nullptr; const RTLIL::Cell *cell = nullptr;
std::vector<const RTLIL::Cell*> print_sync_cells; std::vector<const RTLIL::Cell*> cells;
const RTLIL::Process *process = nullptr; const RTLIL::Process *process = nullptr;
const Mem *mem = nullptr; const Mem *mem = nullptr;
int portidx; int portidx;
@ -480,11 +481,11 @@ struct FlowGraph {
return node; return node;
} }
Node *add_print_sync_node(std::vector<const RTLIL::Cell*> cells) Node *add_effect_sync_node(std::vector<const RTLIL::Cell*> cells)
{ {
Node *node = new Node; Node *node = new Node;
node->type = Node::Type::PRINT_SYNC; node->type = Node::Type::EFFECT_SYNC;
node->print_sync_cells = cells; node->cells = cells;
nodes.push_back(node); nodes.push_back(node);
return node; return node;
} }
@ -1063,99 +1064,6 @@ struct CxxrtlWorker {
f << ".val()"; f << ".val()";
} }
void dump_print(const RTLIL::Cell *cell)
{
Fmt fmt = {};
fmt.parse_rtlil(cell);
f << indent << "if (";
dump_sigspec_rhs(cell->getPort(ID::EN));
f << " == value<1>{1u}) {\n";
inc_indent();
dict<std::string, RTLIL::SigSpec> fmt_args;
f << indent << "struct : public lazy_fmt {\n";
inc_indent();
f << indent << "std::string operator() () const override {\n";
inc_indent();
fmt.emit_cxxrtl(f, indent, [&](const RTLIL::SigSpec &sig) {
if (sig.size() == 0)
f << "value<0>()";
else {
std::string arg_name = "arg" + std::to_string(fmt_args.size());
fmt_args[arg_name] = sig;
f << arg_name;
}
}, "performer");
dec_indent();
f << indent << "}\n";
f << indent << "struct performer *performer;\n";
for (auto arg : fmt_args)
f << indent << "value<" << arg.second.size() << "> " << arg.first << ";\n";
dec_indent();
f << indent << "} formatter;\n";
f << indent << "formatter.performer = performer;\n";
for (auto arg : fmt_args) {
f << indent << "formatter." << arg.first << " = ";
dump_sigspec_rhs(arg.second);
f << ";\n";
}
f << indent << "if (performer) {\n";
inc_indent();
f << indent << "static const metadata_map attributes = ";
dump_metadata_map(cell->attributes);
f << ";\n";
f << indent << "performer->on_print(formatter, attributes);\n";
dec_indent();
f << indent << "} else {\n";
inc_indent();
f << indent << print_output << " << formatter();\n";
dec_indent();
f << indent << "}\n";
dec_indent();
f << indent << "}\n";
}
void dump_sync_print(std::vector<const RTLIL::Cell*> &cells)
{
log_assert(!cells.empty());
const auto &trg = cells[0]->getPort(ID::TRG);
const auto &trg_polarity = cells[0]->getParam(ID::TRG_POLARITY);
f << indent << "if (";
for (int i = 0; i < trg.size(); i++) {
RTLIL::SigBit trg_bit = trg[i];
trg_bit = sigmaps[trg_bit.wire->module](trg_bit);
log_assert(trg_bit.wire);
if (i != 0)
f << " || ";
if (trg_polarity[i] == State::S1)
f << "posedge_";
else
f << "negedge_";
f << mangle(trg_bit);
}
f << ") {\n";
inc_indent();
std::sort(cells.begin(), cells.end(), [](const RTLIL::Cell *a, const RTLIL::Cell *b) {
return a->getParam(ID::PRIORITY).as_int() > b->getParam(ID::PRIORITY).as_int();
});
for (auto cell : cells) {
log_assert(cell->getParam(ID::TRG_ENABLE).as_bool());
log_assert(cell->getPort(ID::TRG) == trg);
log_assert(cell->getParam(ID::TRG_POLARITY) == trg_polarity);
std::vector<const RTLIL::Cell*> inlined_cells;
collect_cell_eval(cell, /*for_debug=*/false, inlined_cells);
dump_inlined_cells(inlined_cells);
dump_print(cell);
}
dec_indent();
f << indent << "}\n";
}
void dump_inlined_cells(const std::vector<const RTLIL::Cell*> &cells) void dump_inlined_cells(const std::vector<const RTLIL::Cell*> &cells)
{ {
if (cells.empty()) { if (cells.empty()) {
@ -1309,6 +1217,144 @@ struct CxxrtlWorker {
} }
} }
void dump_print(const RTLIL::Cell *cell)
{
Fmt fmt;
fmt.parse_rtlil(cell);
f << indent << "if (";
dump_sigspec_rhs(cell->getPort(ID::EN));
f << " == value<1>{1u}) {\n";
inc_indent();
dict<std::string, RTLIL::SigSpec> fmt_args;
f << indent << "struct : public lazy_fmt {\n";
inc_indent();
f << indent << "std::string operator() () const override {\n";
inc_indent();
fmt.emit_cxxrtl(f, indent, [&](const RTLIL::SigSpec &sig) {
if (sig.size() == 0)
f << "value<0>()";
else {
std::string arg_name = "arg" + std::to_string(fmt_args.size());
fmt_args[arg_name] = sig;
f << arg_name;
}
}, "performer");
dec_indent();
f << indent << "}\n";
f << indent << "struct performer *performer;\n";
for (auto arg : fmt_args)
f << indent << "value<" << arg.second.size() << "> " << arg.first << ";\n";
dec_indent();
f << indent << "} formatter;\n";
f << indent << "formatter.performer = performer;\n";
for (auto arg : fmt_args) {
f << indent << "formatter." << arg.first << " = ";
dump_sigspec_rhs(arg.second);
f << ";\n";
}
f << indent << "if (performer) {\n";
inc_indent();
f << indent << "static const metadata_map attributes = ";
dump_metadata_map(cell->attributes);
f << ";\n";
f << indent << "performer->on_print(formatter, attributes);\n";
dec_indent();
f << indent << "} else {\n";
inc_indent();
f << indent << print_output << " << formatter();\n";
dec_indent();
f << indent << "}\n";
dec_indent();
f << indent << "}\n";
}
void dump_effect(const RTLIL::Cell *cell)
{
Fmt fmt;
fmt.parse_rtlil(cell);
f << indent << "if (";
dump_sigspec_rhs(cell->getPort(ID::EN));
f << ") {\n";
inc_indent();
dict<std::string, RTLIL::SigSpec> fmt_args;
f << indent << "struct : public lazy_fmt {\n";
inc_indent();
f << indent << "std::string operator() () const override {\n";
inc_indent();
fmt.emit_cxxrtl(f, indent, [&](const RTLIL::SigSpec &sig) {
if (sig.size() == 0)
f << "value<0>()";
else {
std::string arg_name = "arg" + std::to_string(fmt_args.size());
fmt_args[arg_name] = sig;
f << arg_name;
}
}, "performer");
dec_indent();
f << indent << "}\n";
f << indent << "struct performer *performer;\n";
for (auto arg : fmt_args)
f << indent << "value<" << arg.second.size() << "> " << arg.first << ";\n";
dec_indent();
f << indent << "} formatter;\n";
f << indent << "formatter.performer = performer;\n";
for (auto arg : fmt_args) {
f << indent << "formatter." << arg.first << " = ";
dump_sigspec_rhs(arg.second);
f << ";\n";
}
if (cell->hasPort(ID::A)) {
f << indent << "bool condition = (bool)";
dump_sigspec_rhs(cell->getPort(ID::A));
f << ";\n";
}
f << indent << "if (performer) {\n";
inc_indent();
f << indent << "static const metadata_map attributes = ";
dump_metadata_map(cell->attributes);
f << ";\n";
if (cell->type == ID($print)) {
f << indent << "performer->on_print(formatter, attributes);\n";
} else if (cell->type == ID($check)) {
std::string flavor = cell->getParam(ID::FLAVOR).decode_string();
f << indent << "performer->on_check(";
if (flavor == "assert")
f << "flavor::ASSERT";
else if (flavor == "assume")
f << "flavor::ASSUME";
else if (flavor == "live")
f << "flavor::ASSERT_EVENTUALLY";
else if (flavor == "fair")
f << "flavor::ASSUME_EVENTUALLY";
else if (flavor == "cover")
f << "flavor::COVER";
else log_assert(false);
f << ", condition, formatter, attributes);\n";
} else log_assert(false);
dec_indent();
f << indent << "} else {\n";
inc_indent();
if (cell->type == ID($print)) {
f << indent << print_output << " << formatter();\n";
} else if (cell->type == ID($check)) {
std::string flavor = cell->getParam(ID::FLAVOR).decode_string();
if (flavor == "assert" || flavor == "assume") {
f << indent << "if (!condition) {\n";
inc_indent();
f << indent << "std::cerr << formatter();\n";
dec_indent();
f << indent << "}\n";
f << indent << "CXXRTL_ASSERT(condition && \"Check failed\");\n";
}
} else log_assert(false);
dec_indent();
f << indent << "}\n";
dec_indent();
f << indent << "}\n";
}
void dump_cell_eval(const RTLIL::Cell *cell, bool for_debug = false) void dump_cell_eval(const RTLIL::Cell *cell, bool for_debug = false)
{ {
std::vector<const RTLIL::Cell*> inlined_cells; std::vector<const RTLIL::Cell*> inlined_cells;
@ -1322,30 +1368,34 @@ struct CxxrtlWorker {
f << " = "; f << " = ";
dump_cell_expr(cell, for_debug); dump_cell_expr(cell, for_debug);
f << ";\n"; f << ";\n";
// $print cell // Effectful cells
} else if (cell->type == ID($print)) { } else if (is_effectful_cell(cell->type)) {
log_assert(!for_debug); log_assert(!for_debug);
// Sync $print cells are grouped into PRINT_SYNC nodes in the FlowGraph. // Sync effectful cells are grouped into EFFECT_SYNC nodes in the FlowGraph.
log_assert(!cell->getParam(ID::TRG_ENABLE).as_bool() || (cell->getParam(ID::TRG_ENABLE).as_bool() && cell->getParam(ID::TRG_WIDTH).as_int() == 0)); log_assert(!cell->getParam(ID::TRG_ENABLE).as_bool() || (cell->getParam(ID::TRG_ENABLE).as_bool() && cell->getParam(ID::TRG_WIDTH).as_int() == 0));
if (!cell->getParam(ID::TRG_ENABLE).as_bool()) { // async $print cell if (!cell->getParam(ID::TRG_ENABLE).as_bool()) { // async effectful cell
f << indent << "auto " << mangle(cell) << "_curr = "; f << indent << "auto " << mangle(cell) << "_next = ";
dump_sigspec_rhs(cell->getPort(ID::EN)); dump_sigspec_rhs(cell->getPort(ID::EN));
f << ".concat("; f << ".concat(";
dump_sigspec_rhs(cell->getPort(ID::ARGS)); if (cell->type == ID($print))
dump_sigspec_rhs(cell->getPort(ID::ARGS));
else if (cell->type == ID($check))
dump_sigspec_rhs(cell->getPort(ID::A));
else log_assert(false);
f << ").val();\n"; f << ").val();\n";
f << indent << "if (" << mangle(cell) << " != " << mangle(cell) << "_curr) {\n"; f << indent << "if (" << mangle(cell) << " != " << mangle(cell) << "_next) {\n";
inc_indent(); inc_indent();
dump_print(cell); dump_effect(cell);
f << indent << mangle(cell) << " = " << mangle(cell) << "_curr;\n"; f << indent << mangle(cell) << " = " << mangle(cell) << "_next;\n";
dec_indent(); dec_indent();
f << indent << "}\n"; f << indent << "}\n";
} else { // initial $print cell } else { // initial effectful cell
f << indent << "if (!" << mangle(cell) << ") {\n"; f << indent << "if (!" << mangle(cell) << ") {\n";
inc_indent(); inc_indent();
dump_print(cell); dump_effect(cell);
f << indent << mangle(cell) << " = value<1>{1u};\n"; f << indent << mangle(cell) << " = value<1>{1u};\n";
dec_indent(); dec_indent();
f << indent << "}\n"; f << indent << "}\n";
@ -1446,7 +1496,7 @@ struct CxxrtlWorker {
f << indent; f << indent;
dump_sigspec_lhs(cell->getPort(ID::Q)); dump_sigspec_lhs(cell->getPort(ID::Q));
f << " = "; f << " = ";
dump_sigspec_lhs(cell->getPort(ID::Q)); dump_sigspec_rhs(cell->getPort(ID::Q));
f << ".update("; f << ".update(";
dump_const(RTLIL::Const(RTLIL::S1, cell->getParam(ID::WIDTH).as_int())); dump_const(RTLIL::Const(RTLIL::S1, cell->getParam(ID::WIDTH).as_int()));
f << ", "; f << ", ";
@ -1458,7 +1508,7 @@ struct CxxrtlWorker {
f << indent; f << indent;
dump_sigspec_lhs(cell->getPort(ID::Q)); dump_sigspec_lhs(cell->getPort(ID::Q));
f << " = "; f << " = ";
dump_sigspec_lhs(cell->getPort(ID::Q)); dump_sigspec_rhs(cell->getPort(ID::Q));
f << ".update("; f << ".update(";
dump_const(RTLIL::Const(RTLIL::S0, cell->getParam(ID::WIDTH).as_int())); dump_const(RTLIL::Const(RTLIL::S0, cell->getParam(ID::WIDTH).as_int()));
f << ", "; f << ", ";
@ -1469,8 +1519,9 @@ struct CxxrtlWorker {
} else if (is_internal_cell(cell->type)) { } else if (is_internal_cell(cell->type)) {
log_cmd_error("Unsupported internal cell `%s'.\n", cell->type.c_str()); log_cmd_error("Unsupported internal cell `%s'.\n", cell->type.c_str());
// User cells // User cells
} else if (for_debug) {
// Outlines are called on demand when computing the value of a debug item. Nothing to do here.
} else { } else {
log_assert(!for_debug);
log_assert(cell->known()); log_assert(cell->known());
bool buffered_inputs = false; bool buffered_inputs = false;
const char *access = is_cxxrtl_blackbox_cell(cell) ? "->" : "."; const char *access = is_cxxrtl_blackbox_cell(cell) ? "->" : ".";
@ -1728,6 +1779,47 @@ struct CxxrtlWorker {
} }
} }
void dump_cell_effect_sync(std::vector<const RTLIL::Cell*> &cells)
{
log_assert(!cells.empty());
const auto &trg = cells[0]->getPort(ID::TRG);
const auto &trg_polarity = cells[0]->getParam(ID::TRG_POLARITY);
f << indent << "if (";
for (int i = 0; i < trg.size(); i++) {
RTLIL::SigBit trg_bit = trg[i];
trg_bit = sigmaps[trg_bit.wire->module](trg_bit);
log_assert(trg_bit.wire);
if (i != 0)
f << " || ";
if (trg_polarity[i] == State::S1)
f << "posedge_";
else
f << "negedge_";
f << mangle(trg_bit);
}
f << ") {\n";
inc_indent();
std::sort(cells.begin(), cells.end(), [](const RTLIL::Cell *a, const RTLIL::Cell *b) {
return a->getParam(ID::PRIORITY).as_int() > b->getParam(ID::PRIORITY).as_int();
});
for (auto cell : cells) {
log_assert(cell->getParam(ID::TRG_ENABLE).as_bool());
log_assert(cell->getPort(ID::TRG) == trg);
log_assert(cell->getParam(ID::TRG_POLARITY) == trg_polarity);
std::vector<const RTLIL::Cell*> inlined_cells;
collect_cell_eval(cell, /*for_debug=*/false, inlined_cells);
dump_inlined_cells(inlined_cells);
dump_effect(cell);
}
dec_indent();
f << indent << "}\n";
}
void dump_mem_rdport(const Mem *mem, int portidx, bool for_debug = false) void dump_mem_rdport(const Mem *mem, int portidx, bool for_debug = false)
{ {
auto &port = mem->rd_ports[portidx]; auto &port = mem->rd_ports[portidx];
@ -2047,11 +2139,10 @@ struct CxxrtlWorker {
} }
} }
for (auto cell : module->cells()) { for (auto cell : module->cells()) {
// Certain $print cells have additional state, which must be reset as well. // Async and initial effectful cells have additional state, which must be reset as well.
if (cell->type == ID($print) && !cell->getParam(ID::TRG_ENABLE).as_bool()) if (is_effectful_cell(cell->type))
f << indent << mangle(cell) << " = value<" << (1 + cell->getParam(ID::ARGS_WIDTH).as_int()) << ">();\n"; if (!cell->getParam(ID::TRG_ENABLE).as_bool() || cell->getParam(ID::TRG_WIDTH).as_int() == 0)
if (cell->type == ID($print) && cell->getParam(ID::TRG_ENABLE).as_bool() && cell->getParam(ID::TRG_WIDTH).as_int() == 0) f << indent << mangle(cell) << " = {};\n";
f << indent << mangle(cell) << " = value<1>();\n";
if (is_internal_cell(cell->type)) if (is_internal_cell(cell->type))
continue; continue;
f << indent << mangle(cell); f << indent << mangle(cell);
@ -2099,8 +2190,8 @@ struct CxxrtlWorker {
case FlowGraph::Node::Type::CELL_EVAL: case FlowGraph::Node::Type::CELL_EVAL:
dump_cell_eval(node.cell); dump_cell_eval(node.cell);
break; break;
case FlowGraph::Node::Type::PRINT_SYNC: case FlowGraph::Node::Type::EFFECT_SYNC:
dump_sync_print(node.print_sync_cells); dump_cell_effect_sync(node.cells);
break; break;
case FlowGraph::Node::Type::PROCESS_CASE: case FlowGraph::Node::Type::PROCESS_CASE:
dump_process_case(node.process); dump_process_case(node.process);
@ -2221,11 +2312,14 @@ struct CxxrtlWorker {
dict<RTLIL::IdString, RTLIL::Const> attributes = object->attributes; dict<RTLIL::IdString, RTLIL::Const> attributes = object->attributes;
// Inherently necessary to get access to the object, so a waste of space to emit. // Inherently necessary to get access to the object, so a waste of space to emit.
attributes.erase(ID::hdlname); attributes.erase(ID::hdlname);
// Internal Yosys attribute that should be removed but isn't.
attributes.erase(ID::module_not_derived);
dump_metadata_map(attributes); dump_metadata_map(attributes);
} }
void dump_debug_info_method(RTLIL::Module *module) void dump_debug_info_method(RTLIL::Module *module)
{ {
size_t count_scopes = 0;
size_t count_public_wires = 0; size_t count_public_wires = 0;
size_t count_member_wires = 0; size_t count_member_wires = 0;
size_t count_undriven = 0; size_t count_undriven = 0;
@ -2238,153 +2332,188 @@ struct CxxrtlWorker {
size_t count_skipped_wires = 0; size_t count_skipped_wires = 0;
inc_indent(); inc_indent();
f << indent << "assert(path.empty() || path[path.size() - 1] == ' ');\n"; f << indent << "assert(path.empty() || path[path.size() - 1] == ' ');\n";
for (auto wire : module->wires()) { f << indent << "if (scopes) {\n";
const auto &debug_wire_type = debug_wire_types[wire]; inc_indent();
if (!wire->name.isPublic()) // The module is responsible for adding its own scope.
continue; f << indent << "scopes->add(path.empty() ? path : path.substr(0, path.size() - 1), ";
count_public_wires++; f << escape_cxx_string(get_hdl_name(module)) << ", ";
switch (debug_wire_type.type) { dump_debug_attrs(module);
case WireType::BUFFERED: f << ", std::move(cell_attrs));\n";
case WireType::MEMBER: { count_scopes++;
// Member wire // If there were any submodules that were flattened, the module is also responsible for adding them.
std::vector<std::string> flags; for (auto cell : module->cells()) {
if (cell->type != ID($scopeinfo)) continue;
if (wire->port_input && wire->port_output) if (cell->getParam(ID::TYPE).decode_string() == "module") {
flags.push_back("INOUT"); auto module_attrs = scopeinfo_attributes(cell, ScopeinfoAttrs::Module);
else if (wire->port_output) auto cell_attrs = scopeinfo_attributes(cell, ScopeinfoAttrs::Cell);
flags.push_back("OUTPUT"); cell_attrs.erase(ID::module_not_derived);
else if (wire->port_input) f << indent << "scopes->add(path + " << escape_cxx_string(get_hdl_name(cell)) << ", ";
flags.push_back("INPUT"); f << escape_cxx_string(cell->get_string_attribute(ID(module))) << ", ";
dump_metadata_map(module_attrs);
bool has_driven_sync = false; f << ", ";
bool has_driven_comb = false; dump_metadata_map(cell_attrs);
bool has_undriven = false;
if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) {
for (auto bit : SigSpec(wire))
if (!bit_has_state.count(bit))
has_undriven = true;
else if (bit_has_state[bit])
has_driven_sync = true;
else
has_driven_comb = true;
} else if (wire->port_output) {
switch (cxxrtl_port_type(module, wire->name)) {
case CxxrtlPortType::SYNC:
has_driven_sync = true;
break;
case CxxrtlPortType::COMB:
has_driven_comb = true;
break;
case CxxrtlPortType::UNKNOWN:
has_driven_sync = has_driven_comb = true;
break;
}
} else {
has_undriven = true;
}
if (has_undriven)
flags.push_back("UNDRIVEN");
if (!has_driven_sync && !has_driven_comb && has_undriven)
count_undriven++;
if (has_driven_sync)
flags.push_back("DRIVEN_SYNC");
if (has_driven_sync && !has_driven_comb && !has_undriven)
count_driven_sync++;
if (has_driven_comb)
flags.push_back("DRIVEN_COMB");
if (!has_driven_sync && has_driven_comb && !has_undriven)
count_driven_comb++;
if (has_driven_sync + has_driven_comb + has_undriven > 1)
count_mixed_driver++;
f << indent << "items.add(path + " << escape_cxx_string(get_hdl_name(wire));
f << ", debug_item(" << mangle(wire) << ", " << wire->start_offset;
bool first = true;
for (auto flag : flags) {
if (first) {
first = false;
f << ", ";
} else {
f << "|";
}
f << "debug_item::" << flag;
}
f << "), ";
dump_debug_attrs(wire);
f << ");\n"; f << ");\n";
count_member_wires++; } else log_assert(false && "Unknown $scopeinfo type");
break; count_scopes++;
}
case WireType::ALIAS: {
// Alias of a member wire
const RTLIL::Wire *aliasee = debug_wire_type.sig_subst.as_wire();
f << indent << "items.add(path + " << escape_cxx_string(get_hdl_name(wire));
f << ", debug_item(";
// If the aliasee is an outline, then the alias must be an outline, too; otherwise downstream
// tooling has no way to find out about the outline.
if (debug_wire_types[aliasee].is_outline())
f << "debug_eval_outline";
else
f << "debug_alias()";
f << ", " << mangle(aliasee) << ", " << wire->start_offset << "), ";
dump_debug_attrs(aliasee);
f << ");\n";
count_alias_wires++;
break;
}
case WireType::CONST: {
// Wire tied to a constant
f << indent << "static const value<" << wire->width << "> const_" << mangle(wire) << " = ";
dump_const(debug_wire_type.sig_subst.as_const());
f << ";\n";
f << indent << "items.add(path + " << escape_cxx_string(get_hdl_name(wire));
f << ", debug_item(const_" << mangle(wire) << ", " << wire->start_offset << "), ";
dump_debug_attrs(wire);
f << ");\n";
count_const_wires++;
break;
}
case WireType::OUTLINE: {
// Localized or inlined, but rematerializable wire
f << indent << "items.add(path + " << escape_cxx_string(get_hdl_name(wire));
f << ", debug_item(debug_eval_outline, " << mangle(wire) << ", " << wire->start_offset << "), ";
dump_debug_attrs(wire);
f << ");\n";
count_inline_wires++;
break;
}
default: {
// Localized or inlined wire with no debug information
count_skipped_wires++;
break;
}
} }
} dec_indent();
if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) { f << indent << "}\n";
for (auto &mem : mod_memories[module]) { f << indent << "if (items) {\n";
if (!mem.memid.isPublic()) inc_indent();
for (auto wire : module->wires()) {
const auto &debug_wire_type = debug_wire_types[wire];
if (!wire->name.isPublic())
continue; continue;
f << indent << "items.add(path + " << escape_cxx_string(mem.packed ? get_hdl_name(mem.cell) : get_hdl_name(mem.mem)); count_public_wires++;
f << ", debug_item(" << mangle(&mem) << ", "; switch (debug_wire_type.type) {
f << mem.start_offset << "), "; case WireType::BUFFERED:
if (mem.packed) { case WireType::MEMBER: {
dump_debug_attrs(mem.cell); // Member wire
} else { std::vector<std::string> flags;
dump_debug_attrs(mem.mem);
if (wire->port_input && wire->port_output)
flags.push_back("INOUT");
else if (wire->port_output)
flags.push_back("OUTPUT");
else if (wire->port_input)
flags.push_back("INPUT");
bool has_driven_sync = false;
bool has_driven_comb = false;
bool has_undriven = false;
if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) {
for (auto bit : SigSpec(wire))
if (!bit_has_state.count(bit))
has_undriven = true;
else if (bit_has_state[bit])
has_driven_sync = true;
else
has_driven_comb = true;
} else if (wire->port_output) {
switch (cxxrtl_port_type(module, wire->name)) {
case CxxrtlPortType::SYNC:
has_driven_sync = true;
break;
case CxxrtlPortType::COMB:
has_driven_comb = true;
break;
case CxxrtlPortType::UNKNOWN:
has_driven_sync = has_driven_comb = true;
break;
}
} else {
has_undriven = true;
}
if (has_undriven)
flags.push_back("UNDRIVEN");
if (!has_driven_sync && !has_driven_comb && has_undriven)
count_undriven++;
if (has_driven_sync)
flags.push_back("DRIVEN_SYNC");
if (has_driven_sync && !has_driven_comb && !has_undriven)
count_driven_sync++;
if (has_driven_comb)
flags.push_back("DRIVEN_COMB");
if (!has_driven_sync && has_driven_comb && !has_undriven)
count_driven_comb++;
if (has_driven_sync + has_driven_comb + has_undriven > 1)
count_mixed_driver++;
f << indent << "items->add(path + " << escape_cxx_string(get_hdl_name(wire));
f << ", debug_item(" << mangle(wire) << ", " << wire->start_offset;
bool first = true;
for (auto flag : flags) {
if (first) {
first = false;
f << ", ";
} else {
f << "|";
}
f << "debug_item::" << flag;
}
f << "), ";
dump_debug_attrs(wire);
f << ");\n";
count_member_wires++;
break;
}
case WireType::ALIAS: {
// Alias of a member wire
const RTLIL::Wire *aliasee = debug_wire_type.sig_subst.as_wire();
f << indent << "items->add(path + " << escape_cxx_string(get_hdl_name(wire));
f << ", debug_item(";
// If the aliasee is an outline, then the alias must be an outline, too; otherwise downstream
// tooling has no way to find out about the outline.
if (debug_wire_types[aliasee].is_outline())
f << "debug_eval_outline";
else
f << "debug_alias()";
f << ", " << mangle(aliasee) << ", " << wire->start_offset << "), ";
dump_debug_attrs(aliasee);
f << ");\n";
count_alias_wires++;
break;
}
case WireType::CONST: {
// Wire tied to a constant
f << indent << "static const value<" << wire->width << "> const_" << mangle(wire) << " = ";
dump_const(debug_wire_type.sig_subst.as_const());
f << ";\n";
f << indent << "items->add(path + " << escape_cxx_string(get_hdl_name(wire));
f << ", debug_item(const_" << mangle(wire) << ", " << wire->start_offset << "), ";
dump_debug_attrs(wire);
f << ");\n";
count_const_wires++;
break;
}
case WireType::OUTLINE: {
// Localized or inlined, but rematerializable wire
f << indent << "items->add(path + " << escape_cxx_string(get_hdl_name(wire));
f << ", debug_item(debug_eval_outline, " << mangle(wire) << ", " << wire->start_offset << "), ";
dump_debug_attrs(wire);
f << ");\n";
count_inline_wires++;
break;
}
default: {
// Localized or inlined wire with no debug information
count_skipped_wires++;
break;
}
} }
f << ");\n";
} }
if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) {
for (auto &mem : mod_memories[module]) {
if (!mem.memid.isPublic())
continue;
f << indent << "items->add(path + " << escape_cxx_string(mem.packed ? get_hdl_name(mem.cell) : get_hdl_name(mem.mem));
f << ", debug_item(" << mangle(&mem) << ", ";
f << mem.start_offset << "), ";
if (mem.packed) {
dump_debug_attrs(mem.cell);
} else {
dump_debug_attrs(mem.mem);
}
f << ");\n";
}
}
dec_indent();
f << indent << "}\n";
if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) {
for (auto cell : module->cells()) { for (auto cell : module->cells()) {
if (is_internal_cell(cell->type)) if (is_internal_cell(cell->type))
continue; continue;
const char *access = is_cxxrtl_blackbox_cell(cell) ? "->" : "."; const char *access = is_cxxrtl_blackbox_cell(cell) ? "->" : ".";
f << indent << mangle(cell) << access << "debug_info(items, "; f << indent << mangle(cell) << access;
f << "path + " << escape_cxx_string(get_hdl_name(cell) + ' ') << ");\n"; f << "debug_info(items, scopes, path + " << escape_cxx_string(get_hdl_name(cell) + ' ') << ", ";
dump_debug_attrs(cell);
f << ");\n";
} }
} }
dec_indent(); dec_indent();
log_debug("Debug information statistics for module `%s':\n", log_id(module)); log_debug("Debug information statistics for module `%s':\n", log_id(module));
log_debug(" Scopes: %zu", count_scopes);
log_debug(" Public wires: %zu, of which:\n", count_public_wires); log_debug(" Public wires: %zu, of which:\n", count_public_wires);
log_debug(" Member wires: %zu, of which:\n", count_member_wires); log_debug(" Member wires: %zu, of which:\n", count_member_wires);
log_debug(" Undriven: %zu (incl. inputs)\n", count_undriven); log_debug(" Undriven: %zu (incl. inputs)\n", count_undriven);
@ -2422,18 +2551,18 @@ struct CxxrtlWorker {
dump_eval_method(module); dump_eval_method(module);
f << indent << "}\n"; f << indent << "}\n";
f << "\n"; f << "\n";
f << indent << "template<class ObserverT>\n"; f << indent << "virtual bool commit(observer &observer) {\n";
f << indent << "bool commit(ObserverT &observer) {\n";
dump_commit_method(module); dump_commit_method(module);
f << indent << "}\n"; f << indent << "}\n";
f << "\n"; f << "\n";
f << indent << "bool commit() override {\n"; f << indent << "bool commit() override {\n";
f << indent << indent << "observer observer;\n"; f << indent << indent << "observer observer;\n";
f << indent << indent << "return commit<>(observer);\n"; f << indent << indent << "return commit(observer);\n";
f << indent << "}\n"; f << indent << "}\n";
if (debug_info) { if (debug_info) {
f << "\n"; f << "\n";
f << indent << "void debug_info(debug_items &items, std::string path = \"\") override {\n"; f << indent << "void debug_info(debug_items *items, debug_scopes *scopes, "
<< "std::string path, metadata_map &&cell_attrs = {}) override {\n";
dump_debug_info_method(module); dump_debug_info_method(module);
f << indent << "}\n"; f << indent << "}\n";
} }
@ -2481,11 +2610,15 @@ struct CxxrtlWorker {
f << "\n"; f << "\n";
bool has_cells = false; bool has_cells = false;
for (auto cell : module->cells()) { for (auto cell : module->cells()) {
// Certain $print cells have additional state, which requires storage. // Async and initial effectful cells have additional state, which requires storage.
if (cell->type == ID($print) && !cell->getParam(ID::TRG_ENABLE).as_bool()) if (is_effectful_cell(cell->type)) {
f << indent << "value<" << (1 + cell->getParam(ID::ARGS_WIDTH).as_int()) << "> " << mangle(cell) << ";\n"; if (cell->getParam(ID::TRG_ENABLE).as_bool() && cell->getParam(ID::TRG_WIDTH).as_int() == 0)
if (cell->type == ID($print) && cell->getParam(ID::TRG_ENABLE).as_bool() && cell->getParam(ID::TRG_WIDTH).as_int() == 0) f << indent << "value<1> " << mangle(cell) << ";\n"; // async initial cell
f << indent << "value<1> " << mangle(cell) << ";\n"; if (!cell->getParam(ID::TRG_ENABLE).as_bool() && cell->type == ID($print))
f << indent << "value<" << (1 + cell->getParam(ID::ARGS_WIDTH).as_int()) << "> " << mangle(cell) << ";\n"; // {EN, ARGS}
if (!cell->getParam(ID::TRG_ENABLE).as_bool() && cell->type == ID($check))
f << indent << "value<2> " << mangle(cell) << ";\n"; // {EN, A}
}
if (is_internal_cell(cell->type)) if (is_internal_cell(cell->type))
continue; continue;
dump_attrs(cell); dump_attrs(cell);
@ -2538,7 +2671,8 @@ struct CxxrtlWorker {
} }
} }
f << "\n"; f << "\n";
f << indent << "void debug_info(debug_items &items, std::string path = \"\") override;\n"; f << indent << "void debug_info(debug_items *items, debug_scopes *scopes, "
<< "std::string path, metadata_map &&cell_attrs = {}) override;\n";
} }
dec_indent(); dec_indent();
f << indent << "}; // struct " << mangle(module) << "\n"; f << indent << "}; // struct " << mangle(module) << "\n";
@ -2566,7 +2700,8 @@ struct CxxrtlWorker {
} }
f << "\n"; f << "\n";
f << indent << "CXXRTL_EXTREMELY_COLD\n"; f << indent << "CXXRTL_EXTREMELY_COLD\n";
f << indent << "void " << mangle(module) << "::debug_info(debug_items &items, std::string path) {\n"; f << indent << "void " << mangle(module) << "::debug_info(debug_items *items, debug_scopes *scopes, "
<< "std::string path, metadata_map &&cell_attrs) {\n";
dump_debug_info_method(module); dump_debug_info_method(module);
f << indent << "}\n"; f << indent << "}\n";
} }
@ -2803,8 +2938,8 @@ struct CxxrtlWorker {
cell->parameters[ID::CLK_POLARITY].as_bool() ? RTLIL::STp : RTLIL::STn); cell->parameters[ID::CLK_POLARITY].as_bool() ? RTLIL::STp : RTLIL::STn);
} }
// $print cells may be triggered on posedge/negedge events. // Effectful cells may be triggered on posedge/negedge events.
if (cell->type == ID($print) && cell->getParam(ID::TRG_ENABLE).as_bool()) { if (is_effectful_cell(cell->type) && cell->getParam(ID::TRG_ENABLE).as_bool()) {
for (size_t i = 0; i < (size_t)cell->getParam(ID::TRG_WIDTH).as_int(); i++) { for (size_t i = 0; i < (size_t)cell->getParam(ID::TRG_WIDTH).as_int(); i++) {
RTLIL::SigBit trg = cell->getPort(ID::TRG).extract(i, 1); RTLIL::SigBit trg = cell->getPort(ID::TRG).extract(i, 1);
if (is_valid_clock(trg)) if (is_valid_clock(trg))
@ -2945,10 +3080,12 @@ struct CxxrtlWorker {
// Discover nodes reachable from primary outputs (i.e. members) and collect reachable wire users. // Discover nodes reachable from primary outputs (i.e. members) and collect reachable wire users.
pool<FlowGraph::Node*, hash_ptr_ops> worklist; pool<FlowGraph::Node*, hash_ptr_ops> worklist;
for (auto node : flow.nodes) { for (auto node : flow.nodes) {
if (node->type == FlowGraph::Node::Type::CELL_EVAL && is_effectful_cell(node->cell->type)) if (node->type == FlowGraph::Node::Type::CELL_EVAL && !is_internal_cell(node->cell->type))
worklist.insert(node); // node has effects worklist.insert(node); // node evaluates a submodule
else if (node->type == FlowGraph::Node::Type::PRINT_SYNC) else if (node->type == FlowGraph::Node::Type::CELL_EVAL && is_effectful_cell(node->cell->type))
worklist.insert(node); // node is sync $print worklist.insert(node); // node has async effects
else if (node->type == FlowGraph::Node::Type::EFFECT_SYNC)
worklist.insert(node); // node has sync effects
else if (node->type == FlowGraph::Node::Type::MEM_WRPORTS) else if (node->type == FlowGraph::Node::Type::MEM_WRPORTS)
worklist.insert(node); // node is memory write worklist.insert(node); // node is memory write
else if (node->type == FlowGraph::Node::Type::PROCESS_SYNC && is_memwr_process(node->process)) else if (node->type == FlowGraph::Node::Type::PROCESS_SYNC && is_memwr_process(node->process))
@ -3005,21 +3142,21 @@ struct CxxrtlWorker {
} }
// Emit reachable nodes in eval(). // Emit reachable nodes in eval().
// Accumulate sync $print cells per trigger condition. // Accumulate sync effectful cells per trigger condition.
dict<std::pair<RTLIL::SigSpec, RTLIL::Const>, std::vector<const RTLIL::Cell*>> sync_print_cells; dict<std::pair<RTLIL::SigSpec, RTLIL::Const>, std::vector<const RTLIL::Cell*>> effect_sync_cells;
for (auto node : node_order) for (auto node : node_order)
if (live_nodes[node]) { if (live_nodes[node]) {
if (node->type == FlowGraph::Node::Type::CELL_EVAL && if (node->type == FlowGraph::Node::Type::CELL_EVAL &&
node->cell->type == ID($print) && is_effectful_cell(node->cell->type) &&
node->cell->getParam(ID::TRG_ENABLE).as_bool() && node->cell->getParam(ID::TRG_ENABLE).as_bool() &&
node->cell->getParam(ID::TRG_WIDTH).as_int() != 0) node->cell->getParam(ID::TRG_WIDTH).as_int() != 0)
sync_print_cells[make_pair(node->cell->getPort(ID::TRG), node->cell->getParam(ID::TRG_POLARITY))].push_back(node->cell); effect_sync_cells[make_pair(node->cell->getPort(ID::TRG), node->cell->getParam(ID::TRG_POLARITY))].push_back(node->cell);
else else
schedule[module].push_back(*node); schedule[module].push_back(*node);
} }
for (auto &it : sync_print_cells) { for (auto &it : effect_sync_cells) {
auto node = flow.add_print_sync_node(it.second); auto node = flow.add_effect_sync_node(it.second);
schedule[module].push_back(*node); schedule[module].push_back(*node);
} }
@ -3326,8 +3463,7 @@ struct CxxrtlBackend : public Backend {
log(" wire<8> p_o_data;\n"); log(" wire<8> p_o_data;\n");
log("\n"); log("\n");
log(" bool eval(performer *performer) override;\n"); log(" bool eval(performer *performer) override;\n");
log(" template<class ObserverT>\n"); log(" virtual bool commit(observer &observer);\n");
log(" bool commit(ObserverT &observer);\n");
log(" bool commit() override;\n"); log(" bool commit() override;\n");
log("\n"); log("\n");
log(" static std::unique_ptr<bb_p_debug>\n"); log(" static std::unique_ptr<bb_p_debug>\n");

View File

@ -35,19 +35,19 @@ cxxrtl_handle cxxrtl_create(cxxrtl_toplevel design) {
return cxxrtl_create_at(design, ""); return cxxrtl_create_at(design, "");
} }
cxxrtl_handle cxxrtl_create_at(cxxrtl_toplevel design, const char *root) { cxxrtl_handle cxxrtl_create_at(cxxrtl_toplevel design, const char *top_path_) {
std::string path = root; std::string top_path = top_path_;
if (!path.empty()) { if (!top_path.empty()) {
// module::debug_info() accepts either an empty path, or a path ending in space to simplify // module::debug_info() accepts either an empty path, or a path ending in space to simplify
// the logic in generated code. While this is sketchy at best to expose in the C++ API, this // the logic in generated code. While this is sketchy at best to expose in the C++ API, this
// would be a lot worse in the C API, so don't expose it here. // would be a lot worse in the C API, so don't expose it here.
assert(path.back() != ' '); assert(top_path.back() != ' ');
path += ' '; top_path += ' ';
} }
cxxrtl_handle handle = new _cxxrtl_handle; cxxrtl_handle handle = new _cxxrtl_handle;
handle->module = std::move(design->module); handle->module = std::move(design->module);
handle->module->debug_info(handle->objects, path); handle->module->debug_info(handle->objects, top_path);
delete design; delete design;
return handle; return handle;
} }

View File

@ -55,8 +55,8 @@ cxxrtl_handle cxxrtl_create(cxxrtl_toplevel design);
// Create a design handle at a given hierarchy position from a design toplevel. // Create a design handle at a given hierarchy position from a design toplevel.
// //
// This operation is similar to `cxxrtl_create`, except the full hierarchical name of every object // This operation is similar to `cxxrtl_create`, except the full hierarchical name of every object
// is prepended with `root`. // is prepended with `top_path`.
cxxrtl_handle cxxrtl_create_at(cxxrtl_toplevel design, const char *root); cxxrtl_handle cxxrtl_create_at(cxxrtl_toplevel design, const char *top_path);
// Release all resources used by a design and its handle. // Release all resources used by a design and its handle.
void cxxrtl_destroy(cxxrtl_handle handle); void cxxrtl_destroy(cxxrtl_handle handle);
@ -240,6 +240,11 @@ struct cxxrtl_object {
// through wires, the bits are double buffered. To avoid race conditions, user code should // through wires, the bits are double buffered. To avoid race conditions, user code should
// always read from `curr` and write to `next`. The `curr` pointer is always valid; for objects // always read from `curr` and write to `next`. The `curr` pointer is always valid; for objects
// that cannot be modified, or cannot be modified in a race-free way, `next` is NULL. // that cannot be modified, or cannot be modified in a race-free way, `next` is NULL.
//
// In case where `width == 0`, `curr` is a non-NULL pointer unique for the wire. That is,
// there is a 1-to-1 correspondence between simulation objects and `curr` pointers, regardless
// of whether they have storage or not. (Aliases' `curr` pointer equals that of some other
// simulated object.)
uint32_t *curr; uint32_t *curr;
uint32_t *next; uint32_t *next;

View File

@ -952,7 +952,23 @@ struct lazy_fmt {
virtual std::string operator() () const = 0; virtual std::string operator() () const = 0;
}; };
// An object that can be passed to a `eval()` method in order to act on side effects. // Flavor of a `$check` cell.
enum class flavor {
// Corresponds to a `$assert` cell in other flows, and a Verilog `assert ()` statement.
ASSERT,
// Corresponds to a `$assume` cell in other flows, and a Verilog `assume ()` statement.
ASSUME,
// Corresponds to a `$live` cell in other flows, and a Verilog `assert (eventually)` statement.
ASSERT_EVENTUALLY,
// Corresponds to a `$fair` cell in other flows, and a Verilog `assume (eventually)` statement.
ASSUME_EVENTUALLY,
// Corresponds to a `$cover` cell in other flows, and a Verilog `cover ()` statement.
COVER,
};
// An object that can be passed to a `eval()` method in order to act on side effects. The default behavior implemented
// below is the same as the behavior of `eval(nullptr)`, except that `-print-output` option of `write_cxxrtl` is not
// taken into account.
struct performer { struct performer {
// Called by generated formatting code to evaluate a Verilog `$time` expression. // Called by generated formatting code to evaluate a Verilog `$time` expression.
virtual int64_t vlog_time() const { return 0; } virtual int64_t vlog_time() const { return 0; }
@ -964,6 +980,15 @@ struct performer {
virtual void on_print(const lazy_fmt &formatter, const metadata_map &attributes) { virtual void on_print(const lazy_fmt &formatter, const metadata_map &attributes) {
std::cout << formatter(); std::cout << formatter();
} }
// Called when a `$check` cell is triggered.
virtual void on_check(flavor type, bool condition, const lazy_fmt &formatter, const metadata_map &attributes) {
if (type == flavor::ASSERT || type == flavor::ASSUME) {
if (!condition)
std::cerr << formatter();
CXXRTL_ASSERT(condition && "Check failed");
}
}
}; };
// An object that can be passed to a `commit()` method in order to produce a replay log of every state change in // An object that can be passed to a `commit()` method in order to produce a replay log of every state change in
@ -1151,7 +1176,7 @@ struct debug_item : ::cxxrtl_object {
template<size_t Bits> template<size_t Bits>
debug_item(value<Bits> &item, size_t lsb_offset = 0, uint32_t flags_ = 0) { debug_item(value<Bits> &item, size_t lsb_offset = 0, uint32_t flags_ = 0) {
static_assert(sizeof(item) == value<Bits>::chunks * sizeof(chunk_t), static_assert(Bits == 0 || sizeof(item) == value<Bits>::chunks * sizeof(chunk_t),
"value<Bits> is not compatible with C layout"); "value<Bits> is not compatible with C layout");
type = VALUE; type = VALUE;
flags = flags_; flags = flags_;
@ -1167,7 +1192,7 @@ struct debug_item : ::cxxrtl_object {
template<size_t Bits> template<size_t Bits>
debug_item(const value<Bits> &item, size_t lsb_offset = 0) { debug_item(const value<Bits> &item, size_t lsb_offset = 0) {
static_assert(sizeof(item) == value<Bits>::chunks * sizeof(chunk_t), static_assert(Bits == 0 || sizeof(item) == value<Bits>::chunks * sizeof(chunk_t),
"value<Bits> is not compatible with C layout"); "value<Bits> is not compatible with C layout");
type = VALUE; type = VALUE;
flags = DRIVEN_COMB; flags = DRIVEN_COMB;
@ -1183,8 +1208,9 @@ struct debug_item : ::cxxrtl_object {
template<size_t Bits> template<size_t Bits>
debug_item(wire<Bits> &item, size_t lsb_offset = 0, uint32_t flags_ = 0) { debug_item(wire<Bits> &item, size_t lsb_offset = 0, uint32_t flags_ = 0) {
static_assert(sizeof(item.curr) == value<Bits>::chunks * sizeof(chunk_t) && static_assert(Bits == 0 ||
sizeof(item.next) == value<Bits>::chunks * sizeof(chunk_t), (sizeof(item.curr) == value<Bits>::chunks * sizeof(chunk_t) &&
sizeof(item.next) == value<Bits>::chunks * sizeof(chunk_t)),
"wire<Bits> is not compatible with C layout"); "wire<Bits> is not compatible with C layout");
type = WIRE; type = WIRE;
flags = flags_; flags = flags_;
@ -1200,7 +1226,7 @@ struct debug_item : ::cxxrtl_object {
template<size_t Width> template<size_t Width>
debug_item(memory<Width> &item, size_t zero_offset = 0) { debug_item(memory<Width> &item, size_t zero_offset = 0) {
static_assert(sizeof(item.data[0]) == value<Width>::chunks * sizeof(chunk_t), static_assert(Width == 0 || sizeof(item.data[0]) == value<Width>::chunks * sizeof(chunk_t),
"memory<Width> is not compatible with C layout"); "memory<Width> is not compatible with C layout");
type = MEMORY; type = MEMORY;
flags = 0; flags = 0;
@ -1216,7 +1242,7 @@ struct debug_item : ::cxxrtl_object {
template<size_t Bits> template<size_t Bits>
debug_item(debug_alias, const value<Bits> &item, size_t lsb_offset = 0) { debug_item(debug_alias, const value<Bits> &item, size_t lsb_offset = 0) {
static_assert(sizeof(item) == value<Bits>::chunks * sizeof(chunk_t), static_assert(Bits == 0 || sizeof(item) == value<Bits>::chunks * sizeof(chunk_t),
"value<Bits> is not compatible with C layout"); "value<Bits> is not compatible with C layout");
type = ALIAS; type = ALIAS;
flags = DRIVEN_COMB; flags = DRIVEN_COMB;
@ -1232,8 +1258,9 @@ struct debug_item : ::cxxrtl_object {
template<size_t Bits> template<size_t Bits>
debug_item(debug_alias, const wire<Bits> &item, size_t lsb_offset = 0) { debug_item(debug_alias, const wire<Bits> &item, size_t lsb_offset = 0) {
static_assert(sizeof(item.curr) == value<Bits>::chunks * sizeof(chunk_t) && static_assert(Bits == 0 ||
sizeof(item.next) == value<Bits>::chunks * sizeof(chunk_t), (sizeof(item.curr) == value<Bits>::chunks * sizeof(chunk_t) &&
sizeof(item.next) == value<Bits>::chunks * sizeof(chunk_t)),
"wire<Bits> is not compatible with C layout"); "wire<Bits> is not compatible with C layout");
type = ALIAS; type = ALIAS;
flags = DRIVEN_COMB; flags = DRIVEN_COMB;
@ -1249,7 +1276,7 @@ struct debug_item : ::cxxrtl_object {
template<size_t Bits> template<size_t Bits>
debug_item(debug_outline &group, const value<Bits> &item, size_t lsb_offset = 0) { debug_item(debug_outline &group, const value<Bits> &item, size_t lsb_offset = 0) {
static_assert(sizeof(item) == value<Bits>::chunks * sizeof(chunk_t), static_assert(Bits == 0 || sizeof(item) == value<Bits>::chunks * sizeof(chunk_t),
"value<Bits> is not compatible with C layout"); "value<Bits> is not compatible with C layout");
type = OUTLINE; type = OUTLINE;
flags = DRIVEN_COMB; flags = DRIVEN_COMB;
@ -1293,17 +1320,26 @@ namespace cxxrtl {
using debug_attrs = ::_cxxrtl_attr_set; using debug_attrs = ::_cxxrtl_attr_set;
struct debug_items { struct debug_items {
// Debug items may be composed of multiple parts, but the attributes are shared between all of them.
// There are additional invariants, not all of which are not checked by this code:
// - Memories and non-memories cannot be mixed together.
// - Bit indices (considering `lsb_at` and `width`) must not overlap.
// - Row indices (considering `depth` and `zero_at`) must be the same.
// - The `INPUT` and `OUTPUT` flags must be the same for all parts.
// Other than that, the parts can be quite different, e.g. it is OK to mix a value, a wire, an alias,
// and an outline, in the debug information for a single name in four parts.
std::map<std::string, std::vector<debug_item>> table; std::map<std::string, std::vector<debug_item>> table;
std::map<std::string, std::unique_ptr<debug_attrs>> attrs_table; std::map<std::string, std::unique_ptr<debug_attrs>> attrs_table;
void add(const std::string &name, debug_item &&item, metadata_map &&item_attrs = {}) { void add(const std::string &path, debug_item &&item, metadata_map &&item_attrs = {}) {
std::unique_ptr<debug_attrs> &attrs = attrs_table[name]; assert((path.empty() || path[path.size() - 1] != ' ') && path.find(" ") == std::string::npos);
std::unique_ptr<debug_attrs> &attrs = attrs_table[path];
if (attrs.get() == nullptr) if (attrs.get() == nullptr)
attrs = std::unique_ptr<debug_attrs>(new debug_attrs); attrs = std::unique_ptr<debug_attrs>(new debug_attrs);
for (auto attr : item_attrs) for (auto attr : item_attrs)
attrs->map.insert(attr); attrs->map.insert(attr);
item.attrs = attrs.get(); item.attrs = attrs.get();
std::vector<debug_item> &parts = table[name]; std::vector<debug_item> &parts = table[path];
parts.emplace_back(item); parts.emplace_back(item);
std::sort(parts.begin(), parts.end(), std::sort(parts.begin(), parts.end(),
[](const debug_item &a, const debug_item &b) { [](const debug_item &a, const debug_item &b) {
@ -1311,35 +1347,71 @@ struct debug_items {
}); });
} }
size_t count(const std::string &name) const { size_t count(const std::string &path) const {
if (table.count(name) == 0) if (table.count(path) == 0)
return 0; return 0;
return table.at(name).size(); return table.at(path).size();
} }
const std::vector<debug_item> &parts_at(const std::string &name) const { const std::vector<debug_item> &at(const std::string &path) const {
return table.at(name); return table.at(path);
} }
const debug_item &at(const std::string &name) const { // Like `at()`, but operates only on single-part debug items.
const std::vector<debug_item> &parts = table.at(name); const debug_item &operator [](const std::string &path) const {
const std::vector<debug_item> &parts = table.at(path);
assert(parts.size() == 1); assert(parts.size() == 1);
return parts.at(0); return parts.at(0);
} }
const debug_item &operator [](const std::string &name) const { bool is_memory(const std::string &path) const {
return at(name); return at(path).at(0).type == debug_item::MEMORY;
} }
const metadata_map &attrs(const std::string &name) const { const metadata_map &attrs(const std::string &path) const {
return attrs_table.at(name)->map; return attrs_table.at(path)->map;
} }
}; };
// Tag class to disambiguate the default constructor used by the toplevel module that calls reset(), // Only `module` scopes are defined. The type is implicit, since Yosys does not currently support
// any other scope types.
struct debug_scope {
std::string module_name;
std::unique_ptr<debug_attrs> module_attrs;
std::unique_ptr<debug_attrs> cell_attrs;
};
struct debug_scopes {
std::map<std::string, debug_scope> table;
void add(const std::string &path, const std::string &module_name, metadata_map &&module_attrs, metadata_map &&cell_attrs) {
assert((path.empty() || path[path.size() - 1] != ' ') && path.find(" ") == std::string::npos);
assert(table.count(path) == 0);
debug_scope &scope = table[path];
scope.module_name = module_name;
scope.module_attrs = std::unique_ptr<debug_attrs>(new debug_attrs { module_attrs });
scope.cell_attrs = std::unique_ptr<debug_attrs>(new debug_attrs { cell_attrs });
}
size_t contains(const std::string &path) const {
return table.count(path);
}
const debug_scope &operator [](const std::string &path) const {
return table.at(path);
}
};
// Tag class to disambiguate the default constructor used by the toplevel module that calls `reset()`,
// and the constructor of interior modules that should not call it. // and the constructor of interior modules that should not call it.
struct interior {}; struct interior {};
// The core API of the `module` class consists of only four virtual methods: `reset()`, `eval()`,
// `commit`, and `debug_info()`. (The virtual destructor is made necessary by C++.) Every other method
// is a convenience method, and exists solely to simplify some common pattern for C++ API consumers.
// No behavior may be added to such convenience methods that other parts of CXXRTL can rely on, since
// there is no guarantee they will be called (and, for example, other CXXRTL libraries will often call
// the `eval()` and `commit()` directly instead, as well as being exposed in the C API).
struct module { struct module {
module() {} module() {}
virtual ~module() {} virtual ~module() {}
@ -1355,8 +1427,14 @@ struct module {
virtual void reset() = 0; virtual void reset() = 0;
// The `eval()` callback object, `performer`, is included in the virtual call signature since
// the generated code has broadly identical performance properties.
virtual bool eval(performer *performer = nullptr) = 0; virtual bool eval(performer *performer = nullptr) = 0;
virtual bool commit() = 0; // commit observer isn't available since it avoids virtual calls
// The `commit()` callback object, `observer`, is not included in the virtual call signature since
// the generated code is severely pessimized by it. To observe commit events, the non-virtual
// `commit(observer *)` overload must be called directly on a `module` subclass.
virtual bool commit() = 0;
size_t step(performer *performer = nullptr) { size_t step(performer *performer = nullptr) {
size_t deltas = 0; size_t deltas = 0;
@ -1368,8 +1446,16 @@ struct module {
return deltas; return deltas;
} }
virtual void debug_info(debug_items &items, std::string path = "") { virtual void debug_info(debug_items *items, debug_scopes *scopes, std::string path, metadata_map &&cell_attrs = {}) {
(void)items, (void)path; (void)items, (void)scopes, (void)path, (void)cell_attrs;
}
// Compatibility method.
#if __has_attribute(deprecated)
__attribute__((deprecated("Use `debug_info(path, &items, /*scopes=*/nullptr);` instead. (`path` could be \"top \".)")))
#endif
void debug_info(debug_items &items, std::string path) {
debug_info(&items, /*scopes=*/nullptr, path);
} }
}; };

View File

@ -491,9 +491,9 @@ public:
template<typename ...Args> template<typename ...Args>
recorder(Args &&...args) : writer(std::forward<Args>(args)...) {} recorder(Args &&...args) : writer(std::forward<Args>(args)...) {}
void start(module &module) { void start(module &module, std::string top_path = "") {
debug_items items; debug_items items;
module.debug_info(items); module.debug_info(&items, /*scopes=*/nullptr, top_path);
start(items); start(items);
} }
@ -556,7 +556,7 @@ public:
bool record_incremental(ModuleT &module) { bool record_incremental(ModuleT &module) {
assert(streaming); assert(streaming);
struct { struct : observer {
std::unordered_map<const chunk_t*, spool::ident_t> *ident_lookup; std::unordered_map<const chunk_t*, spool::ident_t> *ident_lookup;
spool::writer *writer; spool::writer *writer;
@ -569,7 +569,9 @@ public:
void on_update(size_t chunks, const chunk_t *base, const chunk_t *value, size_t index) { void on_update(size_t chunks, const chunk_t *base, const chunk_t *value, size_t index) {
writer->write_change(ident_lookup->at(base), chunks, value, index); writer->write_change(ident_lookup->at(base), chunks, value, index);
} }
} record_observer = { &ident_lookup, &writer }; } record_observer;
record_observer.ident_lookup = &ident_lookup;
record_observer.writer = &writer;
writer.write_sample(/*incremental=*/true, pointer++, timestamp); writer.write_sample(/*incremental=*/true, pointer++, timestamp);
for (auto input_index : inputs) { for (auto input_index : inputs) {
@ -619,9 +621,10 @@ public:
template<typename ...Args> template<typename ...Args>
player(Args &&...args) : reader(std::forward<Args>(args)...) {} player(Args &&...args) : reader(std::forward<Args>(args)...) {}
void start(module &module) { // The `top_path` must match the one given to the recorder.
void start(module &module, std::string top_path = "") {
debug_items items; debug_items items;
module.debug_info(items); module.debug_info(&items, /*scopes=*/nullptr, top_path);
start(items); start(items);
} }
@ -641,7 +644,7 @@ public:
assert(items.count(name) != 0); assert(items.count(name) != 0);
assert(part_index < items.count(name)); assert(part_index < items.count(name));
const debug_item &part = items.parts_at(name).at(part_index); const debug_item &part = items.at(name).at(part_index);
assert(chunks == (part.width + sizeof(chunk_t) * 8 - 1) / (sizeof(chunk_t) * 8)); assert(chunks == (part.width + sizeof(chunk_t) * 8 - 1) / (sizeof(chunk_t) * 8));
assert(depth == part.depth); assert(depth == part.depth);

View File

@ -213,6 +213,9 @@ struct EdifBackend : public Backend {
for (auto cell : module->cells()) for (auto cell : module->cells())
{ {
if (cell->type == ID($scopeinfo))
continue;
if (design->module(cell->type) == nullptr || design->module(cell->type)->get_blackbox_attribute()) { if (design->module(cell->type) == nullptr || design->module(cell->type)->get_blackbox_attribute()) {
lib_cell_ports[cell->type]; lib_cell_ports[cell->type];
for (auto p : cell->connections()) for (auto p : cell->connections())

View File

@ -980,6 +980,9 @@ struct FirrtlWorker
register_reverse_wire_map(y_id, cell->getPort(ID::Y)); register_reverse_wire_map(y_id, cell->getPort(ID::Y));
continue; continue;
} }
if (cell->type == ID($scopeinfo))
continue;
log_error("Cell type not supported: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell)); log_error("Cell type not supported: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell));
} }

View File

@ -192,6 +192,10 @@ struct JsonWriter
for (auto c : module->cells()) { for (auto c : module->cells()) {
if (use_selection && !module->selected(c)) if (use_selection && !module->selected(c))
continue; continue;
// Eventually we will want to emit $scopeinfo, but currently this
// will break JSON netlist consumers like nextpnr
if (c->type == ID($scopeinfo))
continue;
f << stringf("%s\n", first ? "" : ","); f << stringf("%s\n", first ? "" : ",");
f << stringf(" %s: {\n", get_name(c->name).c_str()); f << stringf(" %s: {\n", get_name(c->name).c_str());
f << stringf(" \"hide_name\": %s,\n", c->name[0] == '$' ? "1" : "0"); f << stringf(" \"hide_name\": %s,\n", c->name[0] == '$' ? "1" : "0");

View File

@ -920,7 +920,7 @@ class SmtIo:
if len(expr_list) == 0: if len(expr_list) == 0:
return [] return []
self.write("(get-value (%s))" % " ".join(expr_list)) self.write("(get-value (%s))" % " ".join(expr_list))
return [n[1] for n in self.parse(self.read())] return [n[1] for n in self.parse(self.read()) if n]
def get_path(self, mod, path): def get_path(self, mod, path):
assert mod in self.modinfo assert mod in self.modinfo

View File

@ -573,6 +573,9 @@ struct SmvWorker
continue; continue;
} }
if (cell->type == ID($scopeinfo))
continue;
if (cell->type[0] == '$') { if (cell->type[0] == '$') {
if (cell->type.in(ID($dffe), ID($sdff), ID($sdffe), ID($sdffce)) || cell->type.str().substr(0, 6) == "$_SDFF" || (cell->type.str().substr(0, 6) == "$_DFFE" && cell->type.str().size() == 10)) { if (cell->type.in(ID($dffe), ID($sdff), ID($sdffe), ID($sdffce)) || cell->type.str().substr(0, 6) == "$_SDFF" || (cell->type.str().substr(0, 6) == "$_DFFE" && cell->type.str().size() == 10)) {
log_error("Unsupported cell type %s for cell %s.%s -- please run `dffunmap` before `write_smv`.\n", log_error("Unsupported cell type %s for cell %s.%s -- please run `dffunmap` before `write_smv`.\n",

View File

@ -72,6 +72,9 @@ static void print_spice_module(std::ostream &f, RTLIL::Module *module, RTLIL::De
for (auto cell : module->cells()) for (auto cell : module->cells())
{ {
if (cell->type == ID($scopeinfo))
continue;
f << stringf("X%d", cell_counter++); f << stringf("X%d", cell_counter++);
std::vector<RTLIL::SigSpec> port_sigs; std::vector<RTLIL::SigSpec> port_sigs;

View File

@ -1008,7 +1008,7 @@ void dump_cell_expr_binop(std::ostream &f, std::string indent, RTLIL::Cell *cell
void dump_cell_expr_print(std::ostream &f, std::string indent, const RTLIL::Cell *cell) void dump_cell_expr_print(std::ostream &f, std::string indent, const RTLIL::Cell *cell)
{ {
Fmt fmt = {}; Fmt fmt;
fmt.parse_rtlil(cell); fmt.parse_rtlil(cell);
std::vector<VerilogFmtArg> args = fmt.emit_verilog(); std::vector<VerilogFmtArg> args = fmt.emit_verilog();
@ -1041,6 +1041,23 @@ void dump_cell_expr_print(std::ostream &f, std::string indent, const RTLIL::Cell
f << stringf(");\n"); f << stringf(");\n");
} }
void dump_cell_expr_check(std::ostream &f, std::string indent, const RTLIL::Cell *cell)
{
std::string flavor = cell->getParam(ID(FLAVOR)).decode_string();
if (flavor == "assert")
f << stringf("%s" "assert (", indent.c_str());
else if (flavor == "assume")
f << stringf("%s" "assume (", indent.c_str());
else if (flavor == "live")
f << stringf("%s" "assert (eventually ", indent.c_str());
else if (flavor == "fair")
f << stringf("%s" "assume (eventually ", indent.c_str());
else if (flavor == "cover")
f << stringf("%s" "cover (", indent.c_str());
dump_sigspec(f, cell->getPort(ID::A));
f << stringf(");\n");
}
bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
{ {
if (cell->type == ID($_NOT_)) { if (cell->type == ID($_NOT_)) {
@ -1053,6 +1070,15 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
f << stringf(";\n"); f << stringf(";\n");
return true; return true;
} }
if (cell->type == ID($_BUF_)) {
f << stringf("%s" "assign ", indent.c_str());
dump_sigspec(f, cell->getPort(ID::Y));
f << stringf(" = ");
dump_cell_expr_port(f, cell, "A", false);
f << stringf(";\n");
return true;
}
if (cell->type.in(ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_))) { if (cell->type.in(ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_))) {
f << stringf("%s" "assign ", indent.c_str()); f << stringf("%s" "assign ", indent.c_str());
@ -1805,6 +1831,39 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
return true; return true;
} }
if (cell->type == ID($check))
{
// Sync $check cells are accumulated and handled in dump_module.
if (cell->getParam(ID::TRG_ENABLE).as_bool())
return true;
f << stringf("%s" "always @*\n", indent.c_str());
f << stringf("%s" " if (", indent.c_str());
dump_sigspec(f, cell->getPort(ID::EN));
f << stringf(") begin\n");
std::string flavor = cell->getParam(ID::FLAVOR).decode_string();
if (flavor == "assert" || flavor == "assume") {
Fmt fmt;
fmt.parse_rtlil(cell);
if (!fmt.parts.empty()) {
f << stringf("%s" " if (!", indent.c_str());
dump_sigspec(f, cell->getPort(ID::A));
f << stringf(")\n");
dump_cell_expr_print(f, indent + " ", cell);
}
} else {
f << stringf("%s" " /* message omitted */\n", indent.c_str());
}
dump_cell_expr_check(f, indent + " ", cell);
f << stringf("%s" " end\n", indent.c_str());
return true;
}
// FIXME: $fsm // FIXME: $fsm
return false; return false;
@ -1812,6 +1871,13 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell) void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell)
{ {
// To keep the output compatible with other tools we ignore $scopeinfo
// cells that exist only to hold metadata. If in the future that metadata
// should be exposed as part of the write_verilog output it should be
// opt-in and/or represented as something else than a $scopeinfo cell.
if (cell->type == ID($scopeinfo))
return;
// Handled by dump_memory // Handled by dump_memory
if (cell->is_mem_cell()) if (cell->is_mem_cell())
return; return;
@ -1894,7 +1960,7 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell)
} }
} }
void dump_sync_print(std::ostream &f, std::string indent, const RTLIL::SigSpec &trg, const RTLIL::Const &polarity, std::vector<const RTLIL::Cell*> &cells) void dump_sync_effect(std::ostream &f, std::string indent, const RTLIL::SigSpec &trg, const RTLIL::Const &polarity, std::vector<const RTLIL::Cell*> &cells)
{ {
if (trg.size() == 0) { if (trg.size() == 0) {
f << stringf("%s" "initial begin\n", indent.c_str()); f << stringf("%s" "initial begin\n", indent.c_str());
@ -1918,9 +1984,29 @@ void dump_sync_print(std::ostream &f, std::string indent, const RTLIL::SigSpec &
for (auto cell : cells) { for (auto cell : cells) {
f << stringf("%s" " if (", indent.c_str()); f << stringf("%s" " if (", indent.c_str());
dump_sigspec(f, cell->getPort(ID::EN)); dump_sigspec(f, cell->getPort(ID::EN));
f << stringf(")\n"); f << stringf(") begin\n");
dump_cell_expr_print(f, indent + " ", cell); if (cell->type == ID($print)) {
dump_cell_expr_print(f, indent + " ", cell);
} else if (cell->type == ID($check)) {
std::string flavor = cell->getParam(ID::FLAVOR).decode_string();
if (flavor == "assert" || flavor == "assume") {
Fmt fmt;
fmt.parse_rtlil(cell);
if (!fmt.parts.empty()) {
f << stringf("%s" " if (!", indent.c_str());
dump_sigspec(f, cell->getPort(ID::A));
f << stringf(")\n");
dump_cell_expr_print(f, indent + " ", cell);
}
} else {
f << stringf("%s" " /* message omitted */\n", indent.c_str());
}
dump_cell_expr_check(f, indent + " ", cell);
}
f << stringf("%s" " end\n", indent.c_str());
} }
f << stringf("%s" "end\n", indent.c_str()); f << stringf("%s" "end\n", indent.c_str());
@ -1949,13 +2035,8 @@ void dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left,
void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw); void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw);
void dump_case_body(std::ostream &f, std::string indent, RTLIL::CaseRule *cs, bool omit_trailing_begin = false) void dump_case_actions(std::ostream &f, std::string indent, RTLIL::CaseRule *cs)
{ {
int number_of_stmts = cs->switches.size() + cs->actions.size();
if (!omit_trailing_begin && number_of_stmts >= 2)
f << stringf("%s" "begin\n", indent.c_str());
for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it) { for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it) {
if (it->first.size() == 0) if (it->first.size() == 0)
continue; continue;
@ -1965,15 +2046,6 @@ void dump_case_body(std::ostream &f, std::string indent, RTLIL::CaseRule *cs, bo
dump_sigspec(f, it->second); dump_sigspec(f, it->second);
f << stringf(";\n"); f << stringf(";\n");
} }
for (auto it = cs->switches.begin(); it != cs->switches.end(); ++it)
dump_proc_switch(f, indent + " ", *it);
if (!omit_trailing_begin && number_of_stmts == 0)
f << stringf("%s /* empty */;\n", indent.c_str());
if (omit_trailing_begin || number_of_stmts >= 2)
f << stringf("%s" "end\n", indent.c_str());
} }
bool dump_proc_switch_ifelse(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw) bool dump_proc_switch_ifelse(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw)
@ -1996,36 +2068,52 @@ bool dump_proc_switch_ifelse(std::ostream &f, std::string indent, RTLIL::SwitchR
} }
} }
dump_attributes(f, indent, sw->attributes);
f << indent; f << indent;
auto sig_it = sw->signal.begin(); auto sig_it = sw->signal.begin();
for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it, ++sig_it) { for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it, ++sig_it) {
bool had_newline = true;
if (it != sw->cases.begin()) { if (it != sw->cases.begin()) {
if ((*it)->compare.empty()) { if ((*it)->compare.empty())
f << indent << "else\n"; f << " else begin\n";
had_newline = true; else
} else { f << " else ";
f << indent << "else ";
had_newline = false;
}
} }
if (!(*it)->compare.empty()) { if (!(*it)->compare.empty()) {
if (!(*it)->attributes.empty()) {
if (!had_newline)
f << "\n" << indent;
dump_attributes(f, "", (*it)->attributes, "\n" + indent);
}
f << stringf("if ("); f << stringf("if (");
dump_sigspec(f, *sig_it); dump_sigspec(f, *sig_it);
f << stringf(")\n"); f << stringf(") begin\n");
} }
dump_case_body(f, indent, *it);
dump_case_actions(f, indent, (*it));
for (auto it2 = (*it)->switches.begin(); it2 != (*it)->switches.end(); ++it2)
dump_proc_switch(f, indent + " ", *it2);
f << indent << "end";
if ((*it)->compare.empty()) if ((*it)->compare.empty())
break; break;
} }
f << "\n";
return true; return true;
} }
void dump_case_body(std::ostream &f, std::string indent, RTLIL::CaseRule *cs, bool omit_trailing_begin = false)
{
int number_of_stmts = cs->switches.size() + cs->actions.size();
if (!omit_trailing_begin && number_of_stmts >= 2)
f << stringf("%s" "begin\n", indent.c_str());
dump_case_actions(f, indent, cs);
for (auto it = cs->switches.begin(); it != cs->switches.end(); ++it)
dump_proc_switch(f, indent + " ", *it);
if (!omit_trailing_begin && number_of_stmts == 0)
f << stringf("%s /* empty */;\n", indent.c_str());
if (omit_trailing_begin || number_of_stmts >= 2)
f << stringf("%s" "end\n", indent.c_str());
}
void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw) void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw)
{ {
if (sw->signal.size() == 0) { if (sw->signal.size() == 0) {
@ -2171,7 +2259,7 @@ void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, boo
void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
{ {
std::map<std::pair<RTLIL::SigSpec, RTLIL::Const>, std::vector<const RTLIL::Cell*>> sync_print_cells; std::map<std::pair<RTLIL::SigSpec, RTLIL::Const>, std::vector<const RTLIL::Cell*>> sync_effect_cells;
reg_wires.clear(); reg_wires.clear();
reset_auto_counter(module); reset_auto_counter(module);
@ -2203,8 +2291,8 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
std::set<std::pair<RTLIL::Wire*,int>> reg_bits; std::set<std::pair<RTLIL::Wire*,int>> reg_bits;
for (auto cell : module->cells()) for (auto cell : module->cells())
{ {
if (cell->type == ID($print) && cell->getParam(ID::TRG_ENABLE).as_bool()) { if (cell->type.in(ID($print), ID($check)) && cell->getParam(ID::TRG_ENABLE).as_bool()) {
sync_print_cells[make_pair(cell->getPort(ID::TRG), cell->getParam(ID::TRG_POLARITY))].push_back(cell); sync_effect_cells[make_pair(cell->getPort(ID::TRG), cell->getParam(ID::TRG_POLARITY))].push_back(cell);
continue; continue;
} }
@ -2263,8 +2351,8 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
for (auto cell : module->cells()) for (auto cell : module->cells())
dump_cell(f, indent + " ", cell); dump_cell(f, indent + " ", cell);
for (auto &it : sync_print_cells) for (auto &it : sync_effect_cells)
dump_sync_print(f, indent + " ", it.first.first, it.first.second, it.second); dump_sync_effect(f, indent + " ", it.first.first, it.first.second, it.second);
for (auto it = module->processes.begin(); it != module->processes.end(); ++it) for (auto it = module->processes.begin(); it != module->processes.end(); ++it)
dump_process(f, indent + " ", it->second); dump_process(f, indent + " ", it->second);

View File

@ -503,7 +503,7 @@ This process is illustrated in :numref:`Fig. %s <fig:Basics_flow>`.
:name: fig:Basics_flow :name: fig:Basics_flow
Typical design flow. Green boxes represent manually created models. Typical design flow. Green boxes represent manually created models.
Orange boxes represent modesl generated by synthesis tools. Orange boxes represent models generated by synthesis tools.
In this example the System Level Model and the Behavioural Model are both In this example the System Level Model and the Behavioural Model are both

View File

@ -200,7 +200,7 @@ from ``\S`` is set the output is undefined. Cells of this type are used to model
by an optimization). by an optimization).
The ``$tribuf`` cell is used to implement tristate logic. Cells of this type The ``$tribuf`` cell is used to implement tristate logic. Cells of this type
have a ``\B`` parameter and inputs ``\A`` and ``\EN`` and an output ``\Y``. The have a ``\WIDTH`` parameter and inputs ``\A`` and ``\EN`` and an output ``\Y``. The
``\A`` input and ``\Y`` output are ``\WIDTH`` bits wide, and the ``\EN`` input ``\A`` input and ``\Y`` output are ``\WIDTH`` bits wide, and the ``\EN`` input
is one bit wide. When ``\EN`` is 0, the output is not driven. When ``\EN`` is 1, is one bit wide. When ``\EN`` is 0, the output is not driven. When ``\EN`` is 1,
the value from ``\A`` input is sent to the ``\Y`` output. Therefore, the the value from ``\A`` input is sent to the ``\Y`` output. Therefore, the
@ -627,7 +627,7 @@ Add information about ``$specify2``, ``$specify3``, and ``$specrule`` cells.
Formal verification cells Formal verification cells
~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~
Add information about ``$assert``, ``$assume``, ``$live``, ``$fair``, Add information about ``$check``, ``$assert``, ``$assume``, ``$live``, ``$fair``,
``$cover``, ``$equiv``, ``$initstate``, ``$anyconst``, ``$anyseq``, ``$cover``, ``$equiv``, ``$initstate``, ``$anyconst``, ``$anyseq``,
``$anyinit``, ``$allconst``, ``$allseq`` cells. ``$anyinit``, ``$allconst``, ``$allseq`` cells.
@ -660,8 +660,8 @@ If ``\TRG_ENABLE`` is true, the following parameters also apply:
negative-edge triggered. negative-edge triggered.
``\PRIORITY`` ``\PRIORITY``
When multiple ``$print`` cells fire on the same trigger, they execute in When multiple ``$print`` or ``$$check`` cells fire on the same trigger, they\
descending priority order. execute in descending priority order.
Ports: Ports:

View File

@ -0,0 +1,144 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2023 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.
*
*/
// build: yosys-config --build scopeinfo_example.so scopeinfo_example.cc
// use: yosys -m scopeinfo_example.so
#include "backends/rtlil/rtlil_backend.h"
#include "kernel/scopeinfo.h"
#include "kernel/yosys.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
struct ScopeinfoExamplePass : public Pass {
ScopeinfoExamplePass() : Pass("scopeinfo_example", "dump scopeinfo") {}
void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" scopeinfo_example [options] [selection]\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
log_header(design, "Executing SCOPEINFO_EXAMPLE pass.\n");
bool do_wires = false;
bool do_common = false;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
if (args[argidx] == "-wires") {
do_wires = true;
continue;
}
if (args[argidx] == "-common") {
do_common = true;
continue;
}
break;
}
extra_args(args, argidx, design);
if (do_wires) {
for (auto module : design->selected_modules()) {
log("Source hierarchy for all selected wires within %s:\n", log_id(module));
ModuleHdlnameIndex index(module);
index.index_scopeinfo_cells();
for (auto wire : module->selected_wires()) {
if (!wire->name.isPublic())
continue;
auto wire_scope = index.containing_scope(wire);
if (!wire_scope.first.valid()) {
log_warning("Couldn't find containing scope for %s in index\n", log_id(wire));
continue;
}
log("%s %s\n", wire_scope.first.path_str().c_str(), log_id(wire_scope.second));
for (auto src : index.sources(wire))
log(" - %s\n", src.c_str());
}
}
}
if (do_common) {
for (auto module : design->selected_modules()) {
std::vector<Wire *> wires = module->selected_wires();
// Shuffle wires so this example produces more interesting outputs
std::sort(wires.begin(), wires.end(), [](Wire *a, Wire *b) {
return mkhash_xorshift(a->name.hash() * 0x2c9277b5) < mkhash_xorshift(b->name.hash() * 0x2c9277b5);
});
ModuleHdlnameIndex index(module);
index.index_scopeinfo_cells();
for (auto wire_i = wires.begin(), wire_end = wires.end(); wire_i != wire_end; ++wire_i) {
if (!(*wire_i)->name.isPublic())
continue;
std::pair<ModuleHdlnameIndex::Cursor, IdString> scope_i = index.containing_scope(*wire_i);
if (!scope_i.first.valid())
continue;
int limit = 0;
for (auto wire_j = wire_i + 1; wire_j != wire_end; ++wire_j) {
if (!(*wire_j)->name.isPublic())
continue;
std::pair<ModuleHdlnameIndex::Cursor, IdString> scope_j = index.containing_scope(*wire_j);
if (!scope_j.first.valid())
continue;
// Skip wires in the same hierarchy level
if (scope_i.first == scope_j.first)
continue;
ModuleHdlnameIndex::Cursor common = scope_i.first.common_ancestor(scope_j.first);
// Try to show at least some non-root common ancestors
if (common.is_root() && limit > 5)
continue;
log("common_ancestor(%s %s%s%s, %s %s%s%s) = %s %s\n",
log_id(module), scope_i.first.path_str().c_str(), scope_i.first.is_root() ? "" : " ", log_id(scope_i.second),
log_id(module), scope_j.first.path_str().c_str(), scope_j.first.is_root() ? "" : " ", log_id(scope_j.second),
log_id(module), common.path_str().c_str()
);
if (++limit == 10)
break;
}
}
}
}
}
} ScopeinfoExamplePass;
PRIVATE_NAMESPACE_END

View File

@ -224,6 +224,7 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch
port_id = 0; port_id = 0;
range_left = -1; range_left = -1;
range_right = 0; range_right = 0;
unpacked_dimensions = 0;
integer = 0; integer = 0;
realvalue = 0; realvalue = 0;
id2ast = NULL; id2ast = NULL;
@ -349,17 +350,15 @@ void AstNode::dumpAst(FILE *f, std::string indent) const
fprintf(f, " int=%u", (int)integer); fprintf(f, " int=%u", (int)integer);
if (realvalue != 0) if (realvalue != 0)
fprintf(f, " real=%e", realvalue); fprintf(f, " real=%e", realvalue);
if (!multirange_dimensions.empty()) { if (!dimensions.empty()) {
fprintf(f, " multirange=["); fprintf(f, " dimensions=");
for (int v : multirange_dimensions) for (auto &dim : dimensions) {
fprintf(f, " %d", v); int left = dim.range_right + dim.range_width - 1;
fprintf(f, " ]"); int right = dim.range_right;
} if (dim.range_swapped)
if (!multirange_swapped.empty()) { std::swap(left, right);
fprintf(f, " multirange_swapped=["); fprintf(f, "[%d:%d]", left, right);
for (bool v : multirange_swapped) }
fprintf(f, " %d", v);
fprintf(f, " ]");
} }
if (is_enum) { if (is_enum) {
fprintf(f, " type=enum"); fprintf(f, " type=enum");
@ -489,6 +488,20 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const
fprintf(f, ";\n"); fprintf(f, ";\n");
break; break;
if (0) { case AST_MEMRD: txt = "@memrd@"; }
if (0) { case AST_MEMINIT: txt = "@meminit@"; }
if (0) { case AST_MEMWR: txt = "@memwr@"; }
fprintf(f, "%s%s", indent.c_str(), txt.c_str());
for (auto child : children) {
fprintf(f, first ? "(" : ", ");
child->dumpVlog(f, "");
first = false;
}
fprintf(f, ")");
if (type != AST_MEMRD)
fprintf(f, ";\n");
break;
case AST_RANGE: case AST_RANGE:
if (range_valid) { if (range_valid) {
if (range_swapped) if (range_swapped)
@ -505,6 +518,11 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const
} }
break; break;
case AST_MULTIRANGE:
for (auto child : children)
child->dumpVlog(f, "");
break;
case AST_ALWAYS: case AST_ALWAYS:
fprintf(f, "%s" "always @", indent.c_str()); fprintf(f, "%s" "always @", indent.c_str());
for (auto child : children) { for (auto child : children) {
@ -542,7 +560,7 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const
case AST_IDENTIFIER: case AST_IDENTIFIER:
{ {
AST::AstNode *member_node = AST::get_struct_member(this); AstNode *member_node = get_struct_member();
if (member_node) if (member_node)
fprintf(f, "%s[%d:%d]", id2vl(str).c_str(), member_node->range_left, member_node->range_right); fprintf(f, "%s[%d:%d]", id2vl(str).c_str(), member_node->range_left, member_node->range_right);
else else
@ -552,6 +570,12 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const
child->dumpVlog(f, ""); child->dumpVlog(f, "");
break; break;
case AST_STRUCT:
case AST_UNION:
case AST_STRUCT_ITEM:
fprintf(f, "%s", id2vl(str).c_str());
break;
case AST_CONSTANT: case AST_CONSTANT:
if (!str.empty()) if (!str.empty())
fprintf(f, "\"%s\"", str.c_str()); fprintf(f, "\"%s\"", str.c_str());
@ -1796,7 +1820,7 @@ std::string AstModule::derive_common(RTLIL::Design *design, const dict<RTLIL::Id
AstNode *new_ast = ast->clone(); AstNode *new_ast = ast->clone();
if (!new_ast->attributes.count(ID::hdlname)) if (!new_ast->attributes.count(ID::hdlname))
new_ast->set_attribute(ID::hdlname, AstNode::mkconst_str(stripped_name)); new_ast->set_attribute(ID::hdlname, AstNode::mkconst_str(stripped_name.substr(1)));
para_counter = 0; para_counter = 0;
for (auto child : new_ast->children) { for (auto child : new_ast->children) {

View File

@ -202,9 +202,17 @@ namespace AST
// set for IDs typed to an enumeration, not used // set for IDs typed to an enumeration, not used
bool is_enum; bool is_enum;
// if this is a multirange memory then this vector contains offset and length of each dimension // Declared range for array dimension.
std::vector<int> multirange_dimensions; struct dimension_t {
std::vector<bool> multirange_swapped; // true if range is swapped int range_right; // lsb in [msb:lsb]
int range_width; // msb - lsb + 1
bool range_swapped; // if the declared msb < lsb, msb and lsb above are swapped
};
// Packed and unpacked dimensions for arrays.
// Unpacked dimensions go first, to follow the order of indexing.
std::vector<dimension_t> dimensions;
// Number of unpacked dimensions.
int unpacked_dimensions;
// this is set by simplify and used during RTLIL generation // this is set by simplify and used during RTLIL generation
AstNode *id2ast; AstNode *id2ast;
@ -371,6 +379,10 @@ namespace AST
// localized fixups after modifying children/attributes of a particular node // localized fixups after modifying children/attributes of a particular node
void fixup_hierarchy_flags(bool force_descend = false); void fixup_hierarchy_flags(bool force_descend = false);
// helpers for indexing
AstNode *make_index_range(AstNode *node, bool unpacked_range = false);
AstNode *get_struct_member() const;
// helper to print errors from simplify/genrtlil code // helper to print errors from simplify/genrtlil code
[[noreturn]] void input_error(const char *format, ...) const YS_ATTRIBUTE(format(printf, 2, 3)); [[noreturn]] void input_error(const char *format, ...) const YS_ATTRIBUTE(format(printf, 2, 3));
}; };
@ -416,10 +428,6 @@ namespace AST
// Helper for setting the src attribute. // Helper for setting the src attribute.
void set_src_attr(RTLIL::AttrObject *obj, const AstNode *ast); void set_src_attr(RTLIL::AttrObject *obj, const AstNode *ast);
// struct helper exposed from simplify for genrtlil
AstNode *make_struct_member_range(AstNode *node, AstNode *member_node);
AstNode *get_struct_member(const AstNode *node);
// generate standard $paramod... derived module name; parameters should be // generate standard $paramod... derived module name; parameters should be
// in the order they are declared in the instantiated module // in the order they are declared in the instantiated module
std::string derived_module_name(std::string stripped_name, const std::vector<std::pair<RTLIL::IdString, RTLIL::Const>> &parameters); std::string derived_module_name(std::string stripped_name, const std::vector<std::pair<RTLIL::IdString, RTLIL::Const>> &parameters);

View File

@ -163,6 +163,28 @@ static RTLIL::SigSpec mux2rtlil(AstNode *that, const RTLIL::SigSpec &cond, const
return wire; return wire;
} }
static void check_unique_id(RTLIL::Module *module, RTLIL::IdString id,
const AstNode *node, const char *to_add_kind)
{
auto already_exists = [&](const RTLIL::AttrObject *existing, const char *existing_kind) {
std::string src = existing->get_string_attribute(ID::src);
std::string location_str = "earlier";
if (!src.empty())
location_str = "at " + src;
node->input_error("Cannot add %s `%s' because a %s with the same name was already created %s!\n",
to_add_kind, id.c_str(), existing_kind, location_str.c_str());
};
if (const RTLIL::Wire *wire = module->wire(id))
already_exists(wire, "signal");
if (const RTLIL::Cell *cell = module->cell(id))
already_exists(cell, "cell");
if (module->processes.count(id))
already_exists(module->processes.at(id), "process");
if (module->memories.count(id))
already_exists(module->memories.at(id), "memory");
}
// helper class for rewriting simple lookahead references in AST always blocks // helper class for rewriting simple lookahead references in AST always blocks
struct AST_INTERNAL::LookaheadRewriter struct AST_INTERNAL::LookaheadRewriter
{ {
@ -316,10 +338,10 @@ struct AST_INTERNAL::ProcessGenerator
// Buffer for generating the init action // Buffer for generating the init action
RTLIL::SigSpec init_lvalue, init_rvalue; RTLIL::SigSpec init_lvalue, init_rvalue;
// The most recently assigned $print cell \PRIORITY. // The most recently assigned $print or $check cell \PRIORITY.
int last_print_priority; int last_effect_priority;
ProcessGenerator(AstNode *always, RTLIL::SigSpec initSyncSignalsArg = RTLIL::SigSpec()) : always(always), initSyncSignals(initSyncSignalsArg), last_print_priority(0) ProcessGenerator(AstNode *always, RTLIL::SigSpec initSyncSignalsArg = RTLIL::SigSpec()) : always(always), initSyncSignals(initSyncSignalsArg), last_effect_priority(0)
{ {
// rewrite lookahead references // rewrite lookahead references
LookaheadRewriter la_rewriter(always); LookaheadRewriter la_rewriter(always);
@ -703,8 +725,10 @@ struct AST_INTERNAL::ProcessGenerator
std::stringstream sstr; std::stringstream sstr;
sstr << ast->str << "$" << ast->filename << ":" << ast->location.first_line << "$" << (autoidx++); sstr << ast->str << "$" << ast->filename << ":" << ast->location.first_line << "$" << (autoidx++);
RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($print)); Wire *en = current_module->addWire(sstr.str() + "_EN", 1);
set_src_attr(cell, ast); set_src_attr(en, ast);
proc->root_case.actions.push_back(SigSig(en, false));
current_case->actions.push_back(SigSig(en, true));
RTLIL::SigSpec triggers; RTLIL::SigSpec triggers;
RTLIL::Const polarity; RTLIL::Const polarity;
@ -717,18 +741,15 @@ struct AST_INTERNAL::ProcessGenerator
polarity.bits.push_back(RTLIL::S0); polarity.bits.push_back(RTLIL::S0);
} }
} }
cell->parameters[ID::TRG_WIDTH] = triggers.size();
cell->parameters[ID::TRG_ENABLE] = (always->type == AST_INITIAL) || !triggers.empty(); RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($print));
cell->parameters[ID::TRG_POLARITY] = polarity; set_src_attr(cell, ast);
cell->parameters[ID::PRIORITY] = --last_print_priority; cell->setParam(ID::TRG_WIDTH, triggers.size());
cell->setParam(ID::TRG_ENABLE, (always->type == AST_INITIAL) || !triggers.empty());
cell->setParam(ID::TRG_POLARITY, polarity);
cell->setParam(ID::PRIORITY, --last_effect_priority);
cell->setPort(ID::TRG, triggers); cell->setPort(ID::TRG, triggers);
cell->setPort(ID::EN, en);
Wire *wire = current_module->addWire(sstr.str() + "_EN", 1);
set_src_attr(wire, ast);
cell->setPort(ID::EN, wire);
proc->root_case.actions.push_back(SigSig(wire, false));
current_case->actions.push_back(SigSig(wire, true));
int default_base = 10; int default_base = 10;
if (ast->str.back() == 'b') if (ast->str.back() == 'b')
@ -766,7 +787,7 @@ struct AST_INTERNAL::ProcessGenerator
args.push_back(arg); args.push_back(arg);
} }
Fmt fmt = {}; Fmt fmt;
fmt.parse_verilog(args, /*sformat_like=*/false, default_base, /*task_name=*/ast->str, current_module->name); fmt.parse_verilog(args, /*sformat_like=*/false, default_base, /*task_name=*/ast->str, current_module->name);
if (ast->str.substr(0, 8) == "$display") if (ast->str.substr(0, 8) == "$display")
fmt.append_string("\n"); fmt.append_string("\n");
@ -776,6 +797,70 @@ struct AST_INTERNAL::ProcessGenerator
} }
break; break;
// generate $check cells
case AST_ASSERT:
case AST_ASSUME:
case AST_LIVE:
case AST_FAIR:
case AST_COVER:
{
std::string flavor, desc;
if (ast->type == AST_ASSERT) { flavor = "assert"; desc = "assert ()"; }
if (ast->type == AST_ASSUME) { flavor = "assume"; desc = "assume ()"; }
if (ast->type == AST_LIVE) { flavor = "live"; desc = "assert (eventually)"; }
if (ast->type == AST_FAIR) { flavor = "fair"; desc = "assume (eventually)"; }
if (ast->type == AST_COVER) { flavor = "cover"; desc = "cover ()"; }
IdString cellname;
if (ast->str.empty())
cellname = stringf("$%s$%s:%d$%d", flavor.c_str(), RTLIL::encode_filename(ast->filename).c_str(), ast->location.first_line, autoidx++);
else
cellname = ast->str;
check_unique_id(current_module, cellname, ast, "procedural assertion");
RTLIL::SigSpec check = ast->children[0]->genWidthRTLIL(-1, false, &subst_rvalue_map.stdmap());
if (GetSize(check) != 1)
check = current_module->ReduceBool(NEW_ID, check);
Wire *en = current_module->addWire(cellname.str() + "_EN", 1);
set_src_attr(en, ast);
proc->root_case.actions.push_back(SigSig(en, false));
current_case->actions.push_back(SigSig(en, true));
RTLIL::SigSpec triggers;
RTLIL::Const polarity;
for (auto sync : proc->syncs) {
if (sync->type == RTLIL::STp) {
triggers.append(sync->signal);
polarity.bits.push_back(RTLIL::S1);
} else if (sync->type == RTLIL::STn) {
triggers.append(sync->signal);
polarity.bits.push_back(RTLIL::S0);
}
}
RTLIL::Cell *cell = current_module->addCell(cellname, ID($check));
set_src_attr(cell, ast);
for (auto &attr : ast->attributes) {
if (attr.second->type != AST_CONSTANT)
log_file_error(ast->filename, ast->location.first_line, "Attribute `%s' with non-constant value!\n", attr.first.c_str());
cell->attributes[attr.first] = attr.second->asAttrConst();
}
cell->setParam(ID::FLAVOR, flavor);
cell->setParam(ID::TRG_WIDTH, triggers.size());
cell->setParam(ID::TRG_ENABLE, (always->type == AST_INITIAL) || !triggers.empty());
cell->setParam(ID::TRG_POLARITY, polarity);
cell->setParam(ID::PRIORITY, --last_effect_priority);
cell->setPort(ID::TRG, triggers);
cell->setPort(ID::EN, en);
cell->setPort(ID::A, check);
// No message is emitted to ensure Verilog code roundtrips correctly.
Fmt fmt;
fmt.emit_rtlil(cell);
break;
}
case AST_NONE: case AST_NONE:
case AST_FOR: case AST_FOR:
break; break;
@ -960,7 +1045,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
if (children.size() > 1) if (children.size() > 1)
range = children[1]; range = children[1];
} else if (id_ast->type == AST_STRUCT_ITEM || id_ast->type == AST_STRUCT || id_ast->type == AST_UNION) { } else if (id_ast->type == AST_STRUCT_ITEM || id_ast->type == AST_STRUCT || id_ast->type == AST_UNION) {
AstNode *tmp_range = make_struct_member_range(this, id_ast); AstNode *tmp_range = make_index_range(id_ast);
this_width = tmp_range->range_left - tmp_range->range_right + 1; this_width = tmp_range->range_left - tmp_range->range_right + 1;
delete tmp_range; delete tmp_range;
} else } else
@ -1242,28 +1327,6 @@ void AstNode::detectSignWidth(int &width_hint, bool &sign_hint, bool *found_real
width_hint, kWidthLimit); width_hint, kWidthLimit);
} }
static void check_unique_id(RTLIL::Module *module, RTLIL::IdString id,
const AstNode *node, const char *to_add_kind)
{
auto already_exists = [&](const RTLIL::AttrObject *existing, const char *existing_kind) {
std::string src = existing->get_string_attribute(ID::src);
std::string location_str = "earlier";
if (!src.empty())
location_str = "at " + src;
node->input_error("Cannot add %s `%s' because a %s with the same name was already created %s!\n",
to_add_kind, id.c_str(), existing_kind, location_str.c_str());
};
if (const RTLIL::Wire *wire = module->wire(id))
already_exists(wire, "signal");
if (const RTLIL::Cell *cell = module->cell(id))
already_exists(cell, "cell");
if (module->processes.count(id))
already_exists(module->processes.at(id), "process");
if (module->memories.count(id))
already_exists(module->memories.at(id), "memory");
}
// create RTLIL from an AST node // create RTLIL from an AST node
// all generated cells, wires and processes are added to the module pointed to by 'current_module' // all generated cells, wires and processes are added to the module pointed to by 'current_module'
// when the AST node is an expression (AST_ADD, AST_BIT_XOR, etc.), the result signal is returned. // when the AST node is an expression (AST_ADD, AST_BIT_XOR, etc.), the result signal is returned.
@ -1521,7 +1584,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
chunk.width = wire->width; chunk.width = wire->width;
chunk.offset = 0; chunk.offset = 0;
if ((member_node = get_struct_member(this))) { if ((member_node = get_struct_member())) {
// Clamp wire chunk to range of member within struct/union. // Clamp wire chunk to range of member within struct/union.
chunk.width = member_node->range_left - member_node->range_right + 1; chunk.width = member_node->range_left - member_node->range_right + 1;
chunk.offset = member_node->range_right; chunk.offset = member_node->range_right;
@ -1945,48 +2008,50 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
} }
break; break;
// generate $assert cells // generate $check cells
case AST_ASSERT: case AST_ASSERT:
case AST_ASSUME: case AST_ASSUME:
case AST_LIVE: case AST_LIVE:
case AST_FAIR: case AST_FAIR:
case AST_COVER: case AST_COVER:
{ {
IdString celltype; std::string flavor, desc;
if (type == AST_ASSERT) celltype = ID($assert); if (type == AST_ASSERT) { flavor = "assert"; desc = "assert property ()"; }
if (type == AST_ASSUME) celltype = ID($assume); if (type == AST_ASSUME) { flavor = "assume"; desc = "assume property ()"; }
if (type == AST_LIVE) celltype = ID($live); if (type == AST_LIVE) { flavor = "live"; desc = "assert property (eventually)"; }
if (type == AST_FAIR) celltype = ID($fair); if (type == AST_FAIR) { flavor = "fair"; desc = "assume property (eventually)"; }
if (type == AST_COVER) celltype = ID($cover); if (type == AST_COVER) { flavor = "cover"; desc = "cover property ()"; }
log_assert(children.size() == 2); IdString cellname;
if (str.empty())
cellname = stringf("$%s$%s:%d$%d", flavor.c_str(), RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++);
else
cellname = str;
check_unique_id(current_module, cellname, this, "procedural assertion");
RTLIL::SigSpec check = children[0]->genRTLIL(); RTLIL::SigSpec check = children[0]->genRTLIL();
if (GetSize(check) != 1) if (GetSize(check) != 1)
check = current_module->ReduceBool(NEW_ID, check); check = current_module->ReduceBool(NEW_ID, check);
RTLIL::SigSpec en = children[1]->genRTLIL(); RTLIL::Cell *cell = current_module->addCell(cellname, ID($check));
if (GetSize(en) != 1)
en = current_module->ReduceBool(NEW_ID, en);
IdString cellname;
if (str.empty())
cellname = stringf("%s$%s:%d$%d", celltype.c_str(), RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++);
else
cellname = str;
check_unique_id(current_module, cellname, this, "procedural assertion");
RTLIL::Cell *cell = current_module->addCell(cellname, celltype);
set_src_attr(cell, this); set_src_attr(cell, this);
for (auto &attr : attributes) { for (auto &attr : attributes) {
if (attr.second->type != AST_CONSTANT) if (attr.second->type != AST_CONSTANT)
input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str()); input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str());
cell->attributes[attr.first] = attr.second->asAttrConst(); cell->attributes[attr.first] = attr.second->asAttrConst();
} }
cell->setParam(ID(FLAVOR), flavor);
cell->parameters[ID::TRG_WIDTH] = 0;
cell->parameters[ID::TRG_ENABLE] = 0;
cell->parameters[ID::TRG_POLARITY] = 0;
cell->parameters[ID::PRIORITY] = 0;
cell->setPort(ID::TRG, RTLIL::SigSpec());
cell->setPort(ID::EN, RTLIL::S1);
cell->setPort(ID::A, check); cell->setPort(ID::A, check);
cell->setPort(ID::EN, en);
// No message is emitted to ensure Verilog code roundtrips correctly.
Fmt fmt;
fmt.emit_rtlil(cell);
} }
break; break;

File diff suppressed because it is too large Load Diff

View File

@ -256,6 +256,16 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool
continue; continue;
} }
if (!strcmp(cmd, ".area") || !strcmp(cmd, ".delay") || !strcmp(cmd, ".wire_load_slope") || !strcmp(cmd, ".wire") ||
!strcmp(cmd, ".input_arrival") || !strcmp(cmd, ".default_input_arrival") || !strcmp(cmd, ".output_required") ||
!strcmp(cmd, ".default_output_required") || !strcmp(cmd, ".input_drive") || !strcmp(cmd, ".default_input_drive") ||
!strcmp(cmd, ".max_input_load") || !strcmp(cmd, ".default_max_input_load") || !strcmp(cmd, ".output_load") ||
!strcmp(cmd, ".default_output_load"))
{
log_warning("Blif delay constraints (%s) are not supported.", cmd);
continue;
}
if (!strcmp(cmd, ".inputs") || !strcmp(cmd, ".outputs")) if (!strcmp(cmd, ".inputs") || !strcmp(cmd, ".outputs"))
{ {
char *p; char *p;

View File

@ -343,36 +343,46 @@ void VerificImporter::import_attributes(dict<RTLIL::IdString, RTLIL::Const> &att
} }
} }
RTLIL::SigBit VerificImporter::netToSigBit(Verific::Net *net) {
if (net && net->IsGnd())
return RTLIL::State::S0;
else if (net && net->IsPwr())
return RTLIL::State::S1;
else if (net && net->IsX())
return RTLIL::State::Sx;
else if (net)
return net_map_at(net);
else
return RTLIL::State::Sz;
}
RTLIL::SigSpec VerificImporter::operatorInput(Instance *inst) RTLIL::SigSpec VerificImporter::operatorInput(Instance *inst)
{ {
RTLIL::SigSpec sig; RTLIL::SigSpec sig;
for (int i = int(inst->InputSize())-1; i >= 0; i--) for (int i = int(inst->InputSize())-1; i >= 0; i--) {
if (inst->GetInputBit(i)) Net *net = inst->GetInputBit(i);
sig.append(net_map_at(inst->GetInputBit(i))); sig.append(netToSigBit(net));
else }
sig.append(RTLIL::State::Sz);
return sig; return sig;
} }
RTLIL::SigSpec VerificImporter::operatorInput1(Instance *inst) RTLIL::SigSpec VerificImporter::operatorInput1(Instance *inst)
{ {
RTLIL::SigSpec sig; RTLIL::SigSpec sig;
for (int i = int(inst->Input1Size())-1; i >= 0; i--) for (int i = int(inst->Input1Size())-1; i >= 0; i--) {
if (inst->GetInput1Bit(i)) Net *net = inst->GetInput1Bit(i);
sig.append(net_map_at(inst->GetInput1Bit(i))); sig.append(netToSigBit(net));
else }
sig.append(RTLIL::State::Sz);
return sig; return sig;
} }
RTLIL::SigSpec VerificImporter::operatorInput2(Instance *inst) RTLIL::SigSpec VerificImporter::operatorInput2(Instance *inst)
{ {
RTLIL::SigSpec sig; RTLIL::SigSpec sig;
for (int i = int(inst->Input2Size())-1; i >= 0; i--) for (int i = int(inst->Input2Size())-1; i >= 0; i--) {
if (inst->GetInput2Bit(i)) Net *net = inst->GetInput2Bit(i);
sig.append(net_map_at(inst->GetInput2Bit(i))); sig.append(netToSigBit(net));
else }
sig.append(RTLIL::State::Sz);
return sig; return sig;
} }
@ -1980,6 +1990,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
} }
RTLIL::Cell *cell = module->addCell(inst_name, inst_type); RTLIL::Cell *cell = module->addCell(inst_name, inst_type);
import_attributes(cell->attributes, inst);
if (inst->IsPrimitive() && mode_keep) if (inst->IsPrimitive() && mode_keep)
cell->attributes[ID::keep] = 1; cell->attributes[ID::keep] = 1;
@ -2815,6 +2826,9 @@ struct VerificPass : public Pass {
log(" -extnets\n"); log(" -extnets\n");
log(" Resolve references to external nets by adding module ports as needed.\n"); log(" Resolve references to external nets by adding module ports as needed.\n");
log("\n"); log("\n");
log(" -no-split-complex-ports\n");
log(" Complex ports (structs or arrays) are not split and remain packed as a single port.\n");
log("\n");
log(" -autocover\n"); log(" -autocover\n");
log(" Generate automatic cover statements for all asserts\n"); log(" Generate automatic cover statements for all asserts\n");
log("\n"); log("\n");
@ -3548,6 +3562,7 @@ struct VerificPass : public Pass {
bool mode_nosva = false, mode_names = false, mode_verific = false; bool mode_nosva = false, mode_names = false, mode_verific = false;
bool mode_autocover = false, mode_fullinit = false; bool mode_autocover = false, mode_fullinit = false;
bool flatten = false, extnets = false, mode_cells = false; bool flatten = false, extnets = false, mode_cells = false;
bool split_complex_ports = true;
string dumpfile; string dumpfile;
string ppfile; string ppfile;
Map parameters(STRING_HASH); Map parameters(STRING_HASH);
@ -3565,6 +3580,10 @@ struct VerificPass : public Pass {
flatten = true; flatten = true;
continue; continue;
} }
if (args[argidx] == "-no-split-complex-ports") {
split_complex_ports = false;
continue;
}
if (args[argidx] == "-extnets") { if (args[argidx] == "-extnets") {
extnets = true; extnets = true;
continue; continue;
@ -3804,8 +3823,10 @@ struct VerificPass : public Pass {
worker.run(nl.second); worker.run(nl.second);
} }
for (auto nl : nl_todo) if (split_complex_ports) {
nl.second->ChangePortBusStructures(1 /* hierarchical */); for (auto nl : nl_todo)
nl.second->ChangePortBusStructures(1 /* hierarchical */);
}
if (!dumpfile.empty()) { if (!dumpfile.empty()) {
VeriWrite veri_writer; VeriWrite veri_writer;

View File

@ -83,6 +83,7 @@ struct VerificImporter
RTLIL::IdString new_verific_id(Verific::DesignObj *obj); RTLIL::IdString new_verific_id(Verific::DesignObj *obj);
void import_attributes(dict<RTLIL::IdString, RTLIL::Const> &attributes, Verific::DesignObj *obj, Verific::Netlist *nl = nullptr); void import_attributes(dict<RTLIL::IdString, RTLIL::Const> &attributes, Verific::DesignObj *obj, Verific::Netlist *nl = nullptr);
RTLIL::SigBit netToSigBit(Verific::Net *net);
RTLIL::SigSpec operatorInput(Verific::Instance *inst); RTLIL::SigSpec operatorInput(Verific::Instance *inst);
RTLIL::SigSpec operatorInput1(Verific::Instance *inst); RTLIL::SigSpec operatorInput1(Verific::Instance *inst);
RTLIL::SigSpec operatorInput2(Verific::Instance *inst); RTLIL::SigSpec operatorInput2(Verific::Instance *inst);

View File

@ -197,9 +197,20 @@ static AstNode *checkRange(AstNode *type_node, AstNode *range_node)
range_node = makeRange(type_node->range_left, type_node->range_right, false); range_node = makeRange(type_node->range_left, type_node->range_right, false);
} }
} }
if (range_node && range_node->children.size() != 2) {
frontend_verilog_yyerror("wire/reg/logic packed dimension must be of the form: [<expr>:<expr>], [<expr>+:<expr>], or [<expr>-:<expr>]"); if (range_node) {
bool valid = true;
if (range_node->type == AST_RANGE) {
valid = range_node->children.size() == 2;
} else { // AST_MULTIRANGE
for (auto child : range_node->children) {
valid = valid && child->children.size() == 2;
}
}
if (!valid)
frontend_verilog_yyerror("wire/reg/logic packed dimension must be of the form [<expr>:<expr>]");
} }
return range_node; return range_node;
} }
@ -672,7 +683,7 @@ module_arg:
ast_stack.back()->children.push_back(astbuf2); ast_stack.back()->children.push_back(astbuf2);
delete astbuf1; // really only needed if multiple instances of same type. delete astbuf1; // really only needed if multiple instances of same type.
} module_arg_opt_assignment | } module_arg_opt_assignment |
attr wire_type range TOK_ID { attr wire_type range_or_multirange TOK_ID {
AstNode *node = $2; AstNode *node = $2;
node->str = *$4; node->str = *$4;
SET_AST_NODE_LOC(node, @4, @4); SET_AST_NODE_LOC(node, @4, @4);
@ -1165,7 +1176,7 @@ task_func_args:
task_func_port | task_func_args ',' task_func_port; task_func_port | task_func_args ',' task_func_port;
task_func_port: task_func_port:
attr wire_type range { attr wire_type range_or_multirange {
bool prev_was_input = true; bool prev_was_input = true;
bool prev_was_output = false; bool prev_was_output = false;
if (albuf) { if (albuf) {
@ -1889,10 +1900,11 @@ struct_member_type: { astbuf1 = new AstNode(AST_STRUCT_ITEM); } member_type_toke
; ;
member_type_token: member_type_token:
member_type member_type range_or_multirange {
| hierarchical_type_id { AstNode *range = checkRange(astbuf1, $2);
addWiretypeNode($1, astbuf1); if (range)
} astbuf1->children.push_back(range);
}
| { | {
delete astbuf1; delete astbuf1;
} struct_union { } struct_union {
@ -1908,7 +1920,8 @@ member_type_token:
; ;
member_type: type_atom type_signing member_type: type_atom type_signing
| type_vec type_signing range_or_multirange { if ($3) astbuf1->children.push_back($3); } | type_vec type_signing
| hierarchical_type_id { addWiretypeNode($1, astbuf1); }
; ;
struct_var_list: struct_var struct_var_list: struct_var
@ -1928,7 +1941,7 @@ struct_var: TOK_ID { auto *var_node = astbuf2->clone();
///////// /////////
wire_decl: wire_decl:
attr wire_type range { attr wire_type range_or_multirange {
albuf = $1; albuf = $1;
astbuf1 = $2; astbuf1 = $2;
astbuf2 = checkRange(astbuf1, $3); astbuf2 = checkRange(astbuf1, $3);
@ -2104,14 +2117,14 @@ type_name: TOK_ID // first time seen
; ;
typedef_decl: typedef_decl:
TOK_TYPEDEF typedef_base_type range type_name range_or_multirange ';' { TOK_TYPEDEF typedef_base_type range_or_multirange type_name range_or_multirange ';' {
astbuf1 = $2; astbuf1 = $2;
astbuf2 = checkRange(astbuf1, $3); astbuf2 = checkRange(astbuf1, $3);
if (astbuf2) if (astbuf2)
astbuf1->children.push_back(astbuf2); astbuf1->children.push_back(astbuf2);
if ($5 != NULL) { if ($5 != NULL) {
if (!astbuf2) { if (!astbuf2 && !astbuf1->is_custom_type) {
addRange(astbuf1, 0, 0, false); addRange(astbuf1, 0, 0, false);
} }
rewriteAsMemoryNode(astbuf1, $5); rewriteAsMemoryNode(astbuf1, $5);
@ -2484,7 +2497,7 @@ assert:
delete $5; delete $5;
} else { } else {
AstNode *node = new AstNode(assume_asserts_mode ? AST_ASSUME : AST_ASSERT, $5); AstNode *node = new AstNode(assume_asserts_mode ? AST_ASSUME : AST_ASSERT, $5);
SET_AST_NODE_LOC(node, @1, @6); SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @6);
if ($1 != nullptr) if ($1 != nullptr)
node->str = *$1; node->str = *$1;
ast_stack.back()->children.push_back(node); ast_stack.back()->children.push_back(node);
@ -2497,7 +2510,7 @@ assert:
delete $5; delete $5;
} else { } else {
AstNode *node = new AstNode(assert_assumes_mode ? AST_ASSERT : AST_ASSUME, $5); AstNode *node = new AstNode(assert_assumes_mode ? AST_ASSERT : AST_ASSUME, $5);
SET_AST_NODE_LOC(node, @1, @6); SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @6);
if ($1 != nullptr) if ($1 != nullptr)
node->str = *$1; node->str = *$1;
ast_stack.back()->children.push_back(node); ast_stack.back()->children.push_back(node);
@ -2510,7 +2523,7 @@ assert:
delete $6; delete $6;
} else { } else {
AstNode *node = new AstNode(assume_asserts_mode ? AST_FAIR : AST_LIVE, $6); AstNode *node = new AstNode(assume_asserts_mode ? AST_FAIR : AST_LIVE, $6);
SET_AST_NODE_LOC(node, @1, @7); SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @7);
if ($1 != nullptr) if ($1 != nullptr)
node->str = *$1; node->str = *$1;
ast_stack.back()->children.push_back(node); ast_stack.back()->children.push_back(node);
@ -2523,7 +2536,7 @@ assert:
delete $6; delete $6;
} else { } else {
AstNode *node = new AstNode(assert_assumes_mode ? AST_LIVE : AST_FAIR, $6); AstNode *node = new AstNode(assert_assumes_mode ? AST_LIVE : AST_FAIR, $6);
SET_AST_NODE_LOC(node, @1, @7); SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @7);
if ($1 != nullptr) if ($1 != nullptr)
node->str = *$1; node->str = *$1;
ast_stack.back()->children.push_back(node); ast_stack.back()->children.push_back(node);
@ -2533,7 +2546,7 @@ assert:
} | } |
opt_sva_label TOK_COVER opt_property '(' expr ')' ';' { opt_sva_label TOK_COVER opt_property '(' expr ')' ';' {
AstNode *node = new AstNode(AST_COVER, $5); AstNode *node = new AstNode(AST_COVER, $5);
SET_AST_NODE_LOC(node, @1, @6); SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @6);
if ($1 != nullptr) { if ($1 != nullptr) {
node->str = *$1; node->str = *$1;
delete $1; delete $1;
@ -2542,7 +2555,7 @@ assert:
} | } |
opt_sva_label TOK_COVER opt_property '(' ')' ';' { opt_sva_label TOK_COVER opt_property '(' ')' ';' {
AstNode *node = new AstNode(AST_COVER, AstNode::mkconst_int(1, false)); AstNode *node = new AstNode(AST_COVER, AstNode::mkconst_int(1, false));
SET_AST_NODE_LOC(node, @1, @5); SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @5);
if ($1 != nullptr) { if ($1 != nullptr) {
node->str = *$1; node->str = *$1;
delete $1; delete $1;
@ -2551,7 +2564,7 @@ assert:
} | } |
opt_sva_label TOK_COVER ';' { opt_sva_label TOK_COVER ';' {
AstNode *node = new AstNode(AST_COVER, AstNode::mkconst_int(1, false)); AstNode *node = new AstNode(AST_COVER, AstNode::mkconst_int(1, false));
SET_AST_NODE_LOC(node, @1, @2); SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @2);
if ($1 != nullptr) { if ($1 != nullptr) {
node->str = *$1; node->str = *$1;
delete $1; delete $1;
@ -2563,7 +2576,7 @@ assert:
delete $5; delete $5;
} else { } else {
AstNode *node = new AstNode(AST_ASSUME, $5); AstNode *node = new AstNode(AST_ASSUME, $5);
SET_AST_NODE_LOC(node, @1, @6); SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @6);
if ($1 != nullptr) if ($1 != nullptr)
node->str = *$1; node->str = *$1;
ast_stack.back()->children.push_back(node); ast_stack.back()->children.push_back(node);
@ -2578,7 +2591,7 @@ assert:
delete $6; delete $6;
} else { } else {
AstNode *node = new AstNode(AST_FAIR, $6); AstNode *node = new AstNode(AST_FAIR, $6);
SET_AST_NODE_LOC(node, @1, @7); SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @7);
if ($1 != nullptr) if ($1 != nullptr)
node->str = *$1; node->str = *$1;
ast_stack.back()->children.push_back(node); ast_stack.back()->children.push_back(node);

View File

@ -102,11 +102,13 @@ struct CellTypes
setup_type(ID($specify3), {ID::EN, ID::SRC, ID::DST, ID::DAT}, pool<RTLIL::IdString>(), true); setup_type(ID($specify3), {ID::EN, ID::SRC, ID::DST, ID::DAT}, pool<RTLIL::IdString>(), true);
setup_type(ID($specrule), {ID::EN_SRC, ID::EN_DST, ID::SRC, ID::DST}, pool<RTLIL::IdString>(), true); setup_type(ID($specrule), {ID::EN_SRC, ID::EN_DST, ID::SRC, ID::DST}, pool<RTLIL::IdString>(), true);
setup_type(ID($print), {ID::EN, ID::ARGS, ID::TRG}, pool<RTLIL::IdString>()); setup_type(ID($print), {ID::EN, ID::ARGS, ID::TRG}, pool<RTLIL::IdString>());
setup_type(ID($check), {ID::A, ID::EN, ID::ARGS, ID::TRG}, pool<RTLIL::IdString>());
setup_type(ID($set_tag), {ID::A, ID::SET, ID::CLR}, {ID::Y}); setup_type(ID($set_tag), {ID::A, ID::SET, ID::CLR}, {ID::Y});
setup_type(ID($get_tag), {ID::A}, {ID::Y}); setup_type(ID($get_tag), {ID::A}, {ID::Y});
setup_type(ID($overwrite_tag), {ID::A, ID::SET, ID::CLR}, pool<RTLIL::IdString>()); setup_type(ID($overwrite_tag), {ID::A, ID::SET, ID::CLR}, pool<RTLIL::IdString>());
setup_type(ID($original_tag), {ID::A}, {ID::Y}); setup_type(ID($original_tag), {ID::A}, {ID::Y});
setup_type(ID($future_ff), {ID::A}, {ID::Y}); setup_type(ID($future_ff), {ID::A}, {ID::Y});
setup_type(ID($scopeinfo), {}, {});
} }
void setup_internals_eval() void setup_internals_eval()

View File

@ -88,6 +88,7 @@ X(equiv_merged)
X(equiv_region) X(equiv_region)
X(extract_order) X(extract_order)
X(F) X(F)
X(FLAVOR)
X(FORMAT) X(FORMAT)
X(force_downto) X(force_downto)
X(force_upto) X(force_upto)

View File

@ -92,8 +92,15 @@ int getopt(int argc, char **argv, const char *optstring)
return optopt; return optopt;
} }
optarg = argv[++optind]; if (++optind >= argc) {
fprintf(stderr, "%s: option '-%c' expects an argument\n", argv[0], optopt);
optopt = '?';
return optopt;
}
optarg = argv[optind];
optind++, optcur = 1; optind++, optcur = 1;
return optopt; return optopt;
} }
@ -243,14 +250,6 @@ int main(int argc, char **argv)
bool mode_v = false; bool mode_v = false;
bool mode_q = false; bool mode_q = false;
#if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE)
if (getenv("HOME") != NULL) {
yosys_history_file = stringf("%s/.yosys_history", getenv("HOME"));
read_history(yosys_history_file.c_str());
yosys_history_offset = where_history();
}
#endif
if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "-help") || !strcmp(argv[1], "--help"))) if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "-help") || !strcmp(argv[1], "--help")))
{ {
printf("\n"); printf("\n");
@ -538,6 +537,36 @@ int main(int argc, char **argv)
if (print_banner) if (print_banner)
yosys_banner(); yosys_banner();
#if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE)
std::string state_dir;
#if defined(_WIN32)
if (getenv("HOMEDRIVE") != NULL && getenv("HOMEPATH") != NULL) {
state_dir = stringf("%s%s/.local/state", getenv("HOMEDRIVE"), getenv("HOMEPATH"));
} else {
log_debug("$HOMEDRIVE and/or $HOMEPATH is empty. No history file will be created.\n");
}
#else
if (getenv("XDG_STATE_HOME") == NULL || getenv("XDG_STATE_HOME")[0] == '\0') {
if (getenv("HOME") != NULL) {
state_dir = stringf("%s/.local/state", getenv("HOME"));
} else {
log_debug("$HOME is empty. No history file will be created.\n");
}
} else {
state_dir = stringf("%s", getenv("XDG_STATE_HOME"));
}
#endif
if (!state_dir.empty()) {
std::string yosys_dir = state_dir + "/yosys";
create_directory(yosys_dir);
yosys_history_file = yosys_dir + "/history";
read_history(yosys_history_file.c_str());
yosys_history_offset = where_history();
}
#endif
if (print_stats) if (print_stats)
log_hasher = new SHA1; log_hasher = new SHA1;
@ -569,6 +598,8 @@ int main(int argc, char **argv)
for (auto &fn : plugin_filenames) for (auto &fn : plugin_filenames)
load_plugin(fn, {}); load_plugin(fn, {});
log_suppressed();
if (!vlog_defines.empty()) { if (!vlog_defines.empty()) {
std::string vdef_cmd = "read -define"; std::string vdef_cmd = "read -define";
for (auto vdef : vlog_defines) for (auto vdef : vlog_defines)

View File

@ -17,6 +17,8 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <stdint.h>
namespace hashlib { namespace hashlib {
const int hashtable_size_trigger = 2; const int hashtable_size_trigger = 2;

View File

@ -108,9 +108,8 @@ Pass::Pass(std::string name, std::string short_help) : pass_name(name), short_he
void Pass::run_register() void Pass::run_register()
{ {
if (pass_register.count(pass_name)) if (pass_register.count(pass_name) && !replace_existing_pass())
log_error("Unable to register pass '%s', pass already exists!\n", pass_name.c_str()); log_error("Unable to register pass '%s', pass already exists!\n", pass_name.c_str());
pass_register[pass_name] = this; pass_register[pass_name] = this;
} }
@ -447,13 +446,12 @@ Frontend::Frontend(std::string name, std::string short_help) :
void Frontend::run_register() void Frontend::run_register()
{ {
if (pass_register.count(pass_name)) if (pass_register.count(pass_name) && !replace_existing_pass())
log_error("Unable to register pass '%s', pass already exists!\n", pass_name.c_str()); log_error("Unable to register pass '%s', pass already exists!\n", pass_name.c_str());
pass_register[pass_name] = this; pass_register[pass_name] = this;
if (frontend_register.count(frontend_name)) if (frontend_register.count(frontend_name) && !replace_existing_pass())
log_error("Unable to register frontend '%s', frontend already exists!\n", frontend_name.c_str()); log_error("Unable to register frontend '%s', frontend already exists!\n", frontend_name.c_str());
frontend_register[frontend_name] = this; frontend_register[frontend_name] = this;
} }
@ -993,4 +991,44 @@ struct MinisatSatSolver : public SatSolver {
} }
} MinisatSatSolver; } MinisatSatSolver;
struct LicensePass : public Pass {
LicensePass() : Pass("license", "print license terms") { }
void help() override
{
log("\n");
log(" license\n");
log("\n");
log("This command produces the following notice.\n");
notice();
}
void execute(std::vector<std::string> args, RTLIL::Design*) override
{
notice();
}
void notice()
{
log("\n");
log(" /----------------------------------------------------------------------------\\\n");
log(" | |\n");
log(" | yosys -- Yosys Open SYnthesis Suite |\n");
log(" | |\n");
log(" | Copyright (C) 2012 - 2024 Claire Xenia Wolf <claire@yosyshq.com> |\n");
log(" | |\n");
log(" | Permission to use, copy, modify, and/or distribute this software for any |\n");
log(" | purpose with or without fee is hereby granted, provided that the above |\n");
log(" | copyright notice and this permission notice appear in all copies. |\n");
log(" | |\n");
log(" | THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |\n");
log(" | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |\n");
log(" | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |\n");
log(" | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |\n");
log(" | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |\n");
log(" | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |\n");
log(" | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |\n");
log(" | |\n");
log(" \\----------------------------------------------------------------------------/\n");
log("\n");
}
} LicensePass;
YOSYS_NAMESPACE_END YOSYS_NAMESPACE_END

View File

@ -70,6 +70,7 @@ struct Pass
virtual void on_register(); virtual void on_register();
virtual void on_shutdown(); virtual void on_shutdown();
virtual bool replace_existing_pass() const { return false; }
}; };
struct ScriptPass : Pass struct ScriptPass : Pass

View File

@ -1068,6 +1068,12 @@ namespace {
error(__LINE__); error(__LINE__);
} }
std::string param_string(const RTLIL::IdString &name)
{
param(name);
return cell->parameters.at(name).decode_string();
}
void port(const RTLIL::IdString& name, int width) void port(const RTLIL::IdString& name, int width)
{ {
auto it = cell->connections_.find(name); auto it = cell->connections_.find(name);
@ -1747,6 +1753,31 @@ namespace {
return; return;
} }
if (cell->type == ID($check)) {
std::string flavor = param_string(ID(FLAVOR));
if (!(flavor == "assert" || flavor == "assume" || flavor == "live" || flavor == "fair" || flavor == "cover"))
error(__LINE__);
param(ID(FORMAT));
param_bool(ID::TRG_ENABLE);
param(ID::TRG_POLARITY);
param(ID::PRIORITY);
port(ID::A, 1);
port(ID::EN, 1);
port(ID::TRG, param(ID::TRG_WIDTH));
port(ID::ARGS, param(ID::ARGS_WIDTH));
check_expected();
return;
}
if (cell->type == ID($scopeinfo)) {
param(ID::TYPE);
check_expected();
std::string scope_type = cell->getParam(ID::TYPE).decode_string();
if (scope_type != "module" && scope_type != "struct")
error(__LINE__);
return;
}
if (cell->type == ID($_BUF_)) { port(ID::A,1); port(ID::Y,1); check_expected(); return; } if (cell->type == ID($_BUF_)) { port(ID::A,1); port(ID::Y,1); check_expected(); return; }
if (cell->type == ID($_NOT_)) { port(ID::A,1); port(ID::Y,1); check_expected(); return; } if (cell->type == ID($_NOT_)) { port(ID::A,1); port(ID::Y,1); check_expected(); return; }
if (cell->type == ID($_AND_)) { port(ID::A,1); port(ID::B,1); port(ID::Y,1); check_expected(); return; } if (cell->type == ID($_AND_)) { port(ID::A,1); port(ID::B,1); port(ID::Y,1); check_expected(); return; }
@ -2157,17 +2188,10 @@ void RTLIL::Module::remove(const pool<RTLIL::Wire*> &wires)
} }
void operator()(RTLIL::SigSpec &lhs, RTLIL::SigSpec &rhs) { void operator()(RTLIL::SigSpec &lhs, RTLIL::SigSpec &rhs) {
log_assert(GetSize(lhs) == GetSize(rhs)); // If a deleted wire occurs on the lhs or rhs we just remove that part
lhs.unpack(); // of the assignment
rhs.unpack(); lhs.remove2(*wires_p, &rhs);
for (int i = 0; i < GetSize(lhs); i++) { rhs.remove2(*wires_p, &lhs);
RTLIL::SigBit &lhs_bit = lhs.bits_[i];
RTLIL::SigBit &rhs_bit = rhs.bits_[i];
if ((lhs_bit.wire != nullptr && wires_p->count(lhs_bit.wire)) || (rhs_bit.wire != nullptr && wires_p->count(rhs_bit.wire))) {
lhs_bit = State::Sx;
rhs_bit = State::Sx;
}
}
} }
}; };
@ -3693,6 +3717,9 @@ RTLIL::SigChunk::SigChunk(const RTLIL::SigBit &bit)
RTLIL::SigChunk RTLIL::SigChunk::extract(int offset, int length) const RTLIL::SigChunk RTLIL::SigChunk::extract(int offset, int length) const
{ {
log_assert(offset >= 0);
log_assert(length >= 0);
log_assert(offset + length <= width);
RTLIL::SigChunk ret; RTLIL::SigChunk ret;
if (wire) { if (wire) {
ret.wire = wire; ret.wire = wire;
@ -4238,6 +4265,34 @@ void RTLIL::SigSpec::remove2(const std::set<RTLIL::SigBit> &pattern, RTLIL::SigS
check(); check();
} }
void RTLIL::SigSpec::remove2(const pool<RTLIL::Wire*> &pattern, RTLIL::SigSpec *other)
{
if (other)
cover("kernel.rtlil.sigspec.remove_other");
else
cover("kernel.rtlil.sigspec.remove");
unpack();
if (other != NULL) {
log_assert(width_ == other->width_);
other->unpack();
}
for (int i = GetSize(bits_) - 1; i >= 0; i--) {
if (bits_[i].wire != NULL && pattern.count(bits_[i].wire)) {
bits_.erase(bits_.begin() + i);
width_--;
if (other != NULL) {
other->bits_.erase(other->bits_.begin() + i);
other->width_--;
}
}
}
check();
}
RTLIL::SigSpec RTLIL::SigSpec::extract(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec *other) const RTLIL::SigSpec RTLIL::SigSpec::extract(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec *other) const
{ {
if (other) if (other)
@ -4377,6 +4432,9 @@ void RTLIL::SigSpec::remove(int offset, int length)
RTLIL::SigSpec RTLIL::SigSpec::extract(int offset, int length) const RTLIL::SigSpec RTLIL::SigSpec::extract(int offset, int length) const
{ {
log_assert(offset >= 0);
log_assert(length >= 0);
log_assert(offset + length <= width_);
unpack(); unpack();
cover("kernel.rtlil.sigspec.extract_pos"); cover("kernel.rtlil.sigspec.extract_pos");
return std::vector<RTLIL::SigBit>(bits_.begin() + offset, bits_.begin() + offset + length); return std::vector<RTLIL::SigBit>(bits_.begin() + offset, bits_.begin() + offset + length);

View File

@ -712,7 +712,7 @@ struct RTLIL::Const
inline unsigned int hash() const { inline unsigned int hash() const {
unsigned int h = mkhash_init; unsigned int h = mkhash_init;
for (auto b : bits) for (auto b : bits)
mkhash(h, b); h = mkhash(h, b);
return h; return h;
} }
}; };
@ -924,6 +924,7 @@ public:
void remove(const pool<RTLIL::SigBit> &pattern, RTLIL::SigSpec *other) const; void remove(const pool<RTLIL::SigBit> &pattern, RTLIL::SigSpec *other) const;
void remove2(const pool<RTLIL::SigBit> &pattern, RTLIL::SigSpec *other); void remove2(const pool<RTLIL::SigBit> &pattern, RTLIL::SigSpec *other);
void remove2(const std::set<RTLIL::SigBit> &pattern, RTLIL::SigSpec *other); void remove2(const std::set<RTLIL::SigBit> &pattern, RTLIL::SigSpec *other);
void remove2(const pool<RTLIL::Wire*> &pattern, RTLIL::SigSpec *other);
void remove(int offset, int length = 1); void remove(int offset, int length = 1);
void remove_const(); void remove_const();

View File

@ -1379,6 +1379,11 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep)
return true; return true;
} }
if (cell->type == ID($scopeinfo))
{
return true;
}
// Unsupported internal cell types: $pow $fsm $mem* // Unsupported internal cell types: $pow $fsm $mem*
// .. and all sequential cells with asynchronous inputs // .. and all sequential cells with asynchronous inputs
return false; return false;

129
kernel/scopeinfo.cc Normal file
View File

@ -0,0 +1,129 @@
/*
* 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/scopeinfo.h"
YOSYS_NAMESPACE_BEGIN
template <typename I, typename Filter> void ModuleHdlnameIndex::index_items(I begin, I end, Filter filter)
{
for (; begin != end; ++begin) {
auto const &item = *begin;
if (!filter(item))
continue;
std::vector<IdString> path = parse_hdlname(item);
if (!path.empty())
lookup.emplace(item, tree.insert(path, item));
}
}
void ModuleHdlnameIndex::index()
{
index_wires();
index_cells();
}
void ModuleHdlnameIndex::index_wires()
{
auto wires = module->wires();
index_items(wires.begin(), wires.end(), [](Wire *) { return true; });
}
void ModuleHdlnameIndex::index_cells()
{
auto cells = module->cells();
index_items(cells.begin(), cells.end(), [](Cell *) { return true; });
}
void ModuleHdlnameIndex::index_scopeinfo_cells()
{
auto cells = module->cells();
index_items(cells.begin(), cells.end(), [](Cell *cell) { return cell->type == ID($scopeinfo); });
}
std::vector<std::string> ModuleHdlnameIndex::scope_sources(Cursor cursor)
{
std::vector<std::string> result;
for (; !cursor.is_root(); cursor = cursor.parent()) {
if (!cursor.has_entry()) {
result.push_back("");
result.push_back("");
continue;
}
Cell *cell = cursor.entry().cell();
if (cell == nullptr || cell->type != ID($scopeinfo)) {
result.push_back("");
result.push_back("");
continue;
}
result.push_back(scopeinfo_get_attribute(cell, ScopeinfoAttrs::Module, ID::src).decode_string());
result.push_back(scopeinfo_get_attribute(cell, ScopeinfoAttrs::Cell, ID::src).decode_string());
}
result.push_back(module->get_src_attribute());
std::reverse(result.begin(), result.end());
return result;
}
static const char *attr_prefix(ScopeinfoAttrs attrs)
{
switch (attrs) {
case ScopeinfoAttrs::Cell:
return "\\cell_";
case ScopeinfoAttrs::Module:
return "\\module_";
default:
log_abort();
}
}
bool scopeinfo_has_attribute(const RTLIL::Cell *scopeinfo, ScopeinfoAttrs attrs, const RTLIL::IdString &id)
{
log_assert(scopeinfo->type == ID($scopeinfo));
return scopeinfo->has_attribute(attr_prefix(attrs) + RTLIL::unescape_id(id));
}
RTLIL::Const scopeinfo_get_attribute(const RTLIL::Cell *scopeinfo, ScopeinfoAttrs attrs, const RTLIL::IdString &id)
{
log_assert(scopeinfo->type == ID($scopeinfo));
auto found = scopeinfo->attributes.find(attr_prefix(attrs) + RTLIL::unescape_id(id));
if (found == scopeinfo->attributes.end())
return RTLIL::Const();
return found->second;
}
dict<RTLIL::IdString, RTLIL::Const> scopeinfo_attributes(const RTLIL::Cell *scopeinfo, ScopeinfoAttrs attrs)
{
dict<RTLIL::IdString, RTLIL::Const> attributes;
const char *prefix = attr_prefix(attrs);
int prefix_len = strlen(prefix);
for (auto const &entry : scopeinfo->attributes)
if (entry.first.begins_with(prefix))
attributes.emplace(RTLIL::escape_id(entry.first.c_str() + prefix_len), entry.second);
return attributes;
}
YOSYS_NAMESPACE_END

432
kernel/scopeinfo.h Normal file
View File

@ -0,0 +1,432 @@
/*
* 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 SCOPEINFO_H
#define SCOPEINFO_H
#include <vector>
#include <algorithm>
#include "kernel/yosys.h"
#include "kernel/celltypes.h"
YOSYS_NAMESPACE_BEGIN
template<typename T>
class IdTree
{
public:
struct Cursor;
protected:
IdTree *parent = nullptr;
IdString scope_name;
int depth = 0;
pool<IdString> names;
dict<IdString, T> entries;
public: // XXX
dict<IdString, std::unique_ptr<IdTree>> subtrees;
template<typename P, typename T_ref>
static Cursor do_insert(IdTree *tree, P begin, P end, T_ref &&value)
{
log_assert(begin != end && "path must be non-empty");
while (true) {
IdString name = *begin;
++begin;
log_assert(!name.empty());
tree->names.insert(name);
if (begin == end) {
tree->entries.emplace(name, std::forward<T_ref>(value));
return Cursor(tree, name);
}
auto &unique = tree->subtrees[name];
if (!unique) {
unique.reset(new IdTree);
unique->scope_name = name;
unique->parent = tree;
unique->depth = tree->depth + 1;
}
tree = unique.get();
}
}
public:
IdTree() = default;
IdTree(const IdTree &) = delete;
IdTree(IdTree &&) = delete;
// A cursor remains valid as long as the (sub-)IdTree it points at is alive
struct Cursor
{
friend class IdTree;
protected:
public:
IdTree *target;
IdString scope_name;
Cursor() : target(nullptr) {}
Cursor(IdTree *target, IdString scope_name) : target(target), scope_name(scope_name) {
if (scope_name.empty())
log_assert(target->parent == nullptr);
}
Cursor do_first_child() {
IdTree *tree = nullptr;
if (scope_name.empty()) {
tree = target;
} else {
auto found = target->subtrees.find(scope_name);
if (found != target->subtrees.end()) {
tree = found->second.get();
} else {
return Cursor();
}
}
if (tree->names.empty()) {
return Cursor();
}
return Cursor(tree, *tree->names.begin());
}
Cursor do_next_sibling() {
if (scope_name.empty())
return Cursor();
auto found = target->names.find(scope_name);
if (found == target->names.end())
return Cursor();
++found;
if (found == target->names.end())
return Cursor();
return Cursor(target, *found);
}
Cursor do_parent() {
if (scope_name.empty())
return Cursor();
if (target->parent != nullptr)
return Cursor(target->parent, target->scope_name);
return Cursor(target, IdString());
}
Cursor do_next_preorder() {
Cursor current = *this;
Cursor next = current.do_first_child();
if (next.valid())
return next;
while (current.valid()) {
if (next.valid())
return next;
next = current.do_next_sibling();
if (next.valid())
return next;
current = current.do_parent();
}
return current;
}
Cursor do_child(IdString name) {
IdTree *tree = nullptr;
if (scope_name.empty()) {
tree = target;
} else {
auto found = target->subtrees.find(scope_name);
if (found != target->subtrees.end()) {
tree = found->second.get();
} else {
return Cursor();
}
}
auto found = tree->names.find(name);
if (found == tree->names.end()) {
return Cursor();
}
return Cursor(tree, *found);
}
public:
bool operator==(const Cursor &other) const {
return target == other.target && scope_name == other.scope_name;
}
bool operator!=(const Cursor &other) const {
return !(*this == other);
}
bool valid() const {
return target != nullptr;
}
int depth() const {
log_assert(valid());
return target->depth + !scope_name.empty();
}
bool is_root() const {
return target != nullptr && scope_name.empty();
}
bool has_entry() const {
log_assert(valid());
return !scope_name.empty() && target->entries.count(scope_name);
}
T &entry() {
log_assert(!scope_name.empty());
return target->entries.at(scope_name);
}
void assign_path_to(std::vector<IdString> &out_path) {
log_assert(valid());
out_path.clear();
if (scope_name.empty())
return;
out_path.push_back(scope_name);
IdTree *current = target;
while (current->parent) {
out_path.push_back(current->scope_name);
current = current->parent;
}
std::reverse(out_path.begin(), out_path.end());
}
std::vector<IdString> path() {
std::vector<IdString> result;
assign_path_to(result);
return result;
}
std::string path_str() {
std::string result;
for (const auto &item : path()) {
if (!result.empty())
result.push_back(' ');
result += RTLIL::unescape_id(item);
}
return result;
}
Cursor first_child() {
log_assert(valid());
return do_first_child();
}
Cursor next_preorder() {
log_assert(valid());
return do_next_preorder();
}
Cursor parent() {
log_assert(valid());
return do_parent();
}
Cursor child(IdString name) {
log_assert(valid());
return do_child(name);
}
Cursor common_ancestor(Cursor other) {
Cursor current = *this;
while (current != other) {
if (!current.valid() || !other.valid())
return Cursor();
int delta = current.depth() - other.depth();
if (delta >= 0)
current = current.do_parent();
if (delta <= 0)
other = other.do_parent();
}
return current;
}
};
template<typename P>
Cursor insert(P begin, P end, const T &value) {
return do_insert(this, begin, end, value);
}
template<typename P>
Cursor insert(P begin, P end, T &&value) {
return do_insert(this, begin, end, std::move(value));
}
template<typename P>
Cursor insert(const P &path, const T &value) {
return do_insert(this, path.begin(), path.end(), value);
}
template<typename P>
Cursor insert(const P &path, T &&value) {
return do_insert(this, path.begin(), path.end(), std::move(value));
}
Cursor cursor() {
return parent ? Cursor(this->parent, this->scope_name) : Cursor(this, IdString());
}
template<typename P>
Cursor cursor(P begin, P end) {
Cursor current = cursor();
for (; begin != end; ++begin) {
current = current.do_child(*begin);
if (!current.valid())
break;
}
return current;
}
template<typename P>
Cursor cursor(const P &path) {
return cursor(path.begin(), path.end());
}
};
struct ModuleItem {
enum class Type {
Wire,
Cell,
};
Type type;
void *ptr;
ModuleItem(Wire *wire) : type(Type::Wire), ptr(wire) {}
ModuleItem(Cell *cell) : type(Type::Cell), ptr(cell) {}
bool is_wire() const { return type == Type::Wire; }
bool is_cell() const { return type == Type::Cell; }
Wire *wire() const { return type == Type::Wire ? static_cast<Wire *>(ptr) : nullptr; }
Cell *cell() const { return type == Type::Cell ? static_cast<Cell *>(ptr) : nullptr; }
bool operator==(const ModuleItem &other) const { return ptr == other.ptr && type == other.type; }
unsigned int hash() const { return (uintptr_t)ptr; }
};
static inline void log_dump_val_worker(typename IdTree<ModuleItem>::Cursor cursor ) { log("%p %s", cursor.target, log_id(cursor.scope_name)); }
template<typename T>
static inline void log_dump_val_worker(const typename std::unique_ptr<T> &cursor ) { log("unique %p", cursor.get()); }
template<typename O>
std::vector<IdString> parse_hdlname(const O* object)
{
std::vector<IdString> path;
if (!object->name.isPublic())
return path;
for (auto const &item : object->get_hdlname_attribute())
path.push_back("\\" + item);
if (path.empty())
path.push_back(object->name);
return path;
}
template<typename O>
std::pair<std::vector<IdString>, IdString> parse_scopename(const O* object)
{
std::vector<IdString> path;
IdString trailing = object->name;
if (object->name.isPublic()) {
for (auto const &item : object->get_hdlname_attribute())
path.push_back("\\" + item);
if (!path.empty()) {
trailing = path.back();
path.pop_back();
}
} else {
for (auto const &item : split_tokens(object->get_string_attribute(ID(scopename)), " "))
path.push_back("\\" + item);
}
return {path, trailing};
}
struct ModuleHdlnameIndex {
typedef IdTree<ModuleItem>::Cursor Cursor;
RTLIL::Module *module;
IdTree<ModuleItem> tree;
dict<ModuleItem, Cursor> lookup;
ModuleHdlnameIndex(RTLIL::Module *module) : module(module) {}
private:
template<typename I, typename Filter>
void index_items(I begin, I end, Filter filter);
public:
// Index all wires and cells of the module
void index();
// Index all wires of the module
void index_wires();
// Index all cells of the module
void index_cells();
// Index only the $scopeinfo cells of the module.
// This is sufficient when using `containing_scope`.
void index_scopeinfo_cells();
// Return the cursor for the containing scope of some RTLIL object (Wire/Cell/...)
template<typename O>
std::pair<Cursor, IdString> containing_scope(O *object) {
auto pair = parse_scopename(object);
return {tree.cursor(pair.first), pair.second};
}
// Return a vector of source locations starting from the indexed module to
// the scope represented by the cursor. The vector alternates module and
// module item source locations, using empty strings for missing src
// attributes.
std::vector<std::string> scope_sources(Cursor cursor);
// Return a vector of source locations starting from the indexed module to
// the passed RTLIL object (Wire/Cell/...). The vector alternates module
// and module item source locations, using empty strings for missing src
// attributes.
template<typename O>
std::vector<std::string> sources(O *object) {
auto pair = parse_scopename(object);
std::vector<std::string> result = scope_sources(tree.cursor(pair.first));
result.push_back(object->get_src_attribute());
return result;
}
};
enum class ScopeinfoAttrs {
Module,
Cell,
};
// Check whether the flattened module or flattened cell corresponding to a $scopeinfo cell had a specific attribute.
bool scopeinfo_has_attribute(const RTLIL::Cell *scopeinfo, ScopeinfoAttrs attrs, const RTLIL::IdString &id);
// Get a specific attribute from the flattened module or flattened cell corresponding to a $scopeinfo cell.
RTLIL::Const scopeinfo_get_attribute(const RTLIL::Cell *scopeinfo, ScopeinfoAttrs attrs, const RTLIL::IdString &id);
// Get all attribute from the flattened module or flattened cell corresponding to a $scopeinfo cell.
dict<RTLIL::IdString, RTLIL::Const> scopeinfo_attributes(const RTLIL::Cell *scopeinfo, ScopeinfoAttrs attrs);
YOSYS_NAMESPACE_END
#endif

View File

@ -55,7 +55,7 @@
# include <glob.h> # include <glob.h>
#endif #endif
#ifdef __FreeBSD__ #if defined(__FreeBSD__) || defined(__NetBSD__)
# include <sys/sysctl.h> # include <sys/sysctl.h>
#endif #endif
@ -138,27 +138,11 @@ void yosys_banner()
{ {
log("\n"); log("\n");
log(" /----------------------------------------------------------------------------\\\n"); log(" /----------------------------------------------------------------------------\\\n");
log(" | |\n");
log(" | yosys -- Yosys Open SYnthesis Suite |\n"); log(" | yosys -- Yosys Open SYnthesis Suite |\n");
log(" | |\n"); log(" | Copyright (C) 2012 - 2024 Claire Xenia Wolf <claire@yosyshq.com> |\n");
log(" | Copyright (C) 2012 - 2020 Claire Xenia Wolf <claire@yosyshq.com> |\n"); log(" | Distributed under an ISC-like license, type \"license\" to see terms |\n");
log(" | |\n");
log(" | Permission to use, copy, modify, and/or distribute this software for any |\n");
log(" | purpose with or without fee is hereby granted, provided that the above |\n");
log(" | copyright notice and this permission notice appear in all copies. |\n");
log(" | |\n");
log(" | THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |\n");
log(" | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |\n");
log(" | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |\n");
log(" | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |\n");
log(" | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |\n");
log(" | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |\n");
log(" | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |\n");
log(" | |\n");
log(" \\----------------------------------------------------------------------------/\n"); log(" \\----------------------------------------------------------------------------/\n");
log("\n");
log(" %s\n", yosys_version_str); log(" %s\n", yosys_version_str);
log("\n");
} }
int ceil_log2(int x) int ceil_log2(int x)
@ -436,6 +420,25 @@ std::string make_temp_dir(std::string template_str)
#endif #endif
} }
bool check_directory_exists(const std::string& dirname)
{
#if defined(_WIN32)
struct _stat info;
if (_stat(dirname.c_str(), &info) != 0)
{
return false;
}
return (info.st_mode & _S_IFDIR) != 0;
#else
struct stat info;
if (stat(dirname.c_str(), &info) != 0)
{
return false;
}
return (info.st_mode & S_IFDIR) != 0;
#endif
}
#ifdef _WIN32 #ifdef _WIN32
bool check_file_exists(std::string filename, bool) bool check_file_exists(std::string filename, bool)
{ {
@ -481,6 +484,48 @@ void remove_directory(std::string dirname)
#endif #endif
} }
bool create_directory(const std::string& dirname)
{
#if defined(_WIN32)
int ret = _mkdir(dirname.c_str());
#else
mode_t mode = 0755;
int ret = mkdir(dirname.c_str(), mode);
#endif
if (ret == 0)
return true;
switch (errno)
{
case ENOENT:
// parent didn't exist, try to create it
{
std::string::size_type pos = dirname.find_last_of('/');
if (pos == std::string::npos)
#if defined(_WIN32)
pos = dirname.find_last_of('\\');
if (pos == std::string::npos)
#endif
return false;
if (!create_directory( dirname.substr(0, pos) ))
return false;
}
// now, try to create again
#if defined(_WIN32)
return 0 == _mkdir(dirname.c_str());
#else
return 0 == mkdir(dirname.c_str(), mode);
#endif
case EEXIST:
// done!
return check_directory_exists(dirname);
default:
return false;
}
}
std::string escape_filename_spaces(const std::string& filename) std::string escape_filename_spaces(const std::string& filename)
{ {
std::string out; std::string out;
@ -781,10 +826,10 @@ static int tcl_yosys_cmd(ClientData, Tcl_Interp *interp, int argc, const char *a
int yosys_tcl_iterp_init(Tcl_Interp *interp) int yosys_tcl_iterp_init(Tcl_Interp *interp)
{ {
if (Tcl_Init(interp)!=TCL_OK) if (Tcl_Init(interp)!=TCL_OK)
log_warning("Tcl_Init() call failed - %s\n",Tcl_ErrnoMsg(Tcl_GetErrno())); log_warning("Tcl_Init() call failed - %s\n",Tcl_ErrnoMsg(Tcl_GetErrno()));
Tcl_CreateCommand(interp, "yosys", tcl_yosys_cmd, NULL, NULL); Tcl_CreateCommand(interp, "yosys", tcl_yosys_cmd, NULL, NULL);
return TCL_OK ; return TCL_OK ;
} }
void yosys_tcl_activate_repl() void yosys_tcl_activate_repl()
@ -856,10 +901,14 @@ std::string proc_self_dirname()
buflen--; buflen--;
return std::string(path, buflen); return std::string(path, buflen);
} }
#elif defined(__FreeBSD__) #elif defined(__FreeBSD__) || defined(__NetBSD__)
std::string proc_self_dirname() std::string proc_self_dirname()
{ {
#ifdef __NetBSD__
int mib[4] = {CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_PATHNAME};
#else
int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1}; int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
#endif
size_t buflen; size_t buflen;
char *buffer; char *buffer;
std::string path; std::string path;

View File

@ -66,6 +66,8 @@
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <limits.h> #include <limits.h>
#include <sys/stat.h>
#include <errno.h>
#ifdef WITH_PYTHON #ifdef WITH_PYTHON
#include <Python.h> #include <Python.h>
@ -341,8 +343,10 @@ std::string get_base_tmpdir();
std::string make_temp_file(std::string template_str = get_base_tmpdir() + "/yosys_XXXXXX"); std::string make_temp_file(std::string template_str = get_base_tmpdir() + "/yosys_XXXXXX");
std::string make_temp_dir(std::string template_str = get_base_tmpdir() + "/yosys_XXXXXX"); std::string make_temp_dir(std::string template_str = get_base_tmpdir() + "/yosys_XXXXXX");
bool check_file_exists(std::string filename, bool is_exec = false); bool check_file_exists(std::string filename, bool is_exec = false);
bool check_directory_exists(const std::string& dirname);
bool is_absolute_path(std::string filename); bool is_absolute_path(std::string filename);
void remove_directory(std::string dirname); void remove_directory(std::string dirname);
bool create_directory(const std::string& dirname);
std::string escape_filename_spaces(const std::string& filename); std::string escape_filename_spaces(const std::string& filename);
template<typename T> int GetSize(const T &obj) { return obj.size(); } template<typename T> int GetSize(const T &obj) { return obj.size(); }

View File

@ -1,9 +1,9 @@
CC = clang CC = clang
CXX = clang CXX = clang++
CXXFLAGS = -MD -Wall -Wextra -ggdb CXXFLAGS = -MD -Wall -Wextra -ggdb
CXXFLAGS += -std=c++11 -O0 CXXFLAGS += -std=c++11 -O0
LDLIBS = ../minisat/Options.cc ../minisat/SimpSolver.cc ../minisat/Solver.cc ../minisat/System.cc -lm -lstdc++ LIBS = ../minisat/Options.cc ../minisat/SimpSolver.cc ../minisat/Solver.cc ../minisat/System.cc -lm -lstdc++
all: demo_vec demo_bit demo_cmp testbench puzzle3d all: demo_vec demo_bit demo_cmp testbench puzzle3d
@ -27,4 +27,3 @@ clean:
.PHONY: all test clean .PHONY: all test clean
-include *.d -include *.d

View File

@ -5,9 +5,9 @@ CONFIG := clang-debug
# CONFIG := release # CONFIG := release
CC = clang CC = clang
CXX = clang CXX = clang++
CXXFLAGS = -MD -Wall -Wextra -ggdb CXXFLAGS = -MD -Wall -Wextra -ggdb
LDLIBS = -lstdc++ LIBS = -lstdc++
ifeq ($(CONFIG),clang-debug) ifeq ($(CONFIG),clang-debug)
CXXFLAGS += -std=c++11 -O0 CXXFLAGS += -std=c++11 -O0
@ -15,19 +15,19 @@ endif
ifeq ($(CONFIG),gcc-debug) ifeq ($(CONFIG),gcc-debug)
CC = gcc CC = gcc
CXX = gcc CXX = g++
CXXFLAGS += -std=gnu++0x -O0 CXXFLAGS += -std=gnu++0x -O0
endif endif
ifeq ($(CONFIG),profile) ifeq ($(CONFIG),profile)
CC = gcc CC = gcc
CXX = gcc CXX = g++
CXXFLAGS += -std=gnu++0x -Os -DNDEBUG CXXFLAGS += -std=gnu++0x -Os -DNDEBUG
endif endif
ifeq ($(CONFIG),release) ifeq ($(CONFIG),release)
CC = gcc CC = gcc
CXX = gcc CXX = g++
CXXFLAGS += -std=gnu++0x -march=native -O3 -DNDEBUG CXXFLAGS += -std=gnu++0x -march=native -O3 -DNDEBUG
endif endif
@ -50,4 +50,3 @@ clean:
.PHONY: all test clean .PHONY: all test clean
-include *.d -include *.d

View File

@ -1257,6 +1257,7 @@ class WFunction:
func.is_static = False func.is_static = False
func.is_inline = False func.is_inline = False
func.is_virtual = False func.is_virtual = False
func.is_const = False
func.ret_attr_type = attr_types.default func.ret_attr_type = attr_types.default
func.is_operator = False func.is_operator = False
func.member_of = None func.member_of = None
@ -1334,6 +1335,11 @@ class WFunction:
found = find_closing(str_def, "(", ")") found = find_closing(str_def, "(", ")")
if found == -1: if found == -1:
return None return None
post_qualifiers = str_def[found + 1:].lstrip().replace("{", " {") + " "
if post_qualifiers.startswith("const "):
func.is_const = True
str_def = str_def[0:found] str_def = str_def[0:found]
if func.name in blacklist_methods: if func.name in blacklist_methods:
return None return None
@ -1379,6 +1385,12 @@ class WFunction:
def gen_alias(self): def gen_alias(self):
self.alias = self.mangled_name self.alias = self.mangled_name
def gen_post_qualifiers(self, derived=False):
if self.member_of != None and self.member_of.link_type == link_types.derive and self.is_virtual and derived:
# we drop the qualifiers when deriving callbacks to be implemented in Python
return ''
return ' const' if self.is_const else ''
def gen_decl(self): def gen_decl(self):
if self.duplicate: if self.duplicate:
return "" return ""
@ -1392,7 +1404,7 @@ class WFunction:
text += ", " text += ", "
if len(self.args) > 0: if len(self.args) > 0:
text = text[:-2] text = text[:-2]
text += ");\n" text += f"){self.gen_post_qualifiers()};\n"
return text return text
def gen_decl_virtual(self): def gen_decl_virtual(self):
@ -1411,12 +1423,18 @@ class WFunction:
if len(self.args) > 0: if len(self.args) > 0:
text = text[:-2] text = text[:-2]
text += ")" text += ")"
if len(self.args) == 0: if len(self.args) == 0 and self.ret_type.name == "void":
text += "{}" text += "{}"
else: else:
text += "\n\t\t{" text += "\n\t\t{"
for arg in self.args: for arg in self.args:
text += "\n\t\t\t(void)" + arg.gen_varname() + ";" text += "\n\t\t\t(void)" + arg.gen_varname() + ";"
if self.ret_type.name == "void":
pass
elif self.ret_type.name == "bool":
text += "\n\t\t\treturn false;"
else:
raise NotImplementedError(self.ret_type.name)
text += "\n\t\t}\n" text += "\n\t\t}\n"
text += "\n\t\tvirtual " text += "\n\t\tvirtual "
if self.is_static: if self.is_static:
@ -1427,7 +1445,7 @@ class WFunction:
text += ", " text += ", "
if len(self.args) > 0: if len(self.args) > 0:
text = text[:-2] text = text[:-2]
text += ") override;\n" text += f"){self.gen_post_qualifiers()} override;\n"
return text return text
def gen_decl_hash_py(self): def gen_decl_hash_py(self):
@ -1452,7 +1470,7 @@ class WFunction:
text += ", " text += ", "
if len(self.args) > 0: if len(self.args) > 0:
text = text[:-2] text = text[:-2]
text +=")\n\t{" text += f"){self.gen_post_qualifiers()}\n\t{{"
for arg in self.args: for arg in self.args:
text += arg.gen_translation() text += arg.gen_translation()
text += "\n\t\t" text += "\n\t\t"
@ -1507,16 +1525,17 @@ class WFunction:
text += ", " text += ", "
if len(self.args) > 0: if len(self.args) > 0:
text = text[:-2] text = text[:-2]
text += ")\n\t{" text += f"){self.gen_post_qualifiers()}\n\t{{"
for arg in self.args: for arg in self.args:
text += arg.gen_translation_cpp() text += arg.gen_translation_cpp()
text += "\n\t\t" return_stmt = "return " if self.ret_type.name != "void" else ""
text += f"\n\t\t{return_stmt}"
if self.member_of == None: if self.member_of == None:
text += "::" + self.namespace + "::" + self.alias + "(" text += "::" + self.namespace + "::" + self.alias + "("
elif self.is_static: elif self.is_static:
text += self.member_of.namespace + "::" + self.member_of.name + "::" + self.name + "(" text += self.member_of.namespace + "::" + self.member_of.name + "::" + self.name + "("
else: else:
text += "py_" + self.alias + "(" text += f"const_cast<{self.member_of.name}*>(this)->py_" + self.alias + "("
for arg in self.args: for arg in self.args:
text += arg.gen_call_cpp() + ", " text += arg.gen_call_cpp() + ", "
if len(self.args) > 0: if len(self.args) > 0:
@ -1547,11 +1566,13 @@ class WFunction:
call_string = call_string[0:-2] call_string = call_string[0:-2]
call_string += ");" call_string += ");"
return_stmt = "return " if self.ret_type.name != "void" else ""
text += ")\n\t\t{" text += ")\n\t\t{"
text += "\n\t\t\tif(boost::python::override py_" + self.alias + " = this->get_override(\"py_" + self.alias + "\"))" text += "\n\t\t\tif (boost::python::override py_" + self.alias + " = this->get_override(\"py_" + self.alias + "\"))"
text += "\n\t\t\t\t" + call_string text += f"\n\t\t\t\t{return_stmt}" + call_string
text += "\n\t\t\telse" text += "\n\t\t\telse"
text += "\n\t\t\t\t" + self.member_of.name + "::" + call_string text += f"\n\t\t\t\t{return_stmt}" + self.member_of.name + "::" + call_string
text += "\n\t\t}" text += "\n\t\t}"
text += "\n\n\t\t" + self.ret_type.gen_text() + " default_py_" + self.alias + "(" text += "\n\n\t\t" + self.ret_type.gen_text() + " default_py_" + self.alias + "("
@ -1559,8 +1580,8 @@ class WFunction:
text += arg.gen_listitem() + ", " text += arg.gen_listitem() + ", "
if len(self.args) > 0: if len(self.args) > 0:
text = text[:-2] text = text[:-2]
text += ")\n\t\t{" text += f")\n\t\t{{"
text += "\n\t\t\tthis->" + self.member_of.name + "::" + call_string text += f"\n\t\t\t{return_stmt}this->" + self.member_of.name + "::" + call_string
text += "\n\t\t}" text += "\n\t\t}"
return text return text
@ -1584,9 +1605,9 @@ class WFunction:
for a in self.args: for a in self.args:
text += a.gen_listitem_hash() + ", " text += a.gen_listitem_hash() + ", "
if len(self.args) > 0: if len(self.args) > 0:
text = text[0:-2] + ")>" text = text[0:-2] + f"){self.gen_post_qualifiers(True)}>"
else: else:
text += "void)>" text += f"void){self.gen_post_qualifiers(True)}>"
if self.is_operator: if self.is_operator:
text += "(\"" + wrappable_operators[self.name.replace("operator","")] + "\"" text += "(\"" + wrappable_operators[self.name.replace("operator","")] + "\""

19
misc/yosys-config.in Normal file → Executable file
View File

@ -9,8 +9,10 @@ help() {
echo "Replacement args:" echo "Replacement args:"
echo " --cxx @CXX@" echo " --cxx @CXX@"
echo " --cxxflags $( echo '@CXXFLAGS@' | fmt -w60 | sed ':a;N;$!ba;s/\n/ \\\n /g' )" echo " --cxxflags $( echo '@CXXFLAGS@' | fmt -w60 | sed ':a;N;$!ba;s/\n/ \\\n /g' )"
echo " --ldflags @LDFLAGS@" echo " --linkflags @LINKFLAGS@"
echo " --ldlibs @LDLIBS@" echo " --ldflags (alias of --linkflags)"
echo " --libs @LIBS@"
echo " --ldlibs (alias of --libs)"
echo " --bindir @BINDIR@" echo " --bindir @BINDIR@"
echo " --datdir @DATDIR@" echo " --datdir @DATDIR@"
echo "" echo ""
@ -18,7 +20,7 @@ help() {
echo "" echo ""
echo "Use --exec to call a command instead of generating output. Example usage:" echo "Use --exec to call a command instead of generating output. Example usage:"
echo "" echo ""
echo " $0 --exec --cxx --cxxflags --ldflags -o plugin.so -shared plugin.cc --ldlibs" echo " $0 --exec --cxx --cxxflags --ldflags -o plugin.so -shared plugin.cc --libs"
echo "" echo ""
echo "The above command can be abbreviated as:" echo "The above command can be abbreviated as:"
echo "" echo ""
@ -44,7 +46,7 @@ fi
if [ "$1" == "--build" ]; then if [ "$1" == "--build" ]; then
modname="$2"; shift 2 modname="$2"; shift 2
set -- --exec --cxx --cxxflags --ldflags -o "$modname" -shared "$@" --ldlibs set -- --exec --cxx --cxxflags --ldflags -o "$modname" -shared "$@" --libs
fi fi
prefix="--" prefix="--"
@ -63,10 +65,14 @@ for opt; do
tokens=( "${tokens[@]}" @CXX@ ) ;; tokens=( "${tokens[@]}" @CXX@ ) ;;
"$prefix"cxxflags) "$prefix"cxxflags)
tokens=( "${tokens[@]}" @CXXFLAGS@ ) ;; tokens=( "${tokens[@]}" @CXXFLAGS@ ) ;;
"$prefix"linkflags)
tokens=( "${tokens[@]}" @LINKFLAGS@ ) ;;
"$prefix"libs)
tokens=( "${tokens[@]}" @LIBS@ ) ;;
"$prefix"ldflags) "$prefix"ldflags)
tokens=( "${tokens[@]}" @LDFLAGS@ ) ;; tokens=( "${tokens[@]}" @LINKFLAGS@ ) ;;
"$prefix"ldlibs) "$prefix"ldlibs)
tokens=( "${tokens[@]}" @LDLIBS@ ) ;; tokens=( "${tokens[@]}" @LIBS@ ) ;;
"$prefix"bindir) "$prefix"bindir)
tokens=( "${tokens[@]}" '@BINDIR@' ) ;; tokens=( "${tokens[@]}" '@BINDIR@' ) ;;
"$prefix"datdir) "$prefix"datdir)
@ -104,4 +110,3 @@ fi
echo "${tokens[@]}" echo "${tokens[@]}"
exit 0 exit 0

View File

@ -23,6 +23,52 @@
USING_YOSYS_NAMESPACE USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN PRIVATE_NAMESPACE_BEGIN
static RTLIL::IdString formal_flavor(RTLIL::Cell *cell)
{
if (cell->type != ID($check))
return cell->type;
std::string flavor_param = cell->getParam(ID(FLAVOR)).decode_string();
if (flavor_param == "assert")
return ID($assert);
else if (flavor_param == "assume")
return ID($assume);
else if (flavor_param == "cover")
return ID($cover);
else if (flavor_param == "live")
return ID($live);
else if (flavor_param == "fair")
return ID($fair);
else
log_abort();
}
static void set_formal_flavor(RTLIL::Cell *cell, RTLIL::IdString flavor)
{
if (cell->type != ID($check)) {
cell->type = flavor;
return;
}
if (flavor == ID($assert))
cell->setParam(ID(FLAVOR), std::string("assert"));
else if (flavor == ID($assume))
cell->setParam(ID(FLAVOR), std::string("assume"));
else if (flavor == ID($cover))
cell->setParam(ID(FLAVOR), std::string("cover"));
else if (flavor == ID($live))
cell->setParam(ID(FLAVOR), std::string("live"));
else if (flavor == ID($fair))
cell->setParam(ID(FLAVOR), std::string("fair"));
else
log_abort();
}
static bool is_triggered_check_cell(RTLIL::Cell * cell)
{
return cell->type == ID($check) && cell->getParam(ID(TRG_ENABLE)).as_bool();
}
struct ChformalPass : public Pass { struct ChformalPass : public Pass {
ChformalPass() : Pass("chformal", "change formal constraints of the design") { } ChformalPass() : Pass("chformal", "change formal constraints of the design") { }
void help() override void help() override
@ -41,13 +87,18 @@ struct ChformalPass : public Pass {
log(" -fair $fair cells, representing assume(s_eventually ...)\n"); log(" -fair $fair cells, representing assume(s_eventually ...)\n");
log(" -cover $cover cells, representing cover() statements\n"); log(" -cover $cover cells, representing cover() statements\n");
log("\n"); log("\n");
log(" Additionally chformal will operate on $check cells corresponding to the\n");
log(" selected constraint types.\n");
log("\n");
log("Exactly one of the following modes must be specified:\n"); log("Exactly one of the following modes must be specified:\n");
log("\n"); log("\n");
log(" -remove\n"); log(" -remove\n");
log(" remove the cells and thus constraints from the design\n"); log(" remove the cells and thus constraints from the design\n");
log("\n"); log("\n");
log(" -early\n"); log(" -early\n");
log(" bypass FFs that only delay the activation of a constraint\n"); log(" bypass FFs that only delay the activation of a constraint. When inputs\n");
log(" of the bypassed FFs do not remain stable between clock edges, this may\n");
log(" result in unexpected behavior.\n");
log("\n"); log("\n");
log(" -delay <N>\n"); log(" -delay <N>\n");
log(" delay activation of the constraint by <N> clock cycles\n"); log(" delay activation of the constraint by <N> clock cycles\n");
@ -69,6 +120,11 @@ struct ChformalPass : public Pass {
log(" -fair2live\n"); log(" -fair2live\n");
log(" change the roles of cells as indicated. these options can be combined\n"); log(" change the roles of cells as indicated. these options can be combined\n");
log("\n"); log("\n");
log(" -lower\n");
log(" convert each $check cell into an $assert, $assume, $live, $fair or\n");
log(" $cover cell. If the $check cell contains a message, also produce a\n");
log(" $print cell.\n");
log("\n");
} }
void execute(std::vector<std::string> args, RTLIL::Design *design) override void execute(std::vector<std::string> args, RTLIL::Design *design) override
{ {
@ -146,6 +202,10 @@ struct ChformalPass : public Pass {
mode = 'c'; mode = 'c';
continue; continue;
} }
if (mode == 0 && args[argidx] == "-lower") {
mode = 'l';
continue;
}
break; break;
} }
extra_args(args, argidx, design); extra_args(args, argidx, design);
@ -166,7 +226,7 @@ struct ChformalPass : public Pass {
vector<Cell*> constr_cells; vector<Cell*> constr_cells;
for (auto cell : module->selected_cells()) for (auto cell : module->selected_cells())
if (constr_types.count(cell->type)) if (constr_types.count(formal_flavor(cell)))
constr_cells.push_back(cell); constr_cells.push_back(cell);
if (mode == 'r') if (mode == 'r')
@ -216,6 +276,18 @@ struct ChformalPass : public Pass {
} }
for (auto cell : constr_cells) for (auto cell : constr_cells)
{
if (is_triggered_check_cell(cell)) {
if (cell->getParam(ID::TRG_WIDTH).as_int() != 1)
continue;
cell->setPort(ID::TRG, SigSpec());
cell->setParam(ID::TRG_ENABLE, false);
cell->setParam(ID::TRG_WIDTH, 0);
cell->setParam(ID::TRG_POLARITY, false);
}
IdString flavor = formal_flavor(cell);
while (true) while (true)
{ {
SigSpec A = sigmap(cell->getPort(ID::A)); SigSpec A = sigmap(cell->getPort(ID::A));
@ -225,8 +297,8 @@ struct ChformalPass : public Pass {
break; break;
if (!init_zero.count(EN)) { if (!init_zero.count(EN)) {
if (cell->type == ID($cover)) break; if (flavor == ID($cover)) break;
if (cell->type.in(ID($assert), ID($assume)) && !init_one.count(A)) break; if (flavor.in(ID($assert), ID($assume)) && !init_one.count(A)) break;
} }
const auto &A_map = ffmap.at(A); const auto &A_map = ffmap.at(A);
@ -238,25 +310,31 @@ struct ChformalPass : public Pass {
cell->setPort(ID::A, A_map.first); cell->setPort(ID::A, A_map.first);
cell->setPort(ID::EN, EN_map.first); cell->setPort(ID::EN, EN_map.first);
} }
}
} }
else else
if (mode == 'd') if (mode == 'd')
{ {
for (auto cell : constr_cells) for (auto cell : constr_cells)
for (int i = 0; i < mode_arg; i++)
{ {
SigSpec orig_a = cell->getPort(ID::A); if (is_triggered_check_cell(cell))
SigSpec orig_en = cell->getPort(ID::EN); log_error("Cannot delay edge triggered $check cell %s, run async2sync or clk2fflogic first.\n", log_id(cell));
Wire *new_a = module->addWire(NEW_ID); for (int i = 0; i < mode_arg; i++)
Wire *new_en = module->addWire(NEW_ID); {
new_en->attributes[ID::init] = State::S0; SigSpec orig_a = cell->getPort(ID::A);
SigSpec orig_en = cell->getPort(ID::EN);
module->addFf(NEW_ID, orig_a, new_a); Wire *new_a = module->addWire(NEW_ID);
module->addFf(NEW_ID, orig_en, new_en); Wire *new_en = module->addWire(NEW_ID);
new_en->attributes[ID::init] = State::S0;
cell->setPort(ID::A, new_a); module->addFf(NEW_ID, orig_a, new_a);
cell->setPort(ID::EN, new_en); module->addFf(NEW_ID, orig_en, new_en);
cell->setPort(ID::A, new_a);
cell->setPort(ID::EN, new_en);
}
} }
} }
else else
@ -278,21 +356,76 @@ struct ChformalPass : public Pass {
if (mode =='p') if (mode =='p')
{ {
for (auto cell : constr_cells) for (auto cell : constr_cells)
module->addCover(NEW_ID_SUFFIX("coverenable"), {
cell->getPort(ID::EN), State::S1, cell->get_src_attribute()); if (cell->type == ID($check)) {
Cell *cover = module->addCell(NEW_ID_SUFFIX("coverenable"), ID($check));
cover->attributes = cell->attributes;
cover->parameters = cell->parameters;
cover->setParam(ID(FLAVOR), Const("cover"));
for (auto const &conn : cell->connections())
if (!conn.first.in(ID::A, ID::EN))
cover->setPort(conn.first, conn.second);
cover->setPort(ID::A, cell->getPort(ID::EN));
cover->setPort(ID::EN, State::S1);
} else {
module->addCover(NEW_ID_SUFFIX("coverenable"),
cell->getPort(ID::EN), State::S1, cell->get_src_attribute());
}
}
} }
else else
if (mode == 'c') if (mode == 'c')
{ {
for (auto cell : constr_cells) for (auto cell : constr_cells) {
if (assert2assume && cell->type == ID($assert)) IdString flavor = formal_flavor(cell);
cell->type = ID($assume); if (assert2assume && flavor == ID($assert))
else if (assume2assert && cell->type == ID($assume)) set_formal_flavor(cell, ID($assume));
cell->type = ID($assert); else if (assume2assert && flavor == ID($assume))
else if (live2fair && cell->type == ID($live)) set_formal_flavor(cell, ID($assert));
cell->type = ID($fair); else if (live2fair && flavor == ID($live))
else if (fair2live && cell->type == ID($fair)) set_formal_flavor(cell, ID($fair));
cell->type = ID($live); else if (fair2live && flavor == ID($fair))
set_formal_flavor(cell, ID($live));
}
}
else
if (mode == 'l')
{
for (auto cell : constr_cells) {
if (cell->type != ID($check))
continue;
if (is_triggered_check_cell(cell))
log_error("Cannot lower edge triggered $check cell %s, run async2sync or clk2fflogic first.\n", log_id(cell));
Cell *plain_cell = module->addCell(NEW_ID, formal_flavor(cell));
plain_cell->attributes = cell->attributes;
SigBit sig_a = cell->getPort(ID::A);
SigBit sig_en = cell->getPort(ID::EN);
plain_cell->setPort(ID::A, sig_a);
plain_cell->setPort(ID::EN, sig_en);
if (plain_cell->type.in(ID($assert), ID($assume)))
sig_a = module->Not(NEW_ID, sig_a);
SigBit combined_en = module->And(NEW_ID, sig_a, sig_en);
module->swap_names(cell, plain_cell);
if (cell->getPort(ID::ARGS).empty()) {
module->remove(cell);
} else {
cell->type = ID($print);
cell->setPort(ID::EN, combined_en);
cell->unsetPort(ID::A);
cell->unsetParam(ID(FLAVOR));
}
}
} }
} }
} }

View File

@ -47,7 +47,7 @@ struct ConnectPass : public Pass {
{ {
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n"); log("\n");
log(" connect [-nomap] [-nounset] -set <lhs-expr> <rhs-expr>\n"); log(" connect [-nomap] [-nounset] -set <lhs-expr> <rhs-expr> [selection]\n");
log("\n"); log("\n");
log("Create a connection. This is equivalent to adding the statement 'assign\n"); log("Create a connection. This is equivalent to adding the statement 'assign\n");
log("<lhs-expr> = <rhs-expr>;' to the Verilog input. Per default, all existing\n"); log("<lhs-expr> = <rhs-expr>;' to the Verilog input. Per default, all existing\n");
@ -55,12 +55,12 @@ struct ConnectPass : public Pass {
log("the -nounset option.\n"); log("the -nounset option.\n");
log("\n"); log("\n");
log("\n"); log("\n");
log(" connect [-nomap] -unset <expr>\n"); log(" connect [-nomap] -unset <expr> [selection]\n");
log("\n"); log("\n");
log("Unconnect all existing drivers for the specified expression.\n"); log("Unconnect all existing drivers for the specified expression.\n");
log("\n"); log("\n");
log("\n"); log("\n");
log(" connect [-nomap] [-assert] -port <cell> <port> <expr>\n"); log(" connect [-nomap] [-assert] -port <cell> <port> <expr> [selection]\n");
log("\n"); log("\n");
log("Connect the specified cell port to the specified cell port.\n"); log("Connect the specified cell port to the specified cell port.\n");
log("\n"); log("\n");
@ -80,17 +80,6 @@ struct ConnectPass : public Pass {
} }
void execute(std::vector<std::string> args, RTLIL::Design *design) override void execute(std::vector<std::string> args, RTLIL::Design *design) override
{ {
RTLIL::Module *module = nullptr;
for (auto mod : design->selected_modules()) {
if (module != nullptr)
log_cmd_error("Multiple modules selected: %s, %s\n", log_id(module->name), log_id(mod->name));
module = mod;
}
if (module == nullptr)
log_cmd_error("No modules selected.\n");
if (!module->processes.empty())
log_cmd_error("Found processes in selected module.\n");
bool flag_nounset = false, flag_nomap = false, flag_assert = false; bool flag_nounset = false, flag_nomap = false, flag_assert = false;
std::string set_lhs, set_rhs, unset_expr; std::string set_lhs, set_rhs, unset_expr;
std::string port_cell, port_port, port_expr; std::string port_cell, port_port, port_expr;
@ -128,6 +117,18 @@ struct ConnectPass : public Pass {
} }
break; break;
} }
extra_args(args, argidx, design);
RTLIL::Module *module = nullptr;
for (auto mod : design->selected_modules()) {
if (module != nullptr)
log_cmd_error("Multiple modules selected: %s, %s\n", log_id(module->name), log_id(mod->name));
module = mod;
}
if (module == nullptr)
log_cmd_error("No modules selected.\n");
if (!module->processes.empty())
log_cmd_error("Found processes in selected module.\n");
SigMap sigmap; SigMap sigmap;
if (!flag_nomap) if (!flag_nomap)

View File

@ -103,7 +103,6 @@ void load_plugin(std::string filename, std::vector<std::string> aliases)
loaded_plugins[orig_filename] = hdl; loaded_plugins[orig_filename] = hdl;
Pass::init_register(); Pass::init_register();
} }
} }

View File

@ -65,6 +65,7 @@ struct ShowWorker
bool enumerateIds; bool enumerateIds;
bool abbreviateIds; bool abbreviateIds;
bool notitle; bool notitle;
bool href;
int page_counter; int page_counter;
const std::vector<std::pair<std::string, RTLIL::Selection>> &color_selections; const std::vector<std::pair<std::string, RTLIL::Selection>> &color_selections;
@ -432,9 +433,13 @@ struct ShowWorker
if (wire->port_input || wire->port_output) if (wire->port_input || wire->port_output)
shape = "octagon"; shape = "octagon";
if (wire->name.isPublic()) { if (wire->name.isPublic()) {
fprintf(f, "n%d [ shape=%s, label=\"%s\", %s ];\n", std::string src_href;
if (href && wire->attributes.count(ID::src) > 0)
src_href = stringf(", href=\"%s\" ", escape(wire->attributes.at(ID::src).decode_string()));
fprintf(f, "n%d [ shape=%s, label=\"%s\", %s%s];\n",
id2num(wire->name), shape, findLabel(wire->name.str()), id2num(wire->name), shape, findLabel(wire->name.str()),
nextColor(RTLIL::SigSpec(wire), "color=\"black\", fontcolor=\"black\"").c_str()); nextColor(RTLIL::SigSpec(wire), "color=\"black\", fontcolor=\"black\"").c_str(),
src_href.c_str());
if (wire->port_input) if (wire->port_input)
all_sources.insert(stringf("n%d", id2num(wire->name))); all_sources.insert(stringf("n%d", id2num(wire->name)));
else if (wire->port_output) else if (wire->port_output)
@ -496,14 +501,18 @@ struct ShowWorker
conn.second, ct.cell_output(cell->type, conn.first)); conn.second, ct.cell_output(cell->type, conn.first));
} }
std::string src_href;
if (href && cell->attributes.count(ID::src) > 0) {
src_href = stringf("%shref=\"%s\" ", (findColor(cell->name).empty() ? "" :" , "), escape(cell->attributes.at(ID::src).decode_string()));
}
#ifdef CLUSTER_CELLS_AND_PORTBOXES #ifdef CLUSTER_CELLS_AND_PORTBOXES
if (!code.empty()) if (!code.empty())
fprintf(f, "subgraph cluster_c%d {\nc%d [ shape=record, label=\"%s\"%s ];\n%s}\n", fprintf(f, "subgraph cluster_c%d {\nc%d [ shape=record, label=\"%s\"%s%s ];\n%s}\n",
id2num(cell->name), id2num(cell->name), label_string.c_str(), color.c_str(), code.c_str()); id2num(cell->name), id2num(cell->name), label_string.c_str(), color.c_str(), src_href.c_str(), code.c_str());
else else
#endif #endif
fprintf(f, "c%d [ shape=record, label=\"%s\", %s ];\n%s", fprintf(f, "c%d [ shape=record, label=\"%s\", %s%s ];\n%s",
id2num(cell->name), label_string.c_str(), findColor(cell->name).c_str(), code.c_str()); id2num(cell->name), label_string.c_str(), findColor(cell->name).c_str(), src_href.c_str(), code.c_str());
} }
for (auto &it : module->processes) for (auto &it : module->processes)
@ -608,12 +617,12 @@ struct ShowWorker
} }
ShowWorker(FILE *f, RTLIL::Design *design, std::vector<RTLIL::Design*> &libs, uint32_t colorSeed, bool genWidthLabels, ShowWorker(FILE *f, RTLIL::Design *design, std::vector<RTLIL::Design*> &libs, uint32_t colorSeed, bool genWidthLabels,
bool genSignedLabels, bool stretchIO, bool enumerateIds, bool abbreviateIds, bool notitle, bool genSignedLabels, bool stretchIO, bool enumerateIds, bool abbreviateIds, bool notitle, bool href,
const std::vector<std::pair<std::string, RTLIL::Selection>> &color_selections, const std::vector<std::pair<std::string, RTLIL::Selection>> &color_selections,
const std::vector<std::pair<std::string, RTLIL::Selection>> &label_selections, RTLIL::IdString colorattr) : const std::vector<std::pair<std::string, RTLIL::Selection>> &label_selections, RTLIL::IdString colorattr) :
f(f), design(design), currentColor(colorSeed), genWidthLabels(genWidthLabels), f(f), design(design), currentColor(colorSeed), genWidthLabels(genWidthLabels),
genSignedLabels(genSignedLabels), stretchIO(stretchIO), enumerateIds(enumerateIds), abbreviateIds(abbreviateIds), genSignedLabels(genSignedLabels), stretchIO(stretchIO), enumerateIds(enumerateIds), abbreviateIds(abbreviateIds),
notitle(notitle), color_selections(color_selections), label_selections(label_selections), colorattr(colorattr) notitle(notitle), href(href), color_selections(color_selections), label_selections(label_selections), colorattr(colorattr)
{ {
ct.setup_internals(); ct.setup_internals();
ct.setup_internals_mem(); ct.setup_internals_mem();
@ -726,6 +735,10 @@ struct ShowPass : public Pass {
log(" don't run viewer in the background, IE wait for the viewer tool to\n"); log(" don't run viewer in the background, IE wait for the viewer tool to\n");
log(" exit before returning\n"); log(" exit before returning\n");
log("\n"); log("\n");
log(" -href\n");
log(" adds href attribute to all items representing cells and wires, using\n");
log(" src attribute of origin\n");
log("\n");
log("When no <format> is specified, 'dot' is used. When no <format> and <viewer> is\n"); log("When no <format> is specified, 'dot' is used. When no <format> and <viewer> is\n");
log("specified, 'xdot' is used to display the schematic (POSIX systems only).\n"); log("specified, 'xdot' is used to display the schematic (POSIX systems only).\n");
log("\n"); log("\n");
@ -763,6 +776,7 @@ struct ShowPass : public Pass {
bool flag_enum = false; bool flag_enum = false;
bool flag_abbreviate = true; bool flag_abbreviate = true;
bool flag_notitle = false; bool flag_notitle = false;
bool flag_href = false;
bool custom_prefix = false; bool custom_prefix = false;
std::string background = "&"; std::string background = "&";
RTLIL::IdString colorattr; RTLIL::IdString colorattr;
@ -850,6 +864,10 @@ struct ShowPass : public Pass {
background= ""; background= "";
continue; continue;
} }
if (arg == "-href") {
flag_href = true;
continue;
}
break; break;
} }
extra_args(args, argidx, design); extra_args(args, argidx, design);
@ -894,7 +912,7 @@ struct ShowPass : public Pass {
delete lib; delete lib;
log_cmd_error("Can't open dot file `%s' for writing.\n", dot_file.c_str()); log_cmd_error("Can't open dot file `%s' for writing.\n", dot_file.c_str());
} }
ShowWorker worker(f, design, libs, colorSeed, flag_width, flag_signed, flag_stretch, flag_enum, flag_abbreviate, flag_notitle, color_selections, label_selections, colorattr); ShowWorker worker(f, design, libs, colorSeed, flag_width, flag_signed, flag_stretch, flag_enum, flag_abbreviate, flag_notitle, flag_href, color_selections, label_selections, colorattr);
fclose(f); fclose(f);
for (auto lib : libs) for (auto lib : libs)

View File

@ -28,6 +28,11 @@
USING_YOSYS_NAMESPACE USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN PRIVATE_NAMESPACE_BEGIN
struct cell_area_t {
double area;
bool is_sequential;
};
struct statdata_t struct statdata_t
{ {
#define STAT_INT_MEMBERS X(num_wires) X(num_wire_bits) X(num_pub_wires) X(num_pub_wire_bits) \ #define STAT_INT_MEMBERS X(num_wires) X(num_wire_bits) X(num_pub_wires) X(num_pub_wire_bits) \
@ -39,6 +44,7 @@ struct statdata_t
STAT_INT_MEMBERS STAT_INT_MEMBERS
#undef X #undef X
double area; double area;
double sequential_area;
string tech; string tech;
std::map<RTLIL::IdString, int> techinfo; std::map<RTLIL::IdString, int> techinfo;
@ -74,7 +80,7 @@ struct statdata_t
#undef X #undef X
} }
statdata_t(RTLIL::Design *design, RTLIL::Module *mod, bool width_mode, const dict<IdString, double> &cell_area, string techname) statdata_t(RTLIL::Design *design, RTLIL::Module *mod, bool width_mode, const dict<IdString, cell_area_t> &cell_area, string techname)
{ {
tech = techname; tech = techname;
@ -132,10 +138,16 @@ struct statdata_t
} }
if (!cell_area.empty()) { if (!cell_area.empty()) {
if (cell_area.count(cell_type)) if (cell_area.count(cell_type)) {
area += cell_area.at(cell_type); cell_area_t cell_data = cell_area.at(cell_type);
else if (cell_data.is_sequential) {
sequential_area += cell_data.area;
}
area += cell_data.area;
}
else {
unknown_cell_area.insert(cell_type); unknown_cell_area.insert(cell_type);
}
} }
num_cells++; num_cells++;
@ -244,6 +256,7 @@ struct statdata_t
if (area != 0) { if (area != 0) {
log("\n"); log("\n");
log(" Chip area for %smodule '%s': %f\n", (top_mod) ? "top " : "", mod_name.c_str(), area); log(" Chip area for %smodule '%s': %f\n", (top_mod) ? "top " : "", mod_name.c_str(), area);
log(" of which used for sequential elements: %f (%.2f%%)\n", sequential_area, 100.0*sequential_area/area);
} }
if (tech == "xilinx") if (tech == "xilinx")
@ -325,7 +338,7 @@ statdata_t hierarchy_worker(std::map<RTLIL::IdString, statdata_t> &mod_stat, RTL
return mod_data; return mod_data;
} }
void read_liberty_cellarea(dict<IdString, double> &cell_area, string liberty_file) void read_liberty_cellarea(dict<IdString, cell_area_t> &cell_area, string liberty_file)
{ {
std::ifstream f; std::ifstream f;
f.open(liberty_file.c_str()); f.open(liberty_file.c_str());
@ -341,8 +354,9 @@ void read_liberty_cellarea(dict<IdString, double> &cell_area, string liberty_fil
continue; continue;
LibertyAst *ar = cell->find("area"); LibertyAst *ar = cell->find("area");
bool is_flip_flop = cell->find("ff") != nullptr;
if (ar != nullptr && !ar->value.empty()) if (ar != nullptr && !ar->value.empty())
cell_area["\\" + cell->args[0]] = atof(ar->value.c_str()); cell_area["\\" + cell->args[0]] = {/*area=*/atof(ar->value.c_str()), is_flip_flop};
} }
} }
@ -383,7 +397,7 @@ struct StatPass : public Pass {
bool width_mode = false, json_mode = false; bool width_mode = false, json_mode = false;
RTLIL::Module *top_mod = nullptr; RTLIL::Module *top_mod = nullptr;
std::map<RTLIL::IdString, statdata_t> mod_stat; std::map<RTLIL::IdString, statdata_t> mod_stat;
dict<IdString, double> cell_area; dict<IdString, cell_area_t> cell_area;
string techname; string techname;
size_t argidx; size_t argidx;

View File

@ -339,6 +339,8 @@ struct EquivSimplePass : public Pass {
CellTypes ct; CellTypes ct;
ct.setup_internals(); ct.setup_internals();
ct.setup_stdcells(); ct.setup_stdcells();
ct.setup_internals_ff();
ct.setup_stdcells_mem();
for (auto module : design->selected_modules()) for (auto module : design->selected_modules())
{ {

View File

@ -671,7 +671,7 @@ bool set_keep_assert(std::map<RTLIL::Module*, bool> &cache, RTLIL::Module *mod)
if (cache.count(mod) == 0) if (cache.count(mod) == 0)
for (auto c : mod->cells()) { for (auto c : mod->cells()) {
RTLIL::Module *m = mod->design->module(c->type); RTLIL::Module *m = mod->design->module(c->type);
if ((m != nullptr && set_keep_assert(cache, m)) || c->type.in(ID($assert), ID($assume), ID($live), ID($fair), ID($cover))) if ((m != nullptr && set_keep_assert(cache, m)) || c->type.in(ID($check), ID($assert), ID($assume), ID($live), ID($fair), ID($cover)))
return cache[mod] = true; return cache[mod] = true;
} }
return cache[mod]; return cache[mod];
@ -1006,6 +1006,18 @@ struct HierarchyPass : public Pass {
if (mod->get_bool_attribute(ID::top)) if (mod->get_bool_attribute(ID::top))
top_mod = mod; top_mod = mod;
if (top_mod == nullptr)
{
std::vector<IdString> abstract_ids;
for (auto module : design->modules())
if (module->name.begins_with("$abstract"))
abstract_ids.push_back(module->name);
for (auto abstract_id : abstract_ids)
design->module(abstract_id)->derive(design, {});
for (auto abstract_id : abstract_ids)
design->remove(design->module(abstract_id));
}
if (top_mod == nullptr && auto_top_mode) { if (top_mod == nullptr && auto_top_mode) {
log_header(design, "Finding top of design hierarchy..\n"); log_header(design, "Finding top of design hierarchy..\n");
dict<Module*, int> db; dict<Module*, int> db;

View File

@ -39,6 +39,9 @@ struct MemoryCollectPass : public Pass {
log_header(design, "Executing MEMORY_COLLECT pass (generating $mem cells).\n"); log_header(design, "Executing MEMORY_COLLECT pass (generating $mem cells).\n");
extra_args(args, 1, design); extra_args(args, 1, design);
for (auto module : design->selected_modules()) { for (auto module : design->selected_modules()) {
if (module->has_processes_warn())
continue;
for (auto &mem : Mem::get_selected_memories(module)) { for (auto &mem : Mem::get_selected_memories(module)) {
if (!mem.packed) { if (!mem.packed) {
mem.packed = true; mem.packed = true;

View File

@ -2229,6 +2229,9 @@ struct MemoryLibMapPass : public Pass {
Library lib = parse_library(lib_files, defines); Library lib = parse_library(lib_files, defines);
for (auto module : design->selected_modules()) { for (auto module : design->selected_modules()) {
if (module->has_processes_warn())
continue;
MapWorker worker(module); MapWorker worker(module);
auto mems = Mem::get_selected_memories(module); auto mems = Mem::get_selected_memories(module);
for (auto &mem : mems) for (auto &mem : mems)

View File

@ -493,6 +493,9 @@ struct MemoryMapPass : public Pass {
extra_args(args, argidx, design); extra_args(args, argidx, design);
for (auto mod : design->selected_modules()) { for (auto mod : design->selected_modules()) {
if (mod->has_processes_warn())
continue;
MemoryMapWorker worker(design, mod); MemoryMapWorker worker(design, mod);
worker.attr_icase = attr_icase; worker.attr_icase = attr_icase;
worker.attributes = attributes; worker.attributes = attributes;

View File

@ -50,7 +50,7 @@ struct MemoryMemxPass : public Pass {
} }
void execute(std::vector<std::string> args, RTLIL::Design *design) override { void execute(std::vector<std::string> args, RTLIL::Design *design) override {
log_header(design, "Executing MEMORY_MEMX pass (converting $mem cells to logic and flip-flops).\n"); log_header(design, "Executing MEMORY_MEMX pass (emit soft logic for out-of-bounds handling).\n");
extra_args(args, 1, design); extra_args(args, 1, design);
for (auto module : design->selected_modules()) for (auto module : design->selected_modules())

View File

@ -46,6 +46,9 @@ struct MemoryNarrowPass : public Pass {
extra_args(args, argidx, design); extra_args(args, argidx, design);
for (auto module : design->selected_modules()) { for (auto module : design->selected_modules()) {
if (module->has_processes_warn())
continue;
for (auto &mem : Mem::get_selected_memories(module)) for (auto &mem : Mem::get_selected_memories(module))
{ {
bool wide = false; bool wide = false;

View File

@ -558,8 +558,12 @@ struct MemorySharePass : public Pass {
extra_args(args, argidx, design); extra_args(args, argidx, design);
MemoryShareWorker msw(design, flag_widen, flag_sat); MemoryShareWorker msw(design, flag_widen, flag_sat);
for (auto module : design->selected_modules()) for (auto module : design->selected_modules()) {
if (module->has_processes_warn())
continue;
msw(module); msw(module);
}
} }
} MemorySharePass; } MemorySharePass;

View File

@ -35,10 +35,12 @@ struct keep_cache_t
{ {
Design *design; Design *design;
dict<Module*, bool> cache; dict<Module*, bool> cache;
bool purge_mode = false;
void reset(Design *design = nullptr) void reset(Design *design = nullptr, bool purge_mode = false)
{ {
this->design = design; this->design = design;
this->purge_mode = purge_mode;
cache.clear(); cache.clear();
} }
@ -82,12 +84,15 @@ struct keep_cache_t
if (!ignore_specify && cell->type.in(ID($specify2), ID($specify3), ID($specrule))) if (!ignore_specify && cell->type.in(ID($specify2), ID($specify3), ID($specrule)))
return true; return true;
if (cell->type == ID($print)) if (cell->type == ID($print) || cell->type == ID($check))
return true; return true;
if (cell->has_keep_attr()) if (cell->has_keep_attr())
return true; return true;
if (!purge_mode && cell->type == ID($scopeinfo))
return true;
if (cell->module && cell->module->design) if (cell->module && cell->module->design)
return query(cell->module->design->module(cell->type)); return query(cell->module->design->module(cell->type));
@ -236,10 +241,13 @@ int count_nontrivial_wire_attrs(RTLIL::Wire *w)
{ {
int count = w->attributes.size(); int count = w->attributes.size();
count -= w->attributes.count(ID::src); count -= w->attributes.count(ID::src);
count -= w->attributes.count(ID::hdlname);
count -= w->attributes.count(ID(scopename));
count -= w->attributes.count(ID::unused_bits); count -= w->attributes.count(ID::unused_bits);
return count; return count;
} }
// Should we pick `s2` over `s1` to represent a signal?
bool compare_signals(RTLIL::SigBit &s1, RTLIL::SigBit &s2, SigPool &regs, SigPool &conns, pool<RTLIL::Wire*> &direct_wires) bool compare_signals(RTLIL::SigBit &s1, RTLIL::SigBit &s2, SigPool &regs, SigPool &conns, pool<RTLIL::Wire*> &direct_wires)
{ {
RTLIL::Wire *w1 = s1.wire; RTLIL::Wire *w1 = s1.wire;
@ -292,9 +300,10 @@ bool check_public_name(RTLIL::IdString id)
bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbose) bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbose)
{ {
// `register_signals` and `connected_signals` will help us decide later on
// on picking representatives out of groups of connected signals
SigPool register_signals; SigPool register_signals;
SigPool connected_signals; SigPool connected_signals;
if (!purge_mode) if (!purge_mode)
for (auto &it : module->cells_) { for (auto &it : module->cells_) {
RTLIL::Cell *cell = it.second; RTLIL::Cell *cell = it.second;
@ -309,20 +318,27 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
} }
SigMap assign_map(module); SigMap assign_map(module);
pool<RTLIL::SigSpec> direct_sigs;
// construct a pool of wires which are directly driven by a known celltype,
// this will influence our choice of representatives
pool<RTLIL::Wire*> direct_wires; pool<RTLIL::Wire*> direct_wires;
for (auto &it : module->cells_) { {
RTLIL::Cell *cell = it.second; pool<RTLIL::SigSpec> direct_sigs;
if (ct_all.cell_known(cell->type)) for (auto &it : module->cells_) {
for (auto &it2 : cell->connections()) RTLIL::Cell *cell = it.second;
if (ct_all.cell_output(cell->type, it2.first)) if (ct_all.cell_known(cell->type))
direct_sigs.insert(assign_map(it2.second)); for (auto &it2 : cell->connections())
} if (ct_all.cell_output(cell->type, it2.first))
for (auto &it : module->wires_) { direct_sigs.insert(assign_map(it2.second));
if (direct_sigs.count(assign_map(it.second)) || it.second->port_input) }
direct_wires.insert(it.second); for (auto &it : module->wires_) {
if (direct_sigs.count(assign_map(it.second)) || it.second->port_input)
direct_wires.insert(it.second);
}
} }
// weight all options for representatives with `compare_signals`,
// the one that wins will be what `assign_map` maps to
for (auto &it : module->wires_) { for (auto &it : module->wires_) {
RTLIL::Wire *wire = it.second; RTLIL::Wire *wire = it.second;
for (int i = 0; i < wire->width; i++) { for (int i = 0; i < wire->width; i++) {
@ -332,21 +348,30 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
} }
} }
// we are removing all connections
module->connections_.clear(); module->connections_.clear();
// used signals sigmapped
SigPool used_signals; SigPool used_signals;
// used signals pre-sigmapped
SigPool raw_used_signals; SigPool raw_used_signals;
// used signals sigmapped, ignoring drivers (we keep track of this to set `unused_bits`)
SigPool used_signals_nodrivers; SigPool used_signals_nodrivers;
// gather the usage information for cells
for (auto &it : module->cells_) { for (auto &it : module->cells_) {
RTLIL::Cell *cell = it.second; RTLIL::Cell *cell = it.second;
for (auto &it2 : cell->connections_) { for (auto &it2 : cell->connections_) {
assign_map.apply(it2.second); assign_map.apply(it2.second); // modify the cell connection in place
raw_used_signals.add(it2.second); raw_used_signals.add(it2.second);
used_signals.add(it2.second); used_signals.add(it2.second);
if (!ct_all.cell_output(cell->type, it2.first)) if (!ct_all.cell_output(cell->type, it2.first))
used_signals_nodrivers.add(it2.second); used_signals_nodrivers.add(it2.second);
} }
} }
// gather the usage information for ports, wires with `keep`,
// also gather init bits
dict<RTLIL::SigBit, RTLIL::State> init_bits; dict<RTLIL::SigBit, RTLIL::State> init_bits;
for (auto &it : module->wires_) { for (auto &it : module->wires_) {
RTLIL::Wire *wire = it.second; RTLIL::Wire *wire = it.second;
@ -374,6 +399,7 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
} }
} }
// set init attributes on all wires of a connected group
for (auto wire : module->wires()) { for (auto wire : module->wires()) {
bool found = false; bool found = false;
Const val(State::Sx, wire->width); Const val(State::Sx, wire->width);
@ -388,6 +414,7 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
wire->attributes[ID::init] = val; wire->attributes[ID::init] = val;
} }
// now decide for each wire if we should be deleting it
pool<RTLIL::Wire*> del_wires_queue; pool<RTLIL::Wire*> del_wires_queue;
for (auto wire : module->wires()) for (auto wire : module->wires())
{ {
@ -418,6 +445,9 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
goto delete_this_wire; goto delete_this_wire;
} else } else
if (!used_signals.check_any(s2)) { if (!used_signals.check_any(s2)) {
// this path shouldn't be possible: this wire is used directly (otherwise it would get cleaned up above), and indirectly
// used wires are a superset of those used directly
log_assert(false);
// delete wires that aren't used by anything indirectly, even though other wires may alias it // delete wires that aren't used by anything indirectly, even though other wires may alias it
goto delete_this_wire; goto delete_this_wire;
} }
@ -638,7 +668,7 @@ struct OptCleanPass : public Pass {
} }
extra_args(args, argidx, design); extra_args(args, argidx, design);
keep_cache.reset(design); keep_cache.reset(design, purge_mode);
ct_reg.setup_internals_mem(); ct_reg.setup_internals_mem();
ct_reg.setup_internals_anyinit(); ct_reg.setup_internals_anyinit();

View File

@ -353,7 +353,7 @@ struct OptDffWorker
// Try a more complex conversion to plain async reset. // Try a more complex conversion to plain async reset.
State val_neutral = ff.pol_set ? State::S0 : State::S1; State val_neutral = ff.pol_set ? State::S0 : State::S1;
Const val_arst; Const val_arst;
SigSpec sig_arst; SigBit sig_arst;
if (ff.sig_clr[0] == val_neutral) if (ff.sig_clr[0] == val_neutral)
sig_arst = ff.sig_set[0]; sig_arst = ff.sig_set[0];
else else

View File

@ -38,6 +38,7 @@ struct OptFfInvWorker
// - ... which has no other users // - ... which has no other users
// - all users of FF are LUTs // - all users of FF are LUTs
bool push_d_inv(FfData &ff) { bool push_d_inv(FfData &ff) {
log_assert(ff.width == 1);
if (index.query_is_input(ff.sig_d)) if (index.query_is_input(ff.sig_d))
return false; return false;
if (index.query_is_output(ff.sig_d)) if (index.query_is_output(ff.sig_d))
@ -90,7 +91,7 @@ struct OptFfInvWorker
int flip_mask = 0; int flip_mask = 0;
SigSpec sig_a = lut->getPort(ID::A); SigSpec sig_a = lut->getPort(ID::A);
for (int i = 0; i < GetSize(sig_a); i++) { for (int i = 0; i < GetSize(sig_a); i++) {
if (index.sigmap(sig_a[i]) == index.sigmap(ff.sig_q)) { if (index.sigmap(sig_a[i]) == index.sigmap(ff.sig_q[0])) {
flip_mask |= 1 << i; flip_mask |= 1 << i;
} }
} }

View File

@ -167,7 +167,11 @@ struct OptLutWorker
legal = false; legal = false;
break; break;
} }
if (sigmap(lut_input[dlogic_conn.first]) != sigmap(lut_dlogic.second->getPort(dlogic_conn.second)))
if (lut_dlogic.second->getPort(dlogic_conn.second).size() != 1)
continue;
if (sigmap(lut_input[dlogic_conn.first]) != sigmap(lut_dlogic.second->getPort(dlogic_conn.second)[0]))
{ {
log_debug(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic.second->type.c_str(), log_id(module), log_id(lut_dlogic.second)); log_debug(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic.second->type.c_str(), log_id(module), log_id(lut_dlogic.second));
log_debug(" LUT input A[%d] (wire %s) not connected to %s port %s (wire %s).\n", dlogic_conn.first, log_signal(lut_input[dlogic_conn.first]), lut_dlogic.second->type.c_str(), dlogic_conn.second.c_str(), log_signal(lut_dlogic.second->getPort(dlogic_conn.second))); log_debug(" LUT input A[%d] (wire %s) not connected to %s port %s (wire %s).\n", dlogic_conn.first, log_signal(lut_input[dlogic_conn.first]), lut_dlogic.second->type.c_str(), dlogic_conn.second.c_str(), log_signal(lut_dlogic.second->getPort(dlogic_conn.second)));
@ -314,7 +318,7 @@ struct OptLutWorker
auto lutA = worklist.pop(); auto lutA = worklist.pop();
SigSpec lutA_input = sigmap(lutA->getPort(ID::A)); SigSpec lutA_input = sigmap(lutA->getPort(ID::A));
SigSpec lutA_output = sigmap(lutA->getPort(ID::Y)[0]); SigBit lutA_output = sigmap(lutA->getPort(ID::Y)[0]);
int lutA_width = lutA->getParam(ID::WIDTH).as_int(); int lutA_width = lutA->getParam(ID::WIDTH).as_int();
int lutA_arity = luts_arity[lutA]; int lutA_arity = luts_arity[lutA];
pool<int> &lutA_dlogic_inputs = luts_dlogic_inputs[lutA]; pool<int> &lutA_dlogic_inputs = luts_dlogic_inputs[lutA];
@ -529,12 +533,6 @@ struct OptLutPass : public Pass {
log("\n"); log("\n");
log("This pass combines cascaded $lut cells with unused inputs.\n"); log("This pass combines cascaded $lut cells with unused inputs.\n");
log("\n"); log("\n");
log(" -dlogic <type>:<cell-port>=<LUT-input>[:<cell-port>=<LUT-input>...]\n");
log(" preserve connections to dedicated logic cell <type> that has ports\n");
log(" <cell-port> connected to LUT inputs <LUT-input>. this includes\n");
log(" the case where both LUT and dedicated logic input are connected to\n");
log(" the same constant.\n");
log("\n");
log(" -tech ice40\n"); log(" -tech ice40\n");
log(" treat the design as a LUT-mapped circuit for the iCE40 architecture\n"); log(" treat the design as a LUT-mapped circuit for the iCE40 architecture\n");
log(" and preserve connections to SB_CARRY as appropriate\n"); log(" and preserve connections to SB_CARRY as appropriate\n");

View File

@ -52,6 +52,9 @@ struct OptMemPass : public Pass {
int total_count = 0; int total_count = 0;
for (auto module : design->selected_modules()) { for (auto module : design->selected_modules()) {
if (module->has_processes_warn())
continue;
SigMap sigmap(module); SigMap sigmap(module);
FfInitVals initvals(&sigmap, module); FfInitVals initvals(&sigmap, module);
for (auto &mem : Mem::get_selected_memories(module)) { for (auto &mem : Mem::get_selected_memories(module)) {

View File

@ -272,6 +272,9 @@ struct OptMergeWorker
if ((!mode_share_all && !ct.cell_known(cell->type)) || !cell->known()) if ((!mode_share_all && !ct.cell_known(cell->type)) || !cell->known())
continue; continue;
if (cell->type == ID($scopeinfo))
continue;
uint64_t hash = hash_cell_parameters_and_connections(cell); uint64_t hash = hash_cell_parameters_and_connections(cell);
auto r = sharemap.insert(std::make_pair(hash, cell)); auto r = sharemap.insert(std::make_pair(hash, cell));
if (!r.second) { if (!r.second) {

View File

@ -346,7 +346,7 @@ endmatch
code argQ argD code argQ argD
{ {
if (clock != SigBit()) { if (clock != SigBit()) {
if (port(ff, \CLK) != clock) if (port(ff, \CLK)[0] != clock)
reject; reject;
if (param(ff, \CLK_POLARITY).as_bool() != clock_pol) if (param(ff, \CLK_POLARITY).as_bool() != clock_pol)
reject; reject;
@ -393,7 +393,7 @@ endmatch
code argQ code argQ
if (ff) { if (ff) {
if (clock != SigBit()) { if (clock != SigBit()) {
if (port(ff, \CLK) != clock) if (port(ff, \CLK)[0] != clock)
reject; reject;
if (param(ff, \CLK_POLARITY).as_bool() != clock_pol) if (param(ff, \CLK_POLARITY).as_bool() != clock_pol)
reject; reject;

View File

@ -103,8 +103,20 @@ code
new_a.append(old_a); new_a.append(old_a);
} else { } else {
// data >> (...+c) transformed to data[MAX:c] >> (...) // data >> (...+c) transformed to data[MAX:c] >> (...)
new_a.append(old_a.extract_end(offset)); if(offset < GetSize(old_a)) { // some signal bits left?
new_a.append(old_a.extract_end(offset));
} else {
// warn user in case data is empty (no bits left)
std::string location = shift->get_src_attribute();
if (location.empty())
location = shift->name.str();
if(shift->type.in($shiftx))
log_warning("at %s: result of indexed part-selection is always constant (selecting from '%s' with index '%s + %d')\n", \
location.c_str(), log_signal(old_a), log_signal(var_signal), offset);
else
log_warning("at %s: result of shift operation is always constant (shifting '%s' by '%s + %d'-bits)\n", \
location.c_str(), log_signal(old_a), log_signal(var_signal), offset);
}
} }
SigSpec new_b = {var_signal, SigSpec(State::S0, log2scale)}; SigSpec new_b = {var_signal, SigSpec(State::S0, log2scale)};

View File

@ -415,7 +415,7 @@ match ff
filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ) filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
filter clock == SigBit() || port(ff, \CLK) == clock filter clock == SigBit() || port(ff, \CLK)[0] == clock
endmatch endmatch
code argQ code argQ
@ -465,7 +465,7 @@ match ff
filter GetSize(port(ff, \D)) >= offset + GetSize(argD) filter GetSize(port(ff, \D)) >= offset + GetSize(argD)
filter port(ff, \D).extract(offset, GetSize(argD)) == argD filter port(ff, \D).extract(offset, GetSize(argD)) == argD
filter clock == SigBit() || port(ff, \CLK) == clock filter clock == SigBit() || port(ff, \CLK)[0] == clock
endmatch endmatch
code argQ code argQ

View File

@ -354,7 +354,7 @@ match ff
filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ) filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
filter clock == SigBit() || port(ff, \CLK) == clock filter clock == SigBit() || port(ff, \CLK)[0] == clock
endmatch endmatch
code argQ code argQ
@ -404,7 +404,7 @@ match ff
filter GetSize(port(ff, \D)) >= offset + GetSize(argD) filter GetSize(port(ff, \D)) >= offset + GetSize(argD)
filter port(ff, \D).extract(offset, GetSize(argD)) == argD filter port(ff, \D).extract(offset, GetSize(argD)) == argD
filter clock == SigBit() || port(ff, \CLK) == clock filter clock == SigBit() || port(ff, \CLK)[0] == clock
endmatch endmatch
code argQ code argQ

View File

@ -135,7 +135,7 @@ match ff
filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ) filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
filter clock == SigBit() || port(ff, \CLK) == clock filter clock == SigBit() || port(ff, \CLK)[0] == clock
endmatch endmatch
code argQ code argQ

View File

@ -46,7 +46,7 @@ pattern xilinx_dsp_cascade
udata <std::function<SigSpec(const SigSpec&)>> unextend udata <std::function<SigSpec(const SigSpec&)>> unextend
udata <vector<std::tuple<Cell*,int,int,int>>> chain longest_chain udata <vector<std::tuple<Cell*,int,int,int>>> chain longest_chain
state <Cell*> next state <Cell*> next
state <SigSpec> clock state <SigBit> clock
state <int> AREG BREG state <int> AREG BREG
// Variables used for subpatterns // Variables used for subpatterns
@ -395,7 +395,7 @@ match ff
filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ) filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
filter clock == SigBit() || port(ff, \CLK) == clock filter clock == SigBit() || port(ff, \CLK)[0] == clock
endmatch endmatch
code argQ code argQ

View File

@ -46,7 +46,7 @@ struct proc_dlatch_db_t
for (auto cell : module->cells()) for (auto cell : module->cells())
{ {
if (cell->type.in(ID($mux), ID($pmux))) if (cell->type.in(ID($mux), ID($pmux), ID($bwmux)))
{ {
auto sig_y = sigmap(cell->getPort(ID::Y)); auto sig_y = sigmap(cell->getPort(ID::Y));
for (int i = 0; i < GetSize(sig_y); i++) for (int i = 0; i < GetSize(sig_y); i++)
@ -186,6 +186,8 @@ struct proc_dlatch_db_t
Cell *cell = it->second.first; Cell *cell = it->second.first;
int index = it->second.second; int index = it->second.second;
log_assert(cell->type.in(ID($mux), ID($pmux), ID($bwmux)));
bool is_bwmux = (cell->type == ID($bwmux));
SigSpec sig_a = sigmap(cell->getPort(ID::A)); SigSpec sig_a = sigmap(cell->getPort(ID::A));
SigSpec sig_b = sigmap(cell->getPort(ID::B)); SigSpec sig_b = sigmap(cell->getPort(ID::B));
SigSpec sig_s = sigmap(cell->getPort(ID::S)); SigSpec sig_s = sigmap(cell->getPort(ID::S));
@ -200,12 +202,16 @@ struct proc_dlatch_db_t
sig[index] = State::Sx; sig[index] = State::Sx;
cell->setPort(ID::A, sig); cell->setPort(ID::A, sig);
} }
for (int i = 0; i < GetSize(sig_s); i++) if (!is_bwmux) {
n = make_inner(sig_s[i], State::S0, n); for (int i = 0; i < GetSize(sig_s); i++)
n = make_inner(sig_s[i], State::S0, n);
} else {
n = make_inner(sig_s[index], State::S0, n);
}
children.insert(n); children.insert(n);
} }
for (int i = 0; i < GetSize(sig_s); i++) { for (int i = 0; i < (is_bwmux ? 1 : GetSize(sig_s)); i++) {
n = find_mux_feedback(sig_b[i*width + index], needle, set_undef); n = find_mux_feedback(sig_b[i*width + index], needle, set_undef);
if (n != false_node) { if (n != false_node) {
if (set_undef && sig_b[i*width + index] == needle) { if (set_undef && sig_b[i*width + index] == needle) {
@ -213,7 +219,7 @@ struct proc_dlatch_db_t
sig[i*width + index] = State::Sx; sig[i*width + index] = State::Sx;
cell->setPort(ID::B, sig); cell->setPort(ID::B, sig);
} }
children.insert(make_inner(sig_s[i], State::S1, n)); children.insert(make_inner(sig_s[is_bwmux ? index : i], State::S1, n));
} }
} }

View File

@ -66,6 +66,11 @@ struct RomWorker
} }
} }
if (lhs.empty()) {
log_debug("rejecting switch: lhs empty\n");
return;
}
int swsigbits = 0; int swsigbits = 0;
for (int i = 0; i < GetSize(sw->signal); i++) for (int i = 0; i < GetSize(sw->signal); i++)
if (sw->signal[i] != State::S0) if (sw->signal[i] != State::S0)

View File

@ -41,31 +41,88 @@ struct Async2syncPass : public Pass {
log("reset value in the next cycle regardless of the data-in value at the time of\n"); log("reset value in the next cycle regardless of the data-in value at the time of\n");
log("the clock edge.\n"); log("the clock edge.\n");
log("\n"); log("\n");
log(" -nolower\n");
log(" Do not automatically run 'chformal -lower' to lower $check cells.\n");
log("\n");
} }
void execute(std::vector<std::string> args, RTLIL::Design *design) override void execute(std::vector<std::string> args, RTLIL::Design *design) override
{ {
// bool flag_noinit = false; bool flag_nolower = false;
log_header(design, "Executing ASYNC2SYNC pass.\n"); log_header(design, "Executing ASYNC2SYNC pass.\n");
size_t argidx; size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) for (argidx = 1; argidx < args.size(); argidx++)
{ {
// if (args[argidx] == "-noinit") { if (args[argidx] == "-nolower") {
// flag_noinit = true; flag_nolower = true;
// continue; continue;
// } }
break; break;
} }
extra_args(args, argidx, design); extra_args(args, argidx, design);
bool have_check_cells = false;
for (auto module : design->selected_modules()) for (auto module : design->selected_modules())
{ {
SigMap sigmap(module); SigMap sigmap(module);
FfInitVals initvals(&sigmap, module); FfInitVals initvals(&sigmap, module);
SigBit initstate;
for (auto cell : vector<Cell*>(module->selected_cells())) for (auto cell : vector<Cell*>(module->selected_cells()))
{ {
if (cell->type.in(ID($print), ID($check)))
{
if (cell->type == ID($check))
have_check_cells = true;
bool trg_enable = cell->getParam(ID(TRG_ENABLE)).as_bool();
if (!trg_enable)
continue;
int trg_width = cell->getParam(ID(TRG_WIDTH)).as_int();
if (trg_width > 1)
log_error("$check cell %s with TRG_WIDTH > 1 is not support by async2sync, use clk2fflogic.\n", log_id(cell));
if (trg_width == 0) {
if (initstate == State::S0)
initstate = module->Initstate(NEW_ID);
SigBit sig_en = cell->getPort(ID::EN);
cell->setPort(ID::EN, module->And(NEW_ID, sig_en, initstate));
} else {
SigBit sig_en = cell->getPort(ID::EN);
SigSpec sig_args = cell->getPort(ID::ARGS);
bool trg_polarity = cell->getParam(ID(TRG_POLARITY)).as_bool();
SigBit sig_trg = cell->getPort(ID::TRG);
Wire *sig_en_q = module->addWire(NEW_ID);
Wire *sig_args_q = module->addWire(NEW_ID, GetSize(sig_args));
sig_en_q->attributes.emplace(ID::init, State::S0);
module->addDff(NEW_ID, sig_trg, sig_en, sig_en_q, trg_polarity, cell->get_src_attribute());
module->addDff(NEW_ID, sig_trg, sig_args, sig_args_q, trg_polarity, cell->get_src_attribute());
cell->setPort(ID::EN, sig_en_q);
cell->setPort(ID::ARGS, sig_args_q);
if (cell->type == ID($check)) {
SigBit sig_a = cell->getPort(ID::A);
Wire *sig_a_q = module->addWire(NEW_ID);
sig_a_q->attributes.emplace(ID::init, State::S1);
module->addDff(NEW_ID, sig_trg, sig_a, sig_a_q, trg_polarity, cell->get_src_attribute());
cell->setPort(ID::A, sig_a_q);
}
}
cell->setPort(ID::TRG, SigSpec());
cell->setParam(ID::TRG_ENABLE, false);
cell->setParam(ID::TRG_WIDTH, 0);
cell->setParam(ID::TRG_POLARITY, false);
cell->set_bool_attribute(ID(trg_on_gclk));
continue;
}
if (!RTLIL::builtin_ff_cell_types().count(cell->type)) if (!RTLIL::builtin_ff_cell_types().count(cell->type))
continue; continue;
@ -273,6 +330,12 @@ struct Async2syncPass : public Pass {
ff.emit(); ff.emit();
} }
} }
if (have_check_cells && !flag_nolower) {
log_push();
Pass::call(design, "chformal -lower");
log_pop();
}
} }
} Async2syncPass; } Async2syncPass;

View File

@ -48,6 +48,9 @@ struct Clk2fflogicPass : public Pass {
log("reset value in the next cycle regardless of the data-in value at the time of\n"); log("reset value in the next cycle regardless of the data-in value at the time of\n");
log("the clock edge.\n"); log("the clock edge.\n");
log("\n"); log("\n");
log(" -nolower\n");
log(" Do not automatically run 'chformal -lower' to lower $check cells.\n");
log("\n");
} }
// Active-high sampled and current value of a level-triggered control signal. Initial sampled values is low/non-asserted. // Active-high sampled and current value of a level-triggered control signal. Initial sampled values is low/non-asserted.
SampledSig sample_control(Module *module, SigSpec sig, bool polarity, bool is_fine) { SampledSig sample_control(Module *module, SigSpec sig, bool polarity, bool is_fine) {
@ -117,21 +120,23 @@ struct Clk2fflogicPass : public Pass {
} }
void execute(std::vector<std::string> args, RTLIL::Design *design) override void execute(std::vector<std::string> args, RTLIL::Design *design) override
{ {
// bool flag_noinit = false; bool flag_nolower = false;
log_header(design, "Executing CLK2FFLOGIC pass (convert clocked FFs to generic $ff cells).\n"); log_header(design, "Executing CLK2FFLOGIC pass (convert clocked FFs to generic $ff cells).\n");
size_t argidx; size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) for (argidx = 1; argidx < args.size(); argidx++)
{ {
// if (args[argidx] == "-noinit") { if (args[argidx] == "-nolower") {
// flag_noinit = true; flag_nolower = true;
// continue; continue;
// } }
break; break;
} }
extra_args(args, argidx, design); extra_args(args, argidx, design);
bool have_check_cells = false;
for (auto module : design->selected_modules()) for (auto module : design->selected_modules())
{ {
SigMap sigmap(module); SigMap sigmap(module);
@ -194,79 +199,138 @@ struct Clk2fflogicPass : public Pass {
mem.emit(); mem.emit();
} }
SigBit initstate;
for (auto cell : vector<Cell*>(module->selected_cells())) for (auto cell : vector<Cell*>(module->selected_cells()))
{ {
SigSpec qval; if (cell->type.in(ID($print), ID($check)))
if (RTLIL::builtin_ff_cell_types().count(cell->type)) { {
FfData ff(&initvals, cell); if (cell->type == ID($check))
have_check_cells = true;
if (ff.has_gclk) { bool trg_enable = cell->getParam(ID(TRG_ENABLE)).as_bool();
// Already a $ff or $_FF_ cell. if (!trg_enable)
continue; continue;
}
if (ff.has_clk) { int trg_width = cell->getParam(ID(TRG_WIDTH)).as_int();
log("Replacing %s.%s (%s): CLK=%s, D=%s, Q=%s\n",
log_id(module), log_id(cell), log_id(cell->type), if (trg_width == 0) {
log_signal(ff.sig_clk), log_signal(ff.sig_d), log_signal(ff.sig_q)); if (initstate == State::S0)
} else if (ff.has_aload) { initstate = module->Initstate(NEW_ID);
log("Replacing %s.%s (%s): EN=%s, D=%s, Q=%s\n",
log_id(module), log_id(cell), log_id(cell->type), SigBit sig_en = cell->getPort(ID::EN);
log_signal(ff.sig_aload), log_signal(ff.sig_ad), log_signal(ff.sig_q)); cell->setPort(ID::EN, module->And(NEW_ID, sig_en, initstate));
} else { } else {
// $sr. SigBit sig_en = cell->getPort(ID::EN);
log("Replacing %s.%s (%s): SET=%s, CLR=%s, Q=%s\n", SigSpec sig_args = cell->getPort(ID::ARGS);
log_id(module), log_id(cell), log_id(cell->type), Const trg_polarity = cell->getParam(ID(TRG_POLARITY));
log_signal(ff.sig_set), log_signal(ff.sig_clr), log_signal(ff.sig_q)); SigSpec sig_trg = cell->getPort(ID::TRG);
SigSpec sig_trg_sampled;
for (auto const &bit : sig_trg)
sig_trg_sampled.append(sample_control_edge(module, bit, trg_polarity[GetSize(sig_trg_sampled)] == State::S1, false));
SigSpec sig_args_sampled = sample_data(module, sig_args, Const(State::S0, GetSize(sig_args)), false, false).sampled;
SigBit sig_en_sampled = sample_data(module, sig_en, State::S0, false, false).sampled;
SigBit sig_trg_combined = module->ReduceOr(NEW_ID, sig_trg_sampled);
cell->setPort(ID::EN, module->And(NEW_ID, sig_en_sampled, sig_trg_combined));
cell->setPort(ID::ARGS, sig_args_sampled);
if (cell->type == ID($check)) {
SigBit sig_a = cell->getPort(ID::A);
SigBit sig_a_sampled = sample_data(module, sig_a, State::S1, false, false).sampled;
cell->setPort(ID::A, sig_a_sampled);
}
} }
ff.remove(); cell->setPort(ID::TRG, SigSpec());
if (ff.has_clk) cell->setParam(ID::TRG_ENABLE, false);
ff.unmap_ce_srst(); cell->setParam(ID::TRG_WIDTH, 0);
cell->setParam(ID::TRG_POLARITY, false);
cell->set_bool_attribute(ID(trg_on_gclk));
auto next_q = sample_data(module, ff.sig_q, ff.val_init, ff.is_fine, true).sampled; continue;
if (ff.has_clk) {
// The init value for the sampled d is never used, so we can set it to fixed zero, reducing uninit'd FFs
auto sampled_d = sample_data(module, ff.sig_d, RTLIL::Const(State::S0, ff.width), ff.is_fine);
auto clk_edge = sample_control_edge(module, ff.sig_clk, ff.pol_clk, ff.is_fine);
next_q = mux(module, next_q, sampled_d.sampled, clk_edge, ff.is_fine);
}
SampledSig sampled_aload, sampled_ad, sampled_set, sampled_clr, sampled_arst;
// The check for a constant sig_aload is also done by opt_dff, but when using verific and running
// clk2fflogic before opt_dff (which does more and possibly unwanted optimizations) this check avoids
// generating a lot of extra logic.
bool has_nonconst_aload = ff.has_aload && ff.sig_aload != (ff.pol_aload ? State::S0 : State::S1);
if (has_nonconst_aload) {
sampled_aload = sample_control(module, ff.sig_aload, ff.pol_aload, ff.is_fine);
// The init value for the sampled ad is never used, so we can set it to fixed zero, reducing uninit'd FFs
sampled_ad = sample_data(module, ff.sig_ad, RTLIL::Const(State::S0, ff.width), ff.is_fine);
}
if (ff.has_sr) {
sampled_set = sample_control(module, ff.sig_set, ff.pol_set, ff.is_fine);
sampled_clr = sample_control(module, ff.sig_clr, ff.pol_clr, ff.is_fine);
}
if (ff.has_arst)
sampled_arst = sample_control(module, ff.sig_arst, ff.pol_arst, ff.is_fine);
// First perform updates using _only_ sampled values, then again using _only_ current values. Unlike the previous
// implementation, this approach correctly handles all the cases of multiple signals changing simultaneously.
for (int current = 0; current < 2; current++) {
if (has_nonconst_aload)
next_q = mux(module, next_q, sampled_ad[current], sampled_aload[current], ff.is_fine);
if (ff.has_sr)
next_q = bitwise_sr(module, next_q, sampled_set[current], sampled_clr[current], ff.is_fine);
if (ff.has_arst)
next_q = mux(module, next_q, ff.val_arst, sampled_arst[current], ff.is_fine);
}
module->connect(ff.sig_q, next_q);
} }
if (!RTLIL::builtin_ff_cell_types().count(cell->type))
continue;
FfData ff(&initvals, cell);
if (ff.has_gclk) {
// Already a $ff or $_FF_ cell.
continue;
}
if (ff.has_clk) {
log("Replacing %s.%s (%s): CLK=%s, D=%s, Q=%s\n",
log_id(module), log_id(cell), log_id(cell->type),
log_signal(ff.sig_clk), log_signal(ff.sig_d), log_signal(ff.sig_q));
} else if (ff.has_aload) {
log("Replacing %s.%s (%s): EN=%s, D=%s, Q=%s\n",
log_id(module), log_id(cell), log_id(cell->type),
log_signal(ff.sig_aload), log_signal(ff.sig_ad), log_signal(ff.sig_q));
} else {
// $sr.
log("Replacing %s.%s (%s): SET=%s, CLR=%s, Q=%s\n",
log_id(module), log_id(cell), log_id(cell->type),
log_signal(ff.sig_set), log_signal(ff.sig_clr), log_signal(ff.sig_q));
}
ff.remove();
if (ff.has_clk)
ff.unmap_ce_srst();
auto next_q = sample_data(module, ff.sig_q, ff.val_init, ff.is_fine, true).sampled;
if (ff.has_clk) {
// The init value for the sampled d is never used, so we can set it to fixed zero, reducing uninit'd FFs
auto sampled_d = sample_data(module, ff.sig_d, RTLIL::Const(State::S0, ff.width), ff.is_fine);
auto clk_edge = sample_control_edge(module, ff.sig_clk, ff.pol_clk, ff.is_fine);
next_q = mux(module, next_q, sampled_d.sampled, clk_edge, ff.is_fine);
}
SampledSig sampled_aload, sampled_ad, sampled_set, sampled_clr, sampled_arst;
// The check for a constant sig_aload is also done by opt_dff, but when using verific and running
// clk2fflogic before opt_dff (which does more and possibly unwanted optimizations) this check avoids
// generating a lot of extra logic.
bool has_nonconst_aload = ff.has_aload && ff.sig_aload != (ff.pol_aload ? State::S0 : State::S1);
if (has_nonconst_aload) {
sampled_aload = sample_control(module, ff.sig_aload, ff.pol_aload, ff.is_fine);
// The init value for the sampled ad is never used, so we can set it to fixed zero, reducing uninit'd FFs
sampled_ad = sample_data(module, ff.sig_ad, RTLIL::Const(State::S0, ff.width), ff.is_fine);
}
if (ff.has_sr) {
sampled_set = sample_control(module, ff.sig_set, ff.pol_set, ff.is_fine);
sampled_clr = sample_control(module, ff.sig_clr, ff.pol_clr, ff.is_fine);
}
if (ff.has_arst)
sampled_arst = sample_control(module, ff.sig_arst, ff.pol_arst, ff.is_fine);
// First perform updates using _only_ sampled values, then again using _only_ current values. Unlike the previous
// implementation, this approach correctly handles all the cases of multiple signals changing simultaneously.
for (int current = 0; current < 2; current++) {
if (has_nonconst_aload)
next_q = mux(module, next_q, sampled_ad[current], sampled_aload[current], ff.is_fine);
if (ff.has_sr)
next_q = bitwise_sr(module, next_q, sampled_set[current], sampled_clr[current], ff.is_fine);
if (ff.has_arst)
next_q = mux(module, next_q, ff.val_arst, sampled_arst[current], ff.is_fine);
}
module->connect(ff.sig_q, next_q);
} }
} }
if (have_check_cells && !flag_nolower) {
log_push();
Pass::call(design, "chformal -lower");
log_pop();
}
} }
} Clk2fflogicPass; } Clk2fflogicPass;

View File

@ -813,7 +813,7 @@ struct SimInstance
} }
} }
void update_ph3(bool check_assertions) void update_ph3(bool gclk_trigger)
{ {
for (auto &it : ff_database) for (auto &it : ff_database)
{ {
@ -858,49 +858,53 @@ struct SimInstance
Const en = get_state(cell->getPort(ID::EN)); Const en = get_state(cell->getPort(ID::EN));
Const args = get_state(cell->getPort(ID::ARGS)); Const args = get_state(cell->getPort(ID::ARGS));
if (!en.as_bool()) bool sampled = trg_en && trg.size() > 0;
goto update_print;
if (trg.size() > 0 && trg_en) { if (sampled ? print.past_en.as_bool() : en.as_bool()) {
Const trg_pol = cell->getParam(ID::TRG_POLARITY); if (sampled) {
for (int i = 0; i < trg.size(); i++) { sampled = true;
bool pol = trg_pol[i] == State::S1; Const trg_pol = cell->getParam(ID::TRG_POLARITY);
State curr = trg[i], past = print.past_trg[i]; for (int i = 0; i < trg.size(); i++) {
if (pol && curr == State::S1 && past == State::S0) bool pol = trg_pol[i] == State::S1;
State curr = trg[i], past = print.past_trg[i];
if (pol && curr == State::S1 && past == State::S0)
triggered = true;
if (!pol && curr == State::S0 && past == State::S1)
triggered = true;
}
} else if (trg_en) {
// initial $print (TRG width = 0, TRG_ENABLE = true)
if (!print.initial_done && en != print.past_en)
triggered = true; triggered = true;
if (!pol && curr == State::S0 && past == State::S1) } else if (cell->get_bool_attribute(ID(trg_on_gclk))) {
// unified $print for cycle based FV semantics
triggered = gclk_trigger;
} else {
// always @(*) $print
if (args != print.past_args || en != print.past_en)
triggered = true; triggered = true;
} }
} else if (trg_en) {
// initial $print (TRG width = 0, TRG_ENABLE = true)
if (!print.initial_done && en != print.past_en)
triggered = true;
} else {
// always @(*) $print
if (args != print.past_args || en != print.past_en)
triggered = true;
}
if (triggered) { if (triggered) {
int pos = 0; int pos = 0;
for (auto &part : print.fmt.parts) { for (auto &part : print.fmt.parts) {
part.sig = args.extract(pos, part.sig.size()); part.sig = (sampled ? print.past_args : args).extract(pos, part.sig.size());
pos += part.sig.size(); pos += part.sig.size();
}
std::string rendered = print.fmt.render();
log("%s", rendered.c_str());
shared->display_output.emplace_back(shared->step, this, cell, rendered);
} }
std::string rendered = print.fmt.render();
log("%s", rendered.c_str());
shared->display_output.emplace_back(shared->step, this, cell, rendered);
} }
update_print:
print.past_trg = trg; print.past_trg = trg;
print.past_en = en; print.past_en = en;
print.past_args = args; print.past_args = args;
print.initial_done = true; print.initial_done = true;
} }
if (check_assertions) if (gclk_trigger)
{ {
for (auto cell : formal_database) for (auto cell : formal_database)
{ {
@ -932,7 +936,7 @@ struct SimInstance
} }
for (auto it : children) for (auto it : children)
it.second->update_ph3(check_assertions); it.second->update_ph3(gclk_trigger);
} }
void set_initstate_outputs(State state) void set_initstate_outputs(State state)

View File

@ -56,5 +56,5 @@ EXTRA_OBJS += passes/techmap/filterlib.o
$(PROGRAM_PREFIX)yosys-filterlib$(EXE): passes/techmap/filterlib.o $(PROGRAM_PREFIX)yosys-filterlib$(EXE): passes/techmap/filterlib.o
$(Q) mkdir -p $(dir $@) $(Q) mkdir -p $(dir $@)
$(P) $(LD) -o $(PROGRAM_PREFIX)yosys-filterlib$(EXE) $(LDFLAGS) $^ $(LDLIBS) $(P) $(CXX) -o $(PROGRAM_PREFIX)yosys-filterlib$(EXE) $(LINKFLAGS) $^ $(LIBS)
endif endif

View File

@ -115,7 +115,7 @@ static bool parse_pin(LibertyAst *cell, LibertyAst *attr, std::string &pin_name,
return false; return false;
} }
static void find_cell(LibertyAst *ast, IdString cell_type, bool clkpol, bool has_reset, bool rstpol, bool rstval) static void find_cell(LibertyAst *ast, IdString cell_type, bool clkpol, bool has_reset, bool rstpol, bool rstval, std::vector<std::string> &dont_use_cells)
{ {
LibertyAst *best_cell = nullptr; LibertyAst *best_cell = nullptr;
std::map<std::string, char> best_cell_ports; std::map<std::string, char> best_cell_ports;
@ -135,6 +135,18 @@ static void find_cell(LibertyAst *ast, IdString cell_type, bool clkpol, bool has
if (dn != nullptr && dn->value == "true") if (dn != nullptr && dn->value == "true")
continue; continue;
bool dont_use = false;
for (std::string &dont_use_cell : dont_use_cells)
{
if (patmatch(dont_use_cell.c_str(), cell->args[0].c_str()))
{
dont_use = true;
break;
}
}
if (dont_use)
continue;
LibertyAst *ff = cell->find("ff"); LibertyAst *ff = cell->find("ff");
if (ff == nullptr) if (ff == nullptr)
continue; continue;
@ -227,7 +239,7 @@ static void find_cell(LibertyAst *ast, IdString cell_type, bool clkpol, bool has
} }
} }
static void find_cell_sr(LibertyAst *ast, IdString cell_type, bool clkpol, bool setpol, bool clrpol) static void find_cell_sr(LibertyAst *ast, IdString cell_type, bool clkpol, bool setpol, bool clrpol, std::vector<std::string> &dont_use_cells)
{ {
LibertyAst *best_cell = nullptr; LibertyAst *best_cell = nullptr;
std::map<std::string, char> best_cell_ports; std::map<std::string, char> best_cell_ports;
@ -247,6 +259,18 @@ static void find_cell_sr(LibertyAst *ast, IdString cell_type, bool clkpol, bool
if (dn != nullptr && dn->value == "true") if (dn != nullptr && dn->value == "true")
continue; continue;
bool dont_use = false;
for (std::string &dont_use_cell : dont_use_cells)
{
if (patmatch(dont_use_cell.c_str(), cell->args[0].c_str()))
{
dont_use = true;
break;
}
}
if (dont_use)
continue;
LibertyAst *ff = cell->find("ff"); LibertyAst *ff = cell->find("ff");
if (ff == nullptr) if (ff == nullptr)
continue; continue;
@ -414,7 +438,7 @@ struct DfflibmapPass : public Pass {
void help() override void help() override
{ {
log("\n"); log("\n");
log(" dfflibmap [-prepare] [-map-only] [-info] -liberty <file> [selection]\n"); log(" dfflibmap [-prepare] [-map-only] [-info] [-dont_use <cell_name>] -liberty <file> [selection]\n");
log("\n"); log("\n");
log("Map internal flip-flop cells to the flip-flop cells in the technology\n"); log("Map internal flip-flop cells to the flip-flop cells in the technology\n");
log("library specified in the given liberty file.\n"); log("library specified in the given liberty file.\n");
@ -435,6 +459,11 @@ struct DfflibmapPass : public Pass {
log("that would be passed to the dfflegalize pass. The design will not be\n"); log("that would be passed to the dfflegalize pass. The design will not be\n");
log("changed.\n"); log("changed.\n");
log("\n"); log("\n");
log("When called with -dont_use, this command will not map to the specified cell\n");
log("name as an alternative to setting the dont_use property in the Liberty file.\n");
log("This argument can be called multiple times with different cell names. This\n");
log("argument also supports simple glob patterns in the cell name.\n");
log("\n");
} }
void execute(std::vector<std::string> args, RTLIL::Design *design) override void execute(std::vector<std::string> args, RTLIL::Design *design) override
{ {
@ -446,6 +475,8 @@ struct DfflibmapPass : public Pass {
bool map_only_mode = false; bool map_only_mode = false;
bool info_mode = false; bool info_mode = false;
std::vector<std::string> dont_use_cells;
size_t argidx; size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) for (argidx = 1; argidx < args.size(); argidx++)
{ {
@ -467,6 +498,10 @@ struct DfflibmapPass : public Pass {
info_mode = true; info_mode = true;
continue; continue;
} }
if (arg == "-dont_use" && argidx+1 < args.size()) {
dont_use_cells.push_back(args[++argidx]);
continue;
}
break; break;
} }
extra_args(args, argidx, design); extra_args(args, argidx, design);
@ -491,26 +526,26 @@ struct DfflibmapPass : public Pass {
LibertyParser libparser(f); LibertyParser libparser(f);
f.close(); f.close();
find_cell(libparser.ast, ID($_DFF_N_), false, false, false, false); find_cell(libparser.ast, ID($_DFF_N_), false, false, false, false, dont_use_cells);
find_cell(libparser.ast, ID($_DFF_P_), true, false, false, false); find_cell(libparser.ast, ID($_DFF_P_), true, false, false, false, dont_use_cells);
find_cell(libparser.ast, ID($_DFF_NN0_), false, true, false, false); find_cell(libparser.ast, ID($_DFF_NN0_), false, true, false, false, dont_use_cells);
find_cell(libparser.ast, ID($_DFF_NN1_), false, true, false, true); find_cell(libparser.ast, ID($_DFF_NN1_), false, true, false, true, dont_use_cells);
find_cell(libparser.ast, ID($_DFF_NP0_), false, true, true, false); find_cell(libparser.ast, ID($_DFF_NP0_), false, true, true, false, dont_use_cells);
find_cell(libparser.ast, ID($_DFF_NP1_), false, true, true, true); find_cell(libparser.ast, ID($_DFF_NP1_), false, true, true, true, dont_use_cells);
find_cell(libparser.ast, ID($_DFF_PN0_), true, true, false, false); find_cell(libparser.ast, ID($_DFF_PN0_), true, true, false, false, dont_use_cells);
find_cell(libparser.ast, ID($_DFF_PN1_), true, true, false, true); find_cell(libparser.ast, ID($_DFF_PN1_), true, true, false, true, dont_use_cells);
find_cell(libparser.ast, ID($_DFF_PP0_), true, true, true, false); find_cell(libparser.ast, ID($_DFF_PP0_), true, true, true, false, dont_use_cells);
find_cell(libparser.ast, ID($_DFF_PP1_), true, true, true, true); find_cell(libparser.ast, ID($_DFF_PP1_), true, true, true, true, dont_use_cells);
find_cell_sr(libparser.ast, ID($_DFFSR_NNN_), false, false, false); find_cell_sr(libparser.ast, ID($_DFFSR_NNN_), false, false, false, dont_use_cells);
find_cell_sr(libparser.ast, ID($_DFFSR_NNP_), false, false, true); find_cell_sr(libparser.ast, ID($_DFFSR_NNP_), false, false, true, dont_use_cells);
find_cell_sr(libparser.ast, ID($_DFFSR_NPN_), false, true, false); find_cell_sr(libparser.ast, ID($_DFFSR_NPN_), false, true, false, dont_use_cells);
find_cell_sr(libparser.ast, ID($_DFFSR_NPP_), false, true, true); find_cell_sr(libparser.ast, ID($_DFFSR_NPP_), false, true, true, dont_use_cells);
find_cell_sr(libparser.ast, ID($_DFFSR_PNN_), true, false, false); find_cell_sr(libparser.ast, ID($_DFFSR_PNN_), true, false, false, dont_use_cells);
find_cell_sr(libparser.ast, ID($_DFFSR_PNP_), true, false, true); find_cell_sr(libparser.ast, ID($_DFFSR_PNP_), true, false, true, dont_use_cells);
find_cell_sr(libparser.ast, ID($_DFFSR_PPN_), true, true, false); find_cell_sr(libparser.ast, ID($_DFFSR_PPN_), true, true, false, dont_use_cells);
find_cell_sr(libparser.ast, ID($_DFFSR_PPP_), true, true, true); find_cell_sr(libparser.ast, ID($_DFFSR_PPP_), true, true, true, dont_use_cells);
log(" final dff cell mappings:\n"); log(" final dff cell mappings:\n");
logmap_all(); logmap_all();

View File

@ -281,7 +281,7 @@ struct ExtractFaWorker
void assign_new_driver(SigBit bit, SigBit new_driver) void assign_new_driver(SigBit bit, SigBit new_driver)
{ {
Cell *cell = driver.at(bit); Cell *cell = driver.at(bit);
if (sigmap(cell->getPort(ID::Y)) == bit) { if (sigmap(cell->getPort(ID::Y)) == SigSpec(bit)) {
cell->setPort(ID::Y, module->addWire(NEW_ID)); cell->setPort(ID::Y, module->addWire(NEW_ID));
module->connect(bit, new_driver); module->connect(bit, new_driver);
} }

View File

@ -46,24 +46,6 @@ IdString map_name(RTLIL::Cell *cell, T *object)
return cell->module->uniquify(concat_name(cell, object->name)); return cell->module->uniquify(concat_name(cell, object->name));
} }
template<class T>
void map_attributes(RTLIL::Cell *cell, T *object, IdString orig_object_name)
{
if (object->has_attribute(ID::src))
object->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src));
// Preserve original names via the hdlname attribute, but only for objects with a fully public name.
if (cell->name[0] == '\\' && (object->has_attribute(ID::hdlname) || orig_object_name[0] == '\\')) {
std::vector<std::string> hierarchy;
if (object->has_attribute(ID::hdlname))
hierarchy = object->get_hdlname_attribute();
else
hierarchy.push_back(orig_object_name.str().substr(1));
hierarchy.insert(hierarchy.begin(), cell->name.str().substr(1));
object->set_hdlname_attribute(hierarchy);
}
}
void map_sigspec(const dict<RTLIL::Wire*, RTLIL::Wire*> &map, RTLIL::SigSpec &sig, RTLIL::Module *into = nullptr) void map_sigspec(const dict<RTLIL::Wire*, RTLIL::Wire*> &map, RTLIL::SigSpec &sig, RTLIL::Module *into = nullptr)
{ {
vector<SigChunk> chunks = sig; vector<SigChunk> chunks = sig;
@ -76,6 +58,54 @@ void map_sigspec(const dict<RTLIL::Wire*, RTLIL::Wire*> &map, RTLIL::SigSpec &si
struct FlattenWorker struct FlattenWorker
{ {
bool ignore_wb = false; bool ignore_wb = false;
bool create_scopeinfo = true;
bool create_scopename = false;
template<class T>
void map_attributes(RTLIL::Cell *cell, T *object, IdString orig_object_name)
{
if (!create_scopeinfo && object->has_attribute(ID::src))
object->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src));
// Preserve original names via the hdlname attribute, but only for objects with a fully public name.
// If the '-scopename' option is used, also preserve the containing scope of private objects if their scope is fully public.
if (cell->name[0] == '\\') {
if (object->has_attribute(ID::hdlname) || orig_object_name[0] == '\\') {
std::string new_hdlname;
if (cell->has_attribute(ID::hdlname)) {
new_hdlname = cell->get_string_attribute(ID(hdlname));
} else {
log_assert(!cell->name.empty());
new_hdlname = cell->name.c_str() + 1;
}
new_hdlname += ' ';
if (object->has_attribute(ID::hdlname)) {
new_hdlname += object->get_string_attribute(ID(hdlname));
} else {
log_assert(!orig_object_name.empty());
new_hdlname += orig_object_name.c_str() + 1;
}
object->set_string_attribute(ID(hdlname), new_hdlname);
} else if (object->has_attribute(ID(scopename))) {
std::string new_scopename;
if (cell->has_attribute(ID::hdlname)) {
new_scopename = cell->get_string_attribute(ID(hdlname));
} else {
log_assert(!cell->name.empty());
new_scopename = cell->name.c_str() + 1;
}
new_scopename += ' ';
new_scopename += object->get_string_attribute(ID(scopename));
object->set_string_attribute(ID(scopename), new_scopename);
} else if (create_scopename) {
log_assert(!cell->name.empty());
object->set_string_attribute(ID(scopename), cell->name.c_str() + 1);
}
}
}
void flatten_cell(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Cell *cell, RTLIL::Module *tpl, SigMap &sigmap, std::vector<RTLIL::Cell*> &new_cells) void flatten_cell(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Cell *cell, RTLIL::Module *tpl, SigMap &sigmap, std::vector<RTLIL::Cell*> &new_cells)
{ {
@ -220,7 +250,33 @@ struct FlattenWorker
sigmap.add(new_conn.first, new_conn.second); sigmap.add(new_conn.first, new_conn.second);
} }
RTLIL::Cell *scopeinfo = nullptr;
RTLIL::IdString cell_name = cell->name;
if (create_scopeinfo && cell_name.isPublic())
{
// The $scopeinfo's name will be changed below after removing the flattened cell
scopeinfo = module->addCell(NEW_ID, ID($scopeinfo));
scopeinfo->setParam(ID::TYPE, RTLIL::Const("module"));
for (auto const &attr : cell->attributes)
{
if (attr.first == ID::hdlname)
scopeinfo->attributes.insert(attr);
else
scopeinfo->attributes.emplace(stringf("\\cell_%s", RTLIL::unescape_id(attr.first).c_str()), attr.second);
}
for (auto const &attr : tpl->attributes)
scopeinfo->attributes.emplace(stringf("\\module_%s", RTLIL::unescape_id(attr.first).c_str()), attr.second);
scopeinfo->attributes.emplace(ID(module), RTLIL::unescape_id(tpl->name));
}
module->remove(cell); module->remove(cell);
if (scopeinfo != nullptr)
module->rename(scopeinfo, cell_name);
} }
void flatten_module(RTLIL::Design *design, RTLIL::Module *module, pool<RTLIL::Module*> &used_modules) void flatten_module(RTLIL::Design *design, RTLIL::Module *module, pool<RTLIL::Module*> &used_modules)
@ -275,6 +331,20 @@ struct FlattenPass : public Pass {
log(" -wb\n"); log(" -wb\n");
log(" Ignore the 'whitebox' attribute on cell implementations.\n"); log(" Ignore the 'whitebox' attribute on cell implementations.\n");
log("\n"); log("\n");
log(" -noscopeinfo\n");
log(" Do not create '$scopeinfo' cells that preserve attributes of cells and\n");
log(" modules that were removed during flattening. With this option, the\n");
log(" 'src' attribute of a given cell is merged into all objects replacing\n");
log(" that cell, with multiple distinct 'src' locations separated by '|'.\n");
log(" Without this option these 'src' locations can be found via the\n");
log(" cell_src' and 'module_src' attribute of '$scopeinfo' cells.\n");
log("\n");
log(" -scopename\n");
log(" Create 'scopename' attributes for objects with a private name. This\n");
log(" attribute records the 'hdlname' of the enclosing scope. For objects\n");
log(" with a public name the enclosing scope can be found via their\n");
log(" 'hdlname' attribute.\n");
log("\n");
} }
void execute(std::vector<std::string> args, RTLIL::Design *design) override void execute(std::vector<std::string> args, RTLIL::Design *design) override
{ {
@ -289,6 +359,14 @@ struct FlattenPass : public Pass {
worker.ignore_wb = true; worker.ignore_wb = true;
continue; continue;
} }
if (args[argidx] == "-noscopeinfo") {
worker.create_scopeinfo = false;
continue;
}
if (args[argidx] == "-scopename") {
worker.create_scopename = true;
continue;
}
break; break;
} }
extra_args(args, argidx, design); extra_args(args, argidx, design);

View File

@ -1803,11 +1803,12 @@ endmodule
module \$print (EN, TRG, ARGS); module \$print (EN, TRG, ARGS);
parameter PRIORITY = 0;
parameter FORMAT = ""; parameter FORMAT = "";
parameter ARGS_WIDTH = 0; parameter ARGS_WIDTH = 0;
parameter PRIORITY = 0;
parameter TRG_ENABLE = 1;
parameter TRG_ENABLE = 1;
parameter TRG_WIDTH = 0; parameter TRG_WIDTH = 0;
parameter TRG_POLARITY = 0; parameter TRG_POLARITY = 0;
@ -1817,6 +1818,27 @@ input [ARGS_WIDTH-1:0] ARGS;
endmodule endmodule
// --------------------------------------------------------
module \$check (A, EN, TRG, ARGS);
parameter FLAVOR = "";
parameter PRIORITY = 0;
parameter FORMAT = "";
parameter ARGS_WIDTH = 0;
parameter TRG_ENABLE = 1;
parameter TRG_WIDTH = 0;
parameter TRG_POLARITY = 0;
input A;
input EN;
input [TRG_WIDTH-1:0] TRG;
input [ARGS_WIDTH-1:0] ARGS;
endmodule
// -------------------------------------------------------- // --------------------------------------------------------
`ifndef SIMLIB_NOSR `ifndef SIMLIB_NOSR
@ -2741,3 +2763,10 @@ assign Y = A;
endmodule endmodule
// -------------------------------------------------------- // --------------------------------------------------------
(* noblackbox *)
module \$scopeinfo ();
parameter TYPE = "";
endmodule

View File

@ -60,7 +60,7 @@ struct SynthPass : public ScriptPass {
log(" do not run abc (as if yosys was compiled without ABC support)\n"); log(" do not run abc (as if yosys was compiled without ABC support)\n");
log("\n"); log("\n");
log(" -booth\n"); log(" -booth\n");
log(" run the booth pass to convert $mul to Booth encoded multipliers"); log(" run the booth pass to map $mul to Booth encoded multipliers\n");
log("\n"); log("\n");
log(" -noalumacc\n"); log(" -noalumacc\n");
log(" do not run 'alumacc' pass. i.e. keep arithmetic operators in\n"); log(" do not run 'alumacc' pass. i.e. keep arithmetic operators in\n");
@ -230,13 +230,13 @@ struct SynthPass : public ScriptPass {
if (check_label("coarse")) { if (check_label("coarse")) {
run("proc"); run("proc");
if (help_mode || flatten) if (flatten || help_mode)
run("flatten", " (if -flatten)"); run("flatten", " (if -flatten)");
run("opt_expr"); run("opt_expr");
run("opt_clean"); run("opt_clean");
run("check"); run("check");
run("opt -nodffe -nosdff"); run("opt -nodffe -nosdff");
if (!nofsm) if (!nofsm || help_mode)
run("fsm" + fsm_opts, " (unless -nofsm)"); run("fsm" + fsm_opts, " (unless -nofsm)");
run("opt"); run("opt");
run("wreduce"); run("wreduce");
@ -246,8 +246,8 @@ struct SynthPass : public ScriptPass {
run("techmap -map +/cmp2lut.v -map +/cmp2lcu.v", " (if -lut)"); run("techmap -map +/cmp2lut.v -map +/cmp2lcu.v", " (if -lut)");
else if (lut) else if (lut)
run(stringf("techmap -map +/cmp2lut.v -map +/cmp2lcu.v -D LUT_WIDTH=%d", lut)); run(stringf("techmap -map +/cmp2lut.v -map +/cmp2lcu.v -D LUT_WIDTH=%d", lut));
if (booth) if (booth || help_mode)
run("booth"); run("booth", " (if -booth)");
if (!noalumacc) if (!noalumacc)
run("alumacc", " (unless -noalumacc)"); run("alumacc", " (unless -noalumacc)");
if (!noshare) if (!noshare)
@ -274,7 +274,7 @@ struct SynthPass : public ScriptPass {
} }
run("opt -fast"); run("opt -fast");
if (!noabc && !flowmap) { if ((!noabc && !flowmap) || help_mode) {
#ifdef YOSYS_ENABLE_ABC #ifdef YOSYS_ENABLE_ABC
if (help_mode) { if (help_mode) {
run(abc + " -fast", " (unless -noabc, unless -lut)"); run(abc + " -fast", " (unless -noabc, unless -lut)");

View File

@ -1,13 +1,11 @@
ram block $__GOWIN_SP_ { ram block $__GOWIN_SP_ {
abits 14; abits 14;
widths 1 2 4 9 18 36 per_port; widths 1 2 4 9 18 36 per_port;
byte 9;
cost 128; cost 128;
init no_undef; init no_undef;
port srsw "A" { port srsw "A" {
clock posedge; clock posedge;
clken; clken;
wrbe_separate;
option "RESET_MODE" "SYNC" { option "RESET_MODE" "SYNC" {
rdsrst zero ungated; rdsrst zero ungated;
} }
@ -30,13 +28,11 @@ ram block $__GOWIN_SP_ {
ram block $__GOWIN_DP_ { ram block $__GOWIN_DP_ {
abits 14; abits 14;
widths 1 2 4 9 18 per_port; widths 1 2 4 9 18 per_port;
byte 9;
cost 128; cost 128;
init no_undef; init no_undef;
port srsw "A" "B" { port srsw "A" "B" {
clock posedge; clock posedge;
clken; clken;
wrbe_separate;
option "RESET_MODE" "SYNC" { option "RESET_MODE" "SYNC" {
rdsrst zero ungated; rdsrst zero ungated;
} }
@ -59,7 +55,6 @@ ram block $__GOWIN_DP_ {
ram block $__GOWIN_SDP_ { ram block $__GOWIN_SDP_ {
abits 14; abits 14;
widths 1 2 4 9 18 36 per_port; widths 1 2 4 9 18 36 per_port;
byte 9;
cost 128; cost 128;
init no_undef; init no_undef;
port sr "R" { port sr "R" {
@ -76,6 +71,5 @@ ram block $__GOWIN_SDP_ {
port sw "W" { port sw "W" {
clock posedge; clock posedge;
clken; clken;
wrbe_separate;
} }
} }

View File

@ -14,8 +14,7 @@
`define x8_width(width) (width / 9 * 8 + width % 9) `define x8_width(width) (width / 9 * 8 + width % 9)
`define x8_rd_data(data) {1'bx, data[31:24], 1'bx, data[23:16], 1'bx, data[15:8], 1'bx, data[7:0]} `define x8_rd_data(data) {1'bx, data[31:24], 1'bx, data[23:16], 1'bx, data[15:8], 1'bx, data[7:0]}
`define x8_wr_data(data) {data[34:27], data[25:18], data[16:9], data[7:0]} `define x8_wr_data(data) {data[34:27], data[25:18], data[16:9], data[7:0]}
`define wre(width, wr_en, wr_be) (width < 18 ? wr_en | wr_be[0] : wr_en) `define addrbe_always(width, addr) (width < 18 ? addr : width == 18 ? {addr[13:4], 4'b0011} : {addr[13:5], 5'b01111})
`define addrbe(width, addr, wr_be) (width < 18 ? addr : {addr[13:4], wr_be})
`define INIT(func) \ `define INIT(func) \
@ -90,7 +89,6 @@ parameter INIT = 0;
parameter OPTION_RESET_MODE = "SYNC"; parameter OPTION_RESET_MODE = "SYNC";
parameter PORT_A_WIDTH = 36; parameter PORT_A_WIDTH = 36;
parameter PORT_A_WR_BE_WIDTH = 4;
parameter PORT_A_OPTION_WRITE_MODE = 0; parameter PORT_A_OPTION_WRITE_MODE = 0;
input PORT_A_CLK; input PORT_A_CLK;
@ -99,15 +97,13 @@ input PORT_A_WR_EN;
input PORT_A_RD_SRST; input PORT_A_RD_SRST;
input PORT_A_RD_ARST; input PORT_A_RD_ARST;
input [13:0] PORT_A_ADDR; input [13:0] PORT_A_ADDR;
input [PORT_A_WR_BE_WIDTH-1:0] PORT_A_WR_BE;
input [PORT_A_WIDTH-1:0] PORT_A_WR_DATA; input [PORT_A_WIDTH-1:0] PORT_A_WR_DATA;
output [PORT_A_WIDTH-1:0] PORT_A_RD_DATA; output [PORT_A_WIDTH-1:0] PORT_A_RD_DATA;
`DEF_FUNCS `DEF_FUNCS
wire RST = OPTION_RESET_MODE == "SYNC" ? PORT_A_RD_SRST : PORT_A_RD_ARST; wire RST = OPTION_RESET_MODE == "SYNC" ? PORT_A_RD_SRST : PORT_A_RD_ARST;
wire WRE = `wre(PORT_A_WIDTH, PORT_A_WR_EN, PORT_A_WR_BE); wire [13:0] AD = `addrbe_always(PORT_A_WIDTH, PORT_A_ADDR);
wire [13:0] AD = `addrbe(PORT_A_WIDTH, PORT_A_ADDR, PORT_A_WR_BE);
generate generate
@ -129,7 +125,7 @@ if (PORT_A_WIDTH < 9) begin
.BLKSEL(3'b000), .BLKSEL(3'b000),
.CLK(PORT_A_CLK), .CLK(PORT_A_CLK),
.CE(PORT_A_CLK_EN), .CE(PORT_A_CLK_EN),
.WRE(WRE), .WRE(PORT_A_WR_EN),
.RESET(RST), .RESET(RST),
.OCE(1'b1), .OCE(1'b1),
.AD(AD), .AD(AD),
@ -155,7 +151,7 @@ end else begin
.BLKSEL(3'b000), .BLKSEL(3'b000),
.CLK(PORT_A_CLK), .CLK(PORT_A_CLK),
.CE(PORT_A_CLK_EN), .CE(PORT_A_CLK_EN),
.WRE(WRE), .WRE(PORT_A_WR_EN),
.RESET(RST), .RESET(RST),
.OCE(1'b1), .OCE(1'b1),
.AD(AD), .AD(AD),
@ -176,11 +172,9 @@ parameter INIT = 0;
parameter OPTION_RESET_MODE = "SYNC"; parameter OPTION_RESET_MODE = "SYNC";
parameter PORT_A_WIDTH = 18; parameter PORT_A_WIDTH = 18;
parameter PORT_A_WR_BE_WIDTH = 2;
parameter PORT_A_OPTION_WRITE_MODE = 0; parameter PORT_A_OPTION_WRITE_MODE = 0;
parameter PORT_B_WIDTH = 18; parameter PORT_B_WIDTH = 18;
parameter PORT_B_WR_BE_WIDTH = 2;
parameter PORT_B_OPTION_WRITE_MODE = 0; parameter PORT_B_OPTION_WRITE_MODE = 0;
input PORT_A_CLK; input PORT_A_CLK;
@ -189,7 +183,6 @@ input PORT_A_WR_EN;
input PORT_A_RD_SRST; input PORT_A_RD_SRST;
input PORT_A_RD_ARST; input PORT_A_RD_ARST;
input [13:0] PORT_A_ADDR; input [13:0] PORT_A_ADDR;
input [PORT_A_WR_BE_WIDTH-1:0] PORT_A_WR_BE;
input [PORT_A_WIDTH-1:0] PORT_A_WR_DATA; input [PORT_A_WIDTH-1:0] PORT_A_WR_DATA;
output [PORT_A_WIDTH-1:0] PORT_A_RD_DATA; output [PORT_A_WIDTH-1:0] PORT_A_RD_DATA;
@ -199,7 +192,6 @@ input PORT_B_WR_EN;
input PORT_B_RD_SRST; input PORT_B_RD_SRST;
input PORT_B_RD_ARST; input PORT_B_RD_ARST;
input [13:0] PORT_B_ADDR; input [13:0] PORT_B_ADDR;
input [PORT_A_WR_BE_WIDTH-1:0] PORT_B_WR_BE;
input [PORT_A_WIDTH-1:0] PORT_B_WR_DATA; input [PORT_A_WIDTH-1:0] PORT_B_WR_DATA;
output [PORT_A_WIDTH-1:0] PORT_B_RD_DATA; output [PORT_A_WIDTH-1:0] PORT_B_RD_DATA;
@ -207,10 +199,8 @@ output [PORT_A_WIDTH-1:0] PORT_B_RD_DATA;
wire RSTA = OPTION_RESET_MODE == "SYNC" ? PORT_A_RD_SRST : PORT_A_RD_ARST; wire RSTA = OPTION_RESET_MODE == "SYNC" ? PORT_A_RD_SRST : PORT_A_RD_ARST;
wire RSTB = OPTION_RESET_MODE == "SYNC" ? PORT_B_RD_SRST : PORT_B_RD_ARST; wire RSTB = OPTION_RESET_MODE == "SYNC" ? PORT_B_RD_SRST : PORT_B_RD_ARST;
wire WREA = `wre(PORT_A_WIDTH, PORT_A_WR_EN, PORT_A_WR_BE); wire [13:0] ADA = `addrbe_always(PORT_A_WIDTH, PORT_A_ADDR);
wire WREB = `wre(PORT_B_WIDTH, PORT_B_WR_EN, PORT_B_WR_BE); wire [13:0] ADB = `addrbe_always(PORT_B_WIDTH, PORT_B_ADDR);
wire [13:0] ADA = `addrbe(PORT_A_WIDTH, PORT_A_ADDR, PORT_A_WR_BE);
wire [13:0] ADB = `addrbe(PORT_B_WIDTH, PORT_B_ADDR, PORT_B_WR_BE);
generate generate
@ -241,7 +231,7 @@ if (PORT_A_WIDTH < 9 || PORT_B_WIDTH < 9) begin
.CLKA(PORT_A_CLK), .CLKA(PORT_A_CLK),
.CEA(PORT_A_CLK_EN), .CEA(PORT_A_CLK_EN),
.WREA(WREA), .WREA(PORT_A_WR_EN),
.RESETA(RSTA), .RESETA(RSTA),
.OCEA(1'b1), .OCEA(1'b1),
.ADA(ADA), .ADA(ADA),
@ -250,7 +240,7 @@ if (PORT_A_WIDTH < 9 || PORT_B_WIDTH < 9) begin
.CLKB(PORT_B_CLK), .CLKB(PORT_B_CLK),
.CEB(PORT_B_CLK_EN), .CEB(PORT_B_CLK_EN),
.WREB(WREB), .WREB(PORT_B_WR_EN),
.RESETB(RSTB), .RESETB(RSTB),
.OCEB(1'b1), .OCEB(1'b1),
.ADB(ADB), .ADB(ADB),
@ -285,7 +275,7 @@ end else begin
.CLKA(PORT_A_CLK), .CLKA(PORT_A_CLK),
.CEA(PORT_A_CLK_EN), .CEA(PORT_A_CLK_EN),
.WREA(WREA), .WREA(PORT_A_WR_EN),
.RESETA(RSTA), .RESETA(RSTA),
.OCEA(1'b1), .OCEA(1'b1),
.ADA(ADA), .ADA(ADA),
@ -294,7 +284,7 @@ end else begin
.CLKB(PORT_B_CLK), .CLKB(PORT_B_CLK),
.CEB(PORT_B_CLK_EN), .CEB(PORT_B_CLK_EN),
.WREB(WREB), .WREB(PORT_B_WR_EN),
.RESETB(RSTB), .RESETB(RSTB),
.OCEB(1'b1), .OCEB(1'b1),
.ADB(ADB), .ADB(ADB),
@ -315,9 +305,7 @@ parameter INIT = 0;
parameter OPTION_RESET_MODE = "SYNC"; parameter OPTION_RESET_MODE = "SYNC";
parameter PORT_R_WIDTH = 18; parameter PORT_R_WIDTH = 18;
parameter PORT_W_WIDTH = 18; parameter PORT_W_WIDTH = 18;
parameter PORT_W_WR_BE_WIDTH = 2;
input PORT_R_CLK; input PORT_R_CLK;
input PORT_R_CLK_EN; input PORT_R_CLK_EN;
@ -330,14 +318,13 @@ input PORT_W_CLK;
input PORT_W_CLK_EN; input PORT_W_CLK_EN;
input PORT_W_WR_EN; input PORT_W_WR_EN;
input [13:0] PORT_W_ADDR; input [13:0] PORT_W_ADDR;
input [PORT_W_WR_BE_WIDTH-1:0] PORT_W_WR_BE;
input [PORT_W_WIDTH-1:0] PORT_W_WR_DATA; input [PORT_W_WIDTH-1:0] PORT_W_WR_DATA;
`DEF_FUNCS `DEF_FUNCS
wire RST = OPTION_RESET_MODE == "SYNC" ? PORT_R_RD_SRST : PORT_R_RD_ARST; wire RST = OPTION_RESET_MODE == "SYNC" ? PORT_R_RD_SRST : PORT_R_RD_ARST;
wire WRE = `wre(PORT_W_WIDTH, PORT_W_WR_EN, PORT_W_WR_BE); wire [13:0] ADW = `addrbe_always(PORT_W_WIDTH, PORT_W_ADDR);
wire [13:0] ADW = `addrbe(PORT_W_WIDTH, PORT_W_ADDR, PORT_W_WR_BE); wire WRE = PORT_W_CLK_EN & PORT_W_WR_EN;
generate generate
@ -361,7 +348,7 @@ if (PORT_W_WIDTH < 9 || PORT_R_WIDTH < 9) begin
.BLKSELB(3'b000), .BLKSELB(3'b000),
.CLKA(PORT_W_CLK), .CLKA(PORT_W_CLK),
.CEA(PORT_W_CLK_EN), .CEA(WRE),
.RESETA(1'b0), .RESETA(1'b0),
.ADA(ADW), .ADA(ADW),
.DI(DI), .DI(DI),
@ -394,7 +381,7 @@ end else begin
.BLKSELB(3'b000), .BLKSELB(3'b000),
.CLKA(PORT_W_CLK), .CLKA(PORT_W_CLK),
.CEA(PORT_W_CLK_EN), .CEA(WRE),
.RESETA(1'b0), .RESETA(1'b0),
.ADA(ADW), .ADA(ADW),
.DI(DI), .DI(DI),

View File

@ -107,7 +107,7 @@ reg [7:0] i = 0;
always @(posedge clk) begin always @(posedge clk) begin
if (i < VECTORLEN) begin if (i < VECTORLEN) begin
// FIXME: for some reason the first assert fails (despite comparing zero to zero) // FIXME: for some reason the first assert fails (despite comparing zero to zero)
if (i > 0) if (i > 0)
assert(y == y_expected); assert(y == y_expected);
i <= i + 1; i <= i + 1;
end end
@ -117,4 +117,5 @@ EOF
read_verilog +/quicklogic/qlf_k6n10f/dsp_sim.v read_verilog +/quicklogic/qlf_k6n10f/dsp_sim.v
hierarchy -top testbench hierarchy -top testbench
proc proc
async2sync
sim -assert -q -clock clk -n 20 sim -assert -q -clock clk -n 20

View File

@ -36,7 +36,7 @@ blockram_tests: "list[tuple[list[tuple[str, int]], str, list[str]]]" = [
([("ADDRESS_WIDTH", 14), ("DATA_WIDTH", 2)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=2 %i"]), ([("ADDRESS_WIDTH", 14), ("DATA_WIDTH", 2)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=2 %i"]),
([("ADDRESS_WIDTH", 15), ("DATA_WIDTH", 1)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=1 %i"]), ([("ADDRESS_WIDTH", 15), ("DATA_WIDTH", 1)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=1 %i"]),
# 2x asymmetric (1024x36bit write / 2048x18bit read or vice versa = 1TDP36K) # 2x asymmetric (1024x36bit write / 2048x18bit read or vice versa = 1TDP36K)
([("ADDRESS_WIDTH", 11), ("DATA_WIDTH", 18), ("SHIFT_VAL", 1)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]), ([("ADDRESS_WIDTH", 11), ("DATA_WIDTH", 18), ("SHIFT_VAL", 1)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]),
([("ADDRESS_WIDTH", 11), ("DATA_WIDTH", 16), ("SHIFT_VAL", 1)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]), ([("ADDRESS_WIDTH", 11), ("DATA_WIDTH", 16), ("SHIFT_VAL", 1)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]),
([("ADDRESS_WIDTH", 12), ("DATA_WIDTH", 9), ("SHIFT_VAL", 1)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]), ([("ADDRESS_WIDTH", 12), ("DATA_WIDTH", 9), ("SHIFT_VAL", 1)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]),
@ -131,6 +131,7 @@ read_verilog -defer -formal mem_tb.v
chparam{param_str} -set VECTORLEN {vectorlen} TB chparam{param_str} -set VECTORLEN {vectorlen} TB
hierarchy -top TB -check hierarchy -top TB -check
prep prep
async2sync
log ** CHECKING SIMULATION FOR TEST {top} WITH PARAMS{param_str} log ** CHECKING SIMULATION FOR TEST {top} WITH PARAMS{param_str}
sim -clock clk -n {vectorlen} -assert sim -clock clk -n {vectorlen} -assert
""" """
@ -254,16 +255,16 @@ sim_tests: list[TestClass] = [
{"rq_a": 0x5678}, {"rq_a": 0x5678},
] ]
), ),
TestClass( # basic TDP test TestClass( # basic TDP test
# note that the testbench uses ra and wa, while the common TDP model # note that the testbench uses ra and wa, while the common TDP model
# uses a shared address # uses a shared address
params={"ADDRESS_WIDTH": 10, "DATA_WIDTH": 36}, params={"ADDRESS_WIDTH": 10, "DATA_WIDTH": 36},
top="sync_ram_tdp", top="sync_ram_tdp",
assertions=[], assertions=[],
test_steps=[ test_steps=[
{"wce_a": 1, "ra_a": 0x0A, "wce_b": 1, "ra_b": 0xBA, {"wce_a": 1, "ra_a": 0x0A, "wce_b": 1, "ra_b": 0xBA,
"wd_a": 0xdeadbeef, "wd_b": 0x5a5a5a5a}, "wd_a": 0xdeadbeef, "wd_b": 0x5a5a5a5a},
{"wce_a": 1, "ra_a": 0xFF, {"wce_a": 1, "ra_a": 0xFF,
"wd_a": 0}, "wd_a": 0},
{"rce_a": 1, "ra_a": 0x0A, "rce_b": 1, "ra_b": 0x0A}, {"rce_a": 1, "ra_a": 0x0A, "rce_b": 1, "ra_b": 0x0A},
{"rq_a": 0xdeadbeef, "rq_b": 0xdeadbeef}, {"rq_a": 0xdeadbeef, "rq_b": 0xdeadbeef},
@ -276,9 +277,9 @@ sim_tests: list[TestClass] = [
top="sync_ram_tdp", top="sync_ram_tdp",
assertions=[], assertions=[],
test_steps=[ test_steps=[
{"wce_a": 1, "ra_a": 0x0F, "wce_b": 1, "ra_b": 0xBA, {"wce_a": 1, "ra_a": 0x0F, "wce_b": 1, "ra_b": 0xBA,
"wd_a": 0xdeadbeef, "wd_b": 0x5a5a5a5a}, "wd_a": 0xdeadbeef, "wd_b": 0x5a5a5a5a},
{"wce_a": 1, "ra_a": 0xFF, {"wce_a": 1, "ra_a": 0xFF,
"wd_a": 0}, "wd_a": 0},
{"rce_a": 1, "ra_a": 0x0F, "rce_b": 1, "ra_b": 0x0A}, {"rce_a": 1, "ra_a": 0x0F, "rce_b": 1, "ra_b": 0x0A},
{"rq_a": 0, "rq_b": 0x00005a5a}, {"rq_a": 0, "rq_b": 0x00005a5a},
@ -291,7 +292,7 @@ sim_tests: list[TestClass] = [
top="sync_ram_tdp", top="sync_ram_tdp",
assertions=[], assertions=[],
test_steps=[ test_steps=[
{"wce_a": 1, "ra_a": 0x0A, "wce_b": 1, "ra_b": 0xBA, {"wce_a": 1, "ra_a": 0x0A, "wce_b": 1, "ra_b": 0xBA,
"wd_a": 0xdeadbeef, "wd_b": 0x5a5a5a5a}, "wd_a": 0xdeadbeef, "wd_b": 0x5a5a5a5a},
{"wce_a": 1, "ra_a": 0xBA, "rce_b": 1, "ra_b": 0xBA, {"wce_a": 1, "ra_a": 0xBA, "rce_b": 1, "ra_b": 0xBA,
"wd_a": 0xa5a5a5a5}, "wd_a": 0xa5a5a5a5},
@ -409,7 +410,7 @@ for sim_test in sim_tests:
fn = f"t_mem{i}.ys" fn = f"t_mem{i}.ys"
f = open(fn, mode="w") f = open(fn, mode="w")
j = 0 j = 0
# output yosys script test file # output yosys script test file
print( print(
blockram_template.format(param_str=param_str, top=top), blockram_template.format(param_str=param_str, top=top),

View File

@ -10,5 +10,6 @@ select -assert-count 1 t:TDP36K a:is_split=0 %i
select -assert-count 1 t:TDP36K a:was_split_candidate=0 %i select -assert-count 1 t:TDP36K a:was_split_candidate=0 %i
read_verilog +/quicklogic/common/cells_sim.v +/quicklogic/qlf_k6n10f/cells_sim.v +/quicklogic/qlf_k6n10f/brams_sim.v +/quicklogic/qlf_k6n10f/sram1024x18_mem.v +/quicklogic/qlf_k6n10f/ufifo_ctl.v +/quicklogic/qlf_k6n10f/TDP18K_FIFO.v read_verilog +/quicklogic/common/cells_sim.v +/quicklogic/qlf_k6n10f/cells_sim.v +/quicklogic/qlf_k6n10f/brams_sim.v +/quicklogic/qlf_k6n10f/sram1024x18_mem.v +/quicklogic/qlf_k6n10f/ufifo_ctl.v +/quicklogic/qlf_k6n10f/TDP18K_FIFO.v
prep prep
async2sync
hierarchy -top top hierarchy -top top
sim -assert -q -n 12 -clock clk sim -assert -q -n 12 -clock clk

View File

@ -30,6 +30,7 @@ module top(output [42:0] P);
assert property (P == 42*42); assert property (P == 42*42);
endmodule endmodule
EOT EOT
async2sync
techmap -map +/xilinx/xc7_dsp_map.v techmap -map +/xilinx/xc7_dsp_map.v
verilog_defaults -add -D ALLOW_WHITEBOX_DSP48E1 verilog_defaults -add -D ALLOW_WHITEBOX_DSP48E1
synth_xilinx -abc9 synth_xilinx -abc9

View File

@ -16,8 +16,8 @@ generate_target() {
# $ generate_ys_test ys_file [yosys_args] # $ generate_ys_test ys_file [yosys_args]
generate_ys_test() { generate_ys_test() {
ys_file=$1 ys_file=$1
yosys_args=${2:-} yosys_args_=${2:-}
generate_target "$ys_file" "\"$YOSYS_BASEDIR/yosys\" -ql ${ys_file%.*}.log $yosys_args $ys_file" generate_target "$ys_file" "\"$YOSYS_BASEDIR/yosys\" -ql ${ys_file%.*}.log $yosys_args_ $ys_file"
} }
# $ generate_bash_test bash_file # $ generate_bash_test bash_file
@ -75,7 +75,7 @@ generate_tests() {
if [[ $do_sv = true ]]; then if [[ $do_sv = true ]]; then
for x in *.sv; do for x in *.sv; do
if [ ! -f "${x%.sv}.ys" ]; then if [ ! -f "${x%.sv}.ys" ]; then
generate_ys_test "$x" "-p \"prep -top top; sat -enable_undef -verify -prove-asserts\" $yosys_args" generate_ys_test "$x" "-p \"prep -top top; async2sync; sat -enable_undef -verify -prove-asserts\" $yosys_args"
fi; fi;
done done
fi; fi;

View File

@ -186,4 +186,27 @@ design -stash preopt
equiv_opt -assert -run prepare: dummy equiv_opt -assert -run prepare: dummy
design -reset
read_ilang <<EOT
module \m
wire width 3 input 1 \a
process \p
switch \a
case 3'000
case 3'001
case 3'010
case 3'011
case 3'100
case 3'101
case 3'110
case 3'111
end
end
end
EOT
proc_rom

View File

@ -1,3 +1,3 @@
read_verilog -sv asserts.v read_verilog -sv asserts.v
hierarchy; proc; opt hierarchy; proc; opt; async2sync
sat -verify -seq 1 -set-at 1 rst 1 -tempinduct -prove-asserts sat -verify -seq 1 -set-at 1 rst 1 -tempinduct -prove-asserts

View File

@ -1,5 +1,5 @@
read_verilog -sv asserts_seq.v read_verilog -sv asserts_seq.v
hierarchy; proc; opt hierarchy; proc; opt; async2sync
sat -verify -prove-asserts -tempinduct -seq 1 test_001 sat -verify -prove-asserts -tempinduct -seq 1 test_001
sat -falsify -prove-asserts -tempinduct -seq 1 test_002 sat -falsify -prove-asserts -tempinduct -seq 1 test_002

View File

@ -1,5 +1,5 @@
read_verilog -sv initval.v read_verilog -sv initval.v
proc;; proc; async2sync;;
sat -seq 10 -prove-asserts sat -seq 10 -prove-asserts

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