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
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@v3
with:
languages: cpp
queries: security-extended,security-and-quality
@ -26,4 +26,4 @@ jobs:
run: make yosys -j6
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v3

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,9 +2,28 @@
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
--------------------------
* New commands and options

184
Makefile
View File

@ -92,14 +92,14 @@ VPATH := $(YOSYS_SRC)
CXXSTD ?= c++11
CXXFLAGS := $(CXXFLAGS) -Wall -Wextra -ggdb -I. -I"$(YOSYS_SRC)" -MD -MP -D_YOSYS_ -fPIC -I$(PREFIX)/include
LDLIBS := $(LDLIBS) -lstdc++ -lm
PLUGIN_LDFLAGS :=
PLUGIN_LDLIBS :=
EXE_LDFLAGS :=
LIBS := $(LIBS) -lstdc++ -lm
PLUGIN_LINKFLAGS :=
PLUGIN_LIBS :=
EXE_LINKFLAGS :=
ifeq ($(OS), MINGW)
EXE_LDFLAGS := -Wl,--export-all-symbols -Wl,--out-implib,libyosys_exe.a
PLUGIN_LDFLAGS += -L"$(LIBDIR)"
PLUGIN_LDLIBS := -lyosys_exe
EXE_LINKFLAGS := -Wl,--export-all-symbols -Wl,--out-implib,libyosys_exe.a
PLUGIN_LINKFLAGS += -L"$(LIBDIR)"
PLUGIN_LIBS := -lyosys_exe
endif
PKG_CONFIG ?= pkg-config
@ -109,7 +109,7 @@ STRIP ?= strip
AWK ?= awk
ifeq ($(OS), Darwin)
PLUGIN_LDFLAGS += -undefined dynamic_lookup
PLUGIN_LINKFLAGS += -undefined dynamic_lookup
# homebrew search paths
ifneq ($(shell :; command -v brew),)
@ -117,10 +117,10 @@ BREW_PREFIX := $(shell brew --prefix)/opt
$(info $$BREW_PREFIX is [${BREW_PREFIX}])
ifeq ($(ENABLE_PYOSYS),1)
CXXFLAGS += -I$(BREW_PREFIX)/boost/include/boost
LDFLAGS += -L$(BREW_PREFIX)/boost/lib
LINKFLAGS += -L$(BREW_PREFIX)/boost/lib
endif
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)/tcl-tk/lib/pkgconfig:$(PKG_CONFIG_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),)
PORT_PREFIX := $(patsubst %/bin/port,%,$(shell :; command -v port))
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)
export PATH := $(PORT_PREFIX)/bin:$(PATH)
endif
else
LDFLAGS += -rdynamic
LINKFLAGS += -rdynamic
ifneq ($(OS), OpenBSD)
LDLIBS += -lrt
LIBS += -lrt
endif
endif
YOSYS_VER := 0.37+27
YOSYS_VER := 0.38+113
# Note: We arrange for .gitcommit to contain the (short) commit hash in
# tarballs generated with git-archive(1) using .gitattributes. The git repo
@ -157,7 +157,7 @@ endif
OBJS = kernel/version_$(GIT_REV).o
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
#
@ -215,41 +215,38 @@ ABC_ARCHFLAGS += "-DABC_NO_RLIMIT"
endif
ifeq ($(CONFIG),clang)
CXX = clang
LD = clang++
CXX = clang++
CXXFLAGS += -std=$(CXXSTD) -Os
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -Wno-c++11-narrowing $(ABC_ARCHFLAGS)"
ifneq ($(SANITIZER),)
$(info [Clang Sanitizer] $(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)),)
ENABLE_COVER := 0
endif
ifneq ($(findstring memory,$(SANITIZER)),)
CXXFLAGS += -fPIE -fsanitize-memory-track-origins
LDFLAGS += -fPIE -fsanitize-memory-track-origins
LINKFLAGS += -fPIE -fsanitize-memory-track-origins
endif
ifneq ($(findstring cfi,$(SANITIZER)),)
CXXFLAGS += -flto
LDFLAGS += -flto
LINKFLAGS += -flto
endif
endif
else ifeq ($(CONFIG),gcc)
CXX = gcc
LD = gcc
CXX = g++
CXXFLAGS += -std=$(CXXSTD) -Os
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H $(ABC_ARCHFLAGS)"
else ifeq ($(CONFIG),gcc-static)
LD = $(CXX)
LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -static
LDLIBS := $(filter-out -lrt,$(LDLIBS))
LINKFLAGS := $(filter-out -rdynamic,$(LINKFLAGS)) -static
LIBS := $(filter-out -lrt,$(LIBS))
CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS))
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
ifeq ($(DISABLE_ABC_THREADS),1)
ABCMKARGS += "ABC_USE_NO_PTHREADS=1"
@ -257,31 +254,28 @@ endif
else ifeq ($(CONFIG),afl-gcc)
CXX = AFL_QUIET=1 AFL_HARDEN=1 afl-gcc
LD = AFL_QUIET=1 AFL_HARDEN=1 afl-gcc
CXXFLAGS += -std=$(CXXSTD) -Os
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H"
else ifeq ($(CONFIG),cygwin)
CXX = gcc
LD = gcc
CXX = g++
CXXFLAGS += -std=gnu++11 -Os
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H"
else ifeq ($(CONFIG),emcc)
CXX = emcc
LD = emcc
CXXFLAGS := -std=$(CXXSTD) $(filter-out -fPIC -ggdb,$(CXXFLAGS))
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DABC_MEMALIGN=8 -Wno-c++11-narrowing"
EMCC_CXXFLAGS := -Os -Wno-warn-absolute-paths
EMCC_LDFLAGS := --memory-init-file 0 --embed-file share
EMCC_LDFLAGS += -s NO_EXIT_RUNTIME=1
EMCC_LDFLAGS += -s EXPORTED_FUNCTIONS="['_main','_run','_prompt','_errmsg','_memset']"
EMCC_LDFLAGS += -s TOTAL_MEMORY=134217728
EMCC_LDFLAGS += -s EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]'
EMCC_LINKFLAGS := --embed-file share
EMCC_LINKFLAGS += -s NO_EXIT_RUNTIME=1
EMCC_LINKFLAGS += -s EXPORTED_FUNCTIONS="['_main','_run','_prompt','_errmsg','_memset']"
EMCC_LINKFLAGS += -s TOTAL_MEMORY=134217728
EMCC_LINKFLAGS += -s EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]'
# https://github.com/kripken/emscripten/blob/master/src/settings.js
CXXFLAGS += $(EMCC_CXXFLAGS)
LDFLAGS += $(EMCC_LDFLAGS)
LDLIBS =
LINKFLAGS += $(EMCC_LINKFLAGS)
LIBS =
EXE = .js
DISABLE_SPAWN := 1
@ -309,21 +303,19 @@ yosys.html: misc/yosys.html
else ifeq ($(CONFIG),wasi)
ifeq ($(WASI_SDK),)
CXX = clang
LD = clang++
CXX = clang++
AR = llvm-ar
RANLIB = llvm-ranlib
WASIFLAGS := -target wasm32-wasi --sysroot $(WASI_SYSROOT) $(WASIFLAGS)
else
CXX = $(WASI_SDK)/bin/clang
LD = $(WASI_SDK)/bin/clang++
CXX = $(WASI_SDK)/bin/clang++
AR = $(WASI_SDK)/bin/ar
RANLIB = $(WASI_SDK)/bin/ranlib
WASIFLAGS := --sysroot $(WASI_SDK)/share/wasi-sysroot $(WASIFLAGS)
endif
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))
LDLIBS := -lwasi-emulated-process-clocks $(filter-out -lrt,$(LDLIBS))
LINKFLAGS := $(WASIFLAGS) -Wl,-z,stack-size=1048576 $(filter-out -rdynamic,$(LINKFLAGS))
LIBS := -lwasi-emulated-process-clocks $(filter-out -lrt,$(LIBS))
ABCMKARGS += AR="$(AR)" RANLIB="$(RANLIB)"
ABCMKARGS += ARCHFLAGS="$(WASIFLAGS) -D_WASI_EMULATED_PROCESS_CLOCKS -DABC_USE_STDINT_H -DABC_NO_DYNAMIC_LINKING -DABC_NO_RLIMIT -Wno-c++11-narrowing"
ABCMKARGS += OPTFLAGS="-Os"
@ -339,34 +331,31 @@ endif
else ifeq ($(CONFIG),mxe)
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++
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 := $(filter-out -fPIC,$(CXXFLAGS))
LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -s
LDLIBS := $(filter-out -lrt,$(LDLIBS))
LINKFLAGS := $(filter-out -rdynamic,$(LINKFLAGS)) -s
LIBS := $(filter-out -lrt,$(LIBS))
ABCMKARGS += ARCHFLAGS="-DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -fpermissive -w"
# 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
else ifeq ($(CONFIG),msys2-32)
CXX = i686-w64-mingw32-g++
LD = i686-w64-mingw32-g++
CXXFLAGS += -std=$(CXXSTD) -Os -D_POSIX_SOURCE -DYOSYS_WIN32_UNIX_DIR
CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS))
LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -s
LDLIBS := $(filter-out -lrt,$(LDLIBS))
LINKFLAGS := $(filter-out -rdynamic,$(LINKFLAGS)) -s
LIBS := $(filter-out -lrt,$(LIBS))
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)"
EXE = .exe
else ifeq ($(CONFIG),msys2-64)
CXX = x86_64-w64-mingw32-g++
LD = x86_64-w64-mingw32-g++
CXXFLAGS += -std=$(CXXSTD) -Os -D_POSIX_SOURCE -DYOSYS_WIN32_UNIX_DIR
CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS))
LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -s
LDLIBS := $(filter-out -lrt,$(LDLIBS))
LINKFLAGS := $(filter-out -rdynamic,$(LINKFLAGS)) -s
LIBS := $(filter-out -lrt,$(LIBS))
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)"
EXE = .exe
@ -393,9 +382,9 @@ ifeq ($(BOOST_PYTHON_LIB),)
$(error BOOST_PYTHON_LIB could not be detected. Please define manually)
endif
LDLIBS += $(shell $(PYTHON_CONFIG) --libs) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
# python-config --ldflags includes LDLIBS for some reason
LDFLAGS += $(filter-out -l%,$(shell $(PYTHON_CONFIG) --ldflags))
LIBS += $(shell $(PYTHON_CONFIG) --libs) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
# python-config --ldflags includes LIBS for some reason
LINKFLAGS += $(filter-out -l%,$(shell $(PYTHON_CONFIG) --ldflags))
CXXFLAGS += $(shell $(PYTHON_CONFIG) --includes) -DWITH_PYTHON
PY_WRAPPER_FILE = kernel/python_wrappers
@ -409,22 +398,22 @@ CXXFLAGS += -DYOSYS_ENABLE_READLINE
ifeq ($(OS), $(filter $(OS),FreeBSD OpenBSD NetBSD))
CXXFLAGS += -I/usr/local/include
endif
LDLIBS += -lreadline
LIBS += -lreadline
ifeq ($(LINK_CURSES),1)
LDLIBS += -lcurses
LIBS += -lcurses
ABCMKARGS += "ABC_READLINE_LIBRARIES=-lcurses -lreadline"
endif
ifeq ($(LINK_TERMCAP),1)
LDLIBS += -ltermcap
LIBS += -ltermcap
ABCMKARGS += "ABC_READLINE_LIBRARIES=-lreadline -ltermcap"
endif
ifeq ($(CONFIG),mxe)
LDLIBS += -ltermcap
LIBS += -ltermcap
endif
else
ifeq ($(ENABLE_EDITLINE),1)
CXXFLAGS += -DYOSYS_ENABLE_EDITLINE
LDLIBS += -ledit -ltinfo -lbsd
LIBS += -ledit -ltinfo -lbsd
else
ABCMKARGS += "ABC_USE_NO_READLINE=1"
endif
@ -443,9 +432,9 @@ CXXFLAGS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-e
ifeq ($(OS), MINGW)
CXXFLAGS += -Ilibs/dlfcn-win32
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))
LDLIBS += -ldl
LIBS += -ldl
endif
endif
@ -455,7 +444,7 @@ endif
ifeq ($(ENABLE_ZLIB),1)
CXXFLAGS += -DYOSYS_ENABLE_ZLIB
LDLIBS += -lz
LIBS += -lz
endif
@ -472,21 +461,21 @@ endif
ifeq ($(CONFIG),mxe)
CXXFLAGS += -DYOSYS_ENABLE_TCL
LDLIBS += -ltcl86 -lwsock32 -lws2_32 -lnetapi32 -lz -luserenv
LIBS += -ltcl86 -lwsock32 -lws2_32 -lnetapi32 -lz -luserenv
else
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
ifeq ($(ENABLE_GCOV),1)
CXXFLAGS += --coverage
LDFLAGS += --coverage
LINKFLAGS += --coverage
endif
ifeq ($(ENABLE_GPROF),1)
CXXFLAGS += -pg
LDFLAGS += -pg
LINKFLAGS += -pg
endif
ifeq ($(ENABLE_NDEBUG),1)
@ -506,7 +495,7 @@ CXXFLAGS += -DYOSYS_ENABLE_ABC
ifeq ($(LINK_ABC),1)
CXXFLAGS += -DYOSYS_LINK_ABC
ifeq ($(DISABLE_ABC_THREADS),0)
LDLIBS += -lpthread
LIBS += -lpthread
endif
else
ifeq ($(ABCEXTERNAL),)
@ -520,10 +509,10 @@ GHDL_PREFIX ?= $(PREFIX)
GHDL_INCLUDE_DIR ?= $(GHDL_PREFIX)/include
GHDL_LIB_DIR ?= $(GHDL_PREFIX)/lib
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
LDLIBS_VERIFIC =
LIBS_VERIFIC =
ifeq ($(ENABLE_VERIFIC),1)
VERIFIC_DIR ?= /usr/local/src/verific_lib
VERIFIC_COMPONENTS ?= verilog database util containers hier_tree
@ -549,9 +538,9 @@ CXXFLAGS += -DYOSYSHQ_VERIFIC_EXTENSIONS
endif
CXXFLAGS += $(patsubst %,-I$(VERIFIC_DIR)/%,$(VERIFIC_COMPONENTS)) -DYOSYS_ENABLE_VERIFIC
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
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
@ -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/rtlil.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/timinginfo.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/blif/blifparse.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/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)
OBJS += kernel/fstdata.o
endif
@ -679,12 +663,8 @@ OBJS += libs/bigint/BigUnsigned.o libs/bigint/BigUnsignedInABase.o
OBJS += libs/sha1/sha1.o
ifneq ($(SMALL),1)
OBJS += libs/json11/json11.o
OBJS += libs/subcircuit/subcircuit.o
OBJS += libs/ezsat/ezsat.o
OBJS += libs/ezsat/ezminisat.o
@ -699,6 +679,10 @@ OBJS += libs/fst/fastlz.o
OBJS += libs/fst/lz4.o
endif
ifneq ($(SMALL),1)
OBJS += libs/subcircuit/subcircuit.o
include $(YOSYS_SRC)/frontends/*/Makefile.inc
include $(YOSYS_SRC)/passes/*/Makefile.inc
include $(YOSYS_SRC)/backends/*/Makefile.inc
@ -707,6 +691,9 @@ include $(YOSYS_SRC)/techlibs/*/Makefile.inc
else
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/ast/Makefile.inc
include $(YOSYS_SRC)/frontends/blif/Makefile.inc
@ -748,13 +735,13 @@ yosys.js: $(filter-out yosysjs-$(YOSYS_VER).zip,$(EXTRA_TARGETS))
endif
$(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))
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
$(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
%.o: %.cc
@ -763,7 +750,7 @@ endif
%.pyh: %.h
$(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)
$(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)
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
CXXFLAGS_NOVERIFIC = $(CXXFLAGS)
LDLIBS_NOVERIFIC = $(LDLIBS)
LIBS_NOVERIFIC = $(LIBS)
endif
$(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))))#;' \
-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
$(Q) chmod +x $(PROGRAM_PREFIX)yosys-config
@ -844,9 +831,22 @@ else
ABCOPT=""
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)
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)
endif
endif
+cd tests/simple && 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: libyosys.so
@$(MAKE) -C $(UNITESTPATH) CXX="$(CXX)" CPPFLAGS="$(CPPFLAGS)" \
CXXFLAGS="$(CXXFLAGS)" LDLIBS="$(LDLIBS)" ROOTPATH="$(CURDIR)"
CXXFLAGS="$(CXXFLAGS)" LIBS="$(LIBS)" ROOTPATH="$(CURDIR)"
clean-unit-test:
@$(MAKE) -C $(UNITESTPATH) clean

View File

@ -1,7 +1,7 @@
```
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
purpose with or without fee is hereby granted, provided that the above
@ -587,7 +587,13 @@ from SystemVerilog:
- enums are supported (including inside packages)
- 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
ports are inputs or outputs are supported.

View File

@ -54,6 +54,8 @@ struct AigerWriter
vector<pair<int, int>> aig_gates;
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_b = 0, aig_c = 0, aig_j = 0, aig_f = 0;
@ -81,6 +83,23 @@ struct AigerWriter
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()
// since this function is called recursively
@ -101,6 +120,8 @@ struct AigerWriter
a = initstate_ff;
}
bit2aig_stack.pop_back();
if (bit == State::Sx || bit == State::Sz)
log_error("Design contains 'x' or 'z' bits. Use 'setundef' to replace those constants.\n");
@ -299,6 +320,9 @@ struct AigerWriter
continue;
}
if (cell->type == ID($scopeinfo))
continue;
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())
{
if (cell->type == ID($scopeinfo))
continue;
if (config->unbuf_types.count(cell->type)) {
auto portnames = config->unbuf_types.at(cell->type);
f << stringf(".names %s %s\n1 1\n",

View File

@ -1,2 +1,11 @@
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/log.h"
#include "kernel/fmt.h"
#include "kernel/scopeinfo.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@ -218,7 +219,7 @@ bool is_internal_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)
@ -282,7 +283,7 @@ struct FlowGraph {
CONNECT,
CELL_SYNC,
CELL_EVAL,
PRINT_SYNC,
EFFECT_SYNC,
PROCESS_SYNC,
PROCESS_CASE,
MEM_RDPORT,
@ -292,7 +293,7 @@ struct FlowGraph {
Type type;
RTLIL::SigSig connect = {};
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 Mem *mem = nullptr;
int portidx;
@ -480,11 +481,11 @@ struct FlowGraph {
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->type = Node::Type::PRINT_SYNC;
node->print_sync_cells = cells;
node->type = Node::Type::EFFECT_SYNC;
node->cells = cells;
nodes.push_back(node);
return node;
}
@ -1063,99 +1064,6 @@ struct CxxrtlWorker {
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)
{
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)
{
std::vector<const RTLIL::Cell*> inlined_cells;
@ -1322,30 +1368,34 @@ struct CxxrtlWorker {
f << " = ";
dump_cell_expr(cell, for_debug);
f << ";\n";
// $print cell
} else if (cell->type == ID($print)) {
// Effectful cells
} else if (is_effectful_cell(cell->type)) {
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));
if (!cell->getParam(ID::TRG_ENABLE).as_bool()) { // async $print cell
f << indent << "auto " << mangle(cell) << "_curr = ";
if (!cell->getParam(ID::TRG_ENABLE).as_bool()) { // async effectful cell
f << indent << "auto " << mangle(cell) << "_next = ";
dump_sigspec_rhs(cell->getPort(ID::EN));
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 << indent << "if (" << mangle(cell) << " != " << mangle(cell) << "_curr) {\n";
f << indent << "if (" << mangle(cell) << " != " << mangle(cell) << "_next) {\n";
inc_indent();
dump_print(cell);
f << indent << mangle(cell) << " = " << mangle(cell) << "_curr;\n";
dump_effect(cell);
f << indent << mangle(cell) << " = " << mangle(cell) << "_next;\n";
dec_indent();
f << indent << "}\n";
} else { // initial $print cell
} else { // initial effectful cell
f << indent << "if (!" << mangle(cell) << ") {\n";
inc_indent();
dump_print(cell);
dump_effect(cell);
f << indent << mangle(cell) << " = value<1>{1u};\n";
dec_indent();
f << indent << "}\n";
@ -1446,7 +1496,7 @@ struct CxxrtlWorker {
f << indent;
dump_sigspec_lhs(cell->getPort(ID::Q));
f << " = ";
dump_sigspec_lhs(cell->getPort(ID::Q));
dump_sigspec_rhs(cell->getPort(ID::Q));
f << ".update(";
dump_const(RTLIL::Const(RTLIL::S1, cell->getParam(ID::WIDTH).as_int()));
f << ", ";
@ -1458,7 +1508,7 @@ struct CxxrtlWorker {
f << indent;
dump_sigspec_lhs(cell->getPort(ID::Q));
f << " = ";
dump_sigspec_lhs(cell->getPort(ID::Q));
dump_sigspec_rhs(cell->getPort(ID::Q));
f << ".update(";
dump_const(RTLIL::Const(RTLIL::S0, cell->getParam(ID::WIDTH).as_int()));
f << ", ";
@ -1469,8 +1519,9 @@ struct CxxrtlWorker {
} else if (is_internal_cell(cell->type)) {
log_cmd_error("Unsupported internal cell `%s'.\n", cell->type.c_str());
// User cells
} else if (for_debug) {
// Outlines are called on demand when computing the value of a debug item. Nothing to do here.
} else {
log_assert(!for_debug);
log_assert(cell->known());
bool buffered_inputs = false;
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)
{
auto &port = mem->rd_ports[portidx];
@ -2047,11 +2139,10 @@ struct CxxrtlWorker {
}
}
for (auto cell : module->cells()) {
// Certain $print cells have additional state, which must be reset as well.
if (cell->type == ID($print) && !cell->getParam(ID::TRG_ENABLE).as_bool())
f << indent << mangle(cell) << " = value<" << (1 + cell->getParam(ID::ARGS_WIDTH).as_int()) << ">();\n";
if (cell->type == ID($print) && cell->getParam(ID::TRG_ENABLE).as_bool() && cell->getParam(ID::TRG_WIDTH).as_int() == 0)
f << indent << mangle(cell) << " = value<1>();\n";
// Async and initial effectful cells have additional state, which must be reset as well.
if (is_effectful_cell(cell->type))
if (!cell->getParam(ID::TRG_ENABLE).as_bool() || cell->getParam(ID::TRG_WIDTH).as_int() == 0)
f << indent << mangle(cell) << " = {};\n";
if (is_internal_cell(cell->type))
continue;
f << indent << mangle(cell);
@ -2099,8 +2190,8 @@ struct CxxrtlWorker {
case FlowGraph::Node::Type::CELL_EVAL:
dump_cell_eval(node.cell);
break;
case FlowGraph::Node::Type::PRINT_SYNC:
dump_sync_print(node.print_sync_cells);
case FlowGraph::Node::Type::EFFECT_SYNC:
dump_cell_effect_sync(node.cells);
break;
case FlowGraph::Node::Type::PROCESS_CASE:
dump_process_case(node.process);
@ -2221,11 +2312,14 @@ struct CxxrtlWorker {
dict<RTLIL::IdString, RTLIL::Const> attributes = object->attributes;
// Inherently necessary to get access to the object, so a waste of space to emit.
attributes.erase(ID::hdlname);
// Internal Yosys attribute that should be removed but isn't.
attributes.erase(ID::module_not_derived);
dump_metadata_map(attributes);
}
void dump_debug_info_method(RTLIL::Module *module)
{
size_t count_scopes = 0;
size_t count_public_wires = 0;
size_t count_member_wires = 0;
size_t count_undriven = 0;
@ -2238,153 +2332,188 @@ struct CxxrtlWorker {
size_t count_skipped_wires = 0;
inc_indent();
f << indent << "assert(path.empty() || path[path.size() - 1] == ' ');\n";
for (auto wire : module->wires()) {
const auto &debug_wire_type = debug_wire_types[wire];
if (!wire->name.isPublic())
continue;
count_public_wires++;
switch (debug_wire_type.type) {
case WireType::BUFFERED:
case WireType::MEMBER: {
// Member wire
std::vector<std::string> flags;
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 << indent << "if (scopes) {\n";
inc_indent();
// The module is responsible for adding its own scope.
f << indent << "scopes->add(path.empty() ? path : path.substr(0, path.size() - 1), ";
f << escape_cxx_string(get_hdl_name(module)) << ", ";
dump_debug_attrs(module);
f << ", std::move(cell_attrs));\n";
count_scopes++;
// If there were any submodules that were flattened, the module is also responsible for adding them.
for (auto cell : module->cells()) {
if (cell->type != ID($scopeinfo)) continue;
if (cell->getParam(ID::TYPE).decode_string() == "module") {
auto module_attrs = scopeinfo_attributes(cell, ScopeinfoAttrs::Module);
auto cell_attrs = scopeinfo_attributes(cell, ScopeinfoAttrs::Cell);
cell_attrs.erase(ID::module_not_derived);
f << indent << "scopes->add(path + " << escape_cxx_string(get_hdl_name(cell)) << ", ";
f << escape_cxx_string(cell->get_string_attribute(ID(module))) << ", ";
dump_metadata_map(module_attrs);
f << ", ";
dump_metadata_map(cell_attrs);
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;
}
} else log_assert(false && "Unknown $scopeinfo type");
count_scopes++;
}
}
if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) {
for (auto &mem : mod_memories[module]) {
if (!mem.memid.isPublic())
dec_indent();
f << indent << "}\n";
f << indent << "if (items) {\n";
inc_indent();
for (auto wire : module->wires()) {
const auto &debug_wire_type = debug_wire_types[wire];
if (!wire->name.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);
count_public_wires++;
switch (debug_wire_type.type) {
case WireType::BUFFERED:
case WireType::MEMBER: {
// Member wire
std::vector<std::string> flags;
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()) {
if (is_internal_cell(cell->type))
continue;
const char *access = is_cxxrtl_blackbox_cell(cell) ? "->" : ".";
f << indent << mangle(cell) << access << "debug_info(items, ";
f << "path + " << escape_cxx_string(get_hdl_name(cell) + ' ') << ");\n";
f << indent << mangle(cell) << access;
f << "debug_info(items, scopes, path + " << escape_cxx_string(get_hdl_name(cell) + ' ') << ", ";
dump_debug_attrs(cell);
f << ");\n";
}
}
dec_indent();
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(" Member wires: %zu, of which:\n", count_member_wires);
log_debug(" Undriven: %zu (incl. inputs)\n", count_undriven);
@ -2422,18 +2551,18 @@ struct CxxrtlWorker {
dump_eval_method(module);
f << indent << "}\n";
f << "\n";
f << indent << "template<class ObserverT>\n";
f << indent << "bool commit(ObserverT &observer) {\n";
f << indent << "virtual bool commit(observer &observer) {\n";
dump_commit_method(module);
f << indent << "}\n";
f << "\n";
f << indent << "bool commit() override {\n";
f << indent << indent << "observer observer;\n";
f << indent << indent << "return commit<>(observer);\n";
f << indent << indent << "return commit(observer);\n";
f << indent << "}\n";
if (debug_info) {
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);
f << indent << "}\n";
}
@ -2481,11 +2610,15 @@ struct CxxrtlWorker {
f << "\n";
bool has_cells = false;
for (auto cell : module->cells()) {
// Certain $print cells have additional state, which requires storage.
if (cell->type == ID($print) && !cell->getParam(ID::TRG_ENABLE).as_bool())
f << indent << "value<" << (1 + cell->getParam(ID::ARGS_WIDTH).as_int()) << "> " << mangle(cell) << ";\n";
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 and initial effectful cells have additional state, which requires storage.
if (is_effectful_cell(cell->type)) {
if (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
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))
continue;
dump_attrs(cell);
@ -2538,7 +2671,8 @@ struct CxxrtlWorker {
}
}
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();
f << indent << "}; // struct " << mangle(module) << "\n";
@ -2566,7 +2700,8 @@ struct CxxrtlWorker {
}
f << "\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);
f << indent << "}\n";
}
@ -2803,8 +2938,8 @@ struct CxxrtlWorker {
cell->parameters[ID::CLK_POLARITY].as_bool() ? RTLIL::STp : RTLIL::STn);
}
// $print cells may be triggered on posedge/negedge events.
if (cell->type == ID($print) && cell->getParam(ID::TRG_ENABLE).as_bool()) {
// Effectful cells may be triggered on posedge/negedge events.
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++) {
RTLIL::SigBit trg = cell->getPort(ID::TRG).extract(i, 1);
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.
pool<FlowGraph::Node*, hash_ptr_ops> worklist;
for (auto node : flow.nodes) {
if (node->type == FlowGraph::Node::Type::CELL_EVAL && is_effectful_cell(node->cell->type))
worklist.insert(node); // node has effects
else if (node->type == FlowGraph::Node::Type::PRINT_SYNC)
worklist.insert(node); // node is sync $print
if (node->type == FlowGraph::Node::Type::CELL_EVAL && !is_internal_cell(node->cell->type))
worklist.insert(node); // node evaluates a submodule
else if (node->type == FlowGraph::Node::Type::CELL_EVAL && is_effectful_cell(node->cell->type))
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)
worklist.insert(node); // node is memory write
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().
// Accumulate sync $print cells per trigger condition.
dict<std::pair<RTLIL::SigSpec, RTLIL::Const>, std::vector<const RTLIL::Cell*>> sync_print_cells;
// Accumulate sync effectful cells per trigger condition.
dict<std::pair<RTLIL::SigSpec, RTLIL::Const>, std::vector<const RTLIL::Cell*>> effect_sync_cells;
for (auto node : node_order)
if (live_nodes[node]) {
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_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
schedule[module].push_back(*node);
}
for (auto &it : sync_print_cells) {
auto node = flow.add_print_sync_node(it.second);
for (auto &it : effect_sync_cells) {
auto node = flow.add_effect_sync_node(it.second);
schedule[module].push_back(*node);
}
@ -3326,8 +3463,7 @@ struct CxxrtlBackend : public Backend {
log(" wire<8> p_o_data;\n");
log("\n");
log(" bool eval(performer *performer) override;\n");
log(" template<class ObserverT>\n");
log(" bool commit(ObserverT &observer);\n");
log(" virtual bool commit(observer &observer);\n");
log(" bool commit() override;\n");
log("\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, "");
}
cxxrtl_handle cxxrtl_create_at(cxxrtl_toplevel design, const char *root) {
std::string path = root;
if (!path.empty()) {
cxxrtl_handle cxxrtl_create_at(cxxrtl_toplevel design, const char *top_path_) {
std::string top_path = top_path_;
if (!top_path.empty()) {
// 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
// would be a lot worse in the C API, so don't expose it here.
assert(path.back() != ' ');
path += ' ';
assert(top_path.back() != ' ');
top_path += ' ';
}
cxxrtl_handle handle = new _cxxrtl_handle;
handle->module = std::move(design->module);
handle->module->debug_info(handle->objects, path);
handle->module->debug_info(handle->objects, top_path);
delete design;
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.
//
// This operation is similar to `cxxrtl_create`, except the full hierarchical name of every object
// is prepended with `root`.
cxxrtl_handle cxxrtl_create_at(cxxrtl_toplevel design, const char *root);
// is prepended with `top_path`.
cxxrtl_handle cxxrtl_create_at(cxxrtl_toplevel design, const char *top_path);
// Release all resources used by a design and its 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
// 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.
//
// 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 *next;

View File

@ -952,7 +952,23 @@ struct lazy_fmt {
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 {
// Called by generated formatting code to evaluate a Verilog `$time` expression.
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) {
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
@ -1151,7 +1176,7 @@ struct debug_item : ::cxxrtl_object {
template<size_t Bits>
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");
type = VALUE;
flags = flags_;
@ -1167,7 +1192,7 @@ struct debug_item : ::cxxrtl_object {
template<size_t Bits>
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");
type = VALUE;
flags = DRIVEN_COMB;
@ -1183,8 +1208,9 @@ struct debug_item : ::cxxrtl_object {
template<size_t Bits>
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) &&
sizeof(item.next) == value<Bits>::chunks * sizeof(chunk_t),
static_assert(Bits == 0 ||
(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");
type = WIRE;
flags = flags_;
@ -1200,7 +1226,7 @@ struct debug_item : ::cxxrtl_object {
template<size_t Width>
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");
type = MEMORY;
flags = 0;
@ -1216,7 +1242,7 @@ struct debug_item : ::cxxrtl_object {
template<size_t Bits>
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");
type = ALIAS;
flags = DRIVEN_COMB;
@ -1232,8 +1258,9 @@ struct debug_item : ::cxxrtl_object {
template<size_t Bits>
debug_item(debug_alias, const wire<Bits> &item, size_t lsb_offset = 0) {
static_assert(sizeof(item.curr) == value<Bits>::chunks * sizeof(chunk_t) &&
sizeof(item.next) == value<Bits>::chunks * sizeof(chunk_t),
static_assert(Bits == 0 ||
(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");
type = ALIAS;
flags = DRIVEN_COMB;
@ -1249,7 +1276,7 @@ struct debug_item : ::cxxrtl_object {
template<size_t Bits>
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");
type = OUTLINE;
flags = DRIVEN_COMB;
@ -1293,17 +1320,26 @@ namespace cxxrtl {
using debug_attrs = ::_cxxrtl_attr_set;
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::unique_ptr<debug_attrs>> attrs_table;
void add(const std::string &name, debug_item &&item, metadata_map &&item_attrs = {}) {
std::unique_ptr<debug_attrs> &attrs = attrs_table[name];
void add(const std::string &path, debug_item &&item, metadata_map &&item_attrs = {}) {
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)
attrs = std::unique_ptr<debug_attrs>(new debug_attrs);
for (auto attr : item_attrs)
attrs->map.insert(attr);
item.attrs = attrs.get();
std::vector<debug_item> &parts = table[name];
std::vector<debug_item> &parts = table[path];
parts.emplace_back(item);
std::sort(parts.begin(), parts.end(),
[](const debug_item &a, const debug_item &b) {
@ -1311,35 +1347,71 @@ struct debug_items {
});
}
size_t count(const std::string &name) const {
if (table.count(name) == 0)
size_t count(const std::string &path) const {
if (table.count(path) == 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 {
return table.at(name);
const std::vector<debug_item> &at(const std::string &path) const {
return table.at(path);
}
const debug_item &at(const std::string &name) const {
const std::vector<debug_item> &parts = table.at(name);
// Like `at()`, but operates only on single-part debug items.
const debug_item &operator [](const std::string &path) const {
const std::vector<debug_item> &parts = table.at(path);
assert(parts.size() == 1);
return parts.at(0);
}
const debug_item &operator [](const std::string &name) const {
return at(name);
bool is_memory(const std::string &path) const {
return at(path).at(0).type == debug_item::MEMORY;
}
const metadata_map &attrs(const std::string &name) const {
return attrs_table.at(name)->map;
const metadata_map &attrs(const std::string &path) const {
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.
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 {
module() {}
virtual ~module() {}
@ -1355,8 +1427,14 @@ struct module {
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 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 deltas = 0;
@ -1368,8 +1446,16 @@ struct module {
return deltas;
}
virtual void debug_info(debug_items &items, std::string path = "") {
(void)items, (void)path;
virtual void debug_info(debug_items *items, debug_scopes *scopes, std::string path, metadata_map &&cell_attrs = {}) {
(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>
recorder(Args &&...args) : writer(std::forward<Args>(args)...) {}
void start(module &module) {
void start(module &module, std::string top_path = "") {
debug_items items;
module.debug_info(items);
module.debug_info(&items, /*scopes=*/nullptr, top_path);
start(items);
}
@ -556,7 +556,7 @@ public:
bool record_incremental(ModuleT &module) {
assert(streaming);
struct {
struct : observer {
std::unordered_map<const chunk_t*, spool::ident_t> *ident_lookup;
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) {
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);
for (auto input_index : inputs) {
@ -619,9 +621,10 @@ public:
template<typename ...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;
module.debug_info(items);
module.debug_info(&items, /*scopes=*/nullptr, top_path);
start(items);
}
@ -641,7 +644,7 @@ public:
assert(items.count(name) != 0);
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(depth == part.depth);

View File

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

View File

@ -980,6 +980,9 @@ struct FirrtlWorker
register_reverse_wire_map(y_id, cell->getPort(ID::Y));
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));
}

View File

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

View File

@ -920,7 +920,7 @@ class SmtIo:
if len(expr_list) == 0:
return []
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):
assert mod in self.modinfo

View File

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

View File

@ -72,6 +72,9 @@ static void print_spice_module(std::ostream &f, RTLIL::Module *module, RTLIL::De
for (auto cell : module->cells())
{
if (cell->type == ID($scopeinfo))
continue;
f << stringf("X%d", cell_counter++);
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)
{
Fmt fmt = {};
Fmt fmt;
fmt.parse_rtlil(cell);
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");
}
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)
{
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");
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_))) {
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;
}
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
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)
{
// 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
if (cell->is_mem_cell())
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) {
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) {
f << stringf("%s" " if (", indent.c_str());
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());
@ -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_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) {
if (it->first.size() == 0)
continue;
@ -1965,15 +2046,6 @@ void dump_case_body(std::ostream &f, std::string indent, RTLIL::CaseRule *cs, bo
dump_sigspec(f, it->second);
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)
@ -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;
auto sig_it = sw->signal.begin();
for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it, ++sig_it) {
bool had_newline = true;
if (it != sw->cases.begin()) {
if ((*it)->compare.empty()) {
f << indent << "else\n";
had_newline = true;
} else {
f << indent << "else ";
had_newline = false;
}
if ((*it)->compare.empty())
f << " else begin\n";
else
f << " else ";
}
if (!(*it)->compare.empty()) {
if (!(*it)->attributes.empty()) {
if (!had_newline)
f << "\n" << indent;
dump_attributes(f, "", (*it)->attributes, "\n" + indent);
}
f << stringf("if (");
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())
break;
}
f << "\n";
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)
{
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)
{
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();
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;
for (auto cell : module->cells())
{
if (cell->type == ID($print) && cell->getParam(ID::TRG_ENABLE).as_bool()) {
sync_print_cells[make_pair(cell->getPort(ID::TRG), cell->getParam(ID::TRG_POLARITY))].push_back(cell);
if (cell->type.in(ID($print), ID($check)) && cell->getParam(ID::TRG_ENABLE).as_bool()) {
sync_effect_cells[make_pair(cell->getPort(ID::TRG), cell->getParam(ID::TRG_POLARITY))].push_back(cell);
continue;
}
@ -2263,8 +2351,8 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
for (auto cell : module->cells())
dump_cell(f, indent + " ", cell);
for (auto &it : sync_print_cells)
dump_sync_print(f, indent + " ", it.first.first, it.first.second, it.second);
for (auto &it : sync_effect_cells)
dump_sync_effect(f, indent + " ", it.first.first, it.first.second, it.second);
for (auto it = module->processes.begin(); it != module->processes.end(); ++it)
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
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

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).
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
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
@ -627,7 +627,7 @@ Add information about ``$specify2``, ``$specify3``, and ``$specrule`` cells.
Formal verification cells
~~~~~~~~~~~~~~~~~~~~~~~~~
Add information about ``$assert``, ``$assume``, ``$live``, ``$fair``,
Add information about ``$check``, ``$assert``, ``$assume``, ``$live``, ``$fair``,
``$cover``, ``$equiv``, ``$initstate``, ``$anyconst``, ``$anyseq``,
``$anyinit``, ``$allconst``, ``$allseq`` cells.
@ -660,8 +660,8 @@ If ``\TRG_ENABLE`` is true, the following parameters also apply:
negative-edge triggered.
``\PRIORITY``
When multiple ``$print`` cells fire on the same trigger, they execute in
descending priority order.
When multiple ``$print`` or ``$$check`` cells fire on the same trigger, they\
execute in descending priority order.
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;
range_left = -1;
range_right = 0;
unpacked_dimensions = 0;
integer = 0;
realvalue = 0;
id2ast = NULL;
@ -349,17 +350,15 @@ void AstNode::dumpAst(FILE *f, std::string indent) const
fprintf(f, " int=%u", (int)integer);
if (realvalue != 0)
fprintf(f, " real=%e", realvalue);
if (!multirange_dimensions.empty()) {
fprintf(f, " multirange=[");
for (int v : multirange_dimensions)
fprintf(f, " %d", v);
fprintf(f, " ]");
}
if (!multirange_swapped.empty()) {
fprintf(f, " multirange_swapped=[");
for (bool v : multirange_swapped)
fprintf(f, " %d", v);
fprintf(f, " ]");
if (!dimensions.empty()) {
fprintf(f, " dimensions=");
for (auto &dim : dimensions) {
int left = dim.range_right + dim.range_width - 1;
int right = dim.range_right;
if (dim.range_swapped)
std::swap(left, right);
fprintf(f, "[%d:%d]", left, right);
}
}
if (is_enum) {
fprintf(f, " type=enum");
@ -489,6 +488,20 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const
fprintf(f, ";\n");
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:
if (range_valid) {
if (range_swapped)
@ -505,6 +518,11 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const
}
break;
case AST_MULTIRANGE:
for (auto child : children)
child->dumpVlog(f, "");
break;
case AST_ALWAYS:
fprintf(f, "%s" "always @", indent.c_str());
for (auto child : children) {
@ -542,7 +560,7 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const
case AST_IDENTIFIER:
{
AST::AstNode *member_node = AST::get_struct_member(this);
AstNode *member_node = get_struct_member();
if (member_node)
fprintf(f, "%s[%d:%d]", id2vl(str).c_str(), member_node->range_left, member_node->range_right);
else
@ -552,6 +570,12 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const
child->dumpVlog(f, "");
break;
case AST_STRUCT:
case AST_UNION:
case AST_STRUCT_ITEM:
fprintf(f, "%s", id2vl(str).c_str());
break;
case AST_CONSTANT:
if (!str.empty())
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();
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;
for (auto child : new_ast->children) {

View File

@ -202,9 +202,17 @@ namespace AST
// set for IDs typed to an enumeration, not used
bool is_enum;
// if this is a multirange memory then this vector contains offset and length of each dimension
std::vector<int> multirange_dimensions;
std::vector<bool> multirange_swapped; // true if range is swapped
// Declared range for array dimension.
struct dimension_t {
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
AstNode *id2ast;
@ -371,6 +379,10 @@ namespace AST
// localized fixups after modifying children/attributes of a particular node
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
[[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.
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
// 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);

View File

@ -163,6 +163,28 @@ static RTLIL::SigSpec mux2rtlil(AstNode *that, const RTLIL::SigSpec &cond, const
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
struct AST_INTERNAL::LookaheadRewriter
{
@ -316,10 +338,10 @@ struct AST_INTERNAL::ProcessGenerator
// Buffer for generating the init action
RTLIL::SigSpec init_lvalue, init_rvalue;
// The most recently assigned $print cell \PRIORITY.
int last_print_priority;
// The most recently assigned $print or $check cell \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
LookaheadRewriter la_rewriter(always);
@ -703,8 +725,10 @@ struct AST_INTERNAL::ProcessGenerator
std::stringstream sstr;
sstr << ast->str << "$" << ast->filename << ":" << ast->location.first_line << "$" << (autoidx++);
RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($print));
set_src_attr(cell, ast);
Wire *en = current_module->addWire(sstr.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;
@ -717,18 +741,15 @@ struct AST_INTERNAL::ProcessGenerator
polarity.bits.push_back(RTLIL::S0);
}
}
cell->parameters[ID::TRG_WIDTH] = triggers.size();
cell->parameters[ID::TRG_ENABLE] = (always->type == AST_INITIAL) || !triggers.empty();
cell->parameters[ID::TRG_POLARITY] = polarity;
cell->parameters[ID::PRIORITY] = --last_print_priority;
RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($print));
set_src_attr(cell, ast);
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);
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));
cell->setPort(ID::EN, en);
int default_base = 10;
if (ast->str.back() == 'b')
@ -766,7 +787,7 @@ struct AST_INTERNAL::ProcessGenerator
args.push_back(arg);
}
Fmt fmt = {};
Fmt fmt;
fmt.parse_verilog(args, /*sformat_like=*/false, default_base, /*task_name=*/ast->str, current_module->name);
if (ast->str.substr(0, 8) == "$display")
fmt.append_string("\n");
@ -776,6 +797,70 @@ struct AST_INTERNAL::ProcessGenerator
}
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_FOR:
break;
@ -960,7 +1045,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
if (children.size() > 1)
range = children[1];
} 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;
delete tmp_range;
} else
@ -1242,28 +1327,6 @@ void AstNode::detectSignWidth(int &width_hint, bool &sign_hint, bool *found_real
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
// 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.
@ -1521,7 +1584,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
chunk.width = wire->width;
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.
chunk.width = member_node->range_left - member_node->range_right + 1;
chunk.offset = member_node->range_right;
@ -1945,48 +2008,50 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
}
break;
// generate $assert cells
// generate $check cells
case AST_ASSERT:
case AST_ASSUME:
case AST_LIVE:
case AST_FAIR:
case AST_COVER:
{
IdString celltype;
if (type == AST_ASSERT) celltype = ID($assert);
if (type == AST_ASSUME) celltype = ID($assume);
if (type == AST_LIVE) celltype = ID($live);
if (type == AST_FAIR) celltype = ID($fair);
if (type == AST_COVER) celltype = ID($cover);
std::string flavor, desc;
if (type == AST_ASSERT) { flavor = "assert"; desc = "assert property ()"; }
if (type == AST_ASSUME) { flavor = "assume"; desc = "assume property ()"; }
if (type == AST_LIVE) { flavor = "live"; desc = "assert property (eventually)"; }
if (type == AST_FAIR) { flavor = "fair"; desc = "assume property (eventually)"; }
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();
if (GetSize(check) != 1)
check = current_module->ReduceBool(NEW_ID, check);
RTLIL::SigSpec en = children[1]->genRTLIL();
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);
RTLIL::Cell *cell = current_module->addCell(cellname, ID($check));
set_src_attr(cell, this);
for (auto &attr : attributes) {
if (attr.second->type != AST_CONSTANT)
input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str());
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::EN, en);
// No message is emitted to ensure Verilog code roundtrips correctly.
Fmt fmt;
fmt.emit_rtlil(cell);
}
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;
}
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"))
{
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 sig;
for (int i = int(inst->InputSize())-1; i >= 0; i--)
if (inst->GetInputBit(i))
sig.append(net_map_at(inst->GetInputBit(i)));
else
sig.append(RTLIL::State::Sz);
for (int i = int(inst->InputSize())-1; i >= 0; i--) {
Net *net = inst->GetInputBit(i);
sig.append(netToSigBit(net));
}
return sig;
}
RTLIL::SigSpec VerificImporter::operatorInput1(Instance *inst)
{
RTLIL::SigSpec sig;
for (int i = int(inst->Input1Size())-1; i >= 0; i--)
if (inst->GetInput1Bit(i))
sig.append(net_map_at(inst->GetInput1Bit(i)));
else
sig.append(RTLIL::State::Sz);
for (int i = int(inst->Input1Size())-1; i >= 0; i--) {
Net *net = inst->GetInput1Bit(i);
sig.append(netToSigBit(net));
}
return sig;
}
RTLIL::SigSpec VerificImporter::operatorInput2(Instance *inst)
{
RTLIL::SigSpec sig;
for (int i = int(inst->Input2Size())-1; i >= 0; i--)
if (inst->GetInput2Bit(i))
sig.append(net_map_at(inst->GetInput2Bit(i)));
else
sig.append(RTLIL::State::Sz);
for (int i = int(inst->Input2Size())-1; i >= 0; i--) {
Net *net = inst->GetInput2Bit(i);
sig.append(netToSigBit(net));
}
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);
import_attributes(cell->attributes, inst);
if (inst->IsPrimitive() && mode_keep)
cell->attributes[ID::keep] = 1;
@ -2815,6 +2826,9 @@ struct VerificPass : public Pass {
log(" -extnets\n");
log(" Resolve references to external nets by adding module ports as needed.\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(" Generate automatic cover statements for all asserts\n");
log("\n");
@ -3548,6 +3562,7 @@ struct VerificPass : public Pass {
bool mode_nosva = false, mode_names = false, mode_verific = false;
bool mode_autocover = false, mode_fullinit = false;
bool flatten = false, extnets = false, mode_cells = false;
bool split_complex_ports = true;
string dumpfile;
string ppfile;
Map parameters(STRING_HASH);
@ -3565,6 +3580,10 @@ struct VerificPass : public Pass {
flatten = true;
continue;
}
if (args[argidx] == "-no-split-complex-ports") {
split_complex_ports = false;
continue;
}
if (args[argidx] == "-extnets") {
extnets = true;
continue;
@ -3804,8 +3823,10 @@ struct VerificPass : public Pass {
worker.run(nl.second);
}
for (auto nl : nl_todo)
nl.second->ChangePortBusStructures(1 /* hierarchical */);
if (split_complex_ports) {
for (auto nl : nl_todo)
nl.second->ChangePortBusStructures(1 /* hierarchical */);
}
if (!dumpfile.empty()) {
VeriWrite veri_writer;

View File

@ -83,6 +83,7 @@ struct VerificImporter
RTLIL::IdString new_verific_id(Verific::DesignObj *obj);
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 operatorInput1(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);
}
}
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;
}
@ -672,7 +683,7 @@ module_arg:
ast_stack.back()->children.push_back(astbuf2);
delete astbuf1; // really only needed if multiple instances of same type.
} module_arg_opt_assignment |
attr wire_type range TOK_ID {
attr wire_type range_or_multirange TOK_ID {
AstNode *node = $2;
node->str = *$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:
attr wire_type range {
attr wire_type range_or_multirange {
bool prev_was_input = true;
bool prev_was_output = false;
if (albuf) {
@ -1889,10 +1900,11 @@ struct_member_type: { astbuf1 = new AstNode(AST_STRUCT_ITEM); } member_type_toke
;
member_type_token:
member_type
| hierarchical_type_id {
addWiretypeNode($1, astbuf1);
}
member_type range_or_multirange {
AstNode *range = checkRange(astbuf1, $2);
if (range)
astbuf1->children.push_back(range);
}
| {
delete astbuf1;
} struct_union {
@ -1908,7 +1920,8 @@ member_type_token:
;
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
@ -1928,7 +1941,7 @@ struct_var: TOK_ID { auto *var_node = astbuf2->clone();
/////////
wire_decl:
attr wire_type range {
attr wire_type range_or_multirange {
albuf = $1;
astbuf1 = $2;
astbuf2 = checkRange(astbuf1, $3);
@ -2104,14 +2117,14 @@ type_name: TOK_ID // first time seen
;
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;
astbuf2 = checkRange(astbuf1, $3);
if (astbuf2)
astbuf1->children.push_back(astbuf2);
if ($5 != NULL) {
if (!astbuf2) {
if (!astbuf2 && !astbuf1->is_custom_type) {
addRange(astbuf1, 0, 0, false);
}
rewriteAsMemoryNode(astbuf1, $5);
@ -2484,7 +2497,7 @@ assert:
delete $5;
} else {
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)
node->str = *$1;
ast_stack.back()->children.push_back(node);
@ -2497,7 +2510,7 @@ assert:
delete $5;
} else {
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)
node->str = *$1;
ast_stack.back()->children.push_back(node);
@ -2510,7 +2523,7 @@ assert:
delete $6;
} else {
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)
node->str = *$1;
ast_stack.back()->children.push_back(node);
@ -2523,7 +2536,7 @@ assert:
delete $6;
} else {
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)
node->str = *$1;
ast_stack.back()->children.push_back(node);
@ -2533,7 +2546,7 @@ assert:
} |
opt_sva_label TOK_COVER opt_property '(' expr ')' ';' {
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) {
node->str = *$1;
delete $1;
@ -2542,7 +2555,7 @@ assert:
} |
opt_sva_label TOK_COVER opt_property '(' ')' ';' {
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) {
node->str = *$1;
delete $1;
@ -2551,7 +2564,7 @@ assert:
} |
opt_sva_label TOK_COVER ';' {
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) {
node->str = *$1;
delete $1;
@ -2563,7 +2576,7 @@ assert:
delete $5;
} else {
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)
node->str = *$1;
ast_stack.back()->children.push_back(node);
@ -2578,7 +2591,7 @@ assert:
delete $6;
} else {
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)
node->str = *$1;
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($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($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($get_tag), {ID::A}, {ID::Y});
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($future_ff), {ID::A}, {ID::Y});
setup_type(ID($scopeinfo), {}, {});
}
void setup_internals_eval()

View File

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

View File

@ -92,8 +92,15 @@ int getopt(int argc, char **argv, const char *optstring)
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;
return optopt;
}
@ -243,14 +250,6 @@ int main(int argc, char **argv)
bool mode_v = 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")))
{
printf("\n");
@ -538,6 +537,36 @@ int main(int argc, char **argv)
if (print_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)
log_hasher = new SHA1;
@ -569,6 +598,8 @@ int main(int argc, char **argv)
for (auto &fn : plugin_filenames)
load_plugin(fn, {});
log_suppressed();
if (!vlog_defines.empty()) {
std::string vdef_cmd = "read -define";
for (auto vdef : vlog_defines)

View File

@ -17,6 +17,8 @@
#include <string>
#include <vector>
#include <stdint.h>
namespace hashlib {
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()
{
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());
pass_register[pass_name] = this;
}
@ -447,13 +446,12 @@ Frontend::Frontend(std::string name, std::string short_help) :
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());
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());
frontend_register[frontend_name] = this;
}
@ -993,4 +991,44 @@ struct MinisatSatSolver : public SatSolver {
}
} 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

View File

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

View File

@ -1068,6 +1068,12 @@ namespace {
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)
{
auto it = cell->connections_.find(name);
@ -1747,6 +1753,31 @@ namespace {
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($_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; }
@ -2157,17 +2188,10 @@ void RTLIL::Module::remove(const pool<RTLIL::Wire*> &wires)
}
void operator()(RTLIL::SigSpec &lhs, RTLIL::SigSpec &rhs) {
log_assert(GetSize(lhs) == GetSize(rhs));
lhs.unpack();
rhs.unpack();
for (int i = 0; i < GetSize(lhs); i++) {
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;
}
}
// If a deleted wire occurs on the lhs or rhs we just remove that part
// of the assignment
lhs.remove2(*wires_p, &rhs);
rhs.remove2(*wires_p, &lhs);
}
};
@ -3693,6 +3717,9 @@ RTLIL::SigChunk::SigChunk(const RTLIL::SigBit &bit)
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;
if (wire) {
ret.wire = wire;
@ -4238,6 +4265,34 @@ void RTLIL::SigSpec::remove2(const std::set<RTLIL::SigBit> &pattern, RTLIL::SigS
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
{
if (other)
@ -4377,6 +4432,9 @@ void RTLIL::SigSpec::remove(int offset, int length)
RTLIL::SigSpec RTLIL::SigSpec::extract(int offset, int length) const
{
log_assert(offset >= 0);
log_assert(length >= 0);
log_assert(offset + length <= width_);
unpack();
cover("kernel.rtlil.sigspec.extract_pos");
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 {
unsigned int h = mkhash_init;
for (auto b : bits)
mkhash(h, b);
h = mkhash(h, b);
return h;
}
};
@ -924,6 +924,7 @@ public:
void remove(const pool<RTLIL::SigBit> &pattern, RTLIL::SigSpec *other) const;
void remove2(const pool<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_const();

View File

@ -1379,6 +1379,11 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep)
return true;
}
if (cell->type == ID($scopeinfo))
{
return true;
}
// Unsupported internal cell types: $pow $fsm $mem*
// .. and all sequential cells with asynchronous inputs
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>
#endif
#ifdef __FreeBSD__
#if defined(__FreeBSD__) || defined(__NetBSD__)
# include <sys/sysctl.h>
#endif
@ -138,27 +138,11 @@ void yosys_banner()
{
log("\n");
log(" /----------------------------------------------------------------------------\\\n");
log(" | |\n");
log(" | yosys -- Yosys Open SYnthesis Suite |\n");
log(" | |\n");
log(" | Copyright (C) 2012 - 2020 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(" | Copyright (C) 2012 - 2024 Claire Xenia Wolf <claire@yosyshq.com> |\n");
log(" | Distributed under an ISC-like license, type \"license\" to see terms |\n");
log(" \\----------------------------------------------------------------------------/\n");
log("\n");
log(" %s\n", yosys_version_str);
log("\n");
}
int ceil_log2(int x)
@ -436,6 +420,25 @@ std::string make_temp_dir(std::string template_str)
#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
bool check_file_exists(std::string filename, bool)
{
@ -481,6 +484,48 @@ void remove_directory(std::string dirname)
#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 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)
{
if (Tcl_Init(interp)!=TCL_OK)
if (Tcl_Init(interp)!=TCL_OK)
log_warning("Tcl_Init() call failed - %s\n",Tcl_ErrnoMsg(Tcl_GetErrno()));
Tcl_CreateCommand(interp, "yosys", tcl_yosys_cmd, NULL, NULL);
return TCL_OK ;
return TCL_OK ;
}
void yosys_tcl_activate_repl()
@ -856,10 +901,14 @@ std::string proc_self_dirname()
buflen--;
return std::string(path, buflen);
}
#elif defined(__FreeBSD__)
#elif defined(__FreeBSD__) || defined(__NetBSD__)
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};
#endif
size_t buflen;
char *buffer;
std::string path;

View File

@ -66,6 +66,8 @@
#include <stdint.h>
#include <stdio.h>
#include <limits.h>
#include <sys/stat.h>
#include <errno.h>
#ifdef WITH_PYTHON
#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_dir(std::string template_str = get_base_tmpdir() + "/yosys_XXXXXX");
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);
void remove_directory(std::string dirname);
bool create_directory(const std::string& dirname);
std::string escape_filename_spaces(const std::string& filename);
template<typename T> int GetSize(const T &obj) { return obj.size(); }

View File

@ -1,9 +1,9 @@
CC = clang
CXX = clang
CXX = clang++
CXXFLAGS = -MD -Wall -Wextra -ggdb
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
@ -27,4 +27,3 @@ clean:
.PHONY: all test clean
-include *.d

View File

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

View File

@ -1257,6 +1257,7 @@ class WFunction:
func.is_static = False
func.is_inline = False
func.is_virtual = False
func.is_const = False
func.ret_attr_type = attr_types.default
func.is_operator = False
func.member_of = None
@ -1334,6 +1335,11 @@ class WFunction:
found = find_closing(str_def, "(", ")")
if found == -1:
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]
if func.name in blacklist_methods:
return None
@ -1379,6 +1385,12 @@ class WFunction:
def gen_alias(self):
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):
if self.duplicate:
return ""
@ -1392,7 +1404,7 @@ class WFunction:
text += ", "
if len(self.args) > 0:
text = text[:-2]
text += ");\n"
text += f"){self.gen_post_qualifiers()};\n"
return text
def gen_decl_virtual(self):
@ -1411,12 +1423,18 @@ class WFunction:
if len(self.args) > 0:
text = text[:-2]
text += ")"
if len(self.args) == 0:
if len(self.args) == 0 and self.ret_type.name == "void":
text += "{}"
else:
text += "\n\t\t{"
for arg in self.args:
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\tvirtual "
if self.is_static:
@ -1427,7 +1445,7 @@ class WFunction:
text += ", "
if len(self.args) > 0:
text = text[:-2]
text += ") override;\n"
text += f"){self.gen_post_qualifiers()} override;\n"
return text
def gen_decl_hash_py(self):
@ -1452,7 +1470,7 @@ class WFunction:
text += ", "
if len(self.args) > 0:
text = text[:-2]
text +=")\n\t{"
text += f"){self.gen_post_qualifiers()}\n\t{{"
for arg in self.args:
text += arg.gen_translation()
text += "\n\t\t"
@ -1507,16 +1525,17 @@ class WFunction:
text += ", "
if len(self.args) > 0:
text = text[:-2]
text += ")\n\t{"
text += f"){self.gen_post_qualifiers()}\n\t{{"
for arg in self.args:
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:
text += "::" + self.namespace + "::" + self.alias + "("
elif self.is_static:
text += self.member_of.namespace + "::" + self.member_of.name + "::" + self.name + "("
else:
text += "py_" + self.alias + "("
text += f"const_cast<{self.member_of.name}*>(this)->py_" + self.alias + "("
for arg in self.args:
text += arg.gen_call_cpp() + ", "
if len(self.args) > 0:
@ -1547,11 +1566,13 @@ class WFunction:
call_string = call_string[0:-2]
call_string += ");"
return_stmt = "return " if self.ret_type.name != "void" else ""
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\t\t" + call_string
text += "\n\t\t\tif (boost::python::override py_" + self.alias + " = this->get_override(\"py_" + self.alias + "\"))"
text += f"\n\t\t\t\t{return_stmt}" + call_string
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\n\t\t" + self.ret_type.gen_text() + " default_py_" + self.alias + "("
@ -1559,8 +1580,8 @@ class WFunction:
text += arg.gen_listitem() + ", "
if len(self.args) > 0:
text = text[:-2]
text += ")\n\t\t{"
text += "\n\t\t\tthis->" + self.member_of.name + "::" + call_string
text += f")\n\t\t{{"
text += f"\n\t\t\t{return_stmt}this->" + self.member_of.name + "::" + call_string
text += "\n\t\t}"
return text
@ -1584,9 +1605,9 @@ class WFunction:
for a in self.args:
text += a.gen_listitem_hash() + ", "
if len(self.args) > 0:
text = text[0:-2] + ")>"
text = text[0:-2] + f"){self.gen_post_qualifiers(True)}>"
else:
text += "void)>"
text += f"void){self.gen_post_qualifiers(True)}>"
if self.is_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 " --cxx @CXX@"
echo " --cxxflags $( echo '@CXXFLAGS@' | fmt -w60 | sed ':a;N;$!ba;s/\n/ \\\n /g' )"
echo " --ldflags @LDFLAGS@"
echo " --ldlibs @LDLIBS@"
echo " --linkflags @LINKFLAGS@"
echo " --ldflags (alias of --linkflags)"
echo " --libs @LIBS@"
echo " --ldlibs (alias of --libs)"
echo " --bindir @BINDIR@"
echo " --datdir @DATDIR@"
echo ""
@ -18,7 +20,7 @@ help() {
echo ""
echo "Use --exec to call a command instead of generating output. Example usage:"
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 "The above command can be abbreviated as:"
echo ""
@ -44,7 +46,7 @@ fi
if [ "$1" == "--build" ]; then
modname="$2"; shift 2
set -- --exec --cxx --cxxflags --ldflags -o "$modname" -shared "$@" --ldlibs
set -- --exec --cxx --cxxflags --ldflags -o "$modname" -shared "$@" --libs
fi
prefix="--"
@ -63,10 +65,14 @@ for opt; do
tokens=( "${tokens[@]}" @CXX@ ) ;;
"$prefix"cxxflags)
tokens=( "${tokens[@]}" @CXXFLAGS@ ) ;;
"$prefix"linkflags)
tokens=( "${tokens[@]}" @LINKFLAGS@ ) ;;
"$prefix"libs)
tokens=( "${tokens[@]}" @LIBS@ ) ;;
"$prefix"ldflags)
tokens=( "${tokens[@]}" @LDFLAGS@ ) ;;
tokens=( "${tokens[@]}" @LINKFLAGS@ ) ;;
"$prefix"ldlibs)
tokens=( "${tokens[@]}" @LDLIBS@ ) ;;
tokens=( "${tokens[@]}" @LIBS@ ) ;;
"$prefix"bindir)
tokens=( "${tokens[@]}" '@BINDIR@' ) ;;
"$prefix"datdir)
@ -104,4 +110,3 @@ fi
echo "${tokens[@]}"
exit 0

View File

@ -23,6 +23,52 @@
USING_YOSYS_NAMESPACE
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 {
ChformalPass() : Pass("chformal", "change formal constraints of the design") { }
void help() override
@ -41,13 +87,18 @@ struct ChformalPass : public Pass {
log(" -fair $fair cells, representing assume(s_eventually ...)\n");
log(" -cover $cover cells, representing cover() statements\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("\n");
log(" -remove\n");
log(" remove the cells and thus constraints from the design\n");
log("\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(" -delay <N>\n");
log(" delay activation of the constraint by <N> clock cycles\n");
@ -69,6 +120,11 @@ struct ChformalPass : public Pass {
log(" -fair2live\n");
log(" change the roles of cells as indicated. these options can be combined\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
{
@ -146,6 +202,10 @@ struct ChformalPass : public Pass {
mode = 'c';
continue;
}
if (mode == 0 && args[argidx] == "-lower") {
mode = 'l';
continue;
}
break;
}
extra_args(args, argidx, design);
@ -166,7 +226,7 @@ struct ChformalPass : public Pass {
vector<Cell*> constr_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);
if (mode == 'r')
@ -216,6 +276,18 @@ struct ChformalPass : public Pass {
}
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)
{
SigSpec A = sigmap(cell->getPort(ID::A));
@ -225,8 +297,8 @@ struct ChformalPass : public Pass {
break;
if (!init_zero.count(EN)) {
if (cell->type == ID($cover)) break;
if (cell->type.in(ID($assert), ID($assume)) && !init_one.count(A)) break;
if (flavor == ID($cover)) break;
if (flavor.in(ID($assert), ID($assume)) && !init_one.count(A)) break;
}
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::EN, EN_map.first);
}
}
}
else
if (mode == 'd')
{
for (auto cell : constr_cells)
for (int i = 0; i < mode_arg; i++)
{
SigSpec orig_a = cell->getPort(ID::A);
SigSpec orig_en = cell->getPort(ID::EN);
if (is_triggered_check_cell(cell))
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);
Wire *new_en = module->addWire(NEW_ID);
new_en->attributes[ID::init] = State::S0;
for (int i = 0; i < mode_arg; i++)
{
SigSpec orig_a = cell->getPort(ID::A);
SigSpec orig_en = cell->getPort(ID::EN);
module->addFf(NEW_ID, orig_a, new_a);
module->addFf(NEW_ID, orig_en, new_en);
Wire *new_a = module->addWire(NEW_ID);
Wire *new_en = module->addWire(NEW_ID);
new_en->attributes[ID::init] = State::S0;
cell->setPort(ID::A, new_a);
cell->setPort(ID::EN, new_en);
module->addFf(NEW_ID, orig_a, new_a);
module->addFf(NEW_ID, orig_en, new_en);
cell->setPort(ID::A, new_a);
cell->setPort(ID::EN, new_en);
}
}
}
else
@ -278,21 +356,76 @@ struct ChformalPass : public Pass {
if (mode =='p')
{
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
if (mode == 'c')
{
for (auto cell : constr_cells)
if (assert2assume && cell->type == ID($assert))
cell->type = ID($assume);
else if (assume2assert && cell->type == ID($assume))
cell->type = ID($assert);
else if (live2fair && cell->type == ID($live))
cell->type = ID($fair);
else if (fair2live && cell->type == ID($fair))
cell->type = ID($live);
for (auto cell : constr_cells) {
IdString flavor = formal_flavor(cell);
if (assert2assume && flavor == ID($assert))
set_formal_flavor(cell, ID($assume));
else if (assume2assert && flavor == ID($assume))
set_formal_flavor(cell, ID($assert));
else if (live2fair && flavor == ID($live))
set_formal_flavor(cell, ID($fair));
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---|
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("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");
@ -55,12 +55,12 @@ struct ConnectPass : public Pass {
log("the -nounset option.\n");
log("\n");
log("\n");
log(" connect [-nomap] -unset <expr>\n");
log(" connect [-nomap] -unset <expr> [selection]\n");
log("\n");
log("Unconnect all existing drivers for the specified expression.\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("Connect the specified cell port to the specified cell port.\n");
log("\n");
@ -80,17 +80,6 @@ struct ConnectPass : public Pass {
}
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;
std::string set_lhs, set_rhs, unset_expr;
std::string port_cell, port_port, port_expr;
@ -128,6 +117,18 @@ struct ConnectPass : public Pass {
}
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;
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;
Pass::init_register();
}
}

View File

@ -65,6 +65,7 @@ struct ShowWorker
bool enumerateIds;
bool abbreviateIds;
bool notitle;
bool href;
int page_counter;
const std::vector<std::pair<std::string, RTLIL::Selection>> &color_selections;
@ -432,9 +433,13 @@ struct ShowWorker
if (wire->port_input || wire->port_output)
shape = "octagon";
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()),
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)
all_sources.insert(stringf("n%d", id2num(wire->name)));
else if (wire->port_output)
@ -496,14 +501,18 @@ struct ShowWorker
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
if (!code.empty())
fprintf(f, "subgraph cluster_c%d {\nc%d [ shape=record, label=\"%s\"%s ];\n%s}\n",
id2num(cell->name), id2num(cell->name), label_string.c_str(), color.c_str(), code.c_str());
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(), src_href.c_str(), code.c_str());
else
#endif
fprintf(f, "c%d [ shape=record, label=\"%s\", %s ];\n%s",
id2num(cell->name), label_string.c_str(), findColor(cell->name).c_str(), code.c_str());
fprintf(f, "c%d [ shape=record, label=\"%s\", %s%s ];\n%s",
id2num(cell->name), label_string.c_str(), findColor(cell->name).c_str(), src_href.c_str(), code.c_str());
}
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,
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>> &label_selections, RTLIL::IdString colorattr) :
f(f), design(design), currentColor(colorSeed), genWidthLabels(genWidthLabels),
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_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(" exit before returning\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("specified, 'xdot' is used to display the schematic (POSIX systems only).\n");
log("\n");
@ -763,6 +776,7 @@ struct ShowPass : public Pass {
bool flag_enum = false;
bool flag_abbreviate = true;
bool flag_notitle = false;
bool flag_href = false;
bool custom_prefix = false;
std::string background = "&";
RTLIL::IdString colorattr;
@ -850,6 +864,10 @@ struct ShowPass : public Pass {
background= "";
continue;
}
if (arg == "-href") {
flag_href = true;
continue;
}
break;
}
extra_args(args, argidx, design);
@ -894,7 +912,7 @@ struct ShowPass : public Pass {
delete lib;
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);
for (auto lib : libs)

View File

@ -28,6 +28,11 @@
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
struct cell_area_t {
double area;
bool is_sequential;
};
struct statdata_t
{
#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
#undef X
double area;
double sequential_area;
string tech;
std::map<RTLIL::IdString, int> techinfo;
@ -74,7 +80,7 @@ struct statdata_t
#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;
@ -132,10 +138,16 @@ struct statdata_t
}
if (!cell_area.empty()) {
if (cell_area.count(cell_type))
area += cell_area.at(cell_type);
else
if (cell_area.count(cell_type)) {
cell_area_t cell_data = cell_area.at(cell_type);
if (cell_data.is_sequential) {
sequential_area += cell_data.area;
}
area += cell_data.area;
}
else {
unknown_cell_area.insert(cell_type);
}
}
num_cells++;
@ -244,6 +256,7 @@ struct statdata_t
if (area != 0) {
log("\n");
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")
@ -325,7 +338,7 @@ statdata_t hierarchy_worker(std::map<RTLIL::IdString, statdata_t> &mod_stat, RTL
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;
f.open(liberty_file.c_str());
@ -341,8 +354,9 @@ void read_liberty_cellarea(dict<IdString, double> &cell_area, string liberty_fil
continue;
LibertyAst *ar = cell->find("area");
bool is_flip_flop = cell->find("ff") != nullptr;
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;
RTLIL::Module *top_mod = nullptr;
std::map<RTLIL::IdString, statdata_t> mod_stat;
dict<IdString, double> cell_area;
dict<IdString, cell_area_t> cell_area;
string techname;
size_t argidx;

View File

@ -339,6 +339,8 @@ struct EquivSimplePass : public Pass {
CellTypes ct;
ct.setup_internals();
ct.setup_stdcells();
ct.setup_internals_ff();
ct.setup_stdcells_mem();
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)
for (auto c : mod->cells()) {
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];
@ -1006,6 +1006,18 @@ struct HierarchyPass : public Pass {
if (mod->get_bool_attribute(ID::top))
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) {
log_header(design, "Finding top of design hierarchy..\n");
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");
extra_args(args, 1, design);
for (auto module : design->selected_modules()) {
if (module->has_processes_warn())
continue;
for (auto &mem : Mem::get_selected_memories(module)) {
if (!mem.packed) {
mem.packed = true;

View File

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

View File

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

View File

@ -50,7 +50,7 @@ struct MemoryMemxPass : public Pass {
}
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);
for (auto module : design->selected_modules())

View File

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

View File

@ -558,8 +558,12 @@ struct MemorySharePass : public Pass {
extra_args(args, argidx, design);
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);
}
}
} MemorySharePass;

View File

@ -35,10 +35,12 @@ struct keep_cache_t
{
Design *design;
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->purge_mode = purge_mode;
cache.clear();
}
@ -82,12 +84,15 @@ struct keep_cache_t
if (!ignore_specify && cell->type.in(ID($specify2), ID($specify3), ID($specrule)))
return true;
if (cell->type == ID($print))
if (cell->type == ID($print) || cell->type == ID($check))
return true;
if (cell->has_keep_attr())
return true;
if (!purge_mode && cell->type == ID($scopeinfo))
return true;
if (cell->module && cell->module->design)
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();
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);
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)
{
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)
{
// `register_signals` and `connected_signals` will help us decide later on
// on picking representatives out of groups of connected signals
SigPool register_signals;
SigPool connected_signals;
if (!purge_mode)
for (auto &it : module->cells_) {
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);
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;
for (auto &it : module->cells_) {
RTLIL::Cell *cell = it.second;
if (ct_all.cell_known(cell->type))
for (auto &it2 : cell->connections())
if (ct_all.cell_output(cell->type, it2.first))
direct_sigs.insert(assign_map(it2.second));
}
for (auto &it : module->wires_) {
if (direct_sigs.count(assign_map(it.second)) || it.second->port_input)
direct_wires.insert(it.second);
{
pool<RTLIL::SigSpec> direct_sigs;
for (auto &it : module->cells_) {
RTLIL::Cell *cell = it.second;
if (ct_all.cell_known(cell->type))
for (auto &it2 : cell->connections())
if (ct_all.cell_output(cell->type, it2.first))
direct_sigs.insert(assign_map(it2.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_) {
RTLIL::Wire *wire = it.second;
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();
// used signals sigmapped
SigPool used_signals;
// used signals pre-sigmapped
SigPool raw_used_signals;
// used signals sigmapped, ignoring drivers (we keep track of this to set `unused_bits`)
SigPool used_signals_nodrivers;
// gather the usage information for cells
for (auto &it : module->cells_) {
RTLIL::Cell *cell = it.second;
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);
used_signals.add(it2.second);
if (!ct_all.cell_output(cell->type, it2.first))
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;
for (auto &it : module->wires_) {
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()) {
bool found = false;
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;
}
// now decide for each wire if we should be deleting it
pool<RTLIL::Wire*> del_wires_queue;
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;
} else
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
goto delete_this_wire;
}
@ -638,7 +668,7 @@ struct OptCleanPass : public Pass {
}
extra_args(args, argidx, design);
keep_cache.reset(design);
keep_cache.reset(design, purge_mode);
ct_reg.setup_internals_mem();
ct_reg.setup_internals_anyinit();

View File

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

View File

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

View File

@ -167,7 +167,11 @@ struct OptLutWorker
legal = false;
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 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();
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_arity = luts_arity[lutA];
pool<int> &lutA_dlogic_inputs = luts_dlogic_inputs[lutA];
@ -529,12 +533,6 @@ struct OptLutPass : public Pass {
log("\n");
log("This pass combines cascaded $lut cells with unused inputs.\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(" treat the design as a LUT-mapped circuit for the iCE40 architecture\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;
for (auto module : design->selected_modules()) {
if (module->has_processes_warn())
continue;
SigMap sigmap(module);
FfInitVals initvals(&sigmap, 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())
continue;
if (cell->type == ID($scopeinfo))
continue;
uint64_t hash = hash_cell_parameters_and_connections(cell);
auto r = sharemap.insert(std::make_pair(hash, cell));
if (!r.second) {

View File

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

View File

@ -103,8 +103,20 @@ code
new_a.append(old_a);
} else {
// 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)};

View File

@ -415,7 +415,7 @@ match ff
filter GetSize(port(ff, \Q)) >= offset + GetSize(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
code argQ
@ -465,7 +465,7 @@ match ff
filter GetSize(port(ff, \D)) >= offset + GetSize(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
code argQ

View File

@ -354,7 +354,7 @@ match ff
filter GetSize(port(ff, \Q)) >= offset + GetSize(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
code argQ
@ -404,7 +404,7 @@ match ff
filter GetSize(port(ff, \D)) >= offset + GetSize(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
code argQ

View File

@ -135,7 +135,7 @@ match ff
filter GetSize(port(ff, \Q)) >= offset + GetSize(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
code argQ

View File

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

View File

@ -46,7 +46,7 @@ struct proc_dlatch_db_t
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));
for (int i = 0; i < GetSize(sig_y); i++)
@ -186,6 +186,8 @@ struct proc_dlatch_db_t
Cell *cell = it->second.first;
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_b = sigmap(cell->getPort(ID::B));
SigSpec sig_s = sigmap(cell->getPort(ID::S));
@ -200,12 +202,16 @@ struct proc_dlatch_db_t
sig[index] = State::Sx;
cell->setPort(ID::A, sig);
}
for (int i = 0; i < GetSize(sig_s); i++)
n = make_inner(sig_s[i], State::S0, n);
if (!is_bwmux) {
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);
}
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);
if (n != false_node) {
if (set_undef && sig_b[i*width + index] == needle) {
@ -213,7 +219,7 @@ struct proc_dlatch_db_t
sig[i*width + index] = State::Sx;
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;
for (int i = 0; i < GetSize(sw->signal); i++)
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("the clock edge.\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
{
// bool flag_noinit = false;
bool flag_nolower = false;
log_header(design, "Executing ASYNC2SYNC pass.\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
// if (args[argidx] == "-noinit") {
// flag_noinit = true;
// continue;
// }
if (args[argidx] == "-nolower") {
flag_nolower = true;
continue;
}
break;
}
extra_args(args, argidx, design);
bool have_check_cells = false;
for (auto module : design->selected_modules())
{
SigMap sigmap(module);
FfInitVals initvals(&sigmap, module);
SigBit initstate;
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))
continue;
@ -273,6 +330,12 @@ struct Async2syncPass : public Pass {
ff.emit();
}
}
if (have_check_cells && !flag_nolower) {
log_push();
Pass::call(design, "chformal -lower");
log_pop();
}
}
} 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("the clock edge.\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.
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
{
// bool flag_noinit = false;
bool flag_nolower = false;
log_header(design, "Executing CLK2FFLOGIC pass (convert clocked FFs to generic $ff cells).\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
// if (args[argidx] == "-noinit") {
// flag_noinit = true;
// continue;
// }
if (args[argidx] == "-nolower") {
flag_nolower = true;
continue;
}
break;
}
extra_args(args, argidx, design);
bool have_check_cells = false;
for (auto module : design->selected_modules())
{
SigMap sigmap(module);
@ -194,79 +199,138 @@ struct Clk2fflogicPass : public Pass {
mem.emit();
}
SigBit initstate;
for (auto cell : vector<Cell*>(module->selected_cells()))
{
SigSpec qval;
if (RTLIL::builtin_ff_cell_types().count(cell->type)) {
FfData ff(&initvals, cell);
if (cell->type.in(ID($print), ID($check)))
{
if (cell->type == ID($check))
have_check_cells = true;
if (ff.has_gclk) {
// Already a $ff or $_FF_ cell.
bool trg_enable = cell->getParam(ID(TRG_ENABLE)).as_bool();
if (!trg_enable)
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));
int trg_width = cell->getParam(ID(TRG_WIDTH)).as_int();
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 {
// $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));
SigBit sig_en = cell->getPort(ID::EN);
SigSpec sig_args = cell->getPort(ID::ARGS);
Const trg_polarity = cell->getParam(ID(TRG_POLARITY));
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)
ff.unmap_ce_srst();
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));
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);
continue;
}
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;

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)
{
@ -858,49 +858,53 @@ struct SimInstance
Const en = get_state(cell->getPort(ID::EN));
Const args = get_state(cell->getPort(ID::ARGS));
if (!en.as_bool())
goto update_print;
bool sampled = trg_en && trg.size() > 0;
if (trg.size() > 0 && trg_en) {
Const trg_pol = cell->getParam(ID::TRG_POLARITY);
for (int i = 0; i < trg.size(); i++) {
bool pol = trg_pol[i] == State::S1;
State curr = trg[i], past = print.past_trg[i];
if (pol && curr == State::S1 && past == State::S0)
if (sampled ? print.past_en.as_bool() : en.as_bool()) {
if (sampled) {
sampled = true;
Const trg_pol = cell->getParam(ID::TRG_POLARITY);
for (int i = 0; i < trg.size(); i++) {
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;
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;
}
} 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) {
int pos = 0;
for (auto &part : print.fmt.parts) {
part.sig = args.extract(pos, part.sig.size());
pos += part.sig.size();
if (triggered) {
int pos = 0;
for (auto &part : print.fmt.parts) {
part.sig = (sampled ? print.past_args : args).extract(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_en = en;
print.past_args = args;
print.initial_done = true;
}
if (check_assertions)
if (gclk_trigger)
{
for (auto cell : formal_database)
{
@ -932,7 +936,7 @@ struct SimInstance
}
for (auto it : children)
it.second->update_ph3(check_assertions);
it.second->update_ph3(gclk_trigger);
}
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
$(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

View File

@ -115,7 +115,7 @@ static bool parse_pin(LibertyAst *cell, LibertyAst *attr, std::string &pin_name,
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;
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")
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");
if (ff == nullptr)
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;
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")
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");
if (ff == nullptr)
continue;
@ -414,7 +438,7 @@ struct DfflibmapPass : public Pass {
void help() override
{
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("Map internal flip-flop cells to the flip-flop cells in the technology\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("changed.\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
{
@ -446,6 +475,8 @@ struct DfflibmapPass : public Pass {
bool map_only_mode = false;
bool info_mode = false;
std::vector<std::string> dont_use_cells;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
@ -467,6 +498,10 @@ struct DfflibmapPass : public Pass {
info_mode = true;
continue;
}
if (arg == "-dont_use" && argidx+1 < args.size()) {
dont_use_cells.push_back(args[++argidx]);
continue;
}
break;
}
extra_args(args, argidx, design);
@ -491,26 +526,26 @@ struct DfflibmapPass : public Pass {
LibertyParser libparser(f);
f.close();
find_cell(libparser.ast, ID($_DFF_N_), false, false, false, false);
find_cell(libparser.ast, ID($_DFF_P_), true, 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, dont_use_cells);
find_cell(libparser.ast, ID($_DFF_NN0_), false, true, false, false);
find_cell(libparser.ast, ID($_DFF_NN1_), false, true, false, true);
find_cell(libparser.ast, ID($_DFF_NP0_), false, true, true, false);
find_cell(libparser.ast, ID($_DFF_NP1_), false, true, true, true);
find_cell(libparser.ast, ID($_DFF_PN0_), true, true, false, false);
find_cell(libparser.ast, ID($_DFF_PN1_), true, true, false, true);
find_cell(libparser.ast, ID($_DFF_PP0_), true, true, true, false);
find_cell(libparser.ast, ID($_DFF_PP1_), true, true, true, true);
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, dont_use_cells);
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, dont_use_cells);
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, dont_use_cells);
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, dont_use_cells);
find_cell_sr(libparser.ast, ID($_DFFSR_NNN_), false, false, false);
find_cell_sr(libparser.ast, ID($_DFFSR_NNP_), false, false, true);
find_cell_sr(libparser.ast, ID($_DFFSR_NPN_), false, true, false);
find_cell_sr(libparser.ast, ID($_DFFSR_NPP_), false, true, true);
find_cell_sr(libparser.ast, ID($_DFFSR_PNN_), true, false, false);
find_cell_sr(libparser.ast, ID($_DFFSR_PNP_), true, false, true);
find_cell_sr(libparser.ast, ID($_DFFSR_PPN_), true, true, false);
find_cell_sr(libparser.ast, ID($_DFFSR_PPP_), true, true, true);
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, dont_use_cells);
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, dont_use_cells);
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, dont_use_cells);
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, dont_use_cells);
log(" final dff cell mappings:\n");
logmap_all();

View File

@ -281,7 +281,7 @@ struct ExtractFaWorker
void assign_new_driver(SigBit bit, SigBit new_driver)
{
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));
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));
}
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)
{
vector<SigChunk> chunks = sig;
@ -76,6 +58,54 @@ void map_sigspec(const dict<RTLIL::Wire*, RTLIL::Wire*> &map, RTLIL::SigSpec &si
struct FlattenWorker
{
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)
{
@ -220,7 +250,33 @@ struct FlattenWorker
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);
if (scopeinfo != nullptr)
module->rename(scopeinfo, cell_name);
}
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(" Ignore the 'whitebox' attribute on cell implementations.\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
{
@ -289,6 +359,14 @@ struct FlattenPass : public Pass {
worker.ignore_wb = true;
continue;
}
if (args[argidx] == "-noscopeinfo") {
worker.create_scopeinfo = false;
continue;
}
if (args[argidx] == "-scopename") {
worker.create_scopename = true;
continue;
}
break;
}
extra_args(args, argidx, design);

View File

@ -1803,11 +1803,12 @@ endmodule
module \$print (EN, TRG, ARGS);
parameter PRIORITY = 0;
parameter FORMAT = "";
parameter ARGS_WIDTH = 0;
parameter PRIORITY = 0;
parameter TRG_ENABLE = 1;
parameter TRG_ENABLE = 1;
parameter TRG_WIDTH = 0;
parameter TRG_POLARITY = 0;
@ -1817,6 +1818,27 @@ input [ARGS_WIDTH-1:0] ARGS;
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
@ -2741,3 +2763,10 @@ assign Y = A;
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("\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(" -noalumacc\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")) {
run("proc");
if (help_mode || flatten)
if (flatten || help_mode)
run("flatten", " (if -flatten)");
run("opt_expr");
run("opt_clean");
run("check");
run("opt -nodffe -nosdff");
if (!nofsm)
if (!nofsm || help_mode)
run("fsm" + fsm_opts, " (unless -nofsm)");
run("opt");
run("wreduce");
@ -246,8 +246,8 @@ struct SynthPass : public ScriptPass {
run("techmap -map +/cmp2lut.v -map +/cmp2lcu.v", " (if -lut)");
else if (lut)
run(stringf("techmap -map +/cmp2lut.v -map +/cmp2lcu.v -D LUT_WIDTH=%d", lut));
if (booth)
run("booth");
if (booth || help_mode)
run("booth", " (if -booth)");
if (!noalumacc)
run("alumacc", " (unless -noalumacc)");
if (!noshare)
@ -274,7 +274,7 @@ struct SynthPass : public ScriptPass {
}
run("opt -fast");
if (!noabc && !flowmap) {
if ((!noabc && !flowmap) || help_mode) {
#ifdef YOSYS_ENABLE_ABC
if (help_mode) {
run(abc + " -fast", " (unless -noabc, unless -lut)");

View File

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

View File

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

View File

@ -107,7 +107,7 @@ reg [7:0] i = 0;
always @(posedge clk) begin
if (i < VECTORLEN) begin
// FIXME: for some reason the first assert fails (despite comparing zero to zero)
if (i > 0)
if (i > 0)
assert(y == y_expected);
i <= i + 1;
end
@ -117,4 +117,5 @@ EOF
read_verilog +/quicklogic/qlf_k6n10f/dsp_sim.v
hierarchy -top testbench
proc
async2sync
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", 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", 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"]),
@ -131,6 +131,7 @@ read_verilog -defer -formal mem_tb.v
chparam{param_str} -set VECTORLEN {vectorlen} TB
hierarchy -top TB -check
prep
async2sync
log ** CHECKING SIMULATION FOR TEST {top} WITH PARAMS{param_str}
sim -clock clk -n {vectorlen} -assert
"""
@ -254,16 +255,16 @@ sim_tests: list[TestClass] = [
{"rq_a": 0x5678},
]
),
TestClass( # basic TDP test
TestClass( # basic TDP test
# note that the testbench uses ra and wa, while the common TDP model
# uses a shared address
params={"ADDRESS_WIDTH": 10, "DATA_WIDTH": 36},
top="sync_ram_tdp",
assertions=[],
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},
{"wce_a": 1, "ra_a": 0xFF,
{"wce_a": 1, "ra_a": 0xFF,
"wd_a": 0},
{"rce_a": 1, "ra_a": 0x0A, "rce_b": 1, "ra_b": 0x0A},
{"rq_a": 0xdeadbeef, "rq_b": 0xdeadbeef},
@ -276,9 +277,9 @@ sim_tests: list[TestClass] = [
top="sync_ram_tdp",
assertions=[],
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},
{"wce_a": 1, "ra_a": 0xFF,
{"wce_a": 1, "ra_a": 0xFF,
"wd_a": 0},
{"rce_a": 1, "ra_a": 0x0F, "rce_b": 1, "ra_b": 0x0A},
{"rq_a": 0, "rq_b": 0x00005a5a},
@ -291,7 +292,7 @@ sim_tests: list[TestClass] = [
top="sync_ram_tdp",
assertions=[],
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},
{"wce_a": 1, "ra_a": 0xBA, "rce_b": 1, "ra_b": 0xBA,
"wd_a": 0xa5a5a5a5},
@ -409,7 +410,7 @@ for sim_test in sim_tests:
fn = f"t_mem{i}.ys"
f = open(fn, mode="w")
j = 0
# output yosys script test file
print(
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
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
async2sync
hierarchy -top top
sim -assert -q -n 12 -clock clk

View File

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

View File

@ -16,8 +16,8 @@ generate_target() {
# $ generate_ys_test ys_file [yosys_args]
generate_ys_test() {
ys_file=$1
yosys_args=${2:-}
generate_target "$ys_file" "\"$YOSYS_BASEDIR/yosys\" -ql ${ys_file%.*}.log $yosys_args $ys_file"
yosys_args_=${2:-}
generate_target "$ys_file" "\"$YOSYS_BASEDIR/yosys\" -ql ${ys_file%.*}.log $yosys_args_ $ys_file"
}
# $ generate_bash_test bash_file
@ -75,7 +75,7 @@ generate_tests() {
if [[ $do_sv = true ]]; then
for x in *.sv; do
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;
done
fi;

View File

@ -186,4 +186,27 @@ design -stash preopt
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
hierarchy; proc; opt
hierarchy; proc; opt; async2sync
sat -verify -seq 1 -set-at 1 rst 1 -tempinduct -prove-asserts

View File

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

View File

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

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