mirror of https://github.com/YosysHQ/yosys.git
Merge branch 'master' into krys/docs
This commit is contained in:
commit
1455941ab9
|
@ -14,10 +14,10 @@ jobs:
|
||||||
run: sudo apt-get install bison flex libreadline-dev tcl-dev libffi-dev
|
run: sudo apt-get install bison flex libreadline-dev tcl-dev libffi-dev
|
||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v2
|
uses: github/codeql-action/init@v3
|
||||||
with:
|
with:
|
||||||
languages: cpp
|
languages: cpp
|
||||||
queries: security-extended,security-and-quality
|
queries: security-extended,security-and-quality
|
||||||
|
@ -26,4 +26,4 @@ jobs:
|
||||||
run: make yosys -j6
|
run: make yosys -j6
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v2
|
uses: github/codeql-action/analyze@v3
|
||||||
|
|
|
@ -6,13 +6,13 @@ jobs:
|
||||||
emcc:
|
emcc:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: mymindstorm/setup-emsdk@v11
|
- uses: mymindstorm/setup-emsdk@v14
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
make config-emcc
|
make config-emcc
|
||||||
make YOSYS_VER=latest
|
make YOSYS_VER=latest
|
||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: yosysjs
|
name: yosysjs
|
||||||
path: yosysjs-latest.zip
|
path: yosysjs-latest.zip
|
||||||
|
|
|
@ -79,19 +79,18 @@ jobs:
|
||||||
$CXX --version
|
$CXX --version
|
||||||
|
|
||||||
- name: Checkout Yosys
|
- name: Checkout Yosys
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Get iverilog
|
- name: Get iverilog
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
git clone https://github.com/steveicarus/iverilog.git
|
git clone https://github.com/steveicarus/iverilog.git
|
||||||
cd iverilog
|
cd iverilog
|
||||||
git checkout ${{ vars.IVERILOG_VERSION }}
|
|
||||||
echo "IVERILOG_GIT=$(git rev-parse HEAD)" >> $GITHUB_ENV
|
echo "IVERILOG_GIT=$(git rev-parse HEAD)" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Cache iverilog
|
- name: Cache iverilog
|
||||||
id: cache-iverilog
|
id: cache-iverilog
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: .local/
|
path: .local/
|
||||||
key: ${{ matrix.os.id }}-${{ env.IVERILOG_GIT }}
|
key: ${{ matrix.os.id }}-${{ env.IVERILOG_GIT }}
|
||||||
|
@ -111,7 +110,7 @@ jobs:
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
make config-${CC%%-*}
|
make config-${CC%%-*}
|
||||||
make -j${{ env.procs }} CCXXSTD=${{ matrix.cpp_std }} CC=$CC CXX=$CC LD=$CC
|
make -j${{ env.procs }} CXXSTD=${{ matrix.cpp_std }} CC=$CC CXX=$CC LD=$CC
|
||||||
|
|
||||||
- name: Store build artifact
|
- name: Store build artifact
|
||||||
if: (matrix.cpp_std == 'c++11') && (matrix.compiler == 'gcc-11')
|
if: (matrix.cpp_std == 'c++11') && (matrix.compiler == 'gcc-11')
|
||||||
|
@ -125,3 +124,7 @@ jobs:
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
make -j${{ env.procs }} test CXXSTD=${{ matrix.cpp_std }} CC=$CC CXX=$CC LD=$CC
|
make -j${{ env.procs }} test CXXSTD=${{ matrix.cpp_std }} CC=$CC CXX=$CC LD=$CC
|
||||||
|
|
||||||
|
- name: Log yosys-config output
|
||||||
|
run: |
|
||||||
|
./yosys-config || true
|
||||||
|
|
|
@ -35,19 +35,18 @@ jobs:
|
||||||
cc --version
|
cc --version
|
||||||
|
|
||||||
- name: Checkout Yosys
|
- name: Checkout Yosys
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Get iverilog
|
- name: Get iverilog
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
git clone https://github.com/steveicarus/iverilog.git
|
git clone https://github.com/steveicarus/iverilog.git
|
||||||
cd iverilog
|
cd iverilog
|
||||||
git checkout ${{ vars.IVERILOG_VERSION }}
|
|
||||||
echo "IVERILOG_GIT=$(git rev-parse HEAD)" >> $GITHUB_ENV
|
echo "IVERILOG_GIT=$(git rev-parse HEAD)" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Cache iverilog
|
- name: Cache iverilog
|
||||||
id: cache-iverilog
|
id: cache-iverilog
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: .local/
|
path: .local/
|
||||||
key: ${{ matrix.os.id }}-${{ env.IVERILOG_GIT }}
|
key: ${{ matrix.os.id }}-${{ env.IVERILOG_GIT }}
|
||||||
|
|
|
@ -10,7 +10,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Take last commit
|
- name: Take last commit
|
||||||
|
|
|
@ -6,10 +6,10 @@ jobs:
|
||||||
yosys-vcxsrc:
|
yosys-vcxsrc:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Build
|
- name: Build
|
||||||
run: make vcxsrc YOSYS_VER=latest
|
run: make vcxsrc YOSYS_VER=latest
|
||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: vcxsrc
|
name: vcxsrc
|
||||||
path: yosys-win32-vcxsrc-latest.zip
|
path: yosys-win32-vcxsrc-latest.zip
|
||||||
|
@ -18,7 +18,7 @@ jobs:
|
||||||
runs-on: windows-2019
|
runs-on: windows-2019
|
||||||
needs: yosys-vcxsrc
|
needs: yosys-vcxsrc
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/download-artifact@v3
|
- uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: vcxsrc
|
name: vcxsrc
|
||||||
path: .
|
path: .
|
||||||
|
|
|
@ -6,7 +6,7 @@ jobs:
|
||||||
wasi:
|
wasi:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
WASI_SDK=wasi-sdk-19.0
|
WASI_SDK=wasi-sdk-19.0
|
||||||
|
|
21
CHANGELOG
21
CHANGELOG
|
@ -2,9 +2,28 @@
|
||||||
List of major changes and improvements between releases
|
List of major changes and improvements between releases
|
||||||
=======================================================
|
=======================================================
|
||||||
|
|
||||||
Yosys 0.37 .. Yosys 0.38-dev
|
Yosys 0.38 .. Yosys 0.39-dev
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
|
Yosys 0.37 .. Yosys 0.38
|
||||||
|
--------------------------
|
||||||
|
* New commands and options
|
||||||
|
- Added option "-tech" to "opt_lut" pass.
|
||||||
|
- Added option "-nokeep_prints" to "hierarchy" pass.
|
||||||
|
- Added option "-nolower" to "async2sync" and "clk2fflogic" pass.
|
||||||
|
- Added option "-lower" to "chformal" pass.
|
||||||
|
|
||||||
|
* Various
|
||||||
|
- Added $check cell to represent assertions with messages.
|
||||||
|
- Allow capturing $print cell output in CXXRTL.
|
||||||
|
- Added API to overwrite existing pass from plugin.
|
||||||
|
- Follow the XDG Base Directory Specification for storing history files.
|
||||||
|
- Without a known top module, derive all deferred modules (hierarchy pass).
|
||||||
|
- Detect and error out on combinational loops in write_aiger.
|
||||||
|
|
||||||
|
* Verific support
|
||||||
|
- Added option "-no-split-complex-ports" to "verific -import".
|
||||||
|
|
||||||
Yosys 0.36 .. Yosys 0.37
|
Yosys 0.36 .. Yosys 0.37
|
||||||
--------------------------
|
--------------------------
|
||||||
* New commands and options
|
* New commands and options
|
||||||
|
|
184
Makefile
184
Makefile
|
@ -92,14 +92,14 @@ VPATH := $(YOSYS_SRC)
|
||||||
|
|
||||||
CXXSTD ?= c++11
|
CXXSTD ?= c++11
|
||||||
CXXFLAGS := $(CXXFLAGS) -Wall -Wextra -ggdb -I. -I"$(YOSYS_SRC)" -MD -MP -D_YOSYS_ -fPIC -I$(PREFIX)/include
|
CXXFLAGS := $(CXXFLAGS) -Wall -Wextra -ggdb -I. -I"$(YOSYS_SRC)" -MD -MP -D_YOSYS_ -fPIC -I$(PREFIX)/include
|
||||||
LDLIBS := $(LDLIBS) -lstdc++ -lm
|
LIBS := $(LIBS) -lstdc++ -lm
|
||||||
PLUGIN_LDFLAGS :=
|
PLUGIN_LINKFLAGS :=
|
||||||
PLUGIN_LDLIBS :=
|
PLUGIN_LIBS :=
|
||||||
EXE_LDFLAGS :=
|
EXE_LINKFLAGS :=
|
||||||
ifeq ($(OS), MINGW)
|
ifeq ($(OS), MINGW)
|
||||||
EXE_LDFLAGS := -Wl,--export-all-symbols -Wl,--out-implib,libyosys_exe.a
|
EXE_LINKFLAGS := -Wl,--export-all-symbols -Wl,--out-implib,libyosys_exe.a
|
||||||
PLUGIN_LDFLAGS += -L"$(LIBDIR)"
|
PLUGIN_LINKFLAGS += -L"$(LIBDIR)"
|
||||||
PLUGIN_LDLIBS := -lyosys_exe
|
PLUGIN_LIBS := -lyosys_exe
|
||||||
endif
|
endif
|
||||||
|
|
||||||
PKG_CONFIG ?= pkg-config
|
PKG_CONFIG ?= pkg-config
|
||||||
|
@ -109,7 +109,7 @@ STRIP ?= strip
|
||||||
AWK ?= awk
|
AWK ?= awk
|
||||||
|
|
||||||
ifeq ($(OS), Darwin)
|
ifeq ($(OS), Darwin)
|
||||||
PLUGIN_LDFLAGS += -undefined dynamic_lookup
|
PLUGIN_LINKFLAGS += -undefined dynamic_lookup
|
||||||
|
|
||||||
# homebrew search paths
|
# homebrew search paths
|
||||||
ifneq ($(shell :; command -v brew),)
|
ifneq ($(shell :; command -v brew),)
|
||||||
|
@ -117,10 +117,10 @@ BREW_PREFIX := $(shell brew --prefix)/opt
|
||||||
$(info $$BREW_PREFIX is [${BREW_PREFIX}])
|
$(info $$BREW_PREFIX is [${BREW_PREFIX}])
|
||||||
ifeq ($(ENABLE_PYOSYS),1)
|
ifeq ($(ENABLE_PYOSYS),1)
|
||||||
CXXFLAGS += -I$(BREW_PREFIX)/boost/include/boost
|
CXXFLAGS += -I$(BREW_PREFIX)/boost/include/boost
|
||||||
LDFLAGS += -L$(BREW_PREFIX)/boost/lib
|
LINKFLAGS += -L$(BREW_PREFIX)/boost/lib
|
||||||
endif
|
endif
|
||||||
CXXFLAGS += -I$(BREW_PREFIX)/readline/include
|
CXXFLAGS += -I$(BREW_PREFIX)/readline/include
|
||||||
LDFLAGS += -L$(BREW_PREFIX)/readline/lib
|
LINKFLAGS += -L$(BREW_PREFIX)/readline/lib
|
||||||
PKG_CONFIG_PATH := $(BREW_PREFIX)/libffi/lib/pkgconfig:$(PKG_CONFIG_PATH)
|
PKG_CONFIG_PATH := $(BREW_PREFIX)/libffi/lib/pkgconfig:$(PKG_CONFIG_PATH)
|
||||||
PKG_CONFIG_PATH := $(BREW_PREFIX)/tcl-tk/lib/pkgconfig:$(PKG_CONFIG_PATH)
|
PKG_CONFIG_PATH := $(BREW_PREFIX)/tcl-tk/lib/pkgconfig:$(PKG_CONFIG_PATH)
|
||||||
export PATH := $(BREW_PREFIX)/bison/bin:$(BREW_PREFIX)/gettext/bin:$(BREW_PREFIX)/flex/bin:$(PATH)
|
export PATH := $(BREW_PREFIX)/bison/bin:$(BREW_PREFIX)/gettext/bin:$(BREW_PREFIX)/flex/bin:$(PATH)
|
||||||
|
@ -129,19 +129,19 @@ export PATH := $(BREW_PREFIX)/bison/bin:$(BREW_PREFIX)/gettext/bin:$(BREW_PREFIX
|
||||||
else ifneq ($(shell :; command -v port),)
|
else ifneq ($(shell :; command -v port),)
|
||||||
PORT_PREFIX := $(patsubst %/bin/port,%,$(shell :; command -v port))
|
PORT_PREFIX := $(patsubst %/bin/port,%,$(shell :; command -v port))
|
||||||
CXXFLAGS += -I$(PORT_PREFIX)/include
|
CXXFLAGS += -I$(PORT_PREFIX)/include
|
||||||
LDFLAGS += -L$(PORT_PREFIX)/lib
|
LINKFLAGS += -L$(PORT_PREFIX)/lib
|
||||||
PKG_CONFIG_PATH := $(PORT_PREFIX)/lib/pkgconfig:$(PKG_CONFIG_PATH)
|
PKG_CONFIG_PATH := $(PORT_PREFIX)/lib/pkgconfig:$(PKG_CONFIG_PATH)
|
||||||
export PATH := $(PORT_PREFIX)/bin:$(PATH)
|
export PATH := $(PORT_PREFIX)/bin:$(PATH)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
else
|
else
|
||||||
LDFLAGS += -rdynamic
|
LINKFLAGS += -rdynamic
|
||||||
ifneq ($(OS), OpenBSD)
|
ifneq ($(OS), OpenBSD)
|
||||||
LDLIBS += -lrt
|
LIBS += -lrt
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
YOSYS_VER := 0.37+27
|
YOSYS_VER := 0.38+113
|
||||||
|
|
||||||
# Note: We arrange for .gitcommit to contain the (short) commit hash in
|
# Note: We arrange for .gitcommit to contain the (short) commit hash in
|
||||||
# tarballs generated with git-archive(1) using .gitattributes. The git repo
|
# tarballs generated with git-archive(1) using .gitattributes. The git repo
|
||||||
|
@ -157,7 +157,7 @@ endif
|
||||||
OBJS = kernel/version_$(GIT_REV).o
|
OBJS = kernel/version_$(GIT_REV).o
|
||||||
|
|
||||||
bumpversion:
|
bumpversion:
|
||||||
sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline a5c7f69.. | wc -l`/;" Makefile
|
sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 543faed.. | wc -l`/;" Makefile
|
||||||
|
|
||||||
# set 'ABCREV = default' to use abc/ as it is
|
# set 'ABCREV = default' to use abc/ as it is
|
||||||
#
|
#
|
||||||
|
@ -215,41 +215,38 @@ ABC_ARCHFLAGS += "-DABC_NO_RLIMIT"
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(CONFIG),clang)
|
ifeq ($(CONFIG),clang)
|
||||||
CXX = clang
|
CXX = clang++
|
||||||
LD = clang++
|
|
||||||
CXXFLAGS += -std=$(CXXSTD) -Os
|
CXXFLAGS += -std=$(CXXSTD) -Os
|
||||||
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -Wno-c++11-narrowing $(ABC_ARCHFLAGS)"
|
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -Wno-c++11-narrowing $(ABC_ARCHFLAGS)"
|
||||||
|
|
||||||
ifneq ($(SANITIZER),)
|
ifneq ($(SANITIZER),)
|
||||||
$(info [Clang Sanitizer] $(SANITIZER))
|
$(info [Clang Sanitizer] $(SANITIZER))
|
||||||
CXXFLAGS += -g -O1 -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=$(SANITIZER)
|
CXXFLAGS += -g -O1 -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=$(SANITIZER)
|
||||||
LDFLAGS += -g -fsanitize=$(SANITIZER)
|
LINKFLAGS += -g -fsanitize=$(SANITIZER)
|
||||||
ifneq ($(findstring address,$(SANITIZER)),)
|
ifneq ($(findstring address,$(SANITIZER)),)
|
||||||
ENABLE_COVER := 0
|
ENABLE_COVER := 0
|
||||||
endif
|
endif
|
||||||
ifneq ($(findstring memory,$(SANITIZER)),)
|
ifneq ($(findstring memory,$(SANITIZER)),)
|
||||||
CXXFLAGS += -fPIE -fsanitize-memory-track-origins
|
CXXFLAGS += -fPIE -fsanitize-memory-track-origins
|
||||||
LDFLAGS += -fPIE -fsanitize-memory-track-origins
|
LINKFLAGS += -fPIE -fsanitize-memory-track-origins
|
||||||
endif
|
endif
|
||||||
ifneq ($(findstring cfi,$(SANITIZER)),)
|
ifneq ($(findstring cfi,$(SANITIZER)),)
|
||||||
CXXFLAGS += -flto
|
CXXFLAGS += -flto
|
||||||
LDFLAGS += -flto
|
LINKFLAGS += -flto
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
else ifeq ($(CONFIG),gcc)
|
else ifeq ($(CONFIG),gcc)
|
||||||
CXX = gcc
|
CXX = g++
|
||||||
LD = gcc
|
|
||||||
CXXFLAGS += -std=$(CXXSTD) -Os
|
CXXFLAGS += -std=$(CXXSTD) -Os
|
||||||
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H $(ABC_ARCHFLAGS)"
|
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H $(ABC_ARCHFLAGS)"
|
||||||
|
|
||||||
else ifeq ($(CONFIG),gcc-static)
|
else ifeq ($(CONFIG),gcc-static)
|
||||||
LD = $(CXX)
|
LINKFLAGS := $(filter-out -rdynamic,$(LINKFLAGS)) -static
|
||||||
LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -static
|
LIBS := $(filter-out -lrt,$(LIBS))
|
||||||
LDLIBS := $(filter-out -lrt,$(LDLIBS))
|
|
||||||
CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS))
|
CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS))
|
||||||
CXXFLAGS += -std=$(CXXSTD) -Os
|
CXXFLAGS += -std=$(CXXSTD) -Os
|
||||||
ABCMKARGS = CC="$(CC)" CXX="$(CXX)" LD="$(LD)" ABC_USE_LIBSTDCXX=1 LIBS="-lm -lpthread -static" OPTFLAGS="-O" \
|
ABCMKARGS = CC="$(CC)" CXX="$(CXX)" LD="$(CXX)" ABC_USE_LIBSTDCXX=1 LIBS="-lm -lpthread -static" OPTFLAGS="-O" \
|
||||||
ARCHFLAGS="-DABC_USE_STDINT_H -DABC_NO_DYNAMIC_LINKING=1 -Wno-unused-but-set-variable $(ARCHFLAGS)" ABC_USE_NO_READLINE=1
|
ARCHFLAGS="-DABC_USE_STDINT_H -DABC_NO_DYNAMIC_LINKING=1 -Wno-unused-but-set-variable $(ARCHFLAGS)" ABC_USE_NO_READLINE=1
|
||||||
ifeq ($(DISABLE_ABC_THREADS),1)
|
ifeq ($(DISABLE_ABC_THREADS),1)
|
||||||
ABCMKARGS += "ABC_USE_NO_PTHREADS=1"
|
ABCMKARGS += "ABC_USE_NO_PTHREADS=1"
|
||||||
|
@ -257,31 +254,28 @@ endif
|
||||||
|
|
||||||
else ifeq ($(CONFIG),afl-gcc)
|
else ifeq ($(CONFIG),afl-gcc)
|
||||||
CXX = AFL_QUIET=1 AFL_HARDEN=1 afl-gcc
|
CXX = AFL_QUIET=1 AFL_HARDEN=1 afl-gcc
|
||||||
LD = AFL_QUIET=1 AFL_HARDEN=1 afl-gcc
|
|
||||||
CXXFLAGS += -std=$(CXXSTD) -Os
|
CXXFLAGS += -std=$(CXXSTD) -Os
|
||||||
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H"
|
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H"
|
||||||
|
|
||||||
else ifeq ($(CONFIG),cygwin)
|
else ifeq ($(CONFIG),cygwin)
|
||||||
CXX = gcc
|
CXX = g++
|
||||||
LD = gcc
|
|
||||||
CXXFLAGS += -std=gnu++11 -Os
|
CXXFLAGS += -std=gnu++11 -Os
|
||||||
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H"
|
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H"
|
||||||
|
|
||||||
else ifeq ($(CONFIG),emcc)
|
else ifeq ($(CONFIG),emcc)
|
||||||
CXX = emcc
|
CXX = emcc
|
||||||
LD = emcc
|
|
||||||
CXXFLAGS := -std=$(CXXSTD) $(filter-out -fPIC -ggdb,$(CXXFLAGS))
|
CXXFLAGS := -std=$(CXXSTD) $(filter-out -fPIC -ggdb,$(CXXFLAGS))
|
||||||
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DABC_MEMALIGN=8 -Wno-c++11-narrowing"
|
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DABC_MEMALIGN=8 -Wno-c++11-narrowing"
|
||||||
EMCC_CXXFLAGS := -Os -Wno-warn-absolute-paths
|
EMCC_CXXFLAGS := -Os -Wno-warn-absolute-paths
|
||||||
EMCC_LDFLAGS := --memory-init-file 0 --embed-file share
|
EMCC_LINKFLAGS := --embed-file share
|
||||||
EMCC_LDFLAGS += -s NO_EXIT_RUNTIME=1
|
EMCC_LINKFLAGS += -s NO_EXIT_RUNTIME=1
|
||||||
EMCC_LDFLAGS += -s EXPORTED_FUNCTIONS="['_main','_run','_prompt','_errmsg','_memset']"
|
EMCC_LINKFLAGS += -s EXPORTED_FUNCTIONS="['_main','_run','_prompt','_errmsg','_memset']"
|
||||||
EMCC_LDFLAGS += -s TOTAL_MEMORY=134217728
|
EMCC_LINKFLAGS += -s TOTAL_MEMORY=134217728
|
||||||
EMCC_LDFLAGS += -s EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]'
|
EMCC_LINKFLAGS += -s EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]'
|
||||||
# https://github.com/kripken/emscripten/blob/master/src/settings.js
|
# https://github.com/kripken/emscripten/blob/master/src/settings.js
|
||||||
CXXFLAGS += $(EMCC_CXXFLAGS)
|
CXXFLAGS += $(EMCC_CXXFLAGS)
|
||||||
LDFLAGS += $(EMCC_LDFLAGS)
|
LINKFLAGS += $(EMCC_LINKFLAGS)
|
||||||
LDLIBS =
|
LIBS =
|
||||||
EXE = .js
|
EXE = .js
|
||||||
|
|
||||||
DISABLE_SPAWN := 1
|
DISABLE_SPAWN := 1
|
||||||
|
@ -309,21 +303,19 @@ yosys.html: misc/yosys.html
|
||||||
|
|
||||||
else ifeq ($(CONFIG),wasi)
|
else ifeq ($(CONFIG),wasi)
|
||||||
ifeq ($(WASI_SDK),)
|
ifeq ($(WASI_SDK),)
|
||||||
CXX = clang
|
CXX = clang++
|
||||||
LD = clang++
|
|
||||||
AR = llvm-ar
|
AR = llvm-ar
|
||||||
RANLIB = llvm-ranlib
|
RANLIB = llvm-ranlib
|
||||||
WASIFLAGS := -target wasm32-wasi --sysroot $(WASI_SYSROOT) $(WASIFLAGS)
|
WASIFLAGS := -target wasm32-wasi --sysroot $(WASI_SYSROOT) $(WASIFLAGS)
|
||||||
else
|
else
|
||||||
CXX = $(WASI_SDK)/bin/clang
|
CXX = $(WASI_SDK)/bin/clang++
|
||||||
LD = $(WASI_SDK)/bin/clang++
|
|
||||||
AR = $(WASI_SDK)/bin/ar
|
AR = $(WASI_SDK)/bin/ar
|
||||||
RANLIB = $(WASI_SDK)/bin/ranlib
|
RANLIB = $(WASI_SDK)/bin/ranlib
|
||||||
WASIFLAGS := --sysroot $(WASI_SDK)/share/wasi-sysroot $(WASIFLAGS)
|
WASIFLAGS := --sysroot $(WASI_SDK)/share/wasi-sysroot $(WASIFLAGS)
|
||||||
endif
|
endif
|
||||||
CXXFLAGS := $(WASIFLAGS) -std=$(CXXSTD) -Os -D_WASI_EMULATED_PROCESS_CLOCKS $(filter-out -fPIC,$(CXXFLAGS))
|
CXXFLAGS := $(WASIFLAGS) -std=$(CXXSTD) -Os -D_WASI_EMULATED_PROCESS_CLOCKS $(filter-out -fPIC,$(CXXFLAGS))
|
||||||
LDFLAGS := $(WASIFLAGS) -Wl,-z,stack-size=1048576 $(filter-out -rdynamic,$(LDFLAGS))
|
LINKFLAGS := $(WASIFLAGS) -Wl,-z,stack-size=1048576 $(filter-out -rdynamic,$(LINKFLAGS))
|
||||||
LDLIBS := -lwasi-emulated-process-clocks $(filter-out -lrt,$(LDLIBS))
|
LIBS := -lwasi-emulated-process-clocks $(filter-out -lrt,$(LIBS))
|
||||||
ABCMKARGS += AR="$(AR)" RANLIB="$(RANLIB)"
|
ABCMKARGS += AR="$(AR)" RANLIB="$(RANLIB)"
|
||||||
ABCMKARGS += ARCHFLAGS="$(WASIFLAGS) -D_WASI_EMULATED_PROCESS_CLOCKS -DABC_USE_STDINT_H -DABC_NO_DYNAMIC_LINKING -DABC_NO_RLIMIT -Wno-c++11-narrowing"
|
ABCMKARGS += ARCHFLAGS="$(WASIFLAGS) -D_WASI_EMULATED_PROCESS_CLOCKS -DABC_USE_STDINT_H -DABC_NO_DYNAMIC_LINKING -DABC_NO_RLIMIT -Wno-c++11-narrowing"
|
||||||
ABCMKARGS += OPTFLAGS="-Os"
|
ABCMKARGS += OPTFLAGS="-Os"
|
||||||
|
@ -339,34 +331,31 @@ endif
|
||||||
else ifeq ($(CONFIG),mxe)
|
else ifeq ($(CONFIG),mxe)
|
||||||
PKG_CONFIG = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-pkg-config
|
PKG_CONFIG = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-pkg-config
|
||||||
CXX = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-g++
|
CXX = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-g++
|
||||||
LD = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-g++
|
|
||||||
CXXFLAGS += -std=$(CXXSTD) -Os -D_POSIX_SOURCE -DYOSYS_MXE_HACKS -Wno-attributes
|
CXXFLAGS += -std=$(CXXSTD) -Os -D_POSIX_SOURCE -DYOSYS_MXE_HACKS -Wno-attributes
|
||||||
CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS))
|
CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS))
|
||||||
LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -s
|
LINKFLAGS := $(filter-out -rdynamic,$(LINKFLAGS)) -s
|
||||||
LDLIBS := $(filter-out -lrt,$(LDLIBS))
|
LIBS := $(filter-out -lrt,$(LIBS))
|
||||||
ABCMKARGS += ARCHFLAGS="-DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -fpermissive -w"
|
ABCMKARGS += ARCHFLAGS="-DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -fpermissive -w"
|
||||||
# TODO: Try to solve pthread linking issue in more appropriate way
|
# TODO: Try to solve pthread linking issue in more appropriate way
|
||||||
ABCMKARGS += LIBS="lib/x86/pthreadVC2.lib -s" LDFLAGS="-Wl,--allow-multiple-definition" ABC_USE_NO_READLINE=1 CC="/usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-gcc"
|
ABCMKARGS += LIBS="lib/x86/pthreadVC2.lib -s" LINKFLAGS="-Wl,--allow-multiple-definition" ABC_USE_NO_READLINE=1 CC="/usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-gcc"
|
||||||
EXE = .exe
|
EXE = .exe
|
||||||
|
|
||||||
else ifeq ($(CONFIG),msys2-32)
|
else ifeq ($(CONFIG),msys2-32)
|
||||||
CXX = i686-w64-mingw32-g++
|
CXX = i686-w64-mingw32-g++
|
||||||
LD = i686-w64-mingw32-g++
|
|
||||||
CXXFLAGS += -std=$(CXXSTD) -Os -D_POSIX_SOURCE -DYOSYS_WIN32_UNIX_DIR
|
CXXFLAGS += -std=$(CXXSTD) -Os -D_POSIX_SOURCE -DYOSYS_WIN32_UNIX_DIR
|
||||||
CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS))
|
CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS))
|
||||||
LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -s
|
LINKFLAGS := $(filter-out -rdynamic,$(LINKFLAGS)) -s
|
||||||
LDLIBS := $(filter-out -lrt,$(LDLIBS))
|
LIBS := $(filter-out -lrt,$(LIBS))
|
||||||
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -fpermissive -w"
|
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -fpermissive -w"
|
||||||
ABCMKARGS += LIBS="-lpthread -lshlwapi -s" ABC_USE_NO_READLINE=0 CC="i686-w64-mingw32-gcc" CXX="$(CXX)"
|
ABCMKARGS += LIBS="-lpthread -lshlwapi -s" ABC_USE_NO_READLINE=0 CC="i686-w64-mingw32-gcc" CXX="$(CXX)"
|
||||||
EXE = .exe
|
EXE = .exe
|
||||||
|
|
||||||
else ifeq ($(CONFIG),msys2-64)
|
else ifeq ($(CONFIG),msys2-64)
|
||||||
CXX = x86_64-w64-mingw32-g++
|
CXX = x86_64-w64-mingw32-g++
|
||||||
LD = x86_64-w64-mingw32-g++
|
|
||||||
CXXFLAGS += -std=$(CXXSTD) -Os -D_POSIX_SOURCE -DYOSYS_WIN32_UNIX_DIR
|
CXXFLAGS += -std=$(CXXSTD) -Os -D_POSIX_SOURCE -DYOSYS_WIN32_UNIX_DIR
|
||||||
CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS))
|
CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS))
|
||||||
LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -s
|
LINKFLAGS := $(filter-out -rdynamic,$(LINKFLAGS)) -s
|
||||||
LDLIBS := $(filter-out -lrt,$(LDLIBS))
|
LIBS := $(filter-out -lrt,$(LIBS))
|
||||||
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -fpermissive -w"
|
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -fpermissive -w"
|
||||||
ABCMKARGS += LIBS="-lpthread -lshlwapi -s" ABC_USE_NO_READLINE=0 CC="x86_64-w64-mingw32-gcc" CXX="$(CXX)"
|
ABCMKARGS += LIBS="-lpthread -lshlwapi -s" ABC_USE_NO_READLINE=0 CC="x86_64-w64-mingw32-gcc" CXX="$(CXX)"
|
||||||
EXE = .exe
|
EXE = .exe
|
||||||
|
@ -393,9 +382,9 @@ ifeq ($(BOOST_PYTHON_LIB),)
|
||||||
$(error BOOST_PYTHON_LIB could not be detected. Please define manually)
|
$(error BOOST_PYTHON_LIB could not be detected. Please define manually)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
LDLIBS += $(shell $(PYTHON_CONFIG) --libs) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
|
LIBS += $(shell $(PYTHON_CONFIG) --libs) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
|
||||||
# python-config --ldflags includes LDLIBS for some reason
|
# python-config --ldflags includes LIBS for some reason
|
||||||
LDFLAGS += $(filter-out -l%,$(shell $(PYTHON_CONFIG) --ldflags))
|
LINKFLAGS += $(filter-out -l%,$(shell $(PYTHON_CONFIG) --ldflags))
|
||||||
CXXFLAGS += $(shell $(PYTHON_CONFIG) --includes) -DWITH_PYTHON
|
CXXFLAGS += $(shell $(PYTHON_CONFIG) --includes) -DWITH_PYTHON
|
||||||
|
|
||||||
PY_WRAPPER_FILE = kernel/python_wrappers
|
PY_WRAPPER_FILE = kernel/python_wrappers
|
||||||
|
@ -409,22 +398,22 @@ CXXFLAGS += -DYOSYS_ENABLE_READLINE
|
||||||
ifeq ($(OS), $(filter $(OS),FreeBSD OpenBSD NetBSD))
|
ifeq ($(OS), $(filter $(OS),FreeBSD OpenBSD NetBSD))
|
||||||
CXXFLAGS += -I/usr/local/include
|
CXXFLAGS += -I/usr/local/include
|
||||||
endif
|
endif
|
||||||
LDLIBS += -lreadline
|
LIBS += -lreadline
|
||||||
ifeq ($(LINK_CURSES),1)
|
ifeq ($(LINK_CURSES),1)
|
||||||
LDLIBS += -lcurses
|
LIBS += -lcurses
|
||||||
ABCMKARGS += "ABC_READLINE_LIBRARIES=-lcurses -lreadline"
|
ABCMKARGS += "ABC_READLINE_LIBRARIES=-lcurses -lreadline"
|
||||||
endif
|
endif
|
||||||
ifeq ($(LINK_TERMCAP),1)
|
ifeq ($(LINK_TERMCAP),1)
|
||||||
LDLIBS += -ltermcap
|
LIBS += -ltermcap
|
||||||
ABCMKARGS += "ABC_READLINE_LIBRARIES=-lreadline -ltermcap"
|
ABCMKARGS += "ABC_READLINE_LIBRARIES=-lreadline -ltermcap"
|
||||||
endif
|
endif
|
||||||
ifeq ($(CONFIG),mxe)
|
ifeq ($(CONFIG),mxe)
|
||||||
LDLIBS += -ltermcap
|
LIBS += -ltermcap
|
||||||
endif
|
endif
|
||||||
else
|
else
|
||||||
ifeq ($(ENABLE_EDITLINE),1)
|
ifeq ($(ENABLE_EDITLINE),1)
|
||||||
CXXFLAGS += -DYOSYS_ENABLE_EDITLINE
|
CXXFLAGS += -DYOSYS_ENABLE_EDITLINE
|
||||||
LDLIBS += -ledit -ltinfo -lbsd
|
LIBS += -ledit -ltinfo -lbsd
|
||||||
else
|
else
|
||||||
ABCMKARGS += "ABC_USE_NO_READLINE=1"
|
ABCMKARGS += "ABC_USE_NO_READLINE=1"
|
||||||
endif
|
endif
|
||||||
|
@ -443,9 +432,9 @@ CXXFLAGS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-e
|
||||||
ifeq ($(OS), MINGW)
|
ifeq ($(OS), MINGW)
|
||||||
CXXFLAGS += -Ilibs/dlfcn-win32
|
CXXFLAGS += -Ilibs/dlfcn-win32
|
||||||
endif
|
endif
|
||||||
LDLIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --libs libffi || echo -lffi)
|
LIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --libs libffi || echo -lffi)
|
||||||
ifneq ($(OS), $(filter $(OS),FreeBSD OpenBSD NetBSD MINGW))
|
ifneq ($(OS), $(filter $(OS),FreeBSD OpenBSD NetBSD MINGW))
|
||||||
LDLIBS += -ldl
|
LIBS += -ldl
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -455,7 +444,7 @@ endif
|
||||||
|
|
||||||
ifeq ($(ENABLE_ZLIB),1)
|
ifeq ($(ENABLE_ZLIB),1)
|
||||||
CXXFLAGS += -DYOSYS_ENABLE_ZLIB
|
CXXFLAGS += -DYOSYS_ENABLE_ZLIB
|
||||||
LDLIBS += -lz
|
LIBS += -lz
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
@ -472,21 +461,21 @@ endif
|
||||||
|
|
||||||
ifeq ($(CONFIG),mxe)
|
ifeq ($(CONFIG),mxe)
|
||||||
CXXFLAGS += -DYOSYS_ENABLE_TCL
|
CXXFLAGS += -DYOSYS_ENABLE_TCL
|
||||||
LDLIBS += -ltcl86 -lwsock32 -lws2_32 -lnetapi32 -lz -luserenv
|
LIBS += -ltcl86 -lwsock32 -lws2_32 -lnetapi32 -lz -luserenv
|
||||||
else
|
else
|
||||||
CXXFLAGS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --cflags tcl || echo -I$(TCL_INCLUDE)) -DYOSYS_ENABLE_TCL
|
CXXFLAGS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --cflags tcl || echo -I$(TCL_INCLUDE)) -DYOSYS_ENABLE_TCL
|
||||||
LDLIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --libs tcl || echo $(TCL_LIBS))
|
LIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --libs tcl || echo $(TCL_LIBS))
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(ENABLE_GCOV),1)
|
ifeq ($(ENABLE_GCOV),1)
|
||||||
CXXFLAGS += --coverage
|
CXXFLAGS += --coverage
|
||||||
LDFLAGS += --coverage
|
LINKFLAGS += --coverage
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(ENABLE_GPROF),1)
|
ifeq ($(ENABLE_GPROF),1)
|
||||||
CXXFLAGS += -pg
|
CXXFLAGS += -pg
|
||||||
LDFLAGS += -pg
|
LINKFLAGS += -pg
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(ENABLE_NDEBUG),1)
|
ifeq ($(ENABLE_NDEBUG),1)
|
||||||
|
@ -506,7 +495,7 @@ CXXFLAGS += -DYOSYS_ENABLE_ABC
|
||||||
ifeq ($(LINK_ABC),1)
|
ifeq ($(LINK_ABC),1)
|
||||||
CXXFLAGS += -DYOSYS_LINK_ABC
|
CXXFLAGS += -DYOSYS_LINK_ABC
|
||||||
ifeq ($(DISABLE_ABC_THREADS),0)
|
ifeq ($(DISABLE_ABC_THREADS),0)
|
||||||
LDLIBS += -lpthread
|
LIBS += -lpthread
|
||||||
endif
|
endif
|
||||||
else
|
else
|
||||||
ifeq ($(ABCEXTERNAL),)
|
ifeq ($(ABCEXTERNAL),)
|
||||||
|
@ -520,10 +509,10 @@ GHDL_PREFIX ?= $(PREFIX)
|
||||||
GHDL_INCLUDE_DIR ?= $(GHDL_PREFIX)/include
|
GHDL_INCLUDE_DIR ?= $(GHDL_PREFIX)/include
|
||||||
GHDL_LIB_DIR ?= $(GHDL_PREFIX)/lib
|
GHDL_LIB_DIR ?= $(GHDL_PREFIX)/lib
|
||||||
CXXFLAGS += -I$(GHDL_INCLUDE_DIR) -DYOSYS_ENABLE_GHDL
|
CXXFLAGS += -I$(GHDL_INCLUDE_DIR) -DYOSYS_ENABLE_GHDL
|
||||||
LDLIBS += $(GHDL_LIB_DIR)/libghdl.a $(file <$(GHDL_LIB_DIR)/libghdl.link)
|
LIBS += $(GHDL_LIB_DIR)/libghdl.a $(file <$(GHDL_LIB_DIR)/libghdl.link)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
LDLIBS_VERIFIC =
|
LIBS_VERIFIC =
|
||||||
ifeq ($(ENABLE_VERIFIC),1)
|
ifeq ($(ENABLE_VERIFIC),1)
|
||||||
VERIFIC_DIR ?= /usr/local/src/verific_lib
|
VERIFIC_DIR ?= /usr/local/src/verific_lib
|
||||||
VERIFIC_COMPONENTS ?= verilog database util containers hier_tree
|
VERIFIC_COMPONENTS ?= verilog database util containers hier_tree
|
||||||
|
@ -549,9 +538,9 @@ CXXFLAGS += -DYOSYSHQ_VERIFIC_EXTENSIONS
|
||||||
endif
|
endif
|
||||||
CXXFLAGS += $(patsubst %,-I$(VERIFIC_DIR)/%,$(VERIFIC_COMPONENTS)) -DYOSYS_ENABLE_VERIFIC
|
CXXFLAGS += $(patsubst %,-I$(VERIFIC_DIR)/%,$(VERIFIC_COMPONENTS)) -DYOSYS_ENABLE_VERIFIC
|
||||||
ifeq ($(OS), Darwin)
|
ifeq ($(OS), Darwin)
|
||||||
LDLIBS_VERIFIC += $(foreach comp,$(patsubst %,$(VERIFIC_DIR)/%/*-mac.a,$(VERIFIC_COMPONENTS)),-Wl,-force_load $(comp)) -lz
|
LIBS_VERIFIC += $(foreach comp,$(patsubst %,$(VERIFIC_DIR)/%/*-mac.a,$(VERIFIC_COMPONENTS)),-Wl,-force_load $(comp)) -lz
|
||||||
else
|
else
|
||||||
LDLIBS_VERIFIC += -Wl,--whole-archive $(patsubst %,$(VERIFIC_DIR)/%/*-linux.a,$(VERIFIC_COMPONENTS)) -Wl,--no-whole-archive -lz
|
LIBS_VERIFIC += -Wl,--whole-archive $(patsubst %,$(VERIFIC_DIR)/%/*-linux.a,$(VERIFIC_COMPONENTS)) -Wl,--no-whole-archive -lz
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -630,6 +619,7 @@ $(eval $(call add_include_file,kernel/qcsat.h))
|
||||||
$(eval $(call add_include_file,kernel/register.h))
|
$(eval $(call add_include_file,kernel/register.h))
|
||||||
$(eval $(call add_include_file,kernel/rtlil.h))
|
$(eval $(call add_include_file,kernel/rtlil.h))
|
||||||
$(eval $(call add_include_file,kernel/satgen.h))
|
$(eval $(call add_include_file,kernel/satgen.h))
|
||||||
|
$(eval $(call add_include_file,kernel/scopeinfo.h))
|
||||||
$(eval $(call add_include_file,kernel/sigtools.h))
|
$(eval $(call add_include_file,kernel/sigtools.h))
|
||||||
$(eval $(call add_include_file,kernel/timinginfo.h))
|
$(eval $(call add_include_file,kernel/timinginfo.h))
|
||||||
$(eval $(call add_include_file,kernel/utils.h))
|
$(eval $(call add_include_file,kernel/utils.h))
|
||||||
|
@ -647,16 +637,10 @@ $(eval $(call add_include_file,frontends/ast/ast.h))
|
||||||
$(eval $(call add_include_file,frontends/ast/ast_binding.h))
|
$(eval $(call add_include_file,frontends/ast/ast_binding.h))
|
||||||
$(eval $(call add_include_file,frontends/blif/blifparse.h))
|
$(eval $(call add_include_file,frontends/blif/blifparse.h))
|
||||||
$(eval $(call add_include_file,backends/rtlil/rtlil_backend.h))
|
$(eval $(call add_include_file,backends/rtlil/rtlil_backend.h))
|
||||||
$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/cxxrtl.h))
|
|
||||||
$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/cxxrtl_vcd.h))
|
|
||||||
$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi.cc))
|
|
||||||
$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi.h))
|
|
||||||
$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi_vcd.cc))
|
|
||||||
$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi_vcd.h))
|
|
||||||
|
|
||||||
OBJS += kernel/driver.o kernel/register.o kernel/rtlil.o kernel/log.o kernel/calc.o kernel/yosys.o
|
OBJS += kernel/driver.o kernel/register.o kernel/rtlil.o kernel/log.o kernel/calc.o kernel/yosys.o
|
||||||
OBJS += kernel/binding.o
|
OBJS += kernel/binding.o
|
||||||
OBJS += kernel/cellaigs.o kernel/celledges.o kernel/satgen.o kernel/qcsat.o kernel/mem.o kernel/ffmerge.o kernel/ff.o kernel/yw.o kernel/json.o kernel/fmt.o
|
OBJS += kernel/cellaigs.o kernel/celledges.o kernel/satgen.o kernel/scopeinfo.o kernel/qcsat.o kernel/mem.o kernel/ffmerge.o kernel/ff.o kernel/yw.o kernel/json.o kernel/fmt.o
|
||||||
ifeq ($(ENABLE_ZLIB),1)
|
ifeq ($(ENABLE_ZLIB),1)
|
||||||
OBJS += kernel/fstdata.o
|
OBJS += kernel/fstdata.o
|
||||||
endif
|
endif
|
||||||
|
@ -679,12 +663,8 @@ OBJS += libs/bigint/BigUnsigned.o libs/bigint/BigUnsignedInABase.o
|
||||||
|
|
||||||
OBJS += libs/sha1/sha1.o
|
OBJS += libs/sha1/sha1.o
|
||||||
|
|
||||||
ifneq ($(SMALL),1)
|
|
||||||
|
|
||||||
OBJS += libs/json11/json11.o
|
OBJS += libs/json11/json11.o
|
||||||
|
|
||||||
OBJS += libs/subcircuit/subcircuit.o
|
|
||||||
|
|
||||||
OBJS += libs/ezsat/ezsat.o
|
OBJS += libs/ezsat/ezsat.o
|
||||||
OBJS += libs/ezsat/ezminisat.o
|
OBJS += libs/ezsat/ezminisat.o
|
||||||
|
|
||||||
|
@ -699,6 +679,10 @@ OBJS += libs/fst/fastlz.o
|
||||||
OBJS += libs/fst/lz4.o
|
OBJS += libs/fst/lz4.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifneq ($(SMALL),1)
|
||||||
|
|
||||||
|
OBJS += libs/subcircuit/subcircuit.o
|
||||||
|
|
||||||
include $(YOSYS_SRC)/frontends/*/Makefile.inc
|
include $(YOSYS_SRC)/frontends/*/Makefile.inc
|
||||||
include $(YOSYS_SRC)/passes/*/Makefile.inc
|
include $(YOSYS_SRC)/passes/*/Makefile.inc
|
||||||
include $(YOSYS_SRC)/backends/*/Makefile.inc
|
include $(YOSYS_SRC)/backends/*/Makefile.inc
|
||||||
|
@ -707,6 +691,9 @@ include $(YOSYS_SRC)/techlibs/*/Makefile.inc
|
||||||
else
|
else
|
||||||
|
|
||||||
include $(YOSYS_SRC)/frontends/verilog/Makefile.inc
|
include $(YOSYS_SRC)/frontends/verilog/Makefile.inc
|
||||||
|
ifeq ($(ENABLE_VERIFIC),1)
|
||||||
|
include $(YOSYS_SRC)/frontends/verific/Makefile.inc
|
||||||
|
endif
|
||||||
include $(YOSYS_SRC)/frontends/rtlil/Makefile.inc
|
include $(YOSYS_SRC)/frontends/rtlil/Makefile.inc
|
||||||
include $(YOSYS_SRC)/frontends/ast/Makefile.inc
|
include $(YOSYS_SRC)/frontends/ast/Makefile.inc
|
||||||
include $(YOSYS_SRC)/frontends/blif/Makefile.inc
|
include $(YOSYS_SRC)/frontends/blif/Makefile.inc
|
||||||
|
@ -748,13 +735,13 @@ yosys.js: $(filter-out yosysjs-$(YOSYS_VER).zip,$(EXTRA_TARGETS))
|
||||||
endif
|
endif
|
||||||
|
|
||||||
$(PROGRAM_PREFIX)yosys$(EXE): $(OBJS)
|
$(PROGRAM_PREFIX)yosys$(EXE): $(OBJS)
|
||||||
$(P) $(LD) -o $(PROGRAM_PREFIX)yosys$(EXE) $(EXE_LDFLAGS) $(LDFLAGS) $(OBJS) $(LDLIBS) $(LDLIBS_VERIFIC)
|
$(P) $(CXX) -o $(PROGRAM_PREFIX)yosys$(EXE) $(EXE_LINKFLAGS) $(LINKFLAGS) $(OBJS) $(LIBS) $(LIBS_VERIFIC)
|
||||||
|
|
||||||
libyosys.so: $(filter-out kernel/driver.o,$(OBJS))
|
libyosys.so: $(filter-out kernel/driver.o,$(OBJS))
|
||||||
ifeq ($(OS), Darwin)
|
ifeq ($(OS), Darwin)
|
||||||
$(P) $(LD) -o libyosys.so -shared -Wl,-install_name,$(LIBDIR)/libyosys.so $(LDFLAGS) $^ $(LDLIBS) $(LDLIBS_VERIFIC)
|
$(P) $(CXX) -o libyosys.so -shared -Wl,-install_name,$(LIBDIR)/libyosys.so $(LINKFLAGS) $^ $(LIBS) $(LIBS_VERIFIC)
|
||||||
else
|
else
|
||||||
$(P) $(LD) -o libyosys.so -shared -Wl,-soname,$(LIBDIR)/libyosys.so $(LDFLAGS) $^ $(LDLIBS) $(LDLIBS_VERIFIC)
|
$(P) $(CXX) -o libyosys.so -shared -Wl,-soname,$(LIBDIR)/libyosys.so $(LINKFLAGS) $^ $(LIBS) $(LIBS_VERIFIC)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
%.o: %.cc
|
%.o: %.cc
|
||||||
|
@ -763,7 +750,7 @@ endif
|
||||||
|
|
||||||
%.pyh: %.h
|
%.pyh: %.h
|
||||||
$(Q) mkdir -p $(dir $@)
|
$(Q) mkdir -p $(dir $@)
|
||||||
$(P) cat $< | grep -E -v "#[ ]*(include|error)" | $(LD) $(CXXFLAGS) -x c++ -o $@ -E -P -
|
$(P) cat $< | grep -E -v "#[ ]*(include|error)" | $(CXX) $(CXXFLAGS) -x c++ -o $@ -E -P -
|
||||||
|
|
||||||
ifeq ($(ENABLE_PYOSYS),1)
|
ifeq ($(ENABLE_PYOSYS),1)
|
||||||
$(PY_WRAPPER_FILE).cc: misc/$(PY_GEN_SCRIPT).py $(PY_WRAP_INCLUDES)
|
$(PY_WRAPPER_FILE).cc: misc/$(PY_GEN_SCRIPT).py $(PY_WRAP_INCLUDES)
|
||||||
|
@ -784,15 +771,15 @@ kernel/version_$(GIT_REV).cc: $(YOSYS_SRC)/Makefile
|
||||||
|
|
||||||
ifeq ($(ENABLE_VERIFIC),1)
|
ifeq ($(ENABLE_VERIFIC),1)
|
||||||
CXXFLAGS_NOVERIFIC = $(foreach v,$(CXXFLAGS),$(if $(findstring $(VERIFIC_DIR),$(v)),,$(v)))
|
CXXFLAGS_NOVERIFIC = $(foreach v,$(CXXFLAGS),$(if $(findstring $(VERIFIC_DIR),$(v)),,$(v)))
|
||||||
LDLIBS_NOVERIFIC = $(foreach v,$(LDLIBS),$(if $(findstring $(VERIFIC_DIR),$(v)),,$(v)))
|
LIBS_NOVERIFIC = $(foreach v,$(LIBS),$(if $(findstring $(VERIFIC_DIR),$(v)),,$(v)))
|
||||||
else
|
else
|
||||||
CXXFLAGS_NOVERIFIC = $(CXXFLAGS)
|
CXXFLAGS_NOVERIFIC = $(CXXFLAGS)
|
||||||
LDLIBS_NOVERIFIC = $(LDLIBS)
|
LIBS_NOVERIFIC = $(LIBS)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
$(PROGRAM_PREFIX)yosys-config: misc/yosys-config.in
|
$(PROGRAM_PREFIX)yosys-config: misc/yosys-config.in
|
||||||
$(P) $(SED) -e 's#@CXXFLAGS@#$(subst -Ilibs/dlfcn-win32,,$(subst -I. -I"$(YOSYS_SRC)",-I"$(DATDIR)/include",$(strip $(CXXFLAGS_NOVERIFIC))))#;' \
|
$(P) $(SED) -e 's#@CXXFLAGS@#$(subst -Ilibs/dlfcn-win32,,$(subst -I. -I"$(YOSYS_SRC)",-I"$(DATDIR)/include",$(strip $(CXXFLAGS_NOVERIFIC))))#;' \
|
||||||
-e 's#@CXX@#$(strip $(CXX))#;' -e 's#@LDFLAGS@#$(strip $(LDFLAGS) $(PLUGIN_LDFLAGS))#;' -e 's#@LDLIBS@#$(strip $(LDLIBS_NOVERIFIC) $(PLUGIN_LDLIBS))#;' \
|
-e 's#@CXX@#$(strip $(CXX))#;' -e 's#@LINKFLAGS@#$(strip $(LINKFLAGS) $(PLUGIN_LINKFLAGS))#;' -e 's#@LIBS@#$(strip $(LIBS_NOVERIFIC) $(PLUGIN_LIBS))#;' \
|
||||||
-e 's#@BINDIR@#$(strip $(BINDIR))#;' -e 's#@DATDIR@#$(strip $(DATDIR))#;' < $< > $(PROGRAM_PREFIX)yosys-config
|
-e 's#@BINDIR@#$(strip $(BINDIR))#;' -e 's#@DATDIR@#$(strip $(DATDIR))#;' < $< > $(PROGRAM_PREFIX)yosys-config
|
||||||
$(Q) chmod +x $(PROGRAM_PREFIX)yosys-config
|
$(Q) chmod +x $(PROGRAM_PREFIX)yosys-config
|
||||||
|
|
||||||
|
@ -844,9 +831,22 @@ else
|
||||||
ABCOPT=""
|
ABCOPT=""
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
# When YOSYS_NOVERIFIC is set as a make variable, also export it to the
|
||||||
|
# enviornment, so that `YOSYS_NOVERIFIC=1 make test` _and_
|
||||||
|
# `make test YOSYS_NOVERIFIC=1` will run with verific disabled.
|
||||||
|
ifeq ($(YOSYS_NOVERIFIC),1)
|
||||||
|
export YOSYS_NOVERIFIC
|
||||||
|
endif
|
||||||
|
|
||||||
test: $(TARGETS) $(EXTRA_TARGETS)
|
test: $(TARGETS) $(EXTRA_TARGETS)
|
||||||
ifeq ($(ENABLE_VERIFIC),1)
|
ifeq ($(ENABLE_VERIFIC),1)
|
||||||
|
ifeq ($(YOSYS_NOVERIFIC),1)
|
||||||
|
@echo
|
||||||
|
@echo "Running tests without verific support due to YOSYS_NOVERIFIC=1"
|
||||||
|
@echo
|
||||||
|
else
|
||||||
+cd tests/verific && bash run-test.sh $(SEEDOPT)
|
+cd tests/verific && bash run-test.sh $(SEEDOPT)
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
+cd tests/simple && bash run-test.sh $(SEEDOPT)
|
+cd tests/simple && bash run-test.sh $(SEEDOPT)
|
||||||
+cd tests/simple_abc9 && bash run-test.sh $(SEEDOPT)
|
+cd tests/simple_abc9 && bash run-test.sh $(SEEDOPT)
|
||||||
|
@ -918,7 +918,7 @@ ystests: $(TARGETS) $(EXTRA_TARGETS)
|
||||||
# Unit test
|
# Unit test
|
||||||
unit-test: libyosys.so
|
unit-test: libyosys.so
|
||||||
@$(MAKE) -C $(UNITESTPATH) CXX="$(CXX)" CPPFLAGS="$(CPPFLAGS)" \
|
@$(MAKE) -C $(UNITESTPATH) CXX="$(CXX)" CPPFLAGS="$(CPPFLAGS)" \
|
||||||
CXXFLAGS="$(CXXFLAGS)" LDLIBS="$(LDLIBS)" ROOTPATH="$(CURDIR)"
|
CXXFLAGS="$(CXXFLAGS)" LIBS="$(LIBS)" ROOTPATH="$(CURDIR)"
|
||||||
|
|
||||||
clean-unit-test:
|
clean-unit-test:
|
||||||
@$(MAKE) -C $(UNITESTPATH) clean
|
@$(MAKE) -C $(UNITESTPATH) clean
|
||||||
|
|
10
README.md
10
README.md
|
@ -1,7 +1,7 @@
|
||||||
```
|
```
|
||||||
yosys -- Yosys Open SYnthesis Suite
|
yosys -- Yosys Open SYnthesis Suite
|
||||||
|
|
||||||
Copyright (C) 2012 - 2020 Claire Xenia Wolf <claire@yosyshq.com>
|
Copyright (C) 2012 - 2024 Claire Xenia Wolf <claire@yosyshq.com>
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
purpose with or without fee is hereby granted, provided that the above
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -587,7 +587,13 @@ from SystemVerilog:
|
||||||
- enums are supported (including inside packages)
|
- enums are supported (including inside packages)
|
||||||
- but are currently not strongly typed
|
- but are currently not strongly typed
|
||||||
|
|
||||||
- packed structs and unions are supported.
|
- packed structs and unions are supported
|
||||||
|
- arrays of packed structs/unions are currently not supported
|
||||||
|
- structure literals are currently not supported
|
||||||
|
|
||||||
|
- multidimensional arrays are supported
|
||||||
|
- array assignment of unpacked arrays is currently not supported
|
||||||
|
- array literals are currently not supported
|
||||||
|
|
||||||
- SystemVerilog interfaces (SVIs) are supported. Modports for specifying whether
|
- SystemVerilog interfaces (SVIs) are supported. Modports for specifying whether
|
||||||
ports are inputs or outputs are supported.
|
ports are inputs or outputs are supported.
|
||||||
|
|
|
@ -54,6 +54,8 @@ struct AigerWriter
|
||||||
|
|
||||||
vector<pair<int, int>> aig_gates;
|
vector<pair<int, int>> aig_gates;
|
||||||
vector<int> aig_latchin, aig_latchinit, aig_outputs;
|
vector<int> aig_latchin, aig_latchinit, aig_outputs;
|
||||||
|
vector<SigBit> bit2aig_stack;
|
||||||
|
size_t next_loop_check = 1024;
|
||||||
int aig_m = 0, aig_i = 0, aig_l = 0, aig_o = 0, aig_a = 0;
|
int aig_m = 0, aig_i = 0, aig_l = 0, aig_o = 0, aig_a = 0;
|
||||||
int aig_b = 0, aig_c = 0, aig_j = 0, aig_f = 0;
|
int aig_b = 0, aig_c = 0, aig_j = 0, aig_f = 0;
|
||||||
|
|
||||||
|
@ -81,6 +83,23 @@ struct AigerWriter
|
||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (bit2aig_stack.size() == next_loop_check) {
|
||||||
|
for (size_t i = 0; i < next_loop_check; ++i)
|
||||||
|
{
|
||||||
|
SigBit report_bit = bit2aig_stack[i];
|
||||||
|
if (report_bit != bit)
|
||||||
|
continue;
|
||||||
|
for (size_t j = i; j < next_loop_check; ++j) {
|
||||||
|
report_bit = bit2aig_stack[j];
|
||||||
|
if (report_bit.is_wire() && report_bit.wire->name.isPublic())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
log_error("Found combinational logic loop while processing signal %s.\n", log_signal(report_bit));
|
||||||
|
}
|
||||||
|
next_loop_check *= 2;
|
||||||
|
}
|
||||||
|
bit2aig_stack.push_back(bit);
|
||||||
|
|
||||||
// NB: Cannot use iterator returned from aig_map.insert()
|
// NB: Cannot use iterator returned from aig_map.insert()
|
||||||
// since this function is called recursively
|
// since this function is called recursively
|
||||||
|
|
||||||
|
@ -101,6 +120,8 @@ struct AigerWriter
|
||||||
a = initstate_ff;
|
a = initstate_ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bit2aig_stack.pop_back();
|
||||||
|
|
||||||
if (bit == State::Sx || bit == State::Sz)
|
if (bit == State::Sx || bit == State::Sz)
|
||||||
log_error("Design contains 'x' or 'z' bits. Use 'setundef' to replace those constants.\n");
|
log_error("Design contains 'x' or 'z' bits. Use 'setundef' to replace those constants.\n");
|
||||||
|
|
||||||
|
@ -299,6 +320,9 @@ struct AigerWriter
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cell->type == ID($scopeinfo))
|
||||||
|
continue;
|
||||||
|
|
||||||
log_error("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell));
|
log_error("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -226,6 +226,9 @@ struct BlifDumper
|
||||||
|
|
||||||
for (auto cell : module->cells())
|
for (auto cell : module->cells())
|
||||||
{
|
{
|
||||||
|
if (cell->type == ID($scopeinfo))
|
||||||
|
continue;
|
||||||
|
|
||||||
if (config->unbuf_types.count(cell->type)) {
|
if (config->unbuf_types.count(cell->type)) {
|
||||||
auto portnames = config->unbuf_types.at(cell->type);
|
auto portnames = config->unbuf_types.at(cell->type);
|
||||||
f << stringf(".names %s %s\n1 1\n",
|
f << stringf(".names %s %s\n1 1\n",
|
||||||
|
|
|
@ -1,2 +1,11 @@
|
||||||
|
|
||||||
OBJS += backends/cxxrtl/cxxrtl_backend.o
|
OBJS += backends/cxxrtl/cxxrtl_backend.o
|
||||||
|
|
||||||
|
$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/cxxrtl.h))
|
||||||
|
$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/cxxrtl_vcd.h))
|
||||||
|
$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/cxxrtl_time.h))
|
||||||
|
$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/cxxrtl_replay.h))
|
||||||
|
$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi.cc))
|
||||||
|
$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi.h))
|
||||||
|
$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi_vcd.cc))
|
||||||
|
$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi_vcd.h))
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include "kernel/mem.h"
|
#include "kernel/mem.h"
|
||||||
#include "kernel/log.h"
|
#include "kernel/log.h"
|
||||||
#include "kernel/fmt.h"
|
#include "kernel/fmt.h"
|
||||||
|
#include "kernel/scopeinfo.h"
|
||||||
|
|
||||||
USING_YOSYS_NAMESPACE
|
USING_YOSYS_NAMESPACE
|
||||||
PRIVATE_NAMESPACE_BEGIN
|
PRIVATE_NAMESPACE_BEGIN
|
||||||
|
@ -218,7 +219,7 @@ bool is_internal_cell(RTLIL::IdString type)
|
||||||
|
|
||||||
bool is_effectful_cell(RTLIL::IdString type)
|
bool is_effectful_cell(RTLIL::IdString type)
|
||||||
{
|
{
|
||||||
return type.isPublic() || type == ID($print);
|
return type.in(ID($print), ID($check));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_cxxrtl_blackbox_cell(const RTLIL::Cell *cell)
|
bool is_cxxrtl_blackbox_cell(const RTLIL::Cell *cell)
|
||||||
|
@ -282,7 +283,7 @@ struct FlowGraph {
|
||||||
CONNECT,
|
CONNECT,
|
||||||
CELL_SYNC,
|
CELL_SYNC,
|
||||||
CELL_EVAL,
|
CELL_EVAL,
|
||||||
PRINT_SYNC,
|
EFFECT_SYNC,
|
||||||
PROCESS_SYNC,
|
PROCESS_SYNC,
|
||||||
PROCESS_CASE,
|
PROCESS_CASE,
|
||||||
MEM_RDPORT,
|
MEM_RDPORT,
|
||||||
|
@ -292,7 +293,7 @@ struct FlowGraph {
|
||||||
Type type;
|
Type type;
|
||||||
RTLIL::SigSig connect = {};
|
RTLIL::SigSig connect = {};
|
||||||
const RTLIL::Cell *cell = nullptr;
|
const RTLIL::Cell *cell = nullptr;
|
||||||
std::vector<const RTLIL::Cell*> print_sync_cells;
|
std::vector<const RTLIL::Cell*> cells;
|
||||||
const RTLIL::Process *process = nullptr;
|
const RTLIL::Process *process = nullptr;
|
||||||
const Mem *mem = nullptr;
|
const Mem *mem = nullptr;
|
||||||
int portidx;
|
int portidx;
|
||||||
|
@ -480,11 +481,11 @@ struct FlowGraph {
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
Node *add_print_sync_node(std::vector<const RTLIL::Cell*> cells)
|
Node *add_effect_sync_node(std::vector<const RTLIL::Cell*> cells)
|
||||||
{
|
{
|
||||||
Node *node = new Node;
|
Node *node = new Node;
|
||||||
node->type = Node::Type::PRINT_SYNC;
|
node->type = Node::Type::EFFECT_SYNC;
|
||||||
node->print_sync_cells = cells;
|
node->cells = cells;
|
||||||
nodes.push_back(node);
|
nodes.push_back(node);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
@ -1063,99 +1064,6 @@ struct CxxrtlWorker {
|
||||||
f << ".val()";
|
f << ".val()";
|
||||||
}
|
}
|
||||||
|
|
||||||
void dump_print(const RTLIL::Cell *cell)
|
|
||||||
{
|
|
||||||
Fmt fmt = {};
|
|
||||||
fmt.parse_rtlil(cell);
|
|
||||||
|
|
||||||
f << indent << "if (";
|
|
||||||
dump_sigspec_rhs(cell->getPort(ID::EN));
|
|
||||||
f << " == value<1>{1u}) {\n";
|
|
||||||
inc_indent();
|
|
||||||
dict<std::string, RTLIL::SigSpec> fmt_args;
|
|
||||||
f << indent << "struct : public lazy_fmt {\n";
|
|
||||||
inc_indent();
|
|
||||||
f << indent << "std::string operator() () const override {\n";
|
|
||||||
inc_indent();
|
|
||||||
fmt.emit_cxxrtl(f, indent, [&](const RTLIL::SigSpec &sig) {
|
|
||||||
if (sig.size() == 0)
|
|
||||||
f << "value<0>()";
|
|
||||||
else {
|
|
||||||
std::string arg_name = "arg" + std::to_string(fmt_args.size());
|
|
||||||
fmt_args[arg_name] = sig;
|
|
||||||
f << arg_name;
|
|
||||||
}
|
|
||||||
}, "performer");
|
|
||||||
dec_indent();
|
|
||||||
f << indent << "}\n";
|
|
||||||
f << indent << "struct performer *performer;\n";
|
|
||||||
for (auto arg : fmt_args)
|
|
||||||
f << indent << "value<" << arg.second.size() << "> " << arg.first << ";\n";
|
|
||||||
dec_indent();
|
|
||||||
f << indent << "} formatter;\n";
|
|
||||||
f << indent << "formatter.performer = performer;\n";
|
|
||||||
for (auto arg : fmt_args) {
|
|
||||||
f << indent << "formatter." << arg.first << " = ";
|
|
||||||
dump_sigspec_rhs(arg.second);
|
|
||||||
f << ";\n";
|
|
||||||
}
|
|
||||||
f << indent << "if (performer) {\n";
|
|
||||||
inc_indent();
|
|
||||||
f << indent << "static const metadata_map attributes = ";
|
|
||||||
dump_metadata_map(cell->attributes);
|
|
||||||
f << ";\n";
|
|
||||||
f << indent << "performer->on_print(formatter, attributes);\n";
|
|
||||||
dec_indent();
|
|
||||||
f << indent << "} else {\n";
|
|
||||||
inc_indent();
|
|
||||||
f << indent << print_output << " << formatter();\n";
|
|
||||||
dec_indent();
|
|
||||||
f << indent << "}\n";
|
|
||||||
dec_indent();
|
|
||||||
f << indent << "}\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
void dump_sync_print(std::vector<const RTLIL::Cell*> &cells)
|
|
||||||
{
|
|
||||||
log_assert(!cells.empty());
|
|
||||||
const auto &trg = cells[0]->getPort(ID::TRG);
|
|
||||||
const auto &trg_polarity = cells[0]->getParam(ID::TRG_POLARITY);
|
|
||||||
|
|
||||||
f << indent << "if (";
|
|
||||||
for (int i = 0; i < trg.size(); i++) {
|
|
||||||
RTLIL::SigBit trg_bit = trg[i];
|
|
||||||
trg_bit = sigmaps[trg_bit.wire->module](trg_bit);
|
|
||||||
log_assert(trg_bit.wire);
|
|
||||||
|
|
||||||
if (i != 0)
|
|
||||||
f << " || ";
|
|
||||||
|
|
||||||
if (trg_polarity[i] == State::S1)
|
|
||||||
f << "posedge_";
|
|
||||||
else
|
|
||||||
f << "negedge_";
|
|
||||||
f << mangle(trg_bit);
|
|
||||||
}
|
|
||||||
f << ") {\n";
|
|
||||||
inc_indent();
|
|
||||||
std::sort(cells.begin(), cells.end(), [](const RTLIL::Cell *a, const RTLIL::Cell *b) {
|
|
||||||
return a->getParam(ID::PRIORITY).as_int() > b->getParam(ID::PRIORITY).as_int();
|
|
||||||
});
|
|
||||||
for (auto cell : cells) {
|
|
||||||
log_assert(cell->getParam(ID::TRG_ENABLE).as_bool());
|
|
||||||
log_assert(cell->getPort(ID::TRG) == trg);
|
|
||||||
log_assert(cell->getParam(ID::TRG_POLARITY) == trg_polarity);
|
|
||||||
|
|
||||||
std::vector<const RTLIL::Cell*> inlined_cells;
|
|
||||||
collect_cell_eval(cell, /*for_debug=*/false, inlined_cells);
|
|
||||||
dump_inlined_cells(inlined_cells);
|
|
||||||
dump_print(cell);
|
|
||||||
}
|
|
||||||
dec_indent();
|
|
||||||
|
|
||||||
f << indent << "}\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
void dump_inlined_cells(const std::vector<const RTLIL::Cell*> &cells)
|
void dump_inlined_cells(const std::vector<const RTLIL::Cell*> &cells)
|
||||||
{
|
{
|
||||||
if (cells.empty()) {
|
if (cells.empty()) {
|
||||||
|
@ -1309,6 +1217,144 @@ struct CxxrtlWorker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void dump_print(const RTLIL::Cell *cell)
|
||||||
|
{
|
||||||
|
Fmt fmt;
|
||||||
|
fmt.parse_rtlil(cell);
|
||||||
|
|
||||||
|
f << indent << "if (";
|
||||||
|
dump_sigspec_rhs(cell->getPort(ID::EN));
|
||||||
|
f << " == value<1>{1u}) {\n";
|
||||||
|
inc_indent();
|
||||||
|
dict<std::string, RTLIL::SigSpec> fmt_args;
|
||||||
|
f << indent << "struct : public lazy_fmt {\n";
|
||||||
|
inc_indent();
|
||||||
|
f << indent << "std::string operator() () const override {\n";
|
||||||
|
inc_indent();
|
||||||
|
fmt.emit_cxxrtl(f, indent, [&](const RTLIL::SigSpec &sig) {
|
||||||
|
if (sig.size() == 0)
|
||||||
|
f << "value<0>()";
|
||||||
|
else {
|
||||||
|
std::string arg_name = "arg" + std::to_string(fmt_args.size());
|
||||||
|
fmt_args[arg_name] = sig;
|
||||||
|
f << arg_name;
|
||||||
|
}
|
||||||
|
}, "performer");
|
||||||
|
dec_indent();
|
||||||
|
f << indent << "}\n";
|
||||||
|
f << indent << "struct performer *performer;\n";
|
||||||
|
for (auto arg : fmt_args)
|
||||||
|
f << indent << "value<" << arg.second.size() << "> " << arg.first << ";\n";
|
||||||
|
dec_indent();
|
||||||
|
f << indent << "} formatter;\n";
|
||||||
|
f << indent << "formatter.performer = performer;\n";
|
||||||
|
for (auto arg : fmt_args) {
|
||||||
|
f << indent << "formatter." << arg.first << " = ";
|
||||||
|
dump_sigspec_rhs(arg.second);
|
||||||
|
f << ";\n";
|
||||||
|
}
|
||||||
|
f << indent << "if (performer) {\n";
|
||||||
|
inc_indent();
|
||||||
|
f << indent << "static const metadata_map attributes = ";
|
||||||
|
dump_metadata_map(cell->attributes);
|
||||||
|
f << ";\n";
|
||||||
|
f << indent << "performer->on_print(formatter, attributes);\n";
|
||||||
|
dec_indent();
|
||||||
|
f << indent << "} else {\n";
|
||||||
|
inc_indent();
|
||||||
|
f << indent << print_output << " << formatter();\n";
|
||||||
|
dec_indent();
|
||||||
|
f << indent << "}\n";
|
||||||
|
dec_indent();
|
||||||
|
f << indent << "}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump_effect(const RTLIL::Cell *cell)
|
||||||
|
{
|
||||||
|
Fmt fmt;
|
||||||
|
fmt.parse_rtlil(cell);
|
||||||
|
|
||||||
|
f << indent << "if (";
|
||||||
|
dump_sigspec_rhs(cell->getPort(ID::EN));
|
||||||
|
f << ") {\n";
|
||||||
|
inc_indent();
|
||||||
|
dict<std::string, RTLIL::SigSpec> fmt_args;
|
||||||
|
f << indent << "struct : public lazy_fmt {\n";
|
||||||
|
inc_indent();
|
||||||
|
f << indent << "std::string operator() () const override {\n";
|
||||||
|
inc_indent();
|
||||||
|
fmt.emit_cxxrtl(f, indent, [&](const RTLIL::SigSpec &sig) {
|
||||||
|
if (sig.size() == 0)
|
||||||
|
f << "value<0>()";
|
||||||
|
else {
|
||||||
|
std::string arg_name = "arg" + std::to_string(fmt_args.size());
|
||||||
|
fmt_args[arg_name] = sig;
|
||||||
|
f << arg_name;
|
||||||
|
}
|
||||||
|
}, "performer");
|
||||||
|
dec_indent();
|
||||||
|
f << indent << "}\n";
|
||||||
|
f << indent << "struct performer *performer;\n";
|
||||||
|
for (auto arg : fmt_args)
|
||||||
|
f << indent << "value<" << arg.second.size() << "> " << arg.first << ";\n";
|
||||||
|
dec_indent();
|
||||||
|
f << indent << "} formatter;\n";
|
||||||
|
f << indent << "formatter.performer = performer;\n";
|
||||||
|
for (auto arg : fmt_args) {
|
||||||
|
f << indent << "formatter." << arg.first << " = ";
|
||||||
|
dump_sigspec_rhs(arg.second);
|
||||||
|
f << ";\n";
|
||||||
|
}
|
||||||
|
if (cell->hasPort(ID::A)) {
|
||||||
|
f << indent << "bool condition = (bool)";
|
||||||
|
dump_sigspec_rhs(cell->getPort(ID::A));
|
||||||
|
f << ";\n";
|
||||||
|
}
|
||||||
|
f << indent << "if (performer) {\n";
|
||||||
|
inc_indent();
|
||||||
|
f << indent << "static const metadata_map attributes = ";
|
||||||
|
dump_metadata_map(cell->attributes);
|
||||||
|
f << ";\n";
|
||||||
|
if (cell->type == ID($print)) {
|
||||||
|
f << indent << "performer->on_print(formatter, attributes);\n";
|
||||||
|
} else if (cell->type == ID($check)) {
|
||||||
|
std::string flavor = cell->getParam(ID::FLAVOR).decode_string();
|
||||||
|
f << indent << "performer->on_check(";
|
||||||
|
if (flavor == "assert")
|
||||||
|
f << "flavor::ASSERT";
|
||||||
|
else if (flavor == "assume")
|
||||||
|
f << "flavor::ASSUME";
|
||||||
|
else if (flavor == "live")
|
||||||
|
f << "flavor::ASSERT_EVENTUALLY";
|
||||||
|
else if (flavor == "fair")
|
||||||
|
f << "flavor::ASSUME_EVENTUALLY";
|
||||||
|
else if (flavor == "cover")
|
||||||
|
f << "flavor::COVER";
|
||||||
|
else log_assert(false);
|
||||||
|
f << ", condition, formatter, attributes);\n";
|
||||||
|
} else log_assert(false);
|
||||||
|
dec_indent();
|
||||||
|
f << indent << "} else {\n";
|
||||||
|
inc_indent();
|
||||||
|
if (cell->type == ID($print)) {
|
||||||
|
f << indent << print_output << " << formatter();\n";
|
||||||
|
} else if (cell->type == ID($check)) {
|
||||||
|
std::string flavor = cell->getParam(ID::FLAVOR).decode_string();
|
||||||
|
if (flavor == "assert" || flavor == "assume") {
|
||||||
|
f << indent << "if (!condition) {\n";
|
||||||
|
inc_indent();
|
||||||
|
f << indent << "std::cerr << formatter();\n";
|
||||||
|
dec_indent();
|
||||||
|
f << indent << "}\n";
|
||||||
|
f << indent << "CXXRTL_ASSERT(condition && \"Check failed\");\n";
|
||||||
|
}
|
||||||
|
} else log_assert(false);
|
||||||
|
dec_indent();
|
||||||
|
f << indent << "}\n";
|
||||||
|
dec_indent();
|
||||||
|
f << indent << "}\n";
|
||||||
|
}
|
||||||
|
|
||||||
void dump_cell_eval(const RTLIL::Cell *cell, bool for_debug = false)
|
void dump_cell_eval(const RTLIL::Cell *cell, bool for_debug = false)
|
||||||
{
|
{
|
||||||
std::vector<const RTLIL::Cell*> inlined_cells;
|
std::vector<const RTLIL::Cell*> inlined_cells;
|
||||||
|
@ -1322,30 +1368,34 @@ struct CxxrtlWorker {
|
||||||
f << " = ";
|
f << " = ";
|
||||||
dump_cell_expr(cell, for_debug);
|
dump_cell_expr(cell, for_debug);
|
||||||
f << ";\n";
|
f << ";\n";
|
||||||
// $print cell
|
// Effectful cells
|
||||||
} else if (cell->type == ID($print)) {
|
} else if (is_effectful_cell(cell->type)) {
|
||||||
log_assert(!for_debug);
|
log_assert(!for_debug);
|
||||||
|
|
||||||
// Sync $print cells are grouped into PRINT_SYNC nodes in the FlowGraph.
|
// Sync effectful cells are grouped into EFFECT_SYNC nodes in the FlowGraph.
|
||||||
log_assert(!cell->getParam(ID::TRG_ENABLE).as_bool() || (cell->getParam(ID::TRG_ENABLE).as_bool() && cell->getParam(ID::TRG_WIDTH).as_int() == 0));
|
log_assert(!cell->getParam(ID::TRG_ENABLE).as_bool() || (cell->getParam(ID::TRG_ENABLE).as_bool() && cell->getParam(ID::TRG_WIDTH).as_int() == 0));
|
||||||
|
|
||||||
if (!cell->getParam(ID::TRG_ENABLE).as_bool()) { // async $print cell
|
if (!cell->getParam(ID::TRG_ENABLE).as_bool()) { // async effectful cell
|
||||||
f << indent << "auto " << mangle(cell) << "_curr = ";
|
f << indent << "auto " << mangle(cell) << "_next = ";
|
||||||
dump_sigspec_rhs(cell->getPort(ID::EN));
|
dump_sigspec_rhs(cell->getPort(ID::EN));
|
||||||
f << ".concat(";
|
f << ".concat(";
|
||||||
dump_sigspec_rhs(cell->getPort(ID::ARGS));
|
if (cell->type == ID($print))
|
||||||
|
dump_sigspec_rhs(cell->getPort(ID::ARGS));
|
||||||
|
else if (cell->type == ID($check))
|
||||||
|
dump_sigspec_rhs(cell->getPort(ID::A));
|
||||||
|
else log_assert(false);
|
||||||
f << ").val();\n";
|
f << ").val();\n";
|
||||||
|
|
||||||
f << indent << "if (" << mangle(cell) << " != " << mangle(cell) << "_curr) {\n";
|
f << indent << "if (" << mangle(cell) << " != " << mangle(cell) << "_next) {\n";
|
||||||
inc_indent();
|
inc_indent();
|
||||||
dump_print(cell);
|
dump_effect(cell);
|
||||||
f << indent << mangle(cell) << " = " << mangle(cell) << "_curr;\n";
|
f << indent << mangle(cell) << " = " << mangle(cell) << "_next;\n";
|
||||||
dec_indent();
|
dec_indent();
|
||||||
f << indent << "}\n";
|
f << indent << "}\n";
|
||||||
} else { // initial $print cell
|
} else { // initial effectful cell
|
||||||
f << indent << "if (!" << mangle(cell) << ") {\n";
|
f << indent << "if (!" << mangle(cell) << ") {\n";
|
||||||
inc_indent();
|
inc_indent();
|
||||||
dump_print(cell);
|
dump_effect(cell);
|
||||||
f << indent << mangle(cell) << " = value<1>{1u};\n";
|
f << indent << mangle(cell) << " = value<1>{1u};\n";
|
||||||
dec_indent();
|
dec_indent();
|
||||||
f << indent << "}\n";
|
f << indent << "}\n";
|
||||||
|
@ -1446,7 +1496,7 @@ struct CxxrtlWorker {
|
||||||
f << indent;
|
f << indent;
|
||||||
dump_sigspec_lhs(cell->getPort(ID::Q));
|
dump_sigspec_lhs(cell->getPort(ID::Q));
|
||||||
f << " = ";
|
f << " = ";
|
||||||
dump_sigspec_lhs(cell->getPort(ID::Q));
|
dump_sigspec_rhs(cell->getPort(ID::Q));
|
||||||
f << ".update(";
|
f << ".update(";
|
||||||
dump_const(RTLIL::Const(RTLIL::S1, cell->getParam(ID::WIDTH).as_int()));
|
dump_const(RTLIL::Const(RTLIL::S1, cell->getParam(ID::WIDTH).as_int()));
|
||||||
f << ", ";
|
f << ", ";
|
||||||
|
@ -1458,7 +1508,7 @@ struct CxxrtlWorker {
|
||||||
f << indent;
|
f << indent;
|
||||||
dump_sigspec_lhs(cell->getPort(ID::Q));
|
dump_sigspec_lhs(cell->getPort(ID::Q));
|
||||||
f << " = ";
|
f << " = ";
|
||||||
dump_sigspec_lhs(cell->getPort(ID::Q));
|
dump_sigspec_rhs(cell->getPort(ID::Q));
|
||||||
f << ".update(";
|
f << ".update(";
|
||||||
dump_const(RTLIL::Const(RTLIL::S0, cell->getParam(ID::WIDTH).as_int()));
|
dump_const(RTLIL::Const(RTLIL::S0, cell->getParam(ID::WIDTH).as_int()));
|
||||||
f << ", ";
|
f << ", ";
|
||||||
|
@ -1469,8 +1519,9 @@ struct CxxrtlWorker {
|
||||||
} else if (is_internal_cell(cell->type)) {
|
} else if (is_internal_cell(cell->type)) {
|
||||||
log_cmd_error("Unsupported internal cell `%s'.\n", cell->type.c_str());
|
log_cmd_error("Unsupported internal cell `%s'.\n", cell->type.c_str());
|
||||||
// User cells
|
// User cells
|
||||||
|
} else if (for_debug) {
|
||||||
|
// Outlines are called on demand when computing the value of a debug item. Nothing to do here.
|
||||||
} else {
|
} else {
|
||||||
log_assert(!for_debug);
|
|
||||||
log_assert(cell->known());
|
log_assert(cell->known());
|
||||||
bool buffered_inputs = false;
|
bool buffered_inputs = false;
|
||||||
const char *access = is_cxxrtl_blackbox_cell(cell) ? "->" : ".";
|
const char *access = is_cxxrtl_blackbox_cell(cell) ? "->" : ".";
|
||||||
|
@ -1728,6 +1779,47 @@ struct CxxrtlWorker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void dump_cell_effect_sync(std::vector<const RTLIL::Cell*> &cells)
|
||||||
|
{
|
||||||
|
log_assert(!cells.empty());
|
||||||
|
const auto &trg = cells[0]->getPort(ID::TRG);
|
||||||
|
const auto &trg_polarity = cells[0]->getParam(ID::TRG_POLARITY);
|
||||||
|
|
||||||
|
f << indent << "if (";
|
||||||
|
for (int i = 0; i < trg.size(); i++) {
|
||||||
|
RTLIL::SigBit trg_bit = trg[i];
|
||||||
|
trg_bit = sigmaps[trg_bit.wire->module](trg_bit);
|
||||||
|
log_assert(trg_bit.wire);
|
||||||
|
|
||||||
|
if (i != 0)
|
||||||
|
f << " || ";
|
||||||
|
|
||||||
|
if (trg_polarity[i] == State::S1)
|
||||||
|
f << "posedge_";
|
||||||
|
else
|
||||||
|
f << "negedge_";
|
||||||
|
f << mangle(trg_bit);
|
||||||
|
}
|
||||||
|
f << ") {\n";
|
||||||
|
inc_indent();
|
||||||
|
std::sort(cells.begin(), cells.end(), [](const RTLIL::Cell *a, const RTLIL::Cell *b) {
|
||||||
|
return a->getParam(ID::PRIORITY).as_int() > b->getParam(ID::PRIORITY).as_int();
|
||||||
|
});
|
||||||
|
for (auto cell : cells) {
|
||||||
|
log_assert(cell->getParam(ID::TRG_ENABLE).as_bool());
|
||||||
|
log_assert(cell->getPort(ID::TRG) == trg);
|
||||||
|
log_assert(cell->getParam(ID::TRG_POLARITY) == trg_polarity);
|
||||||
|
|
||||||
|
std::vector<const RTLIL::Cell*> inlined_cells;
|
||||||
|
collect_cell_eval(cell, /*for_debug=*/false, inlined_cells);
|
||||||
|
dump_inlined_cells(inlined_cells);
|
||||||
|
dump_effect(cell);
|
||||||
|
}
|
||||||
|
dec_indent();
|
||||||
|
|
||||||
|
f << indent << "}\n";
|
||||||
|
}
|
||||||
|
|
||||||
void dump_mem_rdport(const Mem *mem, int portidx, bool for_debug = false)
|
void dump_mem_rdport(const Mem *mem, int portidx, bool for_debug = false)
|
||||||
{
|
{
|
||||||
auto &port = mem->rd_ports[portidx];
|
auto &port = mem->rd_ports[portidx];
|
||||||
|
@ -2047,11 +2139,10 @@ struct CxxrtlWorker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto cell : module->cells()) {
|
for (auto cell : module->cells()) {
|
||||||
// Certain $print cells have additional state, which must be reset as well.
|
// Async and initial effectful cells have additional state, which must be reset as well.
|
||||||
if (cell->type == ID($print) && !cell->getParam(ID::TRG_ENABLE).as_bool())
|
if (is_effectful_cell(cell->type))
|
||||||
f << indent << mangle(cell) << " = value<" << (1 + cell->getParam(ID::ARGS_WIDTH).as_int()) << ">();\n";
|
if (!cell->getParam(ID::TRG_ENABLE).as_bool() || cell->getParam(ID::TRG_WIDTH).as_int() == 0)
|
||||||
if (cell->type == ID($print) && cell->getParam(ID::TRG_ENABLE).as_bool() && cell->getParam(ID::TRG_WIDTH).as_int() == 0)
|
f << indent << mangle(cell) << " = {};\n";
|
||||||
f << indent << mangle(cell) << " = value<1>();\n";
|
|
||||||
if (is_internal_cell(cell->type))
|
if (is_internal_cell(cell->type))
|
||||||
continue;
|
continue;
|
||||||
f << indent << mangle(cell);
|
f << indent << mangle(cell);
|
||||||
|
@ -2099,8 +2190,8 @@ struct CxxrtlWorker {
|
||||||
case FlowGraph::Node::Type::CELL_EVAL:
|
case FlowGraph::Node::Type::CELL_EVAL:
|
||||||
dump_cell_eval(node.cell);
|
dump_cell_eval(node.cell);
|
||||||
break;
|
break;
|
||||||
case FlowGraph::Node::Type::PRINT_SYNC:
|
case FlowGraph::Node::Type::EFFECT_SYNC:
|
||||||
dump_sync_print(node.print_sync_cells);
|
dump_cell_effect_sync(node.cells);
|
||||||
break;
|
break;
|
||||||
case FlowGraph::Node::Type::PROCESS_CASE:
|
case FlowGraph::Node::Type::PROCESS_CASE:
|
||||||
dump_process_case(node.process);
|
dump_process_case(node.process);
|
||||||
|
@ -2221,11 +2312,14 @@ struct CxxrtlWorker {
|
||||||
dict<RTLIL::IdString, RTLIL::Const> attributes = object->attributes;
|
dict<RTLIL::IdString, RTLIL::Const> attributes = object->attributes;
|
||||||
// Inherently necessary to get access to the object, so a waste of space to emit.
|
// Inherently necessary to get access to the object, so a waste of space to emit.
|
||||||
attributes.erase(ID::hdlname);
|
attributes.erase(ID::hdlname);
|
||||||
|
// Internal Yosys attribute that should be removed but isn't.
|
||||||
|
attributes.erase(ID::module_not_derived);
|
||||||
dump_metadata_map(attributes);
|
dump_metadata_map(attributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void dump_debug_info_method(RTLIL::Module *module)
|
void dump_debug_info_method(RTLIL::Module *module)
|
||||||
{
|
{
|
||||||
|
size_t count_scopes = 0;
|
||||||
size_t count_public_wires = 0;
|
size_t count_public_wires = 0;
|
||||||
size_t count_member_wires = 0;
|
size_t count_member_wires = 0;
|
||||||
size_t count_undriven = 0;
|
size_t count_undriven = 0;
|
||||||
|
@ -2238,153 +2332,188 @@ struct CxxrtlWorker {
|
||||||
size_t count_skipped_wires = 0;
|
size_t count_skipped_wires = 0;
|
||||||
inc_indent();
|
inc_indent();
|
||||||
f << indent << "assert(path.empty() || path[path.size() - 1] == ' ');\n";
|
f << indent << "assert(path.empty() || path[path.size() - 1] == ' ');\n";
|
||||||
for (auto wire : module->wires()) {
|
f << indent << "if (scopes) {\n";
|
||||||
const auto &debug_wire_type = debug_wire_types[wire];
|
inc_indent();
|
||||||
if (!wire->name.isPublic())
|
// The module is responsible for adding its own scope.
|
||||||
continue;
|
f << indent << "scopes->add(path.empty() ? path : path.substr(0, path.size() - 1), ";
|
||||||
count_public_wires++;
|
f << escape_cxx_string(get_hdl_name(module)) << ", ";
|
||||||
switch (debug_wire_type.type) {
|
dump_debug_attrs(module);
|
||||||
case WireType::BUFFERED:
|
f << ", std::move(cell_attrs));\n";
|
||||||
case WireType::MEMBER: {
|
count_scopes++;
|
||||||
// Member wire
|
// If there were any submodules that were flattened, the module is also responsible for adding them.
|
||||||
std::vector<std::string> flags;
|
for (auto cell : module->cells()) {
|
||||||
|
if (cell->type != ID($scopeinfo)) continue;
|
||||||
if (wire->port_input && wire->port_output)
|
if (cell->getParam(ID::TYPE).decode_string() == "module") {
|
||||||
flags.push_back("INOUT");
|
auto module_attrs = scopeinfo_attributes(cell, ScopeinfoAttrs::Module);
|
||||||
else if (wire->port_output)
|
auto cell_attrs = scopeinfo_attributes(cell, ScopeinfoAttrs::Cell);
|
||||||
flags.push_back("OUTPUT");
|
cell_attrs.erase(ID::module_not_derived);
|
||||||
else if (wire->port_input)
|
f << indent << "scopes->add(path + " << escape_cxx_string(get_hdl_name(cell)) << ", ";
|
||||||
flags.push_back("INPUT");
|
f << escape_cxx_string(cell->get_string_attribute(ID(module))) << ", ";
|
||||||
|
dump_metadata_map(module_attrs);
|
||||||
bool has_driven_sync = false;
|
f << ", ";
|
||||||
bool has_driven_comb = false;
|
dump_metadata_map(cell_attrs);
|
||||||
bool has_undriven = false;
|
|
||||||
if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) {
|
|
||||||
for (auto bit : SigSpec(wire))
|
|
||||||
if (!bit_has_state.count(bit))
|
|
||||||
has_undriven = true;
|
|
||||||
else if (bit_has_state[bit])
|
|
||||||
has_driven_sync = true;
|
|
||||||
else
|
|
||||||
has_driven_comb = true;
|
|
||||||
} else if (wire->port_output) {
|
|
||||||
switch (cxxrtl_port_type(module, wire->name)) {
|
|
||||||
case CxxrtlPortType::SYNC:
|
|
||||||
has_driven_sync = true;
|
|
||||||
break;
|
|
||||||
case CxxrtlPortType::COMB:
|
|
||||||
has_driven_comb = true;
|
|
||||||
break;
|
|
||||||
case CxxrtlPortType::UNKNOWN:
|
|
||||||
has_driven_sync = has_driven_comb = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
has_undriven = true;
|
|
||||||
}
|
|
||||||
if (has_undriven)
|
|
||||||
flags.push_back("UNDRIVEN");
|
|
||||||
if (!has_driven_sync && !has_driven_comb && has_undriven)
|
|
||||||
count_undriven++;
|
|
||||||
if (has_driven_sync)
|
|
||||||
flags.push_back("DRIVEN_SYNC");
|
|
||||||
if (has_driven_sync && !has_driven_comb && !has_undriven)
|
|
||||||
count_driven_sync++;
|
|
||||||
if (has_driven_comb)
|
|
||||||
flags.push_back("DRIVEN_COMB");
|
|
||||||
if (!has_driven_sync && has_driven_comb && !has_undriven)
|
|
||||||
count_driven_comb++;
|
|
||||||
if (has_driven_sync + has_driven_comb + has_undriven > 1)
|
|
||||||
count_mixed_driver++;
|
|
||||||
|
|
||||||
f << indent << "items.add(path + " << escape_cxx_string(get_hdl_name(wire));
|
|
||||||
f << ", debug_item(" << mangle(wire) << ", " << wire->start_offset;
|
|
||||||
bool first = true;
|
|
||||||
for (auto flag : flags) {
|
|
||||||
if (first) {
|
|
||||||
first = false;
|
|
||||||
f << ", ";
|
|
||||||
} else {
|
|
||||||
f << "|";
|
|
||||||
}
|
|
||||||
f << "debug_item::" << flag;
|
|
||||||
}
|
|
||||||
f << "), ";
|
|
||||||
dump_debug_attrs(wire);
|
|
||||||
f << ");\n";
|
f << ");\n";
|
||||||
count_member_wires++;
|
} else log_assert(false && "Unknown $scopeinfo type");
|
||||||
break;
|
count_scopes++;
|
||||||
}
|
|
||||||
case WireType::ALIAS: {
|
|
||||||
// Alias of a member wire
|
|
||||||
const RTLIL::Wire *aliasee = debug_wire_type.sig_subst.as_wire();
|
|
||||||
f << indent << "items.add(path + " << escape_cxx_string(get_hdl_name(wire));
|
|
||||||
f << ", debug_item(";
|
|
||||||
// If the aliasee is an outline, then the alias must be an outline, too; otherwise downstream
|
|
||||||
// tooling has no way to find out about the outline.
|
|
||||||
if (debug_wire_types[aliasee].is_outline())
|
|
||||||
f << "debug_eval_outline";
|
|
||||||
else
|
|
||||||
f << "debug_alias()";
|
|
||||||
f << ", " << mangle(aliasee) << ", " << wire->start_offset << "), ";
|
|
||||||
dump_debug_attrs(aliasee);
|
|
||||||
f << ");\n";
|
|
||||||
count_alias_wires++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WireType::CONST: {
|
|
||||||
// Wire tied to a constant
|
|
||||||
f << indent << "static const value<" << wire->width << "> const_" << mangle(wire) << " = ";
|
|
||||||
dump_const(debug_wire_type.sig_subst.as_const());
|
|
||||||
f << ";\n";
|
|
||||||
f << indent << "items.add(path + " << escape_cxx_string(get_hdl_name(wire));
|
|
||||||
f << ", debug_item(const_" << mangle(wire) << ", " << wire->start_offset << "), ";
|
|
||||||
dump_debug_attrs(wire);
|
|
||||||
f << ");\n";
|
|
||||||
count_const_wires++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WireType::OUTLINE: {
|
|
||||||
// Localized or inlined, but rematerializable wire
|
|
||||||
f << indent << "items.add(path + " << escape_cxx_string(get_hdl_name(wire));
|
|
||||||
f << ", debug_item(debug_eval_outline, " << mangle(wire) << ", " << wire->start_offset << "), ";
|
|
||||||
dump_debug_attrs(wire);
|
|
||||||
f << ");\n";
|
|
||||||
count_inline_wires++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
// Localized or inlined wire with no debug information
|
|
||||||
count_skipped_wires++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
dec_indent();
|
||||||
if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) {
|
f << indent << "}\n";
|
||||||
for (auto &mem : mod_memories[module]) {
|
f << indent << "if (items) {\n";
|
||||||
if (!mem.memid.isPublic())
|
inc_indent();
|
||||||
|
for (auto wire : module->wires()) {
|
||||||
|
const auto &debug_wire_type = debug_wire_types[wire];
|
||||||
|
if (!wire->name.isPublic())
|
||||||
continue;
|
continue;
|
||||||
f << indent << "items.add(path + " << escape_cxx_string(mem.packed ? get_hdl_name(mem.cell) : get_hdl_name(mem.mem));
|
count_public_wires++;
|
||||||
f << ", debug_item(" << mangle(&mem) << ", ";
|
switch (debug_wire_type.type) {
|
||||||
f << mem.start_offset << "), ";
|
case WireType::BUFFERED:
|
||||||
if (mem.packed) {
|
case WireType::MEMBER: {
|
||||||
dump_debug_attrs(mem.cell);
|
// Member wire
|
||||||
} else {
|
std::vector<std::string> flags;
|
||||||
dump_debug_attrs(mem.mem);
|
|
||||||
|
if (wire->port_input && wire->port_output)
|
||||||
|
flags.push_back("INOUT");
|
||||||
|
else if (wire->port_output)
|
||||||
|
flags.push_back("OUTPUT");
|
||||||
|
else if (wire->port_input)
|
||||||
|
flags.push_back("INPUT");
|
||||||
|
|
||||||
|
bool has_driven_sync = false;
|
||||||
|
bool has_driven_comb = false;
|
||||||
|
bool has_undriven = false;
|
||||||
|
if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) {
|
||||||
|
for (auto bit : SigSpec(wire))
|
||||||
|
if (!bit_has_state.count(bit))
|
||||||
|
has_undriven = true;
|
||||||
|
else if (bit_has_state[bit])
|
||||||
|
has_driven_sync = true;
|
||||||
|
else
|
||||||
|
has_driven_comb = true;
|
||||||
|
} else if (wire->port_output) {
|
||||||
|
switch (cxxrtl_port_type(module, wire->name)) {
|
||||||
|
case CxxrtlPortType::SYNC:
|
||||||
|
has_driven_sync = true;
|
||||||
|
break;
|
||||||
|
case CxxrtlPortType::COMB:
|
||||||
|
has_driven_comb = true;
|
||||||
|
break;
|
||||||
|
case CxxrtlPortType::UNKNOWN:
|
||||||
|
has_driven_sync = has_driven_comb = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
has_undriven = true;
|
||||||
|
}
|
||||||
|
if (has_undriven)
|
||||||
|
flags.push_back("UNDRIVEN");
|
||||||
|
if (!has_driven_sync && !has_driven_comb && has_undriven)
|
||||||
|
count_undriven++;
|
||||||
|
if (has_driven_sync)
|
||||||
|
flags.push_back("DRIVEN_SYNC");
|
||||||
|
if (has_driven_sync && !has_driven_comb && !has_undriven)
|
||||||
|
count_driven_sync++;
|
||||||
|
if (has_driven_comb)
|
||||||
|
flags.push_back("DRIVEN_COMB");
|
||||||
|
if (!has_driven_sync && has_driven_comb && !has_undriven)
|
||||||
|
count_driven_comb++;
|
||||||
|
if (has_driven_sync + has_driven_comb + has_undriven > 1)
|
||||||
|
count_mixed_driver++;
|
||||||
|
|
||||||
|
f << indent << "items->add(path + " << escape_cxx_string(get_hdl_name(wire));
|
||||||
|
f << ", debug_item(" << mangle(wire) << ", " << wire->start_offset;
|
||||||
|
bool first = true;
|
||||||
|
for (auto flag : flags) {
|
||||||
|
if (first) {
|
||||||
|
first = false;
|
||||||
|
f << ", ";
|
||||||
|
} else {
|
||||||
|
f << "|";
|
||||||
|
}
|
||||||
|
f << "debug_item::" << flag;
|
||||||
|
}
|
||||||
|
f << "), ";
|
||||||
|
dump_debug_attrs(wire);
|
||||||
|
f << ");\n";
|
||||||
|
count_member_wires++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WireType::ALIAS: {
|
||||||
|
// Alias of a member wire
|
||||||
|
const RTLIL::Wire *aliasee = debug_wire_type.sig_subst.as_wire();
|
||||||
|
f << indent << "items->add(path + " << escape_cxx_string(get_hdl_name(wire));
|
||||||
|
f << ", debug_item(";
|
||||||
|
// If the aliasee is an outline, then the alias must be an outline, too; otherwise downstream
|
||||||
|
// tooling has no way to find out about the outline.
|
||||||
|
if (debug_wire_types[aliasee].is_outline())
|
||||||
|
f << "debug_eval_outline";
|
||||||
|
else
|
||||||
|
f << "debug_alias()";
|
||||||
|
f << ", " << mangle(aliasee) << ", " << wire->start_offset << "), ";
|
||||||
|
dump_debug_attrs(aliasee);
|
||||||
|
f << ");\n";
|
||||||
|
count_alias_wires++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WireType::CONST: {
|
||||||
|
// Wire tied to a constant
|
||||||
|
f << indent << "static const value<" << wire->width << "> const_" << mangle(wire) << " = ";
|
||||||
|
dump_const(debug_wire_type.sig_subst.as_const());
|
||||||
|
f << ";\n";
|
||||||
|
f << indent << "items->add(path + " << escape_cxx_string(get_hdl_name(wire));
|
||||||
|
f << ", debug_item(const_" << mangle(wire) << ", " << wire->start_offset << "), ";
|
||||||
|
dump_debug_attrs(wire);
|
||||||
|
f << ");\n";
|
||||||
|
count_const_wires++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WireType::OUTLINE: {
|
||||||
|
// Localized or inlined, but rematerializable wire
|
||||||
|
f << indent << "items->add(path + " << escape_cxx_string(get_hdl_name(wire));
|
||||||
|
f << ", debug_item(debug_eval_outline, " << mangle(wire) << ", " << wire->start_offset << "), ";
|
||||||
|
dump_debug_attrs(wire);
|
||||||
|
f << ");\n";
|
||||||
|
count_inline_wires++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
// Localized or inlined wire with no debug information
|
||||||
|
count_skipped_wires++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
f << ");\n";
|
|
||||||
}
|
}
|
||||||
|
if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) {
|
||||||
|
for (auto &mem : mod_memories[module]) {
|
||||||
|
if (!mem.memid.isPublic())
|
||||||
|
continue;
|
||||||
|
f << indent << "items->add(path + " << escape_cxx_string(mem.packed ? get_hdl_name(mem.cell) : get_hdl_name(mem.mem));
|
||||||
|
f << ", debug_item(" << mangle(&mem) << ", ";
|
||||||
|
f << mem.start_offset << "), ";
|
||||||
|
if (mem.packed) {
|
||||||
|
dump_debug_attrs(mem.cell);
|
||||||
|
} else {
|
||||||
|
dump_debug_attrs(mem.mem);
|
||||||
|
}
|
||||||
|
f << ");\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dec_indent();
|
||||||
|
f << indent << "}\n";
|
||||||
|
if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) {
|
||||||
for (auto cell : module->cells()) {
|
for (auto cell : module->cells()) {
|
||||||
if (is_internal_cell(cell->type))
|
if (is_internal_cell(cell->type))
|
||||||
continue;
|
continue;
|
||||||
const char *access = is_cxxrtl_blackbox_cell(cell) ? "->" : ".";
|
const char *access = is_cxxrtl_blackbox_cell(cell) ? "->" : ".";
|
||||||
f << indent << mangle(cell) << access << "debug_info(items, ";
|
f << indent << mangle(cell) << access;
|
||||||
f << "path + " << escape_cxx_string(get_hdl_name(cell) + ' ') << ");\n";
|
f << "debug_info(items, scopes, path + " << escape_cxx_string(get_hdl_name(cell) + ' ') << ", ";
|
||||||
|
dump_debug_attrs(cell);
|
||||||
|
f << ");\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dec_indent();
|
dec_indent();
|
||||||
|
|
||||||
log_debug("Debug information statistics for module `%s':\n", log_id(module));
|
log_debug("Debug information statistics for module `%s':\n", log_id(module));
|
||||||
|
log_debug(" Scopes: %zu", count_scopes);
|
||||||
log_debug(" Public wires: %zu, of which:\n", count_public_wires);
|
log_debug(" Public wires: %zu, of which:\n", count_public_wires);
|
||||||
log_debug(" Member wires: %zu, of which:\n", count_member_wires);
|
log_debug(" Member wires: %zu, of which:\n", count_member_wires);
|
||||||
log_debug(" Undriven: %zu (incl. inputs)\n", count_undriven);
|
log_debug(" Undriven: %zu (incl. inputs)\n", count_undriven);
|
||||||
|
@ -2422,18 +2551,18 @@ struct CxxrtlWorker {
|
||||||
dump_eval_method(module);
|
dump_eval_method(module);
|
||||||
f << indent << "}\n";
|
f << indent << "}\n";
|
||||||
f << "\n";
|
f << "\n";
|
||||||
f << indent << "template<class ObserverT>\n";
|
f << indent << "virtual bool commit(observer &observer) {\n";
|
||||||
f << indent << "bool commit(ObserverT &observer) {\n";
|
|
||||||
dump_commit_method(module);
|
dump_commit_method(module);
|
||||||
f << indent << "}\n";
|
f << indent << "}\n";
|
||||||
f << "\n";
|
f << "\n";
|
||||||
f << indent << "bool commit() override {\n";
|
f << indent << "bool commit() override {\n";
|
||||||
f << indent << indent << "observer observer;\n";
|
f << indent << indent << "observer observer;\n";
|
||||||
f << indent << indent << "return commit<>(observer);\n";
|
f << indent << indent << "return commit(observer);\n";
|
||||||
f << indent << "}\n";
|
f << indent << "}\n";
|
||||||
if (debug_info) {
|
if (debug_info) {
|
||||||
f << "\n";
|
f << "\n";
|
||||||
f << indent << "void debug_info(debug_items &items, std::string path = \"\") override {\n";
|
f << indent << "void debug_info(debug_items *items, debug_scopes *scopes, "
|
||||||
|
<< "std::string path, metadata_map &&cell_attrs = {}) override {\n";
|
||||||
dump_debug_info_method(module);
|
dump_debug_info_method(module);
|
||||||
f << indent << "}\n";
|
f << indent << "}\n";
|
||||||
}
|
}
|
||||||
|
@ -2481,11 +2610,15 @@ struct CxxrtlWorker {
|
||||||
f << "\n";
|
f << "\n";
|
||||||
bool has_cells = false;
|
bool has_cells = false;
|
||||||
for (auto cell : module->cells()) {
|
for (auto cell : module->cells()) {
|
||||||
// Certain $print cells have additional state, which requires storage.
|
// Async and initial effectful cells have additional state, which requires storage.
|
||||||
if (cell->type == ID($print) && !cell->getParam(ID::TRG_ENABLE).as_bool())
|
if (is_effectful_cell(cell->type)) {
|
||||||
f << indent << "value<" << (1 + cell->getParam(ID::ARGS_WIDTH).as_int()) << "> " << mangle(cell) << ";\n";
|
if (cell->getParam(ID::TRG_ENABLE).as_bool() && cell->getParam(ID::TRG_WIDTH).as_int() == 0)
|
||||||
if (cell->type == ID($print) && cell->getParam(ID::TRG_ENABLE).as_bool() && cell->getParam(ID::TRG_WIDTH).as_int() == 0)
|
f << indent << "value<1> " << mangle(cell) << ";\n"; // async initial cell
|
||||||
f << indent << "value<1> " << mangle(cell) << ";\n";
|
if (!cell->getParam(ID::TRG_ENABLE).as_bool() && cell->type == ID($print))
|
||||||
|
f << indent << "value<" << (1 + cell->getParam(ID::ARGS_WIDTH).as_int()) << "> " << mangle(cell) << ";\n"; // {EN, ARGS}
|
||||||
|
if (!cell->getParam(ID::TRG_ENABLE).as_bool() && cell->type == ID($check))
|
||||||
|
f << indent << "value<2> " << mangle(cell) << ";\n"; // {EN, A}
|
||||||
|
}
|
||||||
if (is_internal_cell(cell->type))
|
if (is_internal_cell(cell->type))
|
||||||
continue;
|
continue;
|
||||||
dump_attrs(cell);
|
dump_attrs(cell);
|
||||||
|
@ -2538,7 +2671,8 @@ struct CxxrtlWorker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f << "\n";
|
f << "\n";
|
||||||
f << indent << "void debug_info(debug_items &items, std::string path = \"\") override;\n";
|
f << indent << "void debug_info(debug_items *items, debug_scopes *scopes, "
|
||||||
|
<< "std::string path, metadata_map &&cell_attrs = {}) override;\n";
|
||||||
}
|
}
|
||||||
dec_indent();
|
dec_indent();
|
||||||
f << indent << "}; // struct " << mangle(module) << "\n";
|
f << indent << "}; // struct " << mangle(module) << "\n";
|
||||||
|
@ -2566,7 +2700,8 @@ struct CxxrtlWorker {
|
||||||
}
|
}
|
||||||
f << "\n";
|
f << "\n";
|
||||||
f << indent << "CXXRTL_EXTREMELY_COLD\n";
|
f << indent << "CXXRTL_EXTREMELY_COLD\n";
|
||||||
f << indent << "void " << mangle(module) << "::debug_info(debug_items &items, std::string path) {\n";
|
f << indent << "void " << mangle(module) << "::debug_info(debug_items *items, debug_scopes *scopes, "
|
||||||
|
<< "std::string path, metadata_map &&cell_attrs) {\n";
|
||||||
dump_debug_info_method(module);
|
dump_debug_info_method(module);
|
||||||
f << indent << "}\n";
|
f << indent << "}\n";
|
||||||
}
|
}
|
||||||
|
@ -2803,8 +2938,8 @@ struct CxxrtlWorker {
|
||||||
cell->parameters[ID::CLK_POLARITY].as_bool() ? RTLIL::STp : RTLIL::STn);
|
cell->parameters[ID::CLK_POLARITY].as_bool() ? RTLIL::STp : RTLIL::STn);
|
||||||
}
|
}
|
||||||
|
|
||||||
// $print cells may be triggered on posedge/negedge events.
|
// Effectful cells may be triggered on posedge/negedge events.
|
||||||
if (cell->type == ID($print) && cell->getParam(ID::TRG_ENABLE).as_bool()) {
|
if (is_effectful_cell(cell->type) && cell->getParam(ID::TRG_ENABLE).as_bool()) {
|
||||||
for (size_t i = 0; i < (size_t)cell->getParam(ID::TRG_WIDTH).as_int(); i++) {
|
for (size_t i = 0; i < (size_t)cell->getParam(ID::TRG_WIDTH).as_int(); i++) {
|
||||||
RTLIL::SigBit trg = cell->getPort(ID::TRG).extract(i, 1);
|
RTLIL::SigBit trg = cell->getPort(ID::TRG).extract(i, 1);
|
||||||
if (is_valid_clock(trg))
|
if (is_valid_clock(trg))
|
||||||
|
@ -2945,10 +3080,12 @@ struct CxxrtlWorker {
|
||||||
// Discover nodes reachable from primary outputs (i.e. members) and collect reachable wire users.
|
// Discover nodes reachable from primary outputs (i.e. members) and collect reachable wire users.
|
||||||
pool<FlowGraph::Node*, hash_ptr_ops> worklist;
|
pool<FlowGraph::Node*, hash_ptr_ops> worklist;
|
||||||
for (auto node : flow.nodes) {
|
for (auto node : flow.nodes) {
|
||||||
if (node->type == FlowGraph::Node::Type::CELL_EVAL && is_effectful_cell(node->cell->type))
|
if (node->type == FlowGraph::Node::Type::CELL_EVAL && !is_internal_cell(node->cell->type))
|
||||||
worklist.insert(node); // node has effects
|
worklist.insert(node); // node evaluates a submodule
|
||||||
else if (node->type == FlowGraph::Node::Type::PRINT_SYNC)
|
else if (node->type == FlowGraph::Node::Type::CELL_EVAL && is_effectful_cell(node->cell->type))
|
||||||
worklist.insert(node); // node is sync $print
|
worklist.insert(node); // node has async effects
|
||||||
|
else if (node->type == FlowGraph::Node::Type::EFFECT_SYNC)
|
||||||
|
worklist.insert(node); // node has sync effects
|
||||||
else if (node->type == FlowGraph::Node::Type::MEM_WRPORTS)
|
else if (node->type == FlowGraph::Node::Type::MEM_WRPORTS)
|
||||||
worklist.insert(node); // node is memory write
|
worklist.insert(node); // node is memory write
|
||||||
else if (node->type == FlowGraph::Node::Type::PROCESS_SYNC && is_memwr_process(node->process))
|
else if (node->type == FlowGraph::Node::Type::PROCESS_SYNC && is_memwr_process(node->process))
|
||||||
|
@ -3005,21 +3142,21 @@ struct CxxrtlWorker {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emit reachable nodes in eval().
|
// Emit reachable nodes in eval().
|
||||||
// Accumulate sync $print cells per trigger condition.
|
// Accumulate sync effectful cells per trigger condition.
|
||||||
dict<std::pair<RTLIL::SigSpec, RTLIL::Const>, std::vector<const RTLIL::Cell*>> sync_print_cells;
|
dict<std::pair<RTLIL::SigSpec, RTLIL::Const>, std::vector<const RTLIL::Cell*>> effect_sync_cells;
|
||||||
for (auto node : node_order)
|
for (auto node : node_order)
|
||||||
if (live_nodes[node]) {
|
if (live_nodes[node]) {
|
||||||
if (node->type == FlowGraph::Node::Type::CELL_EVAL &&
|
if (node->type == FlowGraph::Node::Type::CELL_EVAL &&
|
||||||
node->cell->type == ID($print) &&
|
is_effectful_cell(node->cell->type) &&
|
||||||
node->cell->getParam(ID::TRG_ENABLE).as_bool() &&
|
node->cell->getParam(ID::TRG_ENABLE).as_bool() &&
|
||||||
node->cell->getParam(ID::TRG_WIDTH).as_int() != 0)
|
node->cell->getParam(ID::TRG_WIDTH).as_int() != 0)
|
||||||
sync_print_cells[make_pair(node->cell->getPort(ID::TRG), node->cell->getParam(ID::TRG_POLARITY))].push_back(node->cell);
|
effect_sync_cells[make_pair(node->cell->getPort(ID::TRG), node->cell->getParam(ID::TRG_POLARITY))].push_back(node->cell);
|
||||||
else
|
else
|
||||||
schedule[module].push_back(*node);
|
schedule[module].push_back(*node);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &it : sync_print_cells) {
|
for (auto &it : effect_sync_cells) {
|
||||||
auto node = flow.add_print_sync_node(it.second);
|
auto node = flow.add_effect_sync_node(it.second);
|
||||||
schedule[module].push_back(*node);
|
schedule[module].push_back(*node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3326,8 +3463,7 @@ struct CxxrtlBackend : public Backend {
|
||||||
log(" wire<8> p_o_data;\n");
|
log(" wire<8> p_o_data;\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" bool eval(performer *performer) override;\n");
|
log(" bool eval(performer *performer) override;\n");
|
||||||
log(" template<class ObserverT>\n");
|
log(" virtual bool commit(observer &observer);\n");
|
||||||
log(" bool commit(ObserverT &observer);\n");
|
|
||||||
log(" bool commit() override;\n");
|
log(" bool commit() override;\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" static std::unique_ptr<bb_p_debug>\n");
|
log(" static std::unique_ptr<bb_p_debug>\n");
|
||||||
|
|
|
@ -35,19 +35,19 @@ cxxrtl_handle cxxrtl_create(cxxrtl_toplevel design) {
|
||||||
return cxxrtl_create_at(design, "");
|
return cxxrtl_create_at(design, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
cxxrtl_handle cxxrtl_create_at(cxxrtl_toplevel design, const char *root) {
|
cxxrtl_handle cxxrtl_create_at(cxxrtl_toplevel design, const char *top_path_) {
|
||||||
std::string path = root;
|
std::string top_path = top_path_;
|
||||||
if (!path.empty()) {
|
if (!top_path.empty()) {
|
||||||
// module::debug_info() accepts either an empty path, or a path ending in space to simplify
|
// module::debug_info() accepts either an empty path, or a path ending in space to simplify
|
||||||
// the logic in generated code. While this is sketchy at best to expose in the C++ API, this
|
// the logic in generated code. While this is sketchy at best to expose in the C++ API, this
|
||||||
// would be a lot worse in the C API, so don't expose it here.
|
// would be a lot worse in the C API, so don't expose it here.
|
||||||
assert(path.back() != ' ');
|
assert(top_path.back() != ' ');
|
||||||
path += ' ';
|
top_path += ' ';
|
||||||
}
|
}
|
||||||
|
|
||||||
cxxrtl_handle handle = new _cxxrtl_handle;
|
cxxrtl_handle handle = new _cxxrtl_handle;
|
||||||
handle->module = std::move(design->module);
|
handle->module = std::move(design->module);
|
||||||
handle->module->debug_info(handle->objects, path);
|
handle->module->debug_info(handle->objects, top_path);
|
||||||
delete design;
|
delete design;
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,8 +55,8 @@ cxxrtl_handle cxxrtl_create(cxxrtl_toplevel design);
|
||||||
// Create a design handle at a given hierarchy position from a design toplevel.
|
// Create a design handle at a given hierarchy position from a design toplevel.
|
||||||
//
|
//
|
||||||
// This operation is similar to `cxxrtl_create`, except the full hierarchical name of every object
|
// This operation is similar to `cxxrtl_create`, except the full hierarchical name of every object
|
||||||
// is prepended with `root`.
|
// is prepended with `top_path`.
|
||||||
cxxrtl_handle cxxrtl_create_at(cxxrtl_toplevel design, const char *root);
|
cxxrtl_handle cxxrtl_create_at(cxxrtl_toplevel design, const char *top_path);
|
||||||
|
|
||||||
// Release all resources used by a design and its handle.
|
// Release all resources used by a design and its handle.
|
||||||
void cxxrtl_destroy(cxxrtl_handle handle);
|
void cxxrtl_destroy(cxxrtl_handle handle);
|
||||||
|
@ -240,6 +240,11 @@ struct cxxrtl_object {
|
||||||
// through wires, the bits are double buffered. To avoid race conditions, user code should
|
// through wires, the bits are double buffered. To avoid race conditions, user code should
|
||||||
// always read from `curr` and write to `next`. The `curr` pointer is always valid; for objects
|
// always read from `curr` and write to `next`. The `curr` pointer is always valid; for objects
|
||||||
// that cannot be modified, or cannot be modified in a race-free way, `next` is NULL.
|
// that cannot be modified, or cannot be modified in a race-free way, `next` is NULL.
|
||||||
|
//
|
||||||
|
// In case where `width == 0`, `curr` is a non-NULL pointer unique for the wire. That is,
|
||||||
|
// there is a 1-to-1 correspondence between simulation objects and `curr` pointers, regardless
|
||||||
|
// of whether they have storage or not. (Aliases' `curr` pointer equals that of some other
|
||||||
|
// simulated object.)
|
||||||
uint32_t *curr;
|
uint32_t *curr;
|
||||||
uint32_t *next;
|
uint32_t *next;
|
||||||
|
|
||||||
|
|
|
@ -952,7 +952,23 @@ struct lazy_fmt {
|
||||||
virtual std::string operator() () const = 0;
|
virtual std::string operator() () const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// An object that can be passed to a `eval()` method in order to act on side effects.
|
// Flavor of a `$check` cell.
|
||||||
|
enum class flavor {
|
||||||
|
// Corresponds to a `$assert` cell in other flows, and a Verilog `assert ()` statement.
|
||||||
|
ASSERT,
|
||||||
|
// Corresponds to a `$assume` cell in other flows, and a Verilog `assume ()` statement.
|
||||||
|
ASSUME,
|
||||||
|
// Corresponds to a `$live` cell in other flows, and a Verilog `assert (eventually)` statement.
|
||||||
|
ASSERT_EVENTUALLY,
|
||||||
|
// Corresponds to a `$fair` cell in other flows, and a Verilog `assume (eventually)` statement.
|
||||||
|
ASSUME_EVENTUALLY,
|
||||||
|
// Corresponds to a `$cover` cell in other flows, and a Verilog `cover ()` statement.
|
||||||
|
COVER,
|
||||||
|
};
|
||||||
|
|
||||||
|
// An object that can be passed to a `eval()` method in order to act on side effects. The default behavior implemented
|
||||||
|
// below is the same as the behavior of `eval(nullptr)`, except that `-print-output` option of `write_cxxrtl` is not
|
||||||
|
// taken into account.
|
||||||
struct performer {
|
struct performer {
|
||||||
// Called by generated formatting code to evaluate a Verilog `$time` expression.
|
// Called by generated formatting code to evaluate a Verilog `$time` expression.
|
||||||
virtual int64_t vlog_time() const { return 0; }
|
virtual int64_t vlog_time() const { return 0; }
|
||||||
|
@ -964,6 +980,15 @@ struct performer {
|
||||||
virtual void on_print(const lazy_fmt &formatter, const metadata_map &attributes) {
|
virtual void on_print(const lazy_fmt &formatter, const metadata_map &attributes) {
|
||||||
std::cout << formatter();
|
std::cout << formatter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Called when a `$check` cell is triggered.
|
||||||
|
virtual void on_check(flavor type, bool condition, const lazy_fmt &formatter, const metadata_map &attributes) {
|
||||||
|
if (type == flavor::ASSERT || type == flavor::ASSUME) {
|
||||||
|
if (!condition)
|
||||||
|
std::cerr << formatter();
|
||||||
|
CXXRTL_ASSERT(condition && "Check failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// An object that can be passed to a `commit()` method in order to produce a replay log of every state change in
|
// An object that can be passed to a `commit()` method in order to produce a replay log of every state change in
|
||||||
|
@ -1151,7 +1176,7 @@ struct debug_item : ::cxxrtl_object {
|
||||||
|
|
||||||
template<size_t Bits>
|
template<size_t Bits>
|
||||||
debug_item(value<Bits> &item, size_t lsb_offset = 0, uint32_t flags_ = 0) {
|
debug_item(value<Bits> &item, size_t lsb_offset = 0, uint32_t flags_ = 0) {
|
||||||
static_assert(sizeof(item) == value<Bits>::chunks * sizeof(chunk_t),
|
static_assert(Bits == 0 || sizeof(item) == value<Bits>::chunks * sizeof(chunk_t),
|
||||||
"value<Bits> is not compatible with C layout");
|
"value<Bits> is not compatible with C layout");
|
||||||
type = VALUE;
|
type = VALUE;
|
||||||
flags = flags_;
|
flags = flags_;
|
||||||
|
@ -1167,7 +1192,7 @@ struct debug_item : ::cxxrtl_object {
|
||||||
|
|
||||||
template<size_t Bits>
|
template<size_t Bits>
|
||||||
debug_item(const value<Bits> &item, size_t lsb_offset = 0) {
|
debug_item(const value<Bits> &item, size_t lsb_offset = 0) {
|
||||||
static_assert(sizeof(item) == value<Bits>::chunks * sizeof(chunk_t),
|
static_assert(Bits == 0 || sizeof(item) == value<Bits>::chunks * sizeof(chunk_t),
|
||||||
"value<Bits> is not compatible with C layout");
|
"value<Bits> is not compatible with C layout");
|
||||||
type = VALUE;
|
type = VALUE;
|
||||||
flags = DRIVEN_COMB;
|
flags = DRIVEN_COMB;
|
||||||
|
@ -1183,8 +1208,9 @@ struct debug_item : ::cxxrtl_object {
|
||||||
|
|
||||||
template<size_t Bits>
|
template<size_t Bits>
|
||||||
debug_item(wire<Bits> &item, size_t lsb_offset = 0, uint32_t flags_ = 0) {
|
debug_item(wire<Bits> &item, size_t lsb_offset = 0, uint32_t flags_ = 0) {
|
||||||
static_assert(sizeof(item.curr) == value<Bits>::chunks * sizeof(chunk_t) &&
|
static_assert(Bits == 0 ||
|
||||||
sizeof(item.next) == value<Bits>::chunks * sizeof(chunk_t),
|
(sizeof(item.curr) == value<Bits>::chunks * sizeof(chunk_t) &&
|
||||||
|
sizeof(item.next) == value<Bits>::chunks * sizeof(chunk_t)),
|
||||||
"wire<Bits> is not compatible with C layout");
|
"wire<Bits> is not compatible with C layout");
|
||||||
type = WIRE;
|
type = WIRE;
|
||||||
flags = flags_;
|
flags = flags_;
|
||||||
|
@ -1200,7 +1226,7 @@ struct debug_item : ::cxxrtl_object {
|
||||||
|
|
||||||
template<size_t Width>
|
template<size_t Width>
|
||||||
debug_item(memory<Width> &item, size_t zero_offset = 0) {
|
debug_item(memory<Width> &item, size_t zero_offset = 0) {
|
||||||
static_assert(sizeof(item.data[0]) == value<Width>::chunks * sizeof(chunk_t),
|
static_assert(Width == 0 || sizeof(item.data[0]) == value<Width>::chunks * sizeof(chunk_t),
|
||||||
"memory<Width> is not compatible with C layout");
|
"memory<Width> is not compatible with C layout");
|
||||||
type = MEMORY;
|
type = MEMORY;
|
||||||
flags = 0;
|
flags = 0;
|
||||||
|
@ -1216,7 +1242,7 @@ struct debug_item : ::cxxrtl_object {
|
||||||
|
|
||||||
template<size_t Bits>
|
template<size_t Bits>
|
||||||
debug_item(debug_alias, const value<Bits> &item, size_t lsb_offset = 0) {
|
debug_item(debug_alias, const value<Bits> &item, size_t lsb_offset = 0) {
|
||||||
static_assert(sizeof(item) == value<Bits>::chunks * sizeof(chunk_t),
|
static_assert(Bits == 0 || sizeof(item) == value<Bits>::chunks * sizeof(chunk_t),
|
||||||
"value<Bits> is not compatible with C layout");
|
"value<Bits> is not compatible with C layout");
|
||||||
type = ALIAS;
|
type = ALIAS;
|
||||||
flags = DRIVEN_COMB;
|
flags = DRIVEN_COMB;
|
||||||
|
@ -1232,8 +1258,9 @@ struct debug_item : ::cxxrtl_object {
|
||||||
|
|
||||||
template<size_t Bits>
|
template<size_t Bits>
|
||||||
debug_item(debug_alias, const wire<Bits> &item, size_t lsb_offset = 0) {
|
debug_item(debug_alias, const wire<Bits> &item, size_t lsb_offset = 0) {
|
||||||
static_assert(sizeof(item.curr) == value<Bits>::chunks * sizeof(chunk_t) &&
|
static_assert(Bits == 0 ||
|
||||||
sizeof(item.next) == value<Bits>::chunks * sizeof(chunk_t),
|
(sizeof(item.curr) == value<Bits>::chunks * sizeof(chunk_t) &&
|
||||||
|
sizeof(item.next) == value<Bits>::chunks * sizeof(chunk_t)),
|
||||||
"wire<Bits> is not compatible with C layout");
|
"wire<Bits> is not compatible with C layout");
|
||||||
type = ALIAS;
|
type = ALIAS;
|
||||||
flags = DRIVEN_COMB;
|
flags = DRIVEN_COMB;
|
||||||
|
@ -1249,7 +1276,7 @@ struct debug_item : ::cxxrtl_object {
|
||||||
|
|
||||||
template<size_t Bits>
|
template<size_t Bits>
|
||||||
debug_item(debug_outline &group, const value<Bits> &item, size_t lsb_offset = 0) {
|
debug_item(debug_outline &group, const value<Bits> &item, size_t lsb_offset = 0) {
|
||||||
static_assert(sizeof(item) == value<Bits>::chunks * sizeof(chunk_t),
|
static_assert(Bits == 0 || sizeof(item) == value<Bits>::chunks * sizeof(chunk_t),
|
||||||
"value<Bits> is not compatible with C layout");
|
"value<Bits> is not compatible with C layout");
|
||||||
type = OUTLINE;
|
type = OUTLINE;
|
||||||
flags = DRIVEN_COMB;
|
flags = DRIVEN_COMB;
|
||||||
|
@ -1293,17 +1320,26 @@ namespace cxxrtl {
|
||||||
using debug_attrs = ::_cxxrtl_attr_set;
|
using debug_attrs = ::_cxxrtl_attr_set;
|
||||||
|
|
||||||
struct debug_items {
|
struct debug_items {
|
||||||
|
// Debug items may be composed of multiple parts, but the attributes are shared between all of them.
|
||||||
|
// There are additional invariants, not all of which are not checked by this code:
|
||||||
|
// - Memories and non-memories cannot be mixed together.
|
||||||
|
// - Bit indices (considering `lsb_at` and `width`) must not overlap.
|
||||||
|
// - Row indices (considering `depth` and `zero_at`) must be the same.
|
||||||
|
// - The `INPUT` and `OUTPUT` flags must be the same for all parts.
|
||||||
|
// Other than that, the parts can be quite different, e.g. it is OK to mix a value, a wire, an alias,
|
||||||
|
// and an outline, in the debug information for a single name in four parts.
|
||||||
std::map<std::string, std::vector<debug_item>> table;
|
std::map<std::string, std::vector<debug_item>> table;
|
||||||
std::map<std::string, std::unique_ptr<debug_attrs>> attrs_table;
|
std::map<std::string, std::unique_ptr<debug_attrs>> attrs_table;
|
||||||
|
|
||||||
void add(const std::string &name, debug_item &&item, metadata_map &&item_attrs = {}) {
|
void add(const std::string &path, debug_item &&item, metadata_map &&item_attrs = {}) {
|
||||||
std::unique_ptr<debug_attrs> &attrs = attrs_table[name];
|
assert((path.empty() || path[path.size() - 1] != ' ') && path.find(" ") == std::string::npos);
|
||||||
|
std::unique_ptr<debug_attrs> &attrs = attrs_table[path];
|
||||||
if (attrs.get() == nullptr)
|
if (attrs.get() == nullptr)
|
||||||
attrs = std::unique_ptr<debug_attrs>(new debug_attrs);
|
attrs = std::unique_ptr<debug_attrs>(new debug_attrs);
|
||||||
for (auto attr : item_attrs)
|
for (auto attr : item_attrs)
|
||||||
attrs->map.insert(attr);
|
attrs->map.insert(attr);
|
||||||
item.attrs = attrs.get();
|
item.attrs = attrs.get();
|
||||||
std::vector<debug_item> &parts = table[name];
|
std::vector<debug_item> &parts = table[path];
|
||||||
parts.emplace_back(item);
|
parts.emplace_back(item);
|
||||||
std::sort(parts.begin(), parts.end(),
|
std::sort(parts.begin(), parts.end(),
|
||||||
[](const debug_item &a, const debug_item &b) {
|
[](const debug_item &a, const debug_item &b) {
|
||||||
|
@ -1311,35 +1347,71 @@ struct debug_items {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t count(const std::string &name) const {
|
size_t count(const std::string &path) const {
|
||||||
if (table.count(name) == 0)
|
if (table.count(path) == 0)
|
||||||
return 0;
|
return 0;
|
||||||
return table.at(name).size();
|
return table.at(path).size();
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<debug_item> &parts_at(const std::string &name) const {
|
const std::vector<debug_item> &at(const std::string &path) const {
|
||||||
return table.at(name);
|
return table.at(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
const debug_item &at(const std::string &name) const {
|
// Like `at()`, but operates only on single-part debug items.
|
||||||
const std::vector<debug_item> &parts = table.at(name);
|
const debug_item &operator [](const std::string &path) const {
|
||||||
|
const std::vector<debug_item> &parts = table.at(path);
|
||||||
assert(parts.size() == 1);
|
assert(parts.size() == 1);
|
||||||
return parts.at(0);
|
return parts.at(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
const debug_item &operator [](const std::string &name) const {
|
bool is_memory(const std::string &path) const {
|
||||||
return at(name);
|
return at(path).at(0).type == debug_item::MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
const metadata_map &attrs(const std::string &name) const {
|
const metadata_map &attrs(const std::string &path) const {
|
||||||
return attrs_table.at(name)->map;
|
return attrs_table.at(path)->map;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Tag class to disambiguate the default constructor used by the toplevel module that calls reset(),
|
// Only `module` scopes are defined. The type is implicit, since Yosys does not currently support
|
||||||
|
// any other scope types.
|
||||||
|
struct debug_scope {
|
||||||
|
std::string module_name;
|
||||||
|
std::unique_ptr<debug_attrs> module_attrs;
|
||||||
|
std::unique_ptr<debug_attrs> cell_attrs;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct debug_scopes {
|
||||||
|
std::map<std::string, debug_scope> table;
|
||||||
|
|
||||||
|
void add(const std::string &path, const std::string &module_name, metadata_map &&module_attrs, metadata_map &&cell_attrs) {
|
||||||
|
assert((path.empty() || path[path.size() - 1] != ' ') && path.find(" ") == std::string::npos);
|
||||||
|
assert(table.count(path) == 0);
|
||||||
|
debug_scope &scope = table[path];
|
||||||
|
scope.module_name = module_name;
|
||||||
|
scope.module_attrs = std::unique_ptr<debug_attrs>(new debug_attrs { module_attrs });
|
||||||
|
scope.cell_attrs = std::unique_ptr<debug_attrs>(new debug_attrs { cell_attrs });
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t contains(const std::string &path) const {
|
||||||
|
return table.count(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
const debug_scope &operator [](const std::string &path) const {
|
||||||
|
return table.at(path);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Tag class to disambiguate the default constructor used by the toplevel module that calls `reset()`,
|
||||||
// and the constructor of interior modules that should not call it.
|
// and the constructor of interior modules that should not call it.
|
||||||
struct interior {};
|
struct interior {};
|
||||||
|
|
||||||
|
// The core API of the `module` class consists of only four virtual methods: `reset()`, `eval()`,
|
||||||
|
// `commit`, and `debug_info()`. (The virtual destructor is made necessary by C++.) Every other method
|
||||||
|
// is a convenience method, and exists solely to simplify some common pattern for C++ API consumers.
|
||||||
|
// No behavior may be added to such convenience methods that other parts of CXXRTL can rely on, since
|
||||||
|
// there is no guarantee they will be called (and, for example, other CXXRTL libraries will often call
|
||||||
|
// the `eval()` and `commit()` directly instead, as well as being exposed in the C API).
|
||||||
struct module {
|
struct module {
|
||||||
module() {}
|
module() {}
|
||||||
virtual ~module() {}
|
virtual ~module() {}
|
||||||
|
@ -1355,8 +1427,14 @@ struct module {
|
||||||
|
|
||||||
virtual void reset() = 0;
|
virtual void reset() = 0;
|
||||||
|
|
||||||
|
// The `eval()` callback object, `performer`, is included in the virtual call signature since
|
||||||
|
// the generated code has broadly identical performance properties.
|
||||||
virtual bool eval(performer *performer = nullptr) = 0;
|
virtual bool eval(performer *performer = nullptr) = 0;
|
||||||
virtual bool commit() = 0; // commit observer isn't available since it avoids virtual calls
|
|
||||||
|
// The `commit()` callback object, `observer`, is not included in the virtual call signature since
|
||||||
|
// the generated code is severely pessimized by it. To observe commit events, the non-virtual
|
||||||
|
// `commit(observer *)` overload must be called directly on a `module` subclass.
|
||||||
|
virtual bool commit() = 0;
|
||||||
|
|
||||||
size_t step(performer *performer = nullptr) {
|
size_t step(performer *performer = nullptr) {
|
||||||
size_t deltas = 0;
|
size_t deltas = 0;
|
||||||
|
@ -1368,8 +1446,16 @@ struct module {
|
||||||
return deltas;
|
return deltas;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void debug_info(debug_items &items, std::string path = "") {
|
virtual void debug_info(debug_items *items, debug_scopes *scopes, std::string path, metadata_map &&cell_attrs = {}) {
|
||||||
(void)items, (void)path;
|
(void)items, (void)scopes, (void)path, (void)cell_attrs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compatibility method.
|
||||||
|
#if __has_attribute(deprecated)
|
||||||
|
__attribute__((deprecated("Use `debug_info(path, &items, /*scopes=*/nullptr);` instead. (`path` could be \"top \".)")))
|
||||||
|
#endif
|
||||||
|
void debug_info(debug_items &items, std::string path) {
|
||||||
|
debug_info(&items, /*scopes=*/nullptr, path);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -491,9 +491,9 @@ public:
|
||||||
template<typename ...Args>
|
template<typename ...Args>
|
||||||
recorder(Args &&...args) : writer(std::forward<Args>(args)...) {}
|
recorder(Args &&...args) : writer(std::forward<Args>(args)...) {}
|
||||||
|
|
||||||
void start(module &module) {
|
void start(module &module, std::string top_path = "") {
|
||||||
debug_items items;
|
debug_items items;
|
||||||
module.debug_info(items);
|
module.debug_info(&items, /*scopes=*/nullptr, top_path);
|
||||||
start(items);
|
start(items);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -556,7 +556,7 @@ public:
|
||||||
bool record_incremental(ModuleT &module) {
|
bool record_incremental(ModuleT &module) {
|
||||||
assert(streaming);
|
assert(streaming);
|
||||||
|
|
||||||
struct {
|
struct : observer {
|
||||||
std::unordered_map<const chunk_t*, spool::ident_t> *ident_lookup;
|
std::unordered_map<const chunk_t*, spool::ident_t> *ident_lookup;
|
||||||
spool::writer *writer;
|
spool::writer *writer;
|
||||||
|
|
||||||
|
@ -569,7 +569,9 @@ public:
|
||||||
void on_update(size_t chunks, const chunk_t *base, const chunk_t *value, size_t index) {
|
void on_update(size_t chunks, const chunk_t *base, const chunk_t *value, size_t index) {
|
||||||
writer->write_change(ident_lookup->at(base), chunks, value, index);
|
writer->write_change(ident_lookup->at(base), chunks, value, index);
|
||||||
}
|
}
|
||||||
} record_observer = { &ident_lookup, &writer };
|
} record_observer;
|
||||||
|
record_observer.ident_lookup = &ident_lookup;
|
||||||
|
record_observer.writer = &writer;
|
||||||
|
|
||||||
writer.write_sample(/*incremental=*/true, pointer++, timestamp);
|
writer.write_sample(/*incremental=*/true, pointer++, timestamp);
|
||||||
for (auto input_index : inputs) {
|
for (auto input_index : inputs) {
|
||||||
|
@ -619,9 +621,10 @@ public:
|
||||||
template<typename ...Args>
|
template<typename ...Args>
|
||||||
player(Args &&...args) : reader(std::forward<Args>(args)...) {}
|
player(Args &&...args) : reader(std::forward<Args>(args)...) {}
|
||||||
|
|
||||||
void start(module &module) {
|
// The `top_path` must match the one given to the recorder.
|
||||||
|
void start(module &module, std::string top_path = "") {
|
||||||
debug_items items;
|
debug_items items;
|
||||||
module.debug_info(items);
|
module.debug_info(&items, /*scopes=*/nullptr, top_path);
|
||||||
start(items);
|
start(items);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -641,7 +644,7 @@ public:
|
||||||
assert(items.count(name) != 0);
|
assert(items.count(name) != 0);
|
||||||
assert(part_index < items.count(name));
|
assert(part_index < items.count(name));
|
||||||
|
|
||||||
const debug_item &part = items.parts_at(name).at(part_index);
|
const debug_item &part = items.at(name).at(part_index);
|
||||||
assert(chunks == (part.width + sizeof(chunk_t) * 8 - 1) / (sizeof(chunk_t) * 8));
|
assert(chunks == (part.width + sizeof(chunk_t) * 8 - 1) / (sizeof(chunk_t) * 8));
|
||||||
assert(depth == part.depth);
|
assert(depth == part.depth);
|
||||||
|
|
||||||
|
|
|
@ -213,6 +213,9 @@ struct EdifBackend : public Backend {
|
||||||
|
|
||||||
for (auto cell : module->cells())
|
for (auto cell : module->cells())
|
||||||
{
|
{
|
||||||
|
if (cell->type == ID($scopeinfo))
|
||||||
|
continue;
|
||||||
|
|
||||||
if (design->module(cell->type) == nullptr || design->module(cell->type)->get_blackbox_attribute()) {
|
if (design->module(cell->type) == nullptr || design->module(cell->type)->get_blackbox_attribute()) {
|
||||||
lib_cell_ports[cell->type];
|
lib_cell_ports[cell->type];
|
||||||
for (auto p : cell->connections())
|
for (auto p : cell->connections())
|
||||||
|
|
|
@ -980,6 +980,9 @@ struct FirrtlWorker
|
||||||
register_reverse_wire_map(y_id, cell->getPort(ID::Y));
|
register_reverse_wire_map(y_id, cell->getPort(ID::Y));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cell->type == ID($scopeinfo))
|
||||||
|
continue;
|
||||||
log_error("Cell type not supported: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell));
|
log_error("Cell type not supported: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -192,6 +192,10 @@ struct JsonWriter
|
||||||
for (auto c : module->cells()) {
|
for (auto c : module->cells()) {
|
||||||
if (use_selection && !module->selected(c))
|
if (use_selection && !module->selected(c))
|
||||||
continue;
|
continue;
|
||||||
|
// Eventually we will want to emit $scopeinfo, but currently this
|
||||||
|
// will break JSON netlist consumers like nextpnr
|
||||||
|
if (c->type == ID($scopeinfo))
|
||||||
|
continue;
|
||||||
f << stringf("%s\n", first ? "" : ",");
|
f << stringf("%s\n", first ? "" : ",");
|
||||||
f << stringf(" %s: {\n", get_name(c->name).c_str());
|
f << stringf(" %s: {\n", get_name(c->name).c_str());
|
||||||
f << stringf(" \"hide_name\": %s,\n", c->name[0] == '$' ? "1" : "0");
|
f << stringf(" \"hide_name\": %s,\n", c->name[0] == '$' ? "1" : "0");
|
||||||
|
|
|
@ -920,7 +920,7 @@ class SmtIo:
|
||||||
if len(expr_list) == 0:
|
if len(expr_list) == 0:
|
||||||
return []
|
return []
|
||||||
self.write("(get-value (%s))" % " ".join(expr_list))
|
self.write("(get-value (%s))" % " ".join(expr_list))
|
||||||
return [n[1] for n in self.parse(self.read())]
|
return [n[1] for n in self.parse(self.read()) if n]
|
||||||
|
|
||||||
def get_path(self, mod, path):
|
def get_path(self, mod, path):
|
||||||
assert mod in self.modinfo
|
assert mod in self.modinfo
|
||||||
|
|
|
@ -573,6 +573,9 @@ struct SmvWorker
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cell->type == ID($scopeinfo))
|
||||||
|
continue;
|
||||||
|
|
||||||
if (cell->type[0] == '$') {
|
if (cell->type[0] == '$') {
|
||||||
if (cell->type.in(ID($dffe), ID($sdff), ID($sdffe), ID($sdffce)) || cell->type.str().substr(0, 6) == "$_SDFF" || (cell->type.str().substr(0, 6) == "$_DFFE" && cell->type.str().size() == 10)) {
|
if (cell->type.in(ID($dffe), ID($sdff), ID($sdffe), ID($sdffce)) || cell->type.str().substr(0, 6) == "$_SDFF" || (cell->type.str().substr(0, 6) == "$_DFFE" && cell->type.str().size() == 10)) {
|
||||||
log_error("Unsupported cell type %s for cell %s.%s -- please run `dffunmap` before `write_smv`.\n",
|
log_error("Unsupported cell type %s for cell %s.%s -- please run `dffunmap` before `write_smv`.\n",
|
||||||
|
|
|
@ -72,6 +72,9 @@ static void print_spice_module(std::ostream &f, RTLIL::Module *module, RTLIL::De
|
||||||
|
|
||||||
for (auto cell : module->cells())
|
for (auto cell : module->cells())
|
||||||
{
|
{
|
||||||
|
if (cell->type == ID($scopeinfo))
|
||||||
|
continue;
|
||||||
|
|
||||||
f << stringf("X%d", cell_counter++);
|
f << stringf("X%d", cell_counter++);
|
||||||
|
|
||||||
std::vector<RTLIL::SigSpec> port_sigs;
|
std::vector<RTLIL::SigSpec> port_sigs;
|
||||||
|
|
|
@ -1008,7 +1008,7 @@ void dump_cell_expr_binop(std::ostream &f, std::string indent, RTLIL::Cell *cell
|
||||||
|
|
||||||
void dump_cell_expr_print(std::ostream &f, std::string indent, const RTLIL::Cell *cell)
|
void dump_cell_expr_print(std::ostream &f, std::string indent, const RTLIL::Cell *cell)
|
||||||
{
|
{
|
||||||
Fmt fmt = {};
|
Fmt fmt;
|
||||||
fmt.parse_rtlil(cell);
|
fmt.parse_rtlil(cell);
|
||||||
std::vector<VerilogFmtArg> args = fmt.emit_verilog();
|
std::vector<VerilogFmtArg> args = fmt.emit_verilog();
|
||||||
|
|
||||||
|
@ -1041,6 +1041,23 @@ void dump_cell_expr_print(std::ostream &f, std::string indent, const RTLIL::Cell
|
||||||
f << stringf(");\n");
|
f << stringf(");\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void dump_cell_expr_check(std::ostream &f, std::string indent, const RTLIL::Cell *cell)
|
||||||
|
{
|
||||||
|
std::string flavor = cell->getParam(ID(FLAVOR)).decode_string();
|
||||||
|
if (flavor == "assert")
|
||||||
|
f << stringf("%s" "assert (", indent.c_str());
|
||||||
|
else if (flavor == "assume")
|
||||||
|
f << stringf("%s" "assume (", indent.c_str());
|
||||||
|
else if (flavor == "live")
|
||||||
|
f << stringf("%s" "assert (eventually ", indent.c_str());
|
||||||
|
else if (flavor == "fair")
|
||||||
|
f << stringf("%s" "assume (eventually ", indent.c_str());
|
||||||
|
else if (flavor == "cover")
|
||||||
|
f << stringf("%s" "cover (", indent.c_str());
|
||||||
|
dump_sigspec(f, cell->getPort(ID::A));
|
||||||
|
f << stringf(");\n");
|
||||||
|
}
|
||||||
|
|
||||||
bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
||||||
{
|
{
|
||||||
if (cell->type == ID($_NOT_)) {
|
if (cell->type == ID($_NOT_)) {
|
||||||
|
@ -1053,6 +1070,15 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
||||||
f << stringf(";\n");
|
f << stringf(";\n");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cell->type == ID($_BUF_)) {
|
||||||
|
f << stringf("%s" "assign ", indent.c_str());
|
||||||
|
dump_sigspec(f, cell->getPort(ID::Y));
|
||||||
|
f << stringf(" = ");
|
||||||
|
dump_cell_expr_port(f, cell, "A", false);
|
||||||
|
f << stringf(";\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (cell->type.in(ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_))) {
|
if (cell->type.in(ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_))) {
|
||||||
f << stringf("%s" "assign ", indent.c_str());
|
f << stringf("%s" "assign ", indent.c_str());
|
||||||
|
@ -1805,6 +1831,39 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cell->type == ID($check))
|
||||||
|
{
|
||||||
|
// Sync $check cells are accumulated and handled in dump_module.
|
||||||
|
if (cell->getParam(ID::TRG_ENABLE).as_bool())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
f << stringf("%s" "always @*\n", indent.c_str());
|
||||||
|
|
||||||
|
f << stringf("%s" " if (", indent.c_str());
|
||||||
|
dump_sigspec(f, cell->getPort(ID::EN));
|
||||||
|
f << stringf(") begin\n");
|
||||||
|
|
||||||
|
std::string flavor = cell->getParam(ID::FLAVOR).decode_string();
|
||||||
|
if (flavor == "assert" || flavor == "assume") {
|
||||||
|
Fmt fmt;
|
||||||
|
fmt.parse_rtlil(cell);
|
||||||
|
if (!fmt.parts.empty()) {
|
||||||
|
f << stringf("%s" " if (!", indent.c_str());
|
||||||
|
dump_sigspec(f, cell->getPort(ID::A));
|
||||||
|
f << stringf(")\n");
|
||||||
|
dump_cell_expr_print(f, indent + " ", cell);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
f << stringf("%s" " /* message omitted */\n", indent.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
dump_cell_expr_check(f, indent + " ", cell);
|
||||||
|
|
||||||
|
f << stringf("%s" " end\n", indent.c_str());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: $fsm
|
// FIXME: $fsm
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -1812,6 +1871,13 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
||||||
|
|
||||||
void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
||||||
{
|
{
|
||||||
|
// To keep the output compatible with other tools we ignore $scopeinfo
|
||||||
|
// cells that exist only to hold metadata. If in the future that metadata
|
||||||
|
// should be exposed as part of the write_verilog output it should be
|
||||||
|
// opt-in and/or represented as something else than a $scopeinfo cell.
|
||||||
|
if (cell->type == ID($scopeinfo))
|
||||||
|
return;
|
||||||
|
|
||||||
// Handled by dump_memory
|
// Handled by dump_memory
|
||||||
if (cell->is_mem_cell())
|
if (cell->is_mem_cell())
|
||||||
return;
|
return;
|
||||||
|
@ -1894,7 +1960,7 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void dump_sync_print(std::ostream &f, std::string indent, const RTLIL::SigSpec &trg, const RTLIL::Const &polarity, std::vector<const RTLIL::Cell*> &cells)
|
void dump_sync_effect(std::ostream &f, std::string indent, const RTLIL::SigSpec &trg, const RTLIL::Const &polarity, std::vector<const RTLIL::Cell*> &cells)
|
||||||
{
|
{
|
||||||
if (trg.size() == 0) {
|
if (trg.size() == 0) {
|
||||||
f << stringf("%s" "initial begin\n", indent.c_str());
|
f << stringf("%s" "initial begin\n", indent.c_str());
|
||||||
|
@ -1918,9 +1984,29 @@ void dump_sync_print(std::ostream &f, std::string indent, const RTLIL::SigSpec &
|
||||||
for (auto cell : cells) {
|
for (auto cell : cells) {
|
||||||
f << stringf("%s" " if (", indent.c_str());
|
f << stringf("%s" " if (", indent.c_str());
|
||||||
dump_sigspec(f, cell->getPort(ID::EN));
|
dump_sigspec(f, cell->getPort(ID::EN));
|
||||||
f << stringf(")\n");
|
f << stringf(") begin\n");
|
||||||
|
|
||||||
dump_cell_expr_print(f, indent + " ", cell);
|
if (cell->type == ID($print)) {
|
||||||
|
dump_cell_expr_print(f, indent + " ", cell);
|
||||||
|
} else if (cell->type == ID($check)) {
|
||||||
|
std::string flavor = cell->getParam(ID::FLAVOR).decode_string();
|
||||||
|
if (flavor == "assert" || flavor == "assume") {
|
||||||
|
Fmt fmt;
|
||||||
|
fmt.parse_rtlil(cell);
|
||||||
|
if (!fmt.parts.empty()) {
|
||||||
|
f << stringf("%s" " if (!", indent.c_str());
|
||||||
|
dump_sigspec(f, cell->getPort(ID::A));
|
||||||
|
f << stringf(")\n");
|
||||||
|
dump_cell_expr_print(f, indent + " ", cell);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
f << stringf("%s" " /* message omitted */\n", indent.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
dump_cell_expr_check(f, indent + " ", cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
f << stringf("%s" " end\n", indent.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
f << stringf("%s" "end\n", indent.c_str());
|
f << stringf("%s" "end\n", indent.c_str());
|
||||||
|
@ -1949,13 +2035,8 @@ void dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left,
|
||||||
|
|
||||||
void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw);
|
void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw);
|
||||||
|
|
||||||
void dump_case_body(std::ostream &f, std::string indent, RTLIL::CaseRule *cs, bool omit_trailing_begin = false)
|
void dump_case_actions(std::ostream &f, std::string indent, RTLIL::CaseRule *cs)
|
||||||
{
|
{
|
||||||
int number_of_stmts = cs->switches.size() + cs->actions.size();
|
|
||||||
|
|
||||||
if (!omit_trailing_begin && number_of_stmts >= 2)
|
|
||||||
f << stringf("%s" "begin\n", indent.c_str());
|
|
||||||
|
|
||||||
for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it) {
|
for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it) {
|
||||||
if (it->first.size() == 0)
|
if (it->first.size() == 0)
|
||||||
continue;
|
continue;
|
||||||
|
@ -1965,15 +2046,6 @@ void dump_case_body(std::ostream &f, std::string indent, RTLIL::CaseRule *cs, bo
|
||||||
dump_sigspec(f, it->second);
|
dump_sigspec(f, it->second);
|
||||||
f << stringf(";\n");
|
f << stringf(";\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto it = cs->switches.begin(); it != cs->switches.end(); ++it)
|
|
||||||
dump_proc_switch(f, indent + " ", *it);
|
|
||||||
|
|
||||||
if (!omit_trailing_begin && number_of_stmts == 0)
|
|
||||||
f << stringf("%s /* empty */;\n", indent.c_str());
|
|
||||||
|
|
||||||
if (omit_trailing_begin || number_of_stmts >= 2)
|
|
||||||
f << stringf("%s" "end\n", indent.c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool dump_proc_switch_ifelse(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw)
|
bool dump_proc_switch_ifelse(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw)
|
||||||
|
@ -1996,36 +2068,52 @@ bool dump_proc_switch_ifelse(std::ostream &f, std::string indent, RTLIL::SwitchR
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dump_attributes(f, indent, sw->attributes);
|
||||||
f << indent;
|
f << indent;
|
||||||
auto sig_it = sw->signal.begin();
|
auto sig_it = sw->signal.begin();
|
||||||
for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it, ++sig_it) {
|
for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it, ++sig_it) {
|
||||||
bool had_newline = true;
|
|
||||||
if (it != sw->cases.begin()) {
|
if (it != sw->cases.begin()) {
|
||||||
if ((*it)->compare.empty()) {
|
if ((*it)->compare.empty())
|
||||||
f << indent << "else\n";
|
f << " else begin\n";
|
||||||
had_newline = true;
|
else
|
||||||
} else {
|
f << " else ";
|
||||||
f << indent << "else ";
|
|
||||||
had_newline = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!(*it)->compare.empty()) {
|
if (!(*it)->compare.empty()) {
|
||||||
if (!(*it)->attributes.empty()) {
|
|
||||||
if (!had_newline)
|
|
||||||
f << "\n" << indent;
|
|
||||||
dump_attributes(f, "", (*it)->attributes, "\n" + indent);
|
|
||||||
}
|
|
||||||
f << stringf("if (");
|
f << stringf("if (");
|
||||||
dump_sigspec(f, *sig_it);
|
dump_sigspec(f, *sig_it);
|
||||||
f << stringf(")\n");
|
f << stringf(") begin\n");
|
||||||
}
|
}
|
||||||
dump_case_body(f, indent, *it);
|
|
||||||
|
dump_case_actions(f, indent, (*it));
|
||||||
|
for (auto it2 = (*it)->switches.begin(); it2 != (*it)->switches.end(); ++it2)
|
||||||
|
dump_proc_switch(f, indent + " ", *it2);
|
||||||
|
|
||||||
|
f << indent << "end";
|
||||||
if ((*it)->compare.empty())
|
if ((*it)->compare.empty())
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
f << "\n";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void dump_case_body(std::ostream &f, std::string indent, RTLIL::CaseRule *cs, bool omit_trailing_begin = false)
|
||||||
|
{
|
||||||
|
int number_of_stmts = cs->switches.size() + cs->actions.size();
|
||||||
|
|
||||||
|
if (!omit_trailing_begin && number_of_stmts >= 2)
|
||||||
|
f << stringf("%s" "begin\n", indent.c_str());
|
||||||
|
|
||||||
|
dump_case_actions(f, indent, cs);
|
||||||
|
for (auto it = cs->switches.begin(); it != cs->switches.end(); ++it)
|
||||||
|
dump_proc_switch(f, indent + " ", *it);
|
||||||
|
|
||||||
|
if (!omit_trailing_begin && number_of_stmts == 0)
|
||||||
|
f << stringf("%s /* empty */;\n", indent.c_str());
|
||||||
|
|
||||||
|
if (omit_trailing_begin || number_of_stmts >= 2)
|
||||||
|
f << stringf("%s" "end\n", indent.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw)
|
void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw)
|
||||||
{
|
{
|
||||||
if (sw->signal.size() == 0) {
|
if (sw->signal.size() == 0) {
|
||||||
|
@ -2171,7 +2259,7 @@ void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, boo
|
||||||
|
|
||||||
void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
|
void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
|
||||||
{
|
{
|
||||||
std::map<std::pair<RTLIL::SigSpec, RTLIL::Const>, std::vector<const RTLIL::Cell*>> sync_print_cells;
|
std::map<std::pair<RTLIL::SigSpec, RTLIL::Const>, std::vector<const RTLIL::Cell*>> sync_effect_cells;
|
||||||
|
|
||||||
reg_wires.clear();
|
reg_wires.clear();
|
||||||
reset_auto_counter(module);
|
reset_auto_counter(module);
|
||||||
|
@ -2203,8 +2291,8 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
|
||||||
std::set<std::pair<RTLIL::Wire*,int>> reg_bits;
|
std::set<std::pair<RTLIL::Wire*,int>> reg_bits;
|
||||||
for (auto cell : module->cells())
|
for (auto cell : module->cells())
|
||||||
{
|
{
|
||||||
if (cell->type == ID($print) && cell->getParam(ID::TRG_ENABLE).as_bool()) {
|
if (cell->type.in(ID($print), ID($check)) && cell->getParam(ID::TRG_ENABLE).as_bool()) {
|
||||||
sync_print_cells[make_pair(cell->getPort(ID::TRG), cell->getParam(ID::TRG_POLARITY))].push_back(cell);
|
sync_effect_cells[make_pair(cell->getPort(ID::TRG), cell->getParam(ID::TRG_POLARITY))].push_back(cell);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2263,8 +2351,8 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
|
||||||
for (auto cell : module->cells())
|
for (auto cell : module->cells())
|
||||||
dump_cell(f, indent + " ", cell);
|
dump_cell(f, indent + " ", cell);
|
||||||
|
|
||||||
for (auto &it : sync_print_cells)
|
for (auto &it : sync_effect_cells)
|
||||||
dump_sync_print(f, indent + " ", it.first.first, it.first.second, it.second);
|
dump_sync_effect(f, indent + " ", it.first.first, it.first.second, it.second);
|
||||||
|
|
||||||
for (auto it = module->processes.begin(); it != module->processes.end(); ++it)
|
for (auto it = module->processes.begin(); it != module->processes.end(); ++it)
|
||||||
dump_process(f, indent + " ", it->second);
|
dump_process(f, indent + " ", it->second);
|
||||||
|
|
|
@ -503,7 +503,7 @@ This process is illustrated in :numref:`Fig. %s <fig:Basics_flow>`.
|
||||||
:name: fig:Basics_flow
|
:name: fig:Basics_flow
|
||||||
|
|
||||||
Typical design flow. Green boxes represent manually created models.
|
Typical design flow. Green boxes represent manually created models.
|
||||||
Orange boxes represent modesl generated by synthesis tools.
|
Orange boxes represent models generated by synthesis tools.
|
||||||
|
|
||||||
|
|
||||||
In this example the System Level Model and the Behavioural Model are both
|
In this example the System Level Model and the Behavioural Model are both
|
||||||
|
|
|
@ -200,7 +200,7 @@ from ``\S`` is set the output is undefined. Cells of this type are used to model
|
||||||
by an optimization).
|
by an optimization).
|
||||||
|
|
||||||
The ``$tribuf`` cell is used to implement tristate logic. Cells of this type
|
The ``$tribuf`` cell is used to implement tristate logic. Cells of this type
|
||||||
have a ``\B`` parameter and inputs ``\A`` and ``\EN`` and an output ``\Y``. The
|
have a ``\WIDTH`` parameter and inputs ``\A`` and ``\EN`` and an output ``\Y``. The
|
||||||
``\A`` input and ``\Y`` output are ``\WIDTH`` bits wide, and the ``\EN`` input
|
``\A`` input and ``\Y`` output are ``\WIDTH`` bits wide, and the ``\EN`` input
|
||||||
is one bit wide. When ``\EN`` is 0, the output is not driven. When ``\EN`` is 1,
|
is one bit wide. When ``\EN`` is 0, the output is not driven. When ``\EN`` is 1,
|
||||||
the value from ``\A`` input is sent to the ``\Y`` output. Therefore, the
|
the value from ``\A`` input is sent to the ``\Y`` output. Therefore, the
|
||||||
|
@ -627,7 +627,7 @@ Add information about ``$specify2``, ``$specify3``, and ``$specrule`` cells.
|
||||||
Formal verification cells
|
Formal verification cells
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Add information about ``$assert``, ``$assume``, ``$live``, ``$fair``,
|
Add information about ``$check``, ``$assert``, ``$assume``, ``$live``, ``$fair``,
|
||||||
``$cover``, ``$equiv``, ``$initstate``, ``$anyconst``, ``$anyseq``,
|
``$cover``, ``$equiv``, ``$initstate``, ``$anyconst``, ``$anyseq``,
|
||||||
``$anyinit``, ``$allconst``, ``$allseq`` cells.
|
``$anyinit``, ``$allconst``, ``$allseq`` cells.
|
||||||
|
|
||||||
|
@ -660,8 +660,8 @@ If ``\TRG_ENABLE`` is true, the following parameters also apply:
|
||||||
negative-edge triggered.
|
negative-edge triggered.
|
||||||
|
|
||||||
``\PRIORITY``
|
``\PRIORITY``
|
||||||
When multiple ``$print`` cells fire on the same trigger, they execute in
|
When multiple ``$print`` or ``$$check`` cells fire on the same trigger, they\
|
||||||
descending priority order.
|
execute in descending priority order.
|
||||||
|
|
||||||
Ports:
|
Ports:
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -224,6 +224,7 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch
|
||||||
port_id = 0;
|
port_id = 0;
|
||||||
range_left = -1;
|
range_left = -1;
|
||||||
range_right = 0;
|
range_right = 0;
|
||||||
|
unpacked_dimensions = 0;
|
||||||
integer = 0;
|
integer = 0;
|
||||||
realvalue = 0;
|
realvalue = 0;
|
||||||
id2ast = NULL;
|
id2ast = NULL;
|
||||||
|
@ -349,17 +350,15 @@ void AstNode::dumpAst(FILE *f, std::string indent) const
|
||||||
fprintf(f, " int=%u", (int)integer);
|
fprintf(f, " int=%u", (int)integer);
|
||||||
if (realvalue != 0)
|
if (realvalue != 0)
|
||||||
fprintf(f, " real=%e", realvalue);
|
fprintf(f, " real=%e", realvalue);
|
||||||
if (!multirange_dimensions.empty()) {
|
if (!dimensions.empty()) {
|
||||||
fprintf(f, " multirange=[");
|
fprintf(f, " dimensions=");
|
||||||
for (int v : multirange_dimensions)
|
for (auto &dim : dimensions) {
|
||||||
fprintf(f, " %d", v);
|
int left = dim.range_right + dim.range_width - 1;
|
||||||
fprintf(f, " ]");
|
int right = dim.range_right;
|
||||||
}
|
if (dim.range_swapped)
|
||||||
if (!multirange_swapped.empty()) {
|
std::swap(left, right);
|
||||||
fprintf(f, " multirange_swapped=[");
|
fprintf(f, "[%d:%d]", left, right);
|
||||||
for (bool v : multirange_swapped)
|
}
|
||||||
fprintf(f, " %d", v);
|
|
||||||
fprintf(f, " ]");
|
|
||||||
}
|
}
|
||||||
if (is_enum) {
|
if (is_enum) {
|
||||||
fprintf(f, " type=enum");
|
fprintf(f, " type=enum");
|
||||||
|
@ -489,6 +488,20 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const
|
||||||
fprintf(f, ";\n");
|
fprintf(f, ";\n");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
if (0) { case AST_MEMRD: txt = "@memrd@"; }
|
||||||
|
if (0) { case AST_MEMINIT: txt = "@meminit@"; }
|
||||||
|
if (0) { case AST_MEMWR: txt = "@memwr@"; }
|
||||||
|
fprintf(f, "%s%s", indent.c_str(), txt.c_str());
|
||||||
|
for (auto child : children) {
|
||||||
|
fprintf(f, first ? "(" : ", ");
|
||||||
|
child->dumpVlog(f, "");
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
fprintf(f, ")");
|
||||||
|
if (type != AST_MEMRD)
|
||||||
|
fprintf(f, ";\n");
|
||||||
|
break;
|
||||||
|
|
||||||
case AST_RANGE:
|
case AST_RANGE:
|
||||||
if (range_valid) {
|
if (range_valid) {
|
||||||
if (range_swapped)
|
if (range_swapped)
|
||||||
|
@ -505,6 +518,11 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case AST_MULTIRANGE:
|
||||||
|
for (auto child : children)
|
||||||
|
child->dumpVlog(f, "");
|
||||||
|
break;
|
||||||
|
|
||||||
case AST_ALWAYS:
|
case AST_ALWAYS:
|
||||||
fprintf(f, "%s" "always @", indent.c_str());
|
fprintf(f, "%s" "always @", indent.c_str());
|
||||||
for (auto child : children) {
|
for (auto child : children) {
|
||||||
|
@ -542,7 +560,7 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const
|
||||||
|
|
||||||
case AST_IDENTIFIER:
|
case AST_IDENTIFIER:
|
||||||
{
|
{
|
||||||
AST::AstNode *member_node = AST::get_struct_member(this);
|
AstNode *member_node = get_struct_member();
|
||||||
if (member_node)
|
if (member_node)
|
||||||
fprintf(f, "%s[%d:%d]", id2vl(str).c_str(), member_node->range_left, member_node->range_right);
|
fprintf(f, "%s[%d:%d]", id2vl(str).c_str(), member_node->range_left, member_node->range_right);
|
||||||
else
|
else
|
||||||
|
@ -552,6 +570,12 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const
|
||||||
child->dumpVlog(f, "");
|
child->dumpVlog(f, "");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case AST_STRUCT:
|
||||||
|
case AST_UNION:
|
||||||
|
case AST_STRUCT_ITEM:
|
||||||
|
fprintf(f, "%s", id2vl(str).c_str());
|
||||||
|
break;
|
||||||
|
|
||||||
case AST_CONSTANT:
|
case AST_CONSTANT:
|
||||||
if (!str.empty())
|
if (!str.empty())
|
||||||
fprintf(f, "\"%s\"", str.c_str());
|
fprintf(f, "\"%s\"", str.c_str());
|
||||||
|
@ -1796,7 +1820,7 @@ std::string AstModule::derive_common(RTLIL::Design *design, const dict<RTLIL::Id
|
||||||
|
|
||||||
AstNode *new_ast = ast->clone();
|
AstNode *new_ast = ast->clone();
|
||||||
if (!new_ast->attributes.count(ID::hdlname))
|
if (!new_ast->attributes.count(ID::hdlname))
|
||||||
new_ast->set_attribute(ID::hdlname, AstNode::mkconst_str(stripped_name));
|
new_ast->set_attribute(ID::hdlname, AstNode::mkconst_str(stripped_name.substr(1)));
|
||||||
|
|
||||||
para_counter = 0;
|
para_counter = 0;
|
||||||
for (auto child : new_ast->children) {
|
for (auto child : new_ast->children) {
|
||||||
|
|
|
@ -202,9 +202,17 @@ namespace AST
|
||||||
// set for IDs typed to an enumeration, not used
|
// set for IDs typed to an enumeration, not used
|
||||||
bool is_enum;
|
bool is_enum;
|
||||||
|
|
||||||
// if this is a multirange memory then this vector contains offset and length of each dimension
|
// Declared range for array dimension.
|
||||||
std::vector<int> multirange_dimensions;
|
struct dimension_t {
|
||||||
std::vector<bool> multirange_swapped; // true if range is swapped
|
int range_right; // lsb in [msb:lsb]
|
||||||
|
int range_width; // msb - lsb + 1
|
||||||
|
bool range_swapped; // if the declared msb < lsb, msb and lsb above are swapped
|
||||||
|
};
|
||||||
|
// Packed and unpacked dimensions for arrays.
|
||||||
|
// Unpacked dimensions go first, to follow the order of indexing.
|
||||||
|
std::vector<dimension_t> dimensions;
|
||||||
|
// Number of unpacked dimensions.
|
||||||
|
int unpacked_dimensions;
|
||||||
|
|
||||||
// this is set by simplify and used during RTLIL generation
|
// this is set by simplify and used during RTLIL generation
|
||||||
AstNode *id2ast;
|
AstNode *id2ast;
|
||||||
|
@ -371,6 +379,10 @@ namespace AST
|
||||||
// localized fixups after modifying children/attributes of a particular node
|
// localized fixups after modifying children/attributes of a particular node
|
||||||
void fixup_hierarchy_flags(bool force_descend = false);
|
void fixup_hierarchy_flags(bool force_descend = false);
|
||||||
|
|
||||||
|
// helpers for indexing
|
||||||
|
AstNode *make_index_range(AstNode *node, bool unpacked_range = false);
|
||||||
|
AstNode *get_struct_member() const;
|
||||||
|
|
||||||
// helper to print errors from simplify/genrtlil code
|
// helper to print errors from simplify/genrtlil code
|
||||||
[[noreturn]] void input_error(const char *format, ...) const YS_ATTRIBUTE(format(printf, 2, 3));
|
[[noreturn]] void input_error(const char *format, ...) const YS_ATTRIBUTE(format(printf, 2, 3));
|
||||||
};
|
};
|
||||||
|
@ -416,10 +428,6 @@ namespace AST
|
||||||
// Helper for setting the src attribute.
|
// Helper for setting the src attribute.
|
||||||
void set_src_attr(RTLIL::AttrObject *obj, const AstNode *ast);
|
void set_src_attr(RTLIL::AttrObject *obj, const AstNode *ast);
|
||||||
|
|
||||||
// struct helper exposed from simplify for genrtlil
|
|
||||||
AstNode *make_struct_member_range(AstNode *node, AstNode *member_node);
|
|
||||||
AstNode *get_struct_member(const AstNode *node);
|
|
||||||
|
|
||||||
// generate standard $paramod... derived module name; parameters should be
|
// generate standard $paramod... derived module name; parameters should be
|
||||||
// in the order they are declared in the instantiated module
|
// in the order they are declared in the instantiated module
|
||||||
std::string derived_module_name(std::string stripped_name, const std::vector<std::pair<RTLIL::IdString, RTLIL::Const>> ¶meters);
|
std::string derived_module_name(std::string stripped_name, const std::vector<std::pair<RTLIL::IdString, RTLIL::Const>> ¶meters);
|
||||||
|
|
|
@ -163,6 +163,28 @@ static RTLIL::SigSpec mux2rtlil(AstNode *that, const RTLIL::SigSpec &cond, const
|
||||||
return wire;
|
return wire;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void check_unique_id(RTLIL::Module *module, RTLIL::IdString id,
|
||||||
|
const AstNode *node, const char *to_add_kind)
|
||||||
|
{
|
||||||
|
auto already_exists = [&](const RTLIL::AttrObject *existing, const char *existing_kind) {
|
||||||
|
std::string src = existing->get_string_attribute(ID::src);
|
||||||
|
std::string location_str = "earlier";
|
||||||
|
if (!src.empty())
|
||||||
|
location_str = "at " + src;
|
||||||
|
node->input_error("Cannot add %s `%s' because a %s with the same name was already created %s!\n",
|
||||||
|
to_add_kind, id.c_str(), existing_kind, location_str.c_str());
|
||||||
|
};
|
||||||
|
|
||||||
|
if (const RTLIL::Wire *wire = module->wire(id))
|
||||||
|
already_exists(wire, "signal");
|
||||||
|
if (const RTLIL::Cell *cell = module->cell(id))
|
||||||
|
already_exists(cell, "cell");
|
||||||
|
if (module->processes.count(id))
|
||||||
|
already_exists(module->processes.at(id), "process");
|
||||||
|
if (module->memories.count(id))
|
||||||
|
already_exists(module->memories.at(id), "memory");
|
||||||
|
}
|
||||||
|
|
||||||
// helper class for rewriting simple lookahead references in AST always blocks
|
// helper class for rewriting simple lookahead references in AST always blocks
|
||||||
struct AST_INTERNAL::LookaheadRewriter
|
struct AST_INTERNAL::LookaheadRewriter
|
||||||
{
|
{
|
||||||
|
@ -316,10 +338,10 @@ struct AST_INTERNAL::ProcessGenerator
|
||||||
// Buffer for generating the init action
|
// Buffer for generating the init action
|
||||||
RTLIL::SigSpec init_lvalue, init_rvalue;
|
RTLIL::SigSpec init_lvalue, init_rvalue;
|
||||||
|
|
||||||
// The most recently assigned $print cell \PRIORITY.
|
// The most recently assigned $print or $check cell \PRIORITY.
|
||||||
int last_print_priority;
|
int last_effect_priority;
|
||||||
|
|
||||||
ProcessGenerator(AstNode *always, RTLIL::SigSpec initSyncSignalsArg = RTLIL::SigSpec()) : always(always), initSyncSignals(initSyncSignalsArg), last_print_priority(0)
|
ProcessGenerator(AstNode *always, RTLIL::SigSpec initSyncSignalsArg = RTLIL::SigSpec()) : always(always), initSyncSignals(initSyncSignalsArg), last_effect_priority(0)
|
||||||
{
|
{
|
||||||
// rewrite lookahead references
|
// rewrite lookahead references
|
||||||
LookaheadRewriter la_rewriter(always);
|
LookaheadRewriter la_rewriter(always);
|
||||||
|
@ -703,8 +725,10 @@ struct AST_INTERNAL::ProcessGenerator
|
||||||
std::stringstream sstr;
|
std::stringstream sstr;
|
||||||
sstr << ast->str << "$" << ast->filename << ":" << ast->location.first_line << "$" << (autoidx++);
|
sstr << ast->str << "$" << ast->filename << ":" << ast->location.first_line << "$" << (autoidx++);
|
||||||
|
|
||||||
RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($print));
|
Wire *en = current_module->addWire(sstr.str() + "_EN", 1);
|
||||||
set_src_attr(cell, ast);
|
set_src_attr(en, ast);
|
||||||
|
proc->root_case.actions.push_back(SigSig(en, false));
|
||||||
|
current_case->actions.push_back(SigSig(en, true));
|
||||||
|
|
||||||
RTLIL::SigSpec triggers;
|
RTLIL::SigSpec triggers;
|
||||||
RTLIL::Const polarity;
|
RTLIL::Const polarity;
|
||||||
|
@ -717,18 +741,15 @@ struct AST_INTERNAL::ProcessGenerator
|
||||||
polarity.bits.push_back(RTLIL::S0);
|
polarity.bits.push_back(RTLIL::S0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cell->parameters[ID::TRG_WIDTH] = triggers.size();
|
|
||||||
cell->parameters[ID::TRG_ENABLE] = (always->type == AST_INITIAL) || !triggers.empty();
|
RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($print));
|
||||||
cell->parameters[ID::TRG_POLARITY] = polarity;
|
set_src_attr(cell, ast);
|
||||||
cell->parameters[ID::PRIORITY] = --last_print_priority;
|
cell->setParam(ID::TRG_WIDTH, triggers.size());
|
||||||
|
cell->setParam(ID::TRG_ENABLE, (always->type == AST_INITIAL) || !triggers.empty());
|
||||||
|
cell->setParam(ID::TRG_POLARITY, polarity);
|
||||||
|
cell->setParam(ID::PRIORITY, --last_effect_priority);
|
||||||
cell->setPort(ID::TRG, triggers);
|
cell->setPort(ID::TRG, triggers);
|
||||||
|
cell->setPort(ID::EN, en);
|
||||||
Wire *wire = current_module->addWire(sstr.str() + "_EN", 1);
|
|
||||||
set_src_attr(wire, ast);
|
|
||||||
cell->setPort(ID::EN, wire);
|
|
||||||
|
|
||||||
proc->root_case.actions.push_back(SigSig(wire, false));
|
|
||||||
current_case->actions.push_back(SigSig(wire, true));
|
|
||||||
|
|
||||||
int default_base = 10;
|
int default_base = 10;
|
||||||
if (ast->str.back() == 'b')
|
if (ast->str.back() == 'b')
|
||||||
|
@ -766,7 +787,7 @@ struct AST_INTERNAL::ProcessGenerator
|
||||||
args.push_back(arg);
|
args.push_back(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
Fmt fmt = {};
|
Fmt fmt;
|
||||||
fmt.parse_verilog(args, /*sformat_like=*/false, default_base, /*task_name=*/ast->str, current_module->name);
|
fmt.parse_verilog(args, /*sformat_like=*/false, default_base, /*task_name=*/ast->str, current_module->name);
|
||||||
if (ast->str.substr(0, 8) == "$display")
|
if (ast->str.substr(0, 8) == "$display")
|
||||||
fmt.append_string("\n");
|
fmt.append_string("\n");
|
||||||
|
@ -776,6 +797,70 @@ struct AST_INTERNAL::ProcessGenerator
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// generate $check cells
|
||||||
|
case AST_ASSERT:
|
||||||
|
case AST_ASSUME:
|
||||||
|
case AST_LIVE:
|
||||||
|
case AST_FAIR:
|
||||||
|
case AST_COVER:
|
||||||
|
{
|
||||||
|
std::string flavor, desc;
|
||||||
|
if (ast->type == AST_ASSERT) { flavor = "assert"; desc = "assert ()"; }
|
||||||
|
if (ast->type == AST_ASSUME) { flavor = "assume"; desc = "assume ()"; }
|
||||||
|
if (ast->type == AST_LIVE) { flavor = "live"; desc = "assert (eventually)"; }
|
||||||
|
if (ast->type == AST_FAIR) { flavor = "fair"; desc = "assume (eventually)"; }
|
||||||
|
if (ast->type == AST_COVER) { flavor = "cover"; desc = "cover ()"; }
|
||||||
|
|
||||||
|
IdString cellname;
|
||||||
|
if (ast->str.empty())
|
||||||
|
cellname = stringf("$%s$%s:%d$%d", flavor.c_str(), RTLIL::encode_filename(ast->filename).c_str(), ast->location.first_line, autoidx++);
|
||||||
|
else
|
||||||
|
cellname = ast->str;
|
||||||
|
check_unique_id(current_module, cellname, ast, "procedural assertion");
|
||||||
|
|
||||||
|
RTLIL::SigSpec check = ast->children[0]->genWidthRTLIL(-1, false, &subst_rvalue_map.stdmap());
|
||||||
|
if (GetSize(check) != 1)
|
||||||
|
check = current_module->ReduceBool(NEW_ID, check);
|
||||||
|
|
||||||
|
Wire *en = current_module->addWire(cellname.str() + "_EN", 1);
|
||||||
|
set_src_attr(en, ast);
|
||||||
|
proc->root_case.actions.push_back(SigSig(en, false));
|
||||||
|
current_case->actions.push_back(SigSig(en, true));
|
||||||
|
|
||||||
|
RTLIL::SigSpec triggers;
|
||||||
|
RTLIL::Const polarity;
|
||||||
|
for (auto sync : proc->syncs) {
|
||||||
|
if (sync->type == RTLIL::STp) {
|
||||||
|
triggers.append(sync->signal);
|
||||||
|
polarity.bits.push_back(RTLIL::S1);
|
||||||
|
} else if (sync->type == RTLIL::STn) {
|
||||||
|
triggers.append(sync->signal);
|
||||||
|
polarity.bits.push_back(RTLIL::S0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RTLIL::Cell *cell = current_module->addCell(cellname, ID($check));
|
||||||
|
set_src_attr(cell, ast);
|
||||||
|
for (auto &attr : ast->attributes) {
|
||||||
|
if (attr.second->type != AST_CONSTANT)
|
||||||
|
log_file_error(ast->filename, ast->location.first_line, "Attribute `%s' with non-constant value!\n", attr.first.c_str());
|
||||||
|
cell->attributes[attr.first] = attr.second->asAttrConst();
|
||||||
|
}
|
||||||
|
cell->setParam(ID::FLAVOR, flavor);
|
||||||
|
cell->setParam(ID::TRG_WIDTH, triggers.size());
|
||||||
|
cell->setParam(ID::TRG_ENABLE, (always->type == AST_INITIAL) || !triggers.empty());
|
||||||
|
cell->setParam(ID::TRG_POLARITY, polarity);
|
||||||
|
cell->setParam(ID::PRIORITY, --last_effect_priority);
|
||||||
|
cell->setPort(ID::TRG, triggers);
|
||||||
|
cell->setPort(ID::EN, en);
|
||||||
|
cell->setPort(ID::A, check);
|
||||||
|
|
||||||
|
// No message is emitted to ensure Verilog code roundtrips correctly.
|
||||||
|
Fmt fmt;
|
||||||
|
fmt.emit_rtlil(cell);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case AST_NONE:
|
case AST_NONE:
|
||||||
case AST_FOR:
|
case AST_FOR:
|
||||||
break;
|
break;
|
||||||
|
@ -960,7 +1045,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
|
||||||
if (children.size() > 1)
|
if (children.size() > 1)
|
||||||
range = children[1];
|
range = children[1];
|
||||||
} else if (id_ast->type == AST_STRUCT_ITEM || id_ast->type == AST_STRUCT || id_ast->type == AST_UNION) {
|
} else if (id_ast->type == AST_STRUCT_ITEM || id_ast->type == AST_STRUCT || id_ast->type == AST_UNION) {
|
||||||
AstNode *tmp_range = make_struct_member_range(this, id_ast);
|
AstNode *tmp_range = make_index_range(id_ast);
|
||||||
this_width = tmp_range->range_left - tmp_range->range_right + 1;
|
this_width = tmp_range->range_left - tmp_range->range_right + 1;
|
||||||
delete tmp_range;
|
delete tmp_range;
|
||||||
} else
|
} else
|
||||||
|
@ -1242,28 +1327,6 @@ void AstNode::detectSignWidth(int &width_hint, bool &sign_hint, bool *found_real
|
||||||
width_hint, kWidthLimit);
|
width_hint, kWidthLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void check_unique_id(RTLIL::Module *module, RTLIL::IdString id,
|
|
||||||
const AstNode *node, const char *to_add_kind)
|
|
||||||
{
|
|
||||||
auto already_exists = [&](const RTLIL::AttrObject *existing, const char *existing_kind) {
|
|
||||||
std::string src = existing->get_string_attribute(ID::src);
|
|
||||||
std::string location_str = "earlier";
|
|
||||||
if (!src.empty())
|
|
||||||
location_str = "at " + src;
|
|
||||||
node->input_error("Cannot add %s `%s' because a %s with the same name was already created %s!\n",
|
|
||||||
to_add_kind, id.c_str(), existing_kind, location_str.c_str());
|
|
||||||
};
|
|
||||||
|
|
||||||
if (const RTLIL::Wire *wire = module->wire(id))
|
|
||||||
already_exists(wire, "signal");
|
|
||||||
if (const RTLIL::Cell *cell = module->cell(id))
|
|
||||||
already_exists(cell, "cell");
|
|
||||||
if (module->processes.count(id))
|
|
||||||
already_exists(module->processes.at(id), "process");
|
|
||||||
if (module->memories.count(id))
|
|
||||||
already_exists(module->memories.at(id), "memory");
|
|
||||||
}
|
|
||||||
|
|
||||||
// create RTLIL from an AST node
|
// create RTLIL from an AST node
|
||||||
// all generated cells, wires and processes are added to the module pointed to by 'current_module'
|
// all generated cells, wires and processes are added to the module pointed to by 'current_module'
|
||||||
// when the AST node is an expression (AST_ADD, AST_BIT_XOR, etc.), the result signal is returned.
|
// when the AST node is an expression (AST_ADD, AST_BIT_XOR, etc.), the result signal is returned.
|
||||||
|
@ -1521,7 +1584,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
||||||
chunk.width = wire->width;
|
chunk.width = wire->width;
|
||||||
chunk.offset = 0;
|
chunk.offset = 0;
|
||||||
|
|
||||||
if ((member_node = get_struct_member(this))) {
|
if ((member_node = get_struct_member())) {
|
||||||
// Clamp wire chunk to range of member within struct/union.
|
// Clamp wire chunk to range of member within struct/union.
|
||||||
chunk.width = member_node->range_left - member_node->range_right + 1;
|
chunk.width = member_node->range_left - member_node->range_right + 1;
|
||||||
chunk.offset = member_node->range_right;
|
chunk.offset = member_node->range_right;
|
||||||
|
@ -1945,48 +2008,50 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// generate $assert cells
|
// generate $check cells
|
||||||
case AST_ASSERT:
|
case AST_ASSERT:
|
||||||
case AST_ASSUME:
|
case AST_ASSUME:
|
||||||
case AST_LIVE:
|
case AST_LIVE:
|
||||||
case AST_FAIR:
|
case AST_FAIR:
|
||||||
case AST_COVER:
|
case AST_COVER:
|
||||||
{
|
{
|
||||||
IdString celltype;
|
std::string flavor, desc;
|
||||||
if (type == AST_ASSERT) celltype = ID($assert);
|
if (type == AST_ASSERT) { flavor = "assert"; desc = "assert property ()"; }
|
||||||
if (type == AST_ASSUME) celltype = ID($assume);
|
if (type == AST_ASSUME) { flavor = "assume"; desc = "assume property ()"; }
|
||||||
if (type == AST_LIVE) celltype = ID($live);
|
if (type == AST_LIVE) { flavor = "live"; desc = "assert property (eventually)"; }
|
||||||
if (type == AST_FAIR) celltype = ID($fair);
|
if (type == AST_FAIR) { flavor = "fair"; desc = "assume property (eventually)"; }
|
||||||
if (type == AST_COVER) celltype = ID($cover);
|
if (type == AST_COVER) { flavor = "cover"; desc = "cover property ()"; }
|
||||||
|
|
||||||
log_assert(children.size() == 2);
|
IdString cellname;
|
||||||
|
if (str.empty())
|
||||||
|
cellname = stringf("$%s$%s:%d$%d", flavor.c_str(), RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++);
|
||||||
|
else
|
||||||
|
cellname = str;
|
||||||
|
check_unique_id(current_module, cellname, this, "procedural assertion");
|
||||||
|
|
||||||
RTLIL::SigSpec check = children[0]->genRTLIL();
|
RTLIL::SigSpec check = children[0]->genRTLIL();
|
||||||
if (GetSize(check) != 1)
|
if (GetSize(check) != 1)
|
||||||
check = current_module->ReduceBool(NEW_ID, check);
|
check = current_module->ReduceBool(NEW_ID, check);
|
||||||
|
|
||||||
RTLIL::SigSpec en = children[1]->genRTLIL();
|
RTLIL::Cell *cell = current_module->addCell(cellname, ID($check));
|
||||||
if (GetSize(en) != 1)
|
|
||||||
en = current_module->ReduceBool(NEW_ID, en);
|
|
||||||
|
|
||||||
IdString cellname;
|
|
||||||
if (str.empty())
|
|
||||||
cellname = stringf("%s$%s:%d$%d", celltype.c_str(), RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++);
|
|
||||||
else
|
|
||||||
cellname = str;
|
|
||||||
|
|
||||||
check_unique_id(current_module, cellname, this, "procedural assertion");
|
|
||||||
RTLIL::Cell *cell = current_module->addCell(cellname, celltype);
|
|
||||||
set_src_attr(cell, this);
|
set_src_attr(cell, this);
|
||||||
|
|
||||||
for (auto &attr : attributes) {
|
for (auto &attr : attributes) {
|
||||||
if (attr.second->type != AST_CONSTANT)
|
if (attr.second->type != AST_CONSTANT)
|
||||||
input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str());
|
input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str());
|
||||||
cell->attributes[attr.first] = attr.second->asAttrConst();
|
cell->attributes[attr.first] = attr.second->asAttrConst();
|
||||||
}
|
}
|
||||||
|
cell->setParam(ID(FLAVOR), flavor);
|
||||||
|
cell->parameters[ID::TRG_WIDTH] = 0;
|
||||||
|
cell->parameters[ID::TRG_ENABLE] = 0;
|
||||||
|
cell->parameters[ID::TRG_POLARITY] = 0;
|
||||||
|
cell->parameters[ID::PRIORITY] = 0;
|
||||||
|
cell->setPort(ID::TRG, RTLIL::SigSpec());
|
||||||
|
cell->setPort(ID::EN, RTLIL::S1);
|
||||||
cell->setPort(ID::A, check);
|
cell->setPort(ID::A, check);
|
||||||
cell->setPort(ID::EN, en);
|
|
||||||
|
// No message is emitted to ensure Verilog code roundtrips correctly.
|
||||||
|
Fmt fmt;
|
||||||
|
fmt.emit_rtlil(cell);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -256,6 +256,16 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!strcmp(cmd, ".area") || !strcmp(cmd, ".delay") || !strcmp(cmd, ".wire_load_slope") || !strcmp(cmd, ".wire") ||
|
||||||
|
!strcmp(cmd, ".input_arrival") || !strcmp(cmd, ".default_input_arrival") || !strcmp(cmd, ".output_required") ||
|
||||||
|
!strcmp(cmd, ".default_output_required") || !strcmp(cmd, ".input_drive") || !strcmp(cmd, ".default_input_drive") ||
|
||||||
|
!strcmp(cmd, ".max_input_load") || !strcmp(cmd, ".default_max_input_load") || !strcmp(cmd, ".output_load") ||
|
||||||
|
!strcmp(cmd, ".default_output_load"))
|
||||||
|
{
|
||||||
|
log_warning("Blif delay constraints (%s) are not supported.", cmd);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!strcmp(cmd, ".inputs") || !strcmp(cmd, ".outputs"))
|
if (!strcmp(cmd, ".inputs") || !strcmp(cmd, ".outputs"))
|
||||||
{
|
{
|
||||||
char *p;
|
char *p;
|
||||||
|
|
|
@ -343,36 +343,46 @@ void VerificImporter::import_attributes(dict<RTLIL::IdString, RTLIL::Const> &att
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RTLIL::SigBit VerificImporter::netToSigBit(Verific::Net *net) {
|
||||||
|
if (net && net->IsGnd())
|
||||||
|
return RTLIL::State::S0;
|
||||||
|
else if (net && net->IsPwr())
|
||||||
|
return RTLIL::State::S1;
|
||||||
|
else if (net && net->IsX())
|
||||||
|
return RTLIL::State::Sx;
|
||||||
|
else if (net)
|
||||||
|
return net_map_at(net);
|
||||||
|
else
|
||||||
|
return RTLIL::State::Sz;
|
||||||
|
}
|
||||||
|
|
||||||
RTLIL::SigSpec VerificImporter::operatorInput(Instance *inst)
|
RTLIL::SigSpec VerificImporter::operatorInput(Instance *inst)
|
||||||
{
|
{
|
||||||
RTLIL::SigSpec sig;
|
RTLIL::SigSpec sig;
|
||||||
for (int i = int(inst->InputSize())-1; i >= 0; i--)
|
for (int i = int(inst->InputSize())-1; i >= 0; i--) {
|
||||||
if (inst->GetInputBit(i))
|
Net *net = inst->GetInputBit(i);
|
||||||
sig.append(net_map_at(inst->GetInputBit(i)));
|
sig.append(netToSigBit(net));
|
||||||
else
|
}
|
||||||
sig.append(RTLIL::State::Sz);
|
|
||||||
return sig;
|
return sig;
|
||||||
}
|
}
|
||||||
|
|
||||||
RTLIL::SigSpec VerificImporter::operatorInput1(Instance *inst)
|
RTLIL::SigSpec VerificImporter::operatorInput1(Instance *inst)
|
||||||
{
|
{
|
||||||
RTLIL::SigSpec sig;
|
RTLIL::SigSpec sig;
|
||||||
for (int i = int(inst->Input1Size())-1; i >= 0; i--)
|
for (int i = int(inst->Input1Size())-1; i >= 0; i--) {
|
||||||
if (inst->GetInput1Bit(i))
|
Net *net = inst->GetInput1Bit(i);
|
||||||
sig.append(net_map_at(inst->GetInput1Bit(i)));
|
sig.append(netToSigBit(net));
|
||||||
else
|
}
|
||||||
sig.append(RTLIL::State::Sz);
|
|
||||||
return sig;
|
return sig;
|
||||||
}
|
}
|
||||||
|
|
||||||
RTLIL::SigSpec VerificImporter::operatorInput2(Instance *inst)
|
RTLIL::SigSpec VerificImporter::operatorInput2(Instance *inst)
|
||||||
{
|
{
|
||||||
RTLIL::SigSpec sig;
|
RTLIL::SigSpec sig;
|
||||||
for (int i = int(inst->Input2Size())-1; i >= 0; i--)
|
for (int i = int(inst->Input2Size())-1; i >= 0; i--) {
|
||||||
if (inst->GetInput2Bit(i))
|
Net *net = inst->GetInput2Bit(i);
|
||||||
sig.append(net_map_at(inst->GetInput2Bit(i)));
|
sig.append(netToSigBit(net));
|
||||||
else
|
}
|
||||||
sig.append(RTLIL::State::Sz);
|
|
||||||
return sig;
|
return sig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1980,6 +1990,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
|
||||||
}
|
}
|
||||||
|
|
||||||
RTLIL::Cell *cell = module->addCell(inst_name, inst_type);
|
RTLIL::Cell *cell = module->addCell(inst_name, inst_type);
|
||||||
|
import_attributes(cell->attributes, inst);
|
||||||
|
|
||||||
if (inst->IsPrimitive() && mode_keep)
|
if (inst->IsPrimitive() && mode_keep)
|
||||||
cell->attributes[ID::keep] = 1;
|
cell->attributes[ID::keep] = 1;
|
||||||
|
@ -2815,6 +2826,9 @@ struct VerificPass : public Pass {
|
||||||
log(" -extnets\n");
|
log(" -extnets\n");
|
||||||
log(" Resolve references to external nets by adding module ports as needed.\n");
|
log(" Resolve references to external nets by adding module ports as needed.\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
|
log(" -no-split-complex-ports\n");
|
||||||
|
log(" Complex ports (structs or arrays) are not split and remain packed as a single port.\n");
|
||||||
|
log("\n");
|
||||||
log(" -autocover\n");
|
log(" -autocover\n");
|
||||||
log(" Generate automatic cover statements for all asserts\n");
|
log(" Generate automatic cover statements for all asserts\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
|
@ -3548,6 +3562,7 @@ struct VerificPass : public Pass {
|
||||||
bool mode_nosva = false, mode_names = false, mode_verific = false;
|
bool mode_nosva = false, mode_names = false, mode_verific = false;
|
||||||
bool mode_autocover = false, mode_fullinit = false;
|
bool mode_autocover = false, mode_fullinit = false;
|
||||||
bool flatten = false, extnets = false, mode_cells = false;
|
bool flatten = false, extnets = false, mode_cells = false;
|
||||||
|
bool split_complex_ports = true;
|
||||||
string dumpfile;
|
string dumpfile;
|
||||||
string ppfile;
|
string ppfile;
|
||||||
Map parameters(STRING_HASH);
|
Map parameters(STRING_HASH);
|
||||||
|
@ -3565,6 +3580,10 @@ struct VerificPass : public Pass {
|
||||||
flatten = true;
|
flatten = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (args[argidx] == "-no-split-complex-ports") {
|
||||||
|
split_complex_ports = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (args[argidx] == "-extnets") {
|
if (args[argidx] == "-extnets") {
|
||||||
extnets = true;
|
extnets = true;
|
||||||
continue;
|
continue;
|
||||||
|
@ -3804,8 +3823,10 @@ struct VerificPass : public Pass {
|
||||||
worker.run(nl.second);
|
worker.run(nl.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto nl : nl_todo)
|
if (split_complex_ports) {
|
||||||
nl.second->ChangePortBusStructures(1 /* hierarchical */);
|
for (auto nl : nl_todo)
|
||||||
|
nl.second->ChangePortBusStructures(1 /* hierarchical */);
|
||||||
|
}
|
||||||
|
|
||||||
if (!dumpfile.empty()) {
|
if (!dumpfile.empty()) {
|
||||||
VeriWrite veri_writer;
|
VeriWrite veri_writer;
|
||||||
|
|
|
@ -83,6 +83,7 @@ struct VerificImporter
|
||||||
RTLIL::IdString new_verific_id(Verific::DesignObj *obj);
|
RTLIL::IdString new_verific_id(Verific::DesignObj *obj);
|
||||||
void import_attributes(dict<RTLIL::IdString, RTLIL::Const> &attributes, Verific::DesignObj *obj, Verific::Netlist *nl = nullptr);
|
void import_attributes(dict<RTLIL::IdString, RTLIL::Const> &attributes, Verific::DesignObj *obj, Verific::Netlist *nl = nullptr);
|
||||||
|
|
||||||
|
RTLIL::SigBit netToSigBit(Verific::Net *net);
|
||||||
RTLIL::SigSpec operatorInput(Verific::Instance *inst);
|
RTLIL::SigSpec operatorInput(Verific::Instance *inst);
|
||||||
RTLIL::SigSpec operatorInput1(Verific::Instance *inst);
|
RTLIL::SigSpec operatorInput1(Verific::Instance *inst);
|
||||||
RTLIL::SigSpec operatorInput2(Verific::Instance *inst);
|
RTLIL::SigSpec operatorInput2(Verific::Instance *inst);
|
||||||
|
|
|
@ -197,9 +197,20 @@ static AstNode *checkRange(AstNode *type_node, AstNode *range_node)
|
||||||
range_node = makeRange(type_node->range_left, type_node->range_right, false);
|
range_node = makeRange(type_node->range_left, type_node->range_right, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (range_node && range_node->children.size() != 2) {
|
|
||||||
frontend_verilog_yyerror("wire/reg/logic packed dimension must be of the form: [<expr>:<expr>], [<expr>+:<expr>], or [<expr>-:<expr>]");
|
if (range_node) {
|
||||||
|
bool valid = true;
|
||||||
|
if (range_node->type == AST_RANGE) {
|
||||||
|
valid = range_node->children.size() == 2;
|
||||||
|
} else { // AST_MULTIRANGE
|
||||||
|
for (auto child : range_node->children) {
|
||||||
|
valid = valid && child->children.size() == 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!valid)
|
||||||
|
frontend_verilog_yyerror("wire/reg/logic packed dimension must be of the form [<expr>:<expr>]");
|
||||||
}
|
}
|
||||||
|
|
||||||
return range_node;
|
return range_node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -672,7 +683,7 @@ module_arg:
|
||||||
ast_stack.back()->children.push_back(astbuf2);
|
ast_stack.back()->children.push_back(astbuf2);
|
||||||
delete astbuf1; // really only needed if multiple instances of same type.
|
delete astbuf1; // really only needed if multiple instances of same type.
|
||||||
} module_arg_opt_assignment |
|
} module_arg_opt_assignment |
|
||||||
attr wire_type range TOK_ID {
|
attr wire_type range_or_multirange TOK_ID {
|
||||||
AstNode *node = $2;
|
AstNode *node = $2;
|
||||||
node->str = *$4;
|
node->str = *$4;
|
||||||
SET_AST_NODE_LOC(node, @4, @4);
|
SET_AST_NODE_LOC(node, @4, @4);
|
||||||
|
@ -1165,7 +1176,7 @@ task_func_args:
|
||||||
task_func_port | task_func_args ',' task_func_port;
|
task_func_port | task_func_args ',' task_func_port;
|
||||||
|
|
||||||
task_func_port:
|
task_func_port:
|
||||||
attr wire_type range {
|
attr wire_type range_or_multirange {
|
||||||
bool prev_was_input = true;
|
bool prev_was_input = true;
|
||||||
bool prev_was_output = false;
|
bool prev_was_output = false;
|
||||||
if (albuf) {
|
if (albuf) {
|
||||||
|
@ -1889,10 +1900,11 @@ struct_member_type: { astbuf1 = new AstNode(AST_STRUCT_ITEM); } member_type_toke
|
||||||
;
|
;
|
||||||
|
|
||||||
member_type_token:
|
member_type_token:
|
||||||
member_type
|
member_type range_or_multirange {
|
||||||
| hierarchical_type_id {
|
AstNode *range = checkRange(astbuf1, $2);
|
||||||
addWiretypeNode($1, astbuf1);
|
if (range)
|
||||||
}
|
astbuf1->children.push_back(range);
|
||||||
|
}
|
||||||
| {
|
| {
|
||||||
delete astbuf1;
|
delete astbuf1;
|
||||||
} struct_union {
|
} struct_union {
|
||||||
|
@ -1908,7 +1920,8 @@ member_type_token:
|
||||||
;
|
;
|
||||||
|
|
||||||
member_type: type_atom type_signing
|
member_type: type_atom type_signing
|
||||||
| type_vec type_signing range_or_multirange { if ($3) astbuf1->children.push_back($3); }
|
| type_vec type_signing
|
||||||
|
| hierarchical_type_id { addWiretypeNode($1, astbuf1); }
|
||||||
;
|
;
|
||||||
|
|
||||||
struct_var_list: struct_var
|
struct_var_list: struct_var
|
||||||
|
@ -1928,7 +1941,7 @@ struct_var: TOK_ID { auto *var_node = astbuf2->clone();
|
||||||
/////////
|
/////////
|
||||||
|
|
||||||
wire_decl:
|
wire_decl:
|
||||||
attr wire_type range {
|
attr wire_type range_or_multirange {
|
||||||
albuf = $1;
|
albuf = $1;
|
||||||
astbuf1 = $2;
|
astbuf1 = $2;
|
||||||
astbuf2 = checkRange(astbuf1, $3);
|
astbuf2 = checkRange(astbuf1, $3);
|
||||||
|
@ -2104,14 +2117,14 @@ type_name: TOK_ID // first time seen
|
||||||
;
|
;
|
||||||
|
|
||||||
typedef_decl:
|
typedef_decl:
|
||||||
TOK_TYPEDEF typedef_base_type range type_name range_or_multirange ';' {
|
TOK_TYPEDEF typedef_base_type range_or_multirange type_name range_or_multirange ';' {
|
||||||
astbuf1 = $2;
|
astbuf1 = $2;
|
||||||
astbuf2 = checkRange(astbuf1, $3);
|
astbuf2 = checkRange(astbuf1, $3);
|
||||||
if (astbuf2)
|
if (astbuf2)
|
||||||
astbuf1->children.push_back(astbuf2);
|
astbuf1->children.push_back(astbuf2);
|
||||||
|
|
||||||
if ($5 != NULL) {
|
if ($5 != NULL) {
|
||||||
if (!astbuf2) {
|
if (!astbuf2 && !astbuf1->is_custom_type) {
|
||||||
addRange(astbuf1, 0, 0, false);
|
addRange(astbuf1, 0, 0, false);
|
||||||
}
|
}
|
||||||
rewriteAsMemoryNode(astbuf1, $5);
|
rewriteAsMemoryNode(astbuf1, $5);
|
||||||
|
@ -2484,7 +2497,7 @@ assert:
|
||||||
delete $5;
|
delete $5;
|
||||||
} else {
|
} else {
|
||||||
AstNode *node = new AstNode(assume_asserts_mode ? AST_ASSUME : AST_ASSERT, $5);
|
AstNode *node = new AstNode(assume_asserts_mode ? AST_ASSUME : AST_ASSERT, $5);
|
||||||
SET_AST_NODE_LOC(node, @1, @6);
|
SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @6);
|
||||||
if ($1 != nullptr)
|
if ($1 != nullptr)
|
||||||
node->str = *$1;
|
node->str = *$1;
|
||||||
ast_stack.back()->children.push_back(node);
|
ast_stack.back()->children.push_back(node);
|
||||||
|
@ -2497,7 +2510,7 @@ assert:
|
||||||
delete $5;
|
delete $5;
|
||||||
} else {
|
} else {
|
||||||
AstNode *node = new AstNode(assert_assumes_mode ? AST_ASSERT : AST_ASSUME, $5);
|
AstNode *node = new AstNode(assert_assumes_mode ? AST_ASSERT : AST_ASSUME, $5);
|
||||||
SET_AST_NODE_LOC(node, @1, @6);
|
SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @6);
|
||||||
if ($1 != nullptr)
|
if ($1 != nullptr)
|
||||||
node->str = *$1;
|
node->str = *$1;
|
||||||
ast_stack.back()->children.push_back(node);
|
ast_stack.back()->children.push_back(node);
|
||||||
|
@ -2510,7 +2523,7 @@ assert:
|
||||||
delete $6;
|
delete $6;
|
||||||
} else {
|
} else {
|
||||||
AstNode *node = new AstNode(assume_asserts_mode ? AST_FAIR : AST_LIVE, $6);
|
AstNode *node = new AstNode(assume_asserts_mode ? AST_FAIR : AST_LIVE, $6);
|
||||||
SET_AST_NODE_LOC(node, @1, @7);
|
SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @7);
|
||||||
if ($1 != nullptr)
|
if ($1 != nullptr)
|
||||||
node->str = *$1;
|
node->str = *$1;
|
||||||
ast_stack.back()->children.push_back(node);
|
ast_stack.back()->children.push_back(node);
|
||||||
|
@ -2523,7 +2536,7 @@ assert:
|
||||||
delete $6;
|
delete $6;
|
||||||
} else {
|
} else {
|
||||||
AstNode *node = new AstNode(assert_assumes_mode ? AST_LIVE : AST_FAIR, $6);
|
AstNode *node = new AstNode(assert_assumes_mode ? AST_LIVE : AST_FAIR, $6);
|
||||||
SET_AST_NODE_LOC(node, @1, @7);
|
SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @7);
|
||||||
if ($1 != nullptr)
|
if ($1 != nullptr)
|
||||||
node->str = *$1;
|
node->str = *$1;
|
||||||
ast_stack.back()->children.push_back(node);
|
ast_stack.back()->children.push_back(node);
|
||||||
|
@ -2533,7 +2546,7 @@ assert:
|
||||||
} |
|
} |
|
||||||
opt_sva_label TOK_COVER opt_property '(' expr ')' ';' {
|
opt_sva_label TOK_COVER opt_property '(' expr ')' ';' {
|
||||||
AstNode *node = new AstNode(AST_COVER, $5);
|
AstNode *node = new AstNode(AST_COVER, $5);
|
||||||
SET_AST_NODE_LOC(node, @1, @6);
|
SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @6);
|
||||||
if ($1 != nullptr) {
|
if ($1 != nullptr) {
|
||||||
node->str = *$1;
|
node->str = *$1;
|
||||||
delete $1;
|
delete $1;
|
||||||
|
@ -2542,7 +2555,7 @@ assert:
|
||||||
} |
|
} |
|
||||||
opt_sva_label TOK_COVER opt_property '(' ')' ';' {
|
opt_sva_label TOK_COVER opt_property '(' ')' ';' {
|
||||||
AstNode *node = new AstNode(AST_COVER, AstNode::mkconst_int(1, false));
|
AstNode *node = new AstNode(AST_COVER, AstNode::mkconst_int(1, false));
|
||||||
SET_AST_NODE_LOC(node, @1, @5);
|
SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @5);
|
||||||
if ($1 != nullptr) {
|
if ($1 != nullptr) {
|
||||||
node->str = *$1;
|
node->str = *$1;
|
||||||
delete $1;
|
delete $1;
|
||||||
|
@ -2551,7 +2564,7 @@ assert:
|
||||||
} |
|
} |
|
||||||
opt_sva_label TOK_COVER ';' {
|
opt_sva_label TOK_COVER ';' {
|
||||||
AstNode *node = new AstNode(AST_COVER, AstNode::mkconst_int(1, false));
|
AstNode *node = new AstNode(AST_COVER, AstNode::mkconst_int(1, false));
|
||||||
SET_AST_NODE_LOC(node, @1, @2);
|
SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @2);
|
||||||
if ($1 != nullptr) {
|
if ($1 != nullptr) {
|
||||||
node->str = *$1;
|
node->str = *$1;
|
||||||
delete $1;
|
delete $1;
|
||||||
|
@ -2563,7 +2576,7 @@ assert:
|
||||||
delete $5;
|
delete $5;
|
||||||
} else {
|
} else {
|
||||||
AstNode *node = new AstNode(AST_ASSUME, $5);
|
AstNode *node = new AstNode(AST_ASSUME, $5);
|
||||||
SET_AST_NODE_LOC(node, @1, @6);
|
SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @6);
|
||||||
if ($1 != nullptr)
|
if ($1 != nullptr)
|
||||||
node->str = *$1;
|
node->str = *$1;
|
||||||
ast_stack.back()->children.push_back(node);
|
ast_stack.back()->children.push_back(node);
|
||||||
|
@ -2578,7 +2591,7 @@ assert:
|
||||||
delete $6;
|
delete $6;
|
||||||
} else {
|
} else {
|
||||||
AstNode *node = new AstNode(AST_FAIR, $6);
|
AstNode *node = new AstNode(AST_FAIR, $6);
|
||||||
SET_AST_NODE_LOC(node, @1, @7);
|
SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @7);
|
||||||
if ($1 != nullptr)
|
if ($1 != nullptr)
|
||||||
node->str = *$1;
|
node->str = *$1;
|
||||||
ast_stack.back()->children.push_back(node);
|
ast_stack.back()->children.push_back(node);
|
||||||
|
|
|
@ -102,11 +102,13 @@ struct CellTypes
|
||||||
setup_type(ID($specify3), {ID::EN, ID::SRC, ID::DST, ID::DAT}, pool<RTLIL::IdString>(), true);
|
setup_type(ID($specify3), {ID::EN, ID::SRC, ID::DST, ID::DAT}, pool<RTLIL::IdString>(), true);
|
||||||
setup_type(ID($specrule), {ID::EN_SRC, ID::EN_DST, ID::SRC, ID::DST}, pool<RTLIL::IdString>(), true);
|
setup_type(ID($specrule), {ID::EN_SRC, ID::EN_DST, ID::SRC, ID::DST}, pool<RTLIL::IdString>(), true);
|
||||||
setup_type(ID($print), {ID::EN, ID::ARGS, ID::TRG}, pool<RTLIL::IdString>());
|
setup_type(ID($print), {ID::EN, ID::ARGS, ID::TRG}, pool<RTLIL::IdString>());
|
||||||
|
setup_type(ID($check), {ID::A, ID::EN, ID::ARGS, ID::TRG}, pool<RTLIL::IdString>());
|
||||||
setup_type(ID($set_tag), {ID::A, ID::SET, ID::CLR}, {ID::Y});
|
setup_type(ID($set_tag), {ID::A, ID::SET, ID::CLR}, {ID::Y});
|
||||||
setup_type(ID($get_tag), {ID::A}, {ID::Y});
|
setup_type(ID($get_tag), {ID::A}, {ID::Y});
|
||||||
setup_type(ID($overwrite_tag), {ID::A, ID::SET, ID::CLR}, pool<RTLIL::IdString>());
|
setup_type(ID($overwrite_tag), {ID::A, ID::SET, ID::CLR}, pool<RTLIL::IdString>());
|
||||||
setup_type(ID($original_tag), {ID::A}, {ID::Y});
|
setup_type(ID($original_tag), {ID::A}, {ID::Y});
|
||||||
setup_type(ID($future_ff), {ID::A}, {ID::Y});
|
setup_type(ID($future_ff), {ID::A}, {ID::Y});
|
||||||
|
setup_type(ID($scopeinfo), {}, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup_internals_eval()
|
void setup_internals_eval()
|
||||||
|
|
|
@ -88,6 +88,7 @@ X(equiv_merged)
|
||||||
X(equiv_region)
|
X(equiv_region)
|
||||||
X(extract_order)
|
X(extract_order)
|
||||||
X(F)
|
X(F)
|
||||||
|
X(FLAVOR)
|
||||||
X(FORMAT)
|
X(FORMAT)
|
||||||
X(force_downto)
|
X(force_downto)
|
||||||
X(force_upto)
|
X(force_upto)
|
||||||
|
|
|
@ -92,8 +92,15 @@ int getopt(int argc, char **argv, const char *optstring)
|
||||||
return optopt;
|
return optopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
optarg = argv[++optind];
|
if (++optind >= argc) {
|
||||||
|
fprintf(stderr, "%s: option '-%c' expects an argument\n", argv[0], optopt);
|
||||||
|
optopt = '?';
|
||||||
|
return optopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
optarg = argv[optind];
|
||||||
optind++, optcur = 1;
|
optind++, optcur = 1;
|
||||||
|
|
||||||
return optopt;
|
return optopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,14 +250,6 @@ int main(int argc, char **argv)
|
||||||
bool mode_v = false;
|
bool mode_v = false;
|
||||||
bool mode_q = false;
|
bool mode_q = false;
|
||||||
|
|
||||||
#if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE)
|
|
||||||
if (getenv("HOME") != NULL) {
|
|
||||||
yosys_history_file = stringf("%s/.yosys_history", getenv("HOME"));
|
|
||||||
read_history(yosys_history_file.c_str());
|
|
||||||
yosys_history_offset = where_history();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "-help") || !strcmp(argv[1], "--help")))
|
if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "-help") || !strcmp(argv[1], "--help")))
|
||||||
{
|
{
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
@ -538,6 +537,36 @@ int main(int argc, char **argv)
|
||||||
if (print_banner)
|
if (print_banner)
|
||||||
yosys_banner();
|
yosys_banner();
|
||||||
|
|
||||||
|
#if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE)
|
||||||
|
std::string state_dir;
|
||||||
|
#if defined(_WIN32)
|
||||||
|
if (getenv("HOMEDRIVE") != NULL && getenv("HOMEPATH") != NULL) {
|
||||||
|
state_dir = stringf("%s%s/.local/state", getenv("HOMEDRIVE"), getenv("HOMEPATH"));
|
||||||
|
} else {
|
||||||
|
log_debug("$HOMEDRIVE and/or $HOMEPATH is empty. No history file will be created.\n");
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (getenv("XDG_STATE_HOME") == NULL || getenv("XDG_STATE_HOME")[0] == '\0') {
|
||||||
|
if (getenv("HOME") != NULL) {
|
||||||
|
state_dir = stringf("%s/.local/state", getenv("HOME"));
|
||||||
|
} else {
|
||||||
|
log_debug("$HOME is empty. No history file will be created.\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
state_dir = stringf("%s", getenv("XDG_STATE_HOME"));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!state_dir.empty()) {
|
||||||
|
std::string yosys_dir = state_dir + "/yosys";
|
||||||
|
create_directory(yosys_dir);
|
||||||
|
|
||||||
|
yosys_history_file = yosys_dir + "/history";
|
||||||
|
read_history(yosys_history_file.c_str());
|
||||||
|
yosys_history_offset = where_history();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (print_stats)
|
if (print_stats)
|
||||||
log_hasher = new SHA1;
|
log_hasher = new SHA1;
|
||||||
|
|
||||||
|
@ -569,6 +598,8 @@ int main(int argc, char **argv)
|
||||||
for (auto &fn : plugin_filenames)
|
for (auto &fn : plugin_filenames)
|
||||||
load_plugin(fn, {});
|
load_plugin(fn, {});
|
||||||
|
|
||||||
|
log_suppressed();
|
||||||
|
|
||||||
if (!vlog_defines.empty()) {
|
if (!vlog_defines.empty()) {
|
||||||
std::string vdef_cmd = "read -define";
|
std::string vdef_cmd = "read -define";
|
||||||
for (auto vdef : vlog_defines)
|
for (auto vdef : vlog_defines)
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
namespace hashlib {
|
namespace hashlib {
|
||||||
|
|
||||||
const int hashtable_size_trigger = 2;
|
const int hashtable_size_trigger = 2;
|
||||||
|
|
|
@ -108,9 +108,8 @@ Pass::Pass(std::string name, std::string short_help) : pass_name(name), short_he
|
||||||
|
|
||||||
void Pass::run_register()
|
void Pass::run_register()
|
||||||
{
|
{
|
||||||
if (pass_register.count(pass_name))
|
if (pass_register.count(pass_name) && !replace_existing_pass())
|
||||||
log_error("Unable to register pass '%s', pass already exists!\n", pass_name.c_str());
|
log_error("Unable to register pass '%s', pass already exists!\n", pass_name.c_str());
|
||||||
|
|
||||||
pass_register[pass_name] = this;
|
pass_register[pass_name] = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -447,13 +446,12 @@ Frontend::Frontend(std::string name, std::string short_help) :
|
||||||
|
|
||||||
void Frontend::run_register()
|
void Frontend::run_register()
|
||||||
{
|
{
|
||||||
if (pass_register.count(pass_name))
|
if (pass_register.count(pass_name) && !replace_existing_pass())
|
||||||
log_error("Unable to register pass '%s', pass already exists!\n", pass_name.c_str());
|
log_error("Unable to register pass '%s', pass already exists!\n", pass_name.c_str());
|
||||||
pass_register[pass_name] = this;
|
pass_register[pass_name] = this;
|
||||||
|
|
||||||
if (frontend_register.count(frontend_name))
|
if (frontend_register.count(frontend_name) && !replace_existing_pass())
|
||||||
log_error("Unable to register frontend '%s', frontend already exists!\n", frontend_name.c_str());
|
log_error("Unable to register frontend '%s', frontend already exists!\n", frontend_name.c_str());
|
||||||
|
|
||||||
frontend_register[frontend_name] = this;
|
frontend_register[frontend_name] = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -993,4 +991,44 @@ struct MinisatSatSolver : public SatSolver {
|
||||||
}
|
}
|
||||||
} MinisatSatSolver;
|
} MinisatSatSolver;
|
||||||
|
|
||||||
|
struct LicensePass : public Pass {
|
||||||
|
LicensePass() : Pass("license", "print license terms") { }
|
||||||
|
void help() override
|
||||||
|
{
|
||||||
|
log("\n");
|
||||||
|
log(" license\n");
|
||||||
|
log("\n");
|
||||||
|
log("This command produces the following notice.\n");
|
||||||
|
notice();
|
||||||
|
}
|
||||||
|
void execute(std::vector<std::string> args, RTLIL::Design*) override
|
||||||
|
{
|
||||||
|
notice();
|
||||||
|
}
|
||||||
|
void notice()
|
||||||
|
{
|
||||||
|
log("\n");
|
||||||
|
log(" /----------------------------------------------------------------------------\\\n");
|
||||||
|
log(" | |\n");
|
||||||
|
log(" | yosys -- Yosys Open SYnthesis Suite |\n");
|
||||||
|
log(" | |\n");
|
||||||
|
log(" | Copyright (C) 2012 - 2024 Claire Xenia Wolf <claire@yosyshq.com> |\n");
|
||||||
|
log(" | |\n");
|
||||||
|
log(" | Permission to use, copy, modify, and/or distribute this software for any |\n");
|
||||||
|
log(" | purpose with or without fee is hereby granted, provided that the above |\n");
|
||||||
|
log(" | copyright notice and this permission notice appear in all copies. |\n");
|
||||||
|
log(" | |\n");
|
||||||
|
log(" | THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |\n");
|
||||||
|
log(" | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |\n");
|
||||||
|
log(" | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |\n");
|
||||||
|
log(" | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |\n");
|
||||||
|
log(" | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |\n");
|
||||||
|
log(" | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |\n");
|
||||||
|
log(" | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |\n");
|
||||||
|
log(" | |\n");
|
||||||
|
log(" \\----------------------------------------------------------------------------/\n");
|
||||||
|
log("\n");
|
||||||
|
}
|
||||||
|
} LicensePass;
|
||||||
|
|
||||||
YOSYS_NAMESPACE_END
|
YOSYS_NAMESPACE_END
|
||||||
|
|
|
@ -70,6 +70,7 @@ struct Pass
|
||||||
|
|
||||||
virtual void on_register();
|
virtual void on_register();
|
||||||
virtual void on_shutdown();
|
virtual void on_shutdown();
|
||||||
|
virtual bool replace_existing_pass() const { return false; }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ScriptPass : Pass
|
struct ScriptPass : Pass
|
||||||
|
|
|
@ -1068,6 +1068,12 @@ namespace {
|
||||||
error(__LINE__);
|
error(__LINE__);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string param_string(const RTLIL::IdString &name)
|
||||||
|
{
|
||||||
|
param(name);
|
||||||
|
return cell->parameters.at(name).decode_string();
|
||||||
|
}
|
||||||
|
|
||||||
void port(const RTLIL::IdString& name, int width)
|
void port(const RTLIL::IdString& name, int width)
|
||||||
{
|
{
|
||||||
auto it = cell->connections_.find(name);
|
auto it = cell->connections_.find(name);
|
||||||
|
@ -1747,6 +1753,31 @@ namespace {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cell->type == ID($check)) {
|
||||||
|
std::string flavor = param_string(ID(FLAVOR));
|
||||||
|
if (!(flavor == "assert" || flavor == "assume" || flavor == "live" || flavor == "fair" || flavor == "cover"))
|
||||||
|
error(__LINE__);
|
||||||
|
param(ID(FORMAT));
|
||||||
|
param_bool(ID::TRG_ENABLE);
|
||||||
|
param(ID::TRG_POLARITY);
|
||||||
|
param(ID::PRIORITY);
|
||||||
|
port(ID::A, 1);
|
||||||
|
port(ID::EN, 1);
|
||||||
|
port(ID::TRG, param(ID::TRG_WIDTH));
|
||||||
|
port(ID::ARGS, param(ID::ARGS_WIDTH));
|
||||||
|
check_expected();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cell->type == ID($scopeinfo)) {
|
||||||
|
param(ID::TYPE);
|
||||||
|
check_expected();
|
||||||
|
std::string scope_type = cell->getParam(ID::TYPE).decode_string();
|
||||||
|
if (scope_type != "module" && scope_type != "struct")
|
||||||
|
error(__LINE__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (cell->type == ID($_BUF_)) { port(ID::A,1); port(ID::Y,1); check_expected(); return; }
|
if (cell->type == ID($_BUF_)) { port(ID::A,1); port(ID::Y,1); check_expected(); return; }
|
||||||
if (cell->type == ID($_NOT_)) { port(ID::A,1); port(ID::Y,1); check_expected(); return; }
|
if (cell->type == ID($_NOT_)) { port(ID::A,1); port(ID::Y,1); check_expected(); return; }
|
||||||
if (cell->type == ID($_AND_)) { port(ID::A,1); port(ID::B,1); port(ID::Y,1); check_expected(); return; }
|
if (cell->type == ID($_AND_)) { port(ID::A,1); port(ID::B,1); port(ID::Y,1); check_expected(); return; }
|
||||||
|
@ -2157,17 +2188,10 @@ void RTLIL::Module::remove(const pool<RTLIL::Wire*> &wires)
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator()(RTLIL::SigSpec &lhs, RTLIL::SigSpec &rhs) {
|
void operator()(RTLIL::SigSpec &lhs, RTLIL::SigSpec &rhs) {
|
||||||
log_assert(GetSize(lhs) == GetSize(rhs));
|
// If a deleted wire occurs on the lhs or rhs we just remove that part
|
||||||
lhs.unpack();
|
// of the assignment
|
||||||
rhs.unpack();
|
lhs.remove2(*wires_p, &rhs);
|
||||||
for (int i = 0; i < GetSize(lhs); i++) {
|
rhs.remove2(*wires_p, &lhs);
|
||||||
RTLIL::SigBit &lhs_bit = lhs.bits_[i];
|
|
||||||
RTLIL::SigBit &rhs_bit = rhs.bits_[i];
|
|
||||||
if ((lhs_bit.wire != nullptr && wires_p->count(lhs_bit.wire)) || (rhs_bit.wire != nullptr && wires_p->count(rhs_bit.wire))) {
|
|
||||||
lhs_bit = State::Sx;
|
|
||||||
rhs_bit = State::Sx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3693,6 +3717,9 @@ RTLIL::SigChunk::SigChunk(const RTLIL::SigBit &bit)
|
||||||
|
|
||||||
RTLIL::SigChunk RTLIL::SigChunk::extract(int offset, int length) const
|
RTLIL::SigChunk RTLIL::SigChunk::extract(int offset, int length) const
|
||||||
{
|
{
|
||||||
|
log_assert(offset >= 0);
|
||||||
|
log_assert(length >= 0);
|
||||||
|
log_assert(offset + length <= width);
|
||||||
RTLIL::SigChunk ret;
|
RTLIL::SigChunk ret;
|
||||||
if (wire) {
|
if (wire) {
|
||||||
ret.wire = wire;
|
ret.wire = wire;
|
||||||
|
@ -4238,6 +4265,34 @@ void RTLIL::SigSpec::remove2(const std::set<RTLIL::SigBit> &pattern, RTLIL::SigS
|
||||||
check();
|
check();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RTLIL::SigSpec::remove2(const pool<RTLIL::Wire*> &pattern, RTLIL::SigSpec *other)
|
||||||
|
{
|
||||||
|
if (other)
|
||||||
|
cover("kernel.rtlil.sigspec.remove_other");
|
||||||
|
else
|
||||||
|
cover("kernel.rtlil.sigspec.remove");
|
||||||
|
|
||||||
|
unpack();
|
||||||
|
|
||||||
|
if (other != NULL) {
|
||||||
|
log_assert(width_ == other->width_);
|
||||||
|
other->unpack();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = GetSize(bits_) - 1; i >= 0; i--) {
|
||||||
|
if (bits_[i].wire != NULL && pattern.count(bits_[i].wire)) {
|
||||||
|
bits_.erase(bits_.begin() + i);
|
||||||
|
width_--;
|
||||||
|
if (other != NULL) {
|
||||||
|
other->bits_.erase(other->bits_.begin() + i);
|
||||||
|
other->width_--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
check();
|
||||||
|
}
|
||||||
|
|
||||||
RTLIL::SigSpec RTLIL::SigSpec::extract(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec *other) const
|
RTLIL::SigSpec RTLIL::SigSpec::extract(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec *other) const
|
||||||
{
|
{
|
||||||
if (other)
|
if (other)
|
||||||
|
@ -4377,6 +4432,9 @@ void RTLIL::SigSpec::remove(int offset, int length)
|
||||||
|
|
||||||
RTLIL::SigSpec RTLIL::SigSpec::extract(int offset, int length) const
|
RTLIL::SigSpec RTLIL::SigSpec::extract(int offset, int length) const
|
||||||
{
|
{
|
||||||
|
log_assert(offset >= 0);
|
||||||
|
log_assert(length >= 0);
|
||||||
|
log_assert(offset + length <= width_);
|
||||||
unpack();
|
unpack();
|
||||||
cover("kernel.rtlil.sigspec.extract_pos");
|
cover("kernel.rtlil.sigspec.extract_pos");
|
||||||
return std::vector<RTLIL::SigBit>(bits_.begin() + offset, bits_.begin() + offset + length);
|
return std::vector<RTLIL::SigBit>(bits_.begin() + offset, bits_.begin() + offset + length);
|
||||||
|
|
|
@ -712,7 +712,7 @@ struct RTLIL::Const
|
||||||
inline unsigned int hash() const {
|
inline unsigned int hash() const {
|
||||||
unsigned int h = mkhash_init;
|
unsigned int h = mkhash_init;
|
||||||
for (auto b : bits)
|
for (auto b : bits)
|
||||||
mkhash(h, b);
|
h = mkhash(h, b);
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -924,6 +924,7 @@ public:
|
||||||
void remove(const pool<RTLIL::SigBit> &pattern, RTLIL::SigSpec *other) const;
|
void remove(const pool<RTLIL::SigBit> &pattern, RTLIL::SigSpec *other) const;
|
||||||
void remove2(const pool<RTLIL::SigBit> &pattern, RTLIL::SigSpec *other);
|
void remove2(const pool<RTLIL::SigBit> &pattern, RTLIL::SigSpec *other);
|
||||||
void remove2(const std::set<RTLIL::SigBit> &pattern, RTLIL::SigSpec *other);
|
void remove2(const std::set<RTLIL::SigBit> &pattern, RTLIL::SigSpec *other);
|
||||||
|
void remove2(const pool<RTLIL::Wire*> &pattern, RTLIL::SigSpec *other);
|
||||||
|
|
||||||
void remove(int offset, int length = 1);
|
void remove(int offset, int length = 1);
|
||||||
void remove_const();
|
void remove_const();
|
||||||
|
|
|
@ -1379,6 +1379,11 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cell->type == ID($scopeinfo))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Unsupported internal cell types: $pow $fsm $mem*
|
// Unsupported internal cell types: $pow $fsm $mem*
|
||||||
// .. and all sequential cells with asynchronous inputs
|
// .. and all sequential cells with asynchronous inputs
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -55,7 +55,7 @@
|
||||||
# include <glob.h>
|
# include <glob.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __FreeBSD__
|
#if defined(__FreeBSD__) || defined(__NetBSD__)
|
||||||
# include <sys/sysctl.h>
|
# include <sys/sysctl.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -138,27 +138,11 @@ void yosys_banner()
|
||||||
{
|
{
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" /----------------------------------------------------------------------------\\\n");
|
log(" /----------------------------------------------------------------------------\\\n");
|
||||||
log(" | |\n");
|
|
||||||
log(" | yosys -- Yosys Open SYnthesis Suite |\n");
|
log(" | yosys -- Yosys Open SYnthesis Suite |\n");
|
||||||
log(" | |\n");
|
log(" | Copyright (C) 2012 - 2024 Claire Xenia Wolf <claire@yosyshq.com> |\n");
|
||||||
log(" | Copyright (C) 2012 - 2020 Claire Xenia Wolf <claire@yosyshq.com> |\n");
|
log(" | Distributed under an ISC-like license, type \"license\" to see terms |\n");
|
||||||
log(" | |\n");
|
|
||||||
log(" | Permission to use, copy, modify, and/or distribute this software for any |\n");
|
|
||||||
log(" | purpose with or without fee is hereby granted, provided that the above |\n");
|
|
||||||
log(" | copyright notice and this permission notice appear in all copies. |\n");
|
|
||||||
log(" | |\n");
|
|
||||||
log(" | THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |\n");
|
|
||||||
log(" | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |\n");
|
|
||||||
log(" | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |\n");
|
|
||||||
log(" | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |\n");
|
|
||||||
log(" | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |\n");
|
|
||||||
log(" | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |\n");
|
|
||||||
log(" | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |\n");
|
|
||||||
log(" | |\n");
|
|
||||||
log(" \\----------------------------------------------------------------------------/\n");
|
log(" \\----------------------------------------------------------------------------/\n");
|
||||||
log("\n");
|
|
||||||
log(" %s\n", yosys_version_str);
|
log(" %s\n", yosys_version_str);
|
||||||
log("\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int ceil_log2(int x)
|
int ceil_log2(int x)
|
||||||
|
@ -436,6 +420,25 @@ std::string make_temp_dir(std::string template_str)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool check_directory_exists(const std::string& dirname)
|
||||||
|
{
|
||||||
|
#if defined(_WIN32)
|
||||||
|
struct _stat info;
|
||||||
|
if (_stat(dirname.c_str(), &info) != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (info.st_mode & _S_IFDIR) != 0;
|
||||||
|
#else
|
||||||
|
struct stat info;
|
||||||
|
if (stat(dirname.c_str(), &info) != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (info.st_mode & S_IFDIR) != 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
bool check_file_exists(std::string filename, bool)
|
bool check_file_exists(std::string filename, bool)
|
||||||
{
|
{
|
||||||
|
@ -481,6 +484,48 @@ void remove_directory(std::string dirname)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool create_directory(const std::string& dirname)
|
||||||
|
{
|
||||||
|
#if defined(_WIN32)
|
||||||
|
int ret = _mkdir(dirname.c_str());
|
||||||
|
#else
|
||||||
|
mode_t mode = 0755;
|
||||||
|
int ret = mkdir(dirname.c_str(), mode);
|
||||||
|
#endif
|
||||||
|
if (ret == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
switch (errno)
|
||||||
|
{
|
||||||
|
case ENOENT:
|
||||||
|
// parent didn't exist, try to create it
|
||||||
|
{
|
||||||
|
std::string::size_type pos = dirname.find_last_of('/');
|
||||||
|
if (pos == std::string::npos)
|
||||||
|
#if defined(_WIN32)
|
||||||
|
pos = dirname.find_last_of('\\');
|
||||||
|
if (pos == std::string::npos)
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
if (!create_directory( dirname.substr(0, pos) ))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// now, try to create again
|
||||||
|
#if defined(_WIN32)
|
||||||
|
return 0 == _mkdir(dirname.c_str());
|
||||||
|
#else
|
||||||
|
return 0 == mkdir(dirname.c_str(), mode);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
case EEXIST:
|
||||||
|
// done!
|
||||||
|
return check_directory_exists(dirname);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::string escape_filename_spaces(const std::string& filename)
|
std::string escape_filename_spaces(const std::string& filename)
|
||||||
{
|
{
|
||||||
std::string out;
|
std::string out;
|
||||||
|
@ -781,10 +826,10 @@ static int tcl_yosys_cmd(ClientData, Tcl_Interp *interp, int argc, const char *a
|
||||||
|
|
||||||
int yosys_tcl_iterp_init(Tcl_Interp *interp)
|
int yosys_tcl_iterp_init(Tcl_Interp *interp)
|
||||||
{
|
{
|
||||||
if (Tcl_Init(interp)!=TCL_OK)
|
if (Tcl_Init(interp)!=TCL_OK)
|
||||||
log_warning("Tcl_Init() call failed - %s\n",Tcl_ErrnoMsg(Tcl_GetErrno()));
|
log_warning("Tcl_Init() call failed - %s\n",Tcl_ErrnoMsg(Tcl_GetErrno()));
|
||||||
Tcl_CreateCommand(interp, "yosys", tcl_yosys_cmd, NULL, NULL);
|
Tcl_CreateCommand(interp, "yosys", tcl_yosys_cmd, NULL, NULL);
|
||||||
return TCL_OK ;
|
return TCL_OK ;
|
||||||
}
|
}
|
||||||
|
|
||||||
void yosys_tcl_activate_repl()
|
void yosys_tcl_activate_repl()
|
||||||
|
@ -856,10 +901,14 @@ std::string proc_self_dirname()
|
||||||
buflen--;
|
buflen--;
|
||||||
return std::string(path, buflen);
|
return std::string(path, buflen);
|
||||||
}
|
}
|
||||||
#elif defined(__FreeBSD__)
|
#elif defined(__FreeBSD__) || defined(__NetBSD__)
|
||||||
std::string proc_self_dirname()
|
std::string proc_self_dirname()
|
||||||
{
|
{
|
||||||
|
#ifdef __NetBSD__
|
||||||
|
int mib[4] = {CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_PATHNAME};
|
||||||
|
#else
|
||||||
int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
|
int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
|
||||||
|
#endif
|
||||||
size_t buflen;
|
size_t buflen;
|
||||||
char *buffer;
|
char *buffer;
|
||||||
std::string path;
|
std::string path;
|
||||||
|
|
|
@ -66,6 +66,8 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
#ifdef WITH_PYTHON
|
#ifdef WITH_PYTHON
|
||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
|
@ -341,8 +343,10 @@ std::string get_base_tmpdir();
|
||||||
std::string make_temp_file(std::string template_str = get_base_tmpdir() + "/yosys_XXXXXX");
|
std::string make_temp_file(std::string template_str = get_base_tmpdir() + "/yosys_XXXXXX");
|
||||||
std::string make_temp_dir(std::string template_str = get_base_tmpdir() + "/yosys_XXXXXX");
|
std::string make_temp_dir(std::string template_str = get_base_tmpdir() + "/yosys_XXXXXX");
|
||||||
bool check_file_exists(std::string filename, bool is_exec = false);
|
bool check_file_exists(std::string filename, bool is_exec = false);
|
||||||
|
bool check_directory_exists(const std::string& dirname);
|
||||||
bool is_absolute_path(std::string filename);
|
bool is_absolute_path(std::string filename);
|
||||||
void remove_directory(std::string dirname);
|
void remove_directory(std::string dirname);
|
||||||
|
bool create_directory(const std::string& dirname);
|
||||||
std::string escape_filename_spaces(const std::string& filename);
|
std::string escape_filename_spaces(const std::string& filename);
|
||||||
|
|
||||||
template<typename T> int GetSize(const T &obj) { return obj.size(); }
|
template<typename T> int GetSize(const T &obj) { return obj.size(); }
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
|
|
||||||
CC = clang
|
CC = clang
|
||||||
CXX = clang
|
CXX = clang++
|
||||||
CXXFLAGS = -MD -Wall -Wextra -ggdb
|
CXXFLAGS = -MD -Wall -Wextra -ggdb
|
||||||
CXXFLAGS += -std=c++11 -O0
|
CXXFLAGS += -std=c++11 -O0
|
||||||
LDLIBS = ../minisat/Options.cc ../minisat/SimpSolver.cc ../minisat/Solver.cc ../minisat/System.cc -lm -lstdc++
|
LIBS = ../minisat/Options.cc ../minisat/SimpSolver.cc ../minisat/Solver.cc ../minisat/System.cc -lm -lstdc++
|
||||||
|
|
||||||
|
|
||||||
all: demo_vec demo_bit demo_cmp testbench puzzle3d
|
all: demo_vec demo_bit demo_cmp testbench puzzle3d
|
||||||
|
@ -27,4 +27,3 @@ clean:
|
||||||
.PHONY: all test clean
|
.PHONY: all test clean
|
||||||
|
|
||||||
-include *.d
|
-include *.d
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,9 @@ CONFIG := clang-debug
|
||||||
# CONFIG := release
|
# CONFIG := release
|
||||||
|
|
||||||
CC = clang
|
CC = clang
|
||||||
CXX = clang
|
CXX = clang++
|
||||||
CXXFLAGS = -MD -Wall -Wextra -ggdb
|
CXXFLAGS = -MD -Wall -Wextra -ggdb
|
||||||
LDLIBS = -lstdc++
|
LIBS = -lstdc++
|
||||||
|
|
||||||
ifeq ($(CONFIG),clang-debug)
|
ifeq ($(CONFIG),clang-debug)
|
||||||
CXXFLAGS += -std=c++11 -O0
|
CXXFLAGS += -std=c++11 -O0
|
||||||
|
@ -15,19 +15,19 @@ endif
|
||||||
|
|
||||||
ifeq ($(CONFIG),gcc-debug)
|
ifeq ($(CONFIG),gcc-debug)
|
||||||
CC = gcc
|
CC = gcc
|
||||||
CXX = gcc
|
CXX = g++
|
||||||
CXXFLAGS += -std=gnu++0x -O0
|
CXXFLAGS += -std=gnu++0x -O0
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(CONFIG),profile)
|
ifeq ($(CONFIG),profile)
|
||||||
CC = gcc
|
CC = gcc
|
||||||
CXX = gcc
|
CXX = g++
|
||||||
CXXFLAGS += -std=gnu++0x -Os -DNDEBUG
|
CXXFLAGS += -std=gnu++0x -Os -DNDEBUG
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(CONFIG),release)
|
ifeq ($(CONFIG),release)
|
||||||
CC = gcc
|
CC = gcc
|
||||||
CXX = gcc
|
CXX = g++
|
||||||
CXXFLAGS += -std=gnu++0x -march=native -O3 -DNDEBUG
|
CXXFLAGS += -std=gnu++0x -march=native -O3 -DNDEBUG
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -50,4 +50,3 @@ clean:
|
||||||
.PHONY: all test clean
|
.PHONY: all test clean
|
||||||
|
|
||||||
-include *.d
|
-include *.d
|
||||||
|
|
||||||
|
|
|
@ -1257,6 +1257,7 @@ class WFunction:
|
||||||
func.is_static = False
|
func.is_static = False
|
||||||
func.is_inline = False
|
func.is_inline = False
|
||||||
func.is_virtual = False
|
func.is_virtual = False
|
||||||
|
func.is_const = False
|
||||||
func.ret_attr_type = attr_types.default
|
func.ret_attr_type = attr_types.default
|
||||||
func.is_operator = False
|
func.is_operator = False
|
||||||
func.member_of = None
|
func.member_of = None
|
||||||
|
@ -1334,6 +1335,11 @@ class WFunction:
|
||||||
found = find_closing(str_def, "(", ")")
|
found = find_closing(str_def, "(", ")")
|
||||||
if found == -1:
|
if found == -1:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
post_qualifiers = str_def[found + 1:].lstrip().replace("{", " {") + " "
|
||||||
|
if post_qualifiers.startswith("const "):
|
||||||
|
func.is_const = True
|
||||||
|
|
||||||
str_def = str_def[0:found]
|
str_def = str_def[0:found]
|
||||||
if func.name in blacklist_methods:
|
if func.name in blacklist_methods:
|
||||||
return None
|
return None
|
||||||
|
@ -1379,6 +1385,12 @@ class WFunction:
|
||||||
def gen_alias(self):
|
def gen_alias(self):
|
||||||
self.alias = self.mangled_name
|
self.alias = self.mangled_name
|
||||||
|
|
||||||
|
def gen_post_qualifiers(self, derived=False):
|
||||||
|
if self.member_of != None and self.member_of.link_type == link_types.derive and self.is_virtual and derived:
|
||||||
|
# we drop the qualifiers when deriving callbacks to be implemented in Python
|
||||||
|
return ''
|
||||||
|
return ' const' if self.is_const else ''
|
||||||
|
|
||||||
def gen_decl(self):
|
def gen_decl(self):
|
||||||
if self.duplicate:
|
if self.duplicate:
|
||||||
return ""
|
return ""
|
||||||
|
@ -1392,7 +1404,7 @@ class WFunction:
|
||||||
text += ", "
|
text += ", "
|
||||||
if len(self.args) > 0:
|
if len(self.args) > 0:
|
||||||
text = text[:-2]
|
text = text[:-2]
|
||||||
text += ");\n"
|
text += f"){self.gen_post_qualifiers()};\n"
|
||||||
return text
|
return text
|
||||||
|
|
||||||
def gen_decl_virtual(self):
|
def gen_decl_virtual(self):
|
||||||
|
@ -1411,12 +1423,18 @@ class WFunction:
|
||||||
if len(self.args) > 0:
|
if len(self.args) > 0:
|
||||||
text = text[:-2]
|
text = text[:-2]
|
||||||
text += ")"
|
text += ")"
|
||||||
if len(self.args) == 0:
|
if len(self.args) == 0 and self.ret_type.name == "void":
|
||||||
text += "{}"
|
text += "{}"
|
||||||
else:
|
else:
|
||||||
text += "\n\t\t{"
|
text += "\n\t\t{"
|
||||||
for arg in self.args:
|
for arg in self.args:
|
||||||
text += "\n\t\t\t(void)" + arg.gen_varname() + ";"
|
text += "\n\t\t\t(void)" + arg.gen_varname() + ";"
|
||||||
|
if self.ret_type.name == "void":
|
||||||
|
pass
|
||||||
|
elif self.ret_type.name == "bool":
|
||||||
|
text += "\n\t\t\treturn false;"
|
||||||
|
else:
|
||||||
|
raise NotImplementedError(self.ret_type.name)
|
||||||
text += "\n\t\t}\n"
|
text += "\n\t\t}\n"
|
||||||
text += "\n\t\tvirtual "
|
text += "\n\t\tvirtual "
|
||||||
if self.is_static:
|
if self.is_static:
|
||||||
|
@ -1427,7 +1445,7 @@ class WFunction:
|
||||||
text += ", "
|
text += ", "
|
||||||
if len(self.args) > 0:
|
if len(self.args) > 0:
|
||||||
text = text[:-2]
|
text = text[:-2]
|
||||||
text += ") override;\n"
|
text += f"){self.gen_post_qualifiers()} override;\n"
|
||||||
return text
|
return text
|
||||||
|
|
||||||
def gen_decl_hash_py(self):
|
def gen_decl_hash_py(self):
|
||||||
|
@ -1452,7 +1470,7 @@ class WFunction:
|
||||||
text += ", "
|
text += ", "
|
||||||
if len(self.args) > 0:
|
if len(self.args) > 0:
|
||||||
text = text[:-2]
|
text = text[:-2]
|
||||||
text +=")\n\t{"
|
text += f"){self.gen_post_qualifiers()}\n\t{{"
|
||||||
for arg in self.args:
|
for arg in self.args:
|
||||||
text += arg.gen_translation()
|
text += arg.gen_translation()
|
||||||
text += "\n\t\t"
|
text += "\n\t\t"
|
||||||
|
@ -1507,16 +1525,17 @@ class WFunction:
|
||||||
text += ", "
|
text += ", "
|
||||||
if len(self.args) > 0:
|
if len(self.args) > 0:
|
||||||
text = text[:-2]
|
text = text[:-2]
|
||||||
text += ")\n\t{"
|
text += f"){self.gen_post_qualifiers()}\n\t{{"
|
||||||
for arg in self.args:
|
for arg in self.args:
|
||||||
text += arg.gen_translation_cpp()
|
text += arg.gen_translation_cpp()
|
||||||
text += "\n\t\t"
|
return_stmt = "return " if self.ret_type.name != "void" else ""
|
||||||
|
text += f"\n\t\t{return_stmt}"
|
||||||
if self.member_of == None:
|
if self.member_of == None:
|
||||||
text += "::" + self.namespace + "::" + self.alias + "("
|
text += "::" + self.namespace + "::" + self.alias + "("
|
||||||
elif self.is_static:
|
elif self.is_static:
|
||||||
text += self.member_of.namespace + "::" + self.member_of.name + "::" + self.name + "("
|
text += self.member_of.namespace + "::" + self.member_of.name + "::" + self.name + "("
|
||||||
else:
|
else:
|
||||||
text += "py_" + self.alias + "("
|
text += f"const_cast<{self.member_of.name}*>(this)->py_" + self.alias + "("
|
||||||
for arg in self.args:
|
for arg in self.args:
|
||||||
text += arg.gen_call_cpp() + ", "
|
text += arg.gen_call_cpp() + ", "
|
||||||
if len(self.args) > 0:
|
if len(self.args) > 0:
|
||||||
|
@ -1547,11 +1566,13 @@ class WFunction:
|
||||||
call_string = call_string[0:-2]
|
call_string = call_string[0:-2]
|
||||||
call_string += ");"
|
call_string += ");"
|
||||||
|
|
||||||
|
return_stmt = "return " if self.ret_type.name != "void" else ""
|
||||||
|
|
||||||
text += ")\n\t\t{"
|
text += ")\n\t\t{"
|
||||||
text += "\n\t\t\tif(boost::python::override py_" + self.alias + " = this->get_override(\"py_" + self.alias + "\"))"
|
text += "\n\t\t\tif (boost::python::override py_" + self.alias + " = this->get_override(\"py_" + self.alias + "\"))"
|
||||||
text += "\n\t\t\t\t" + call_string
|
text += f"\n\t\t\t\t{return_stmt}" + call_string
|
||||||
text += "\n\t\t\telse"
|
text += "\n\t\t\telse"
|
||||||
text += "\n\t\t\t\t" + self.member_of.name + "::" + call_string
|
text += f"\n\t\t\t\t{return_stmt}" + self.member_of.name + "::" + call_string
|
||||||
text += "\n\t\t}"
|
text += "\n\t\t}"
|
||||||
|
|
||||||
text += "\n\n\t\t" + self.ret_type.gen_text() + " default_py_" + self.alias + "("
|
text += "\n\n\t\t" + self.ret_type.gen_text() + " default_py_" + self.alias + "("
|
||||||
|
@ -1559,8 +1580,8 @@ class WFunction:
|
||||||
text += arg.gen_listitem() + ", "
|
text += arg.gen_listitem() + ", "
|
||||||
if len(self.args) > 0:
|
if len(self.args) > 0:
|
||||||
text = text[:-2]
|
text = text[:-2]
|
||||||
text += ")\n\t\t{"
|
text += f")\n\t\t{{"
|
||||||
text += "\n\t\t\tthis->" + self.member_of.name + "::" + call_string
|
text += f"\n\t\t\t{return_stmt}this->" + self.member_of.name + "::" + call_string
|
||||||
text += "\n\t\t}"
|
text += "\n\t\t}"
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
@ -1584,9 +1605,9 @@ class WFunction:
|
||||||
for a in self.args:
|
for a in self.args:
|
||||||
text += a.gen_listitem_hash() + ", "
|
text += a.gen_listitem_hash() + ", "
|
||||||
if len(self.args) > 0:
|
if len(self.args) > 0:
|
||||||
text = text[0:-2] + ")>"
|
text = text[0:-2] + f"){self.gen_post_qualifiers(True)}>"
|
||||||
else:
|
else:
|
||||||
text += "void)>"
|
text += f"void){self.gen_post_qualifiers(True)}>"
|
||||||
|
|
||||||
if self.is_operator:
|
if self.is_operator:
|
||||||
text += "(\"" + wrappable_operators[self.name.replace("operator","")] + "\""
|
text += "(\"" + wrappable_operators[self.name.replace("operator","")] + "\""
|
||||||
|
|
|
@ -9,8 +9,10 @@ help() {
|
||||||
echo "Replacement args:"
|
echo "Replacement args:"
|
||||||
echo " --cxx @CXX@"
|
echo " --cxx @CXX@"
|
||||||
echo " --cxxflags $( echo '@CXXFLAGS@' | fmt -w60 | sed ':a;N;$!ba;s/\n/ \\\n /g' )"
|
echo " --cxxflags $( echo '@CXXFLAGS@' | fmt -w60 | sed ':a;N;$!ba;s/\n/ \\\n /g' )"
|
||||||
echo " --ldflags @LDFLAGS@"
|
echo " --linkflags @LINKFLAGS@"
|
||||||
echo " --ldlibs @LDLIBS@"
|
echo " --ldflags (alias of --linkflags)"
|
||||||
|
echo " --libs @LIBS@"
|
||||||
|
echo " --ldlibs (alias of --libs)"
|
||||||
echo " --bindir @BINDIR@"
|
echo " --bindir @BINDIR@"
|
||||||
echo " --datdir @DATDIR@"
|
echo " --datdir @DATDIR@"
|
||||||
echo ""
|
echo ""
|
||||||
|
@ -18,7 +20,7 @@ help() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "Use --exec to call a command instead of generating output. Example usage:"
|
echo "Use --exec to call a command instead of generating output. Example usage:"
|
||||||
echo ""
|
echo ""
|
||||||
echo " $0 --exec --cxx --cxxflags --ldflags -o plugin.so -shared plugin.cc --ldlibs"
|
echo " $0 --exec --cxx --cxxflags --ldflags -o plugin.so -shared plugin.cc --libs"
|
||||||
echo ""
|
echo ""
|
||||||
echo "The above command can be abbreviated as:"
|
echo "The above command can be abbreviated as:"
|
||||||
echo ""
|
echo ""
|
||||||
|
@ -44,7 +46,7 @@ fi
|
||||||
|
|
||||||
if [ "$1" == "--build" ]; then
|
if [ "$1" == "--build" ]; then
|
||||||
modname="$2"; shift 2
|
modname="$2"; shift 2
|
||||||
set -- --exec --cxx --cxxflags --ldflags -o "$modname" -shared "$@" --ldlibs
|
set -- --exec --cxx --cxxflags --ldflags -o "$modname" -shared "$@" --libs
|
||||||
fi
|
fi
|
||||||
|
|
||||||
prefix="--"
|
prefix="--"
|
||||||
|
@ -63,10 +65,14 @@ for opt; do
|
||||||
tokens=( "${tokens[@]}" @CXX@ ) ;;
|
tokens=( "${tokens[@]}" @CXX@ ) ;;
|
||||||
"$prefix"cxxflags)
|
"$prefix"cxxflags)
|
||||||
tokens=( "${tokens[@]}" @CXXFLAGS@ ) ;;
|
tokens=( "${tokens[@]}" @CXXFLAGS@ ) ;;
|
||||||
|
"$prefix"linkflags)
|
||||||
|
tokens=( "${tokens[@]}" @LINKFLAGS@ ) ;;
|
||||||
|
"$prefix"libs)
|
||||||
|
tokens=( "${tokens[@]}" @LIBS@ ) ;;
|
||||||
"$prefix"ldflags)
|
"$prefix"ldflags)
|
||||||
tokens=( "${tokens[@]}" @LDFLAGS@ ) ;;
|
tokens=( "${tokens[@]}" @LINKFLAGS@ ) ;;
|
||||||
"$prefix"ldlibs)
|
"$prefix"ldlibs)
|
||||||
tokens=( "${tokens[@]}" @LDLIBS@ ) ;;
|
tokens=( "${tokens[@]}" @LIBS@ ) ;;
|
||||||
"$prefix"bindir)
|
"$prefix"bindir)
|
||||||
tokens=( "${tokens[@]}" '@BINDIR@' ) ;;
|
tokens=( "${tokens[@]}" '@BINDIR@' ) ;;
|
||||||
"$prefix"datdir)
|
"$prefix"datdir)
|
||||||
|
@ -104,4 +110,3 @@ fi
|
||||||
|
|
||||||
echo "${tokens[@]}"
|
echo "${tokens[@]}"
|
||||||
exit 0
|
exit 0
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,52 @@
|
||||||
USING_YOSYS_NAMESPACE
|
USING_YOSYS_NAMESPACE
|
||||||
PRIVATE_NAMESPACE_BEGIN
|
PRIVATE_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
static RTLIL::IdString formal_flavor(RTLIL::Cell *cell)
|
||||||
|
{
|
||||||
|
if (cell->type != ID($check))
|
||||||
|
return cell->type;
|
||||||
|
|
||||||
|
std::string flavor_param = cell->getParam(ID(FLAVOR)).decode_string();
|
||||||
|
if (flavor_param == "assert")
|
||||||
|
return ID($assert);
|
||||||
|
else if (flavor_param == "assume")
|
||||||
|
return ID($assume);
|
||||||
|
else if (flavor_param == "cover")
|
||||||
|
return ID($cover);
|
||||||
|
else if (flavor_param == "live")
|
||||||
|
return ID($live);
|
||||||
|
else if (flavor_param == "fair")
|
||||||
|
return ID($fair);
|
||||||
|
else
|
||||||
|
log_abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_formal_flavor(RTLIL::Cell *cell, RTLIL::IdString flavor)
|
||||||
|
{
|
||||||
|
if (cell->type != ID($check)) {
|
||||||
|
cell->type = flavor;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flavor == ID($assert))
|
||||||
|
cell->setParam(ID(FLAVOR), std::string("assert"));
|
||||||
|
else if (flavor == ID($assume))
|
||||||
|
cell->setParam(ID(FLAVOR), std::string("assume"));
|
||||||
|
else if (flavor == ID($cover))
|
||||||
|
cell->setParam(ID(FLAVOR), std::string("cover"));
|
||||||
|
else if (flavor == ID($live))
|
||||||
|
cell->setParam(ID(FLAVOR), std::string("live"));
|
||||||
|
else if (flavor == ID($fair))
|
||||||
|
cell->setParam(ID(FLAVOR), std::string("fair"));
|
||||||
|
else
|
||||||
|
log_abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_triggered_check_cell(RTLIL::Cell * cell)
|
||||||
|
{
|
||||||
|
return cell->type == ID($check) && cell->getParam(ID(TRG_ENABLE)).as_bool();
|
||||||
|
}
|
||||||
|
|
||||||
struct ChformalPass : public Pass {
|
struct ChformalPass : public Pass {
|
||||||
ChformalPass() : Pass("chformal", "change formal constraints of the design") { }
|
ChformalPass() : Pass("chformal", "change formal constraints of the design") { }
|
||||||
void help() override
|
void help() override
|
||||||
|
@ -41,13 +87,18 @@ struct ChformalPass : public Pass {
|
||||||
log(" -fair $fair cells, representing assume(s_eventually ...)\n");
|
log(" -fair $fair cells, representing assume(s_eventually ...)\n");
|
||||||
log(" -cover $cover cells, representing cover() statements\n");
|
log(" -cover $cover cells, representing cover() statements\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
|
log(" Additionally chformal will operate on $check cells corresponding to the\n");
|
||||||
|
log(" selected constraint types.\n");
|
||||||
|
log("\n");
|
||||||
log("Exactly one of the following modes must be specified:\n");
|
log("Exactly one of the following modes must be specified:\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" -remove\n");
|
log(" -remove\n");
|
||||||
log(" remove the cells and thus constraints from the design\n");
|
log(" remove the cells and thus constraints from the design\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" -early\n");
|
log(" -early\n");
|
||||||
log(" bypass FFs that only delay the activation of a constraint\n");
|
log(" bypass FFs that only delay the activation of a constraint. When inputs\n");
|
||||||
|
log(" of the bypassed FFs do not remain stable between clock edges, this may\n");
|
||||||
|
log(" result in unexpected behavior.\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" -delay <N>\n");
|
log(" -delay <N>\n");
|
||||||
log(" delay activation of the constraint by <N> clock cycles\n");
|
log(" delay activation of the constraint by <N> clock cycles\n");
|
||||||
|
@ -69,6 +120,11 @@ struct ChformalPass : public Pass {
|
||||||
log(" -fair2live\n");
|
log(" -fair2live\n");
|
||||||
log(" change the roles of cells as indicated. these options can be combined\n");
|
log(" change the roles of cells as indicated. these options can be combined\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
|
log(" -lower\n");
|
||||||
|
log(" convert each $check cell into an $assert, $assume, $live, $fair or\n");
|
||||||
|
log(" $cover cell. If the $check cell contains a message, also produce a\n");
|
||||||
|
log(" $print cell.\n");
|
||||||
|
log("\n");
|
||||||
}
|
}
|
||||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||||
{
|
{
|
||||||
|
@ -146,6 +202,10 @@ struct ChformalPass : public Pass {
|
||||||
mode = 'c';
|
mode = 'c';
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (mode == 0 && args[argidx] == "-lower") {
|
||||||
|
mode = 'l';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
extra_args(args, argidx, design);
|
extra_args(args, argidx, design);
|
||||||
|
@ -166,7 +226,7 @@ struct ChformalPass : public Pass {
|
||||||
vector<Cell*> constr_cells;
|
vector<Cell*> constr_cells;
|
||||||
|
|
||||||
for (auto cell : module->selected_cells())
|
for (auto cell : module->selected_cells())
|
||||||
if (constr_types.count(cell->type))
|
if (constr_types.count(formal_flavor(cell)))
|
||||||
constr_cells.push_back(cell);
|
constr_cells.push_back(cell);
|
||||||
|
|
||||||
if (mode == 'r')
|
if (mode == 'r')
|
||||||
|
@ -216,6 +276,18 @@ struct ChformalPass : public Pass {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto cell : constr_cells)
|
for (auto cell : constr_cells)
|
||||||
|
{
|
||||||
|
if (is_triggered_check_cell(cell)) {
|
||||||
|
if (cell->getParam(ID::TRG_WIDTH).as_int() != 1)
|
||||||
|
continue;
|
||||||
|
cell->setPort(ID::TRG, SigSpec());
|
||||||
|
cell->setParam(ID::TRG_ENABLE, false);
|
||||||
|
cell->setParam(ID::TRG_WIDTH, 0);
|
||||||
|
cell->setParam(ID::TRG_POLARITY, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
IdString flavor = formal_flavor(cell);
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
SigSpec A = sigmap(cell->getPort(ID::A));
|
SigSpec A = sigmap(cell->getPort(ID::A));
|
||||||
|
@ -225,8 +297,8 @@ struct ChformalPass : public Pass {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (!init_zero.count(EN)) {
|
if (!init_zero.count(EN)) {
|
||||||
if (cell->type == ID($cover)) break;
|
if (flavor == ID($cover)) break;
|
||||||
if (cell->type.in(ID($assert), ID($assume)) && !init_one.count(A)) break;
|
if (flavor.in(ID($assert), ID($assume)) && !init_one.count(A)) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto &A_map = ffmap.at(A);
|
const auto &A_map = ffmap.at(A);
|
||||||
|
@ -238,25 +310,31 @@ struct ChformalPass : public Pass {
|
||||||
cell->setPort(ID::A, A_map.first);
|
cell->setPort(ID::A, A_map.first);
|
||||||
cell->setPort(ID::EN, EN_map.first);
|
cell->setPort(ID::EN, EN_map.first);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
if (mode == 'd')
|
if (mode == 'd')
|
||||||
{
|
{
|
||||||
for (auto cell : constr_cells)
|
for (auto cell : constr_cells)
|
||||||
for (int i = 0; i < mode_arg; i++)
|
|
||||||
{
|
{
|
||||||
SigSpec orig_a = cell->getPort(ID::A);
|
if (is_triggered_check_cell(cell))
|
||||||
SigSpec orig_en = cell->getPort(ID::EN);
|
log_error("Cannot delay edge triggered $check cell %s, run async2sync or clk2fflogic first.\n", log_id(cell));
|
||||||
|
|
||||||
Wire *new_a = module->addWire(NEW_ID);
|
for (int i = 0; i < mode_arg; i++)
|
||||||
Wire *new_en = module->addWire(NEW_ID);
|
{
|
||||||
new_en->attributes[ID::init] = State::S0;
|
SigSpec orig_a = cell->getPort(ID::A);
|
||||||
|
SigSpec orig_en = cell->getPort(ID::EN);
|
||||||
|
|
||||||
module->addFf(NEW_ID, orig_a, new_a);
|
Wire *new_a = module->addWire(NEW_ID);
|
||||||
module->addFf(NEW_ID, orig_en, new_en);
|
Wire *new_en = module->addWire(NEW_ID);
|
||||||
|
new_en->attributes[ID::init] = State::S0;
|
||||||
|
|
||||||
cell->setPort(ID::A, new_a);
|
module->addFf(NEW_ID, orig_a, new_a);
|
||||||
cell->setPort(ID::EN, new_en);
|
module->addFf(NEW_ID, orig_en, new_en);
|
||||||
|
|
||||||
|
cell->setPort(ID::A, new_a);
|
||||||
|
cell->setPort(ID::EN, new_en);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -278,21 +356,76 @@ struct ChformalPass : public Pass {
|
||||||
if (mode =='p')
|
if (mode =='p')
|
||||||
{
|
{
|
||||||
for (auto cell : constr_cells)
|
for (auto cell : constr_cells)
|
||||||
module->addCover(NEW_ID_SUFFIX("coverenable"),
|
{
|
||||||
cell->getPort(ID::EN), State::S1, cell->get_src_attribute());
|
if (cell->type == ID($check)) {
|
||||||
|
Cell *cover = module->addCell(NEW_ID_SUFFIX("coverenable"), ID($check));
|
||||||
|
cover->attributes = cell->attributes;
|
||||||
|
cover->parameters = cell->parameters;
|
||||||
|
cover->setParam(ID(FLAVOR), Const("cover"));
|
||||||
|
|
||||||
|
for (auto const &conn : cell->connections())
|
||||||
|
if (!conn.first.in(ID::A, ID::EN))
|
||||||
|
cover->setPort(conn.first, conn.second);
|
||||||
|
cover->setPort(ID::A, cell->getPort(ID::EN));
|
||||||
|
cover->setPort(ID::EN, State::S1);
|
||||||
|
} else {
|
||||||
|
module->addCover(NEW_ID_SUFFIX("coverenable"),
|
||||||
|
cell->getPort(ID::EN), State::S1, cell->get_src_attribute());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
if (mode == 'c')
|
if (mode == 'c')
|
||||||
{
|
{
|
||||||
for (auto cell : constr_cells)
|
for (auto cell : constr_cells) {
|
||||||
if (assert2assume && cell->type == ID($assert))
|
IdString flavor = formal_flavor(cell);
|
||||||
cell->type = ID($assume);
|
if (assert2assume && flavor == ID($assert))
|
||||||
else if (assume2assert && cell->type == ID($assume))
|
set_formal_flavor(cell, ID($assume));
|
||||||
cell->type = ID($assert);
|
else if (assume2assert && flavor == ID($assume))
|
||||||
else if (live2fair && cell->type == ID($live))
|
set_formal_flavor(cell, ID($assert));
|
||||||
cell->type = ID($fair);
|
else if (live2fair && flavor == ID($live))
|
||||||
else if (fair2live && cell->type == ID($fair))
|
set_formal_flavor(cell, ID($fair));
|
||||||
cell->type = ID($live);
|
else if (fair2live && flavor == ID($fair))
|
||||||
|
set_formal_flavor(cell, ID($live));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (mode == 'l')
|
||||||
|
{
|
||||||
|
for (auto cell : constr_cells) {
|
||||||
|
if (cell->type != ID($check))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (is_triggered_check_cell(cell))
|
||||||
|
log_error("Cannot lower edge triggered $check cell %s, run async2sync or clk2fflogic first.\n", log_id(cell));
|
||||||
|
|
||||||
|
|
||||||
|
Cell *plain_cell = module->addCell(NEW_ID, formal_flavor(cell));
|
||||||
|
|
||||||
|
plain_cell->attributes = cell->attributes;
|
||||||
|
|
||||||
|
SigBit sig_a = cell->getPort(ID::A);
|
||||||
|
SigBit sig_en = cell->getPort(ID::EN);
|
||||||
|
|
||||||
|
plain_cell->setPort(ID::A, sig_a);
|
||||||
|
plain_cell->setPort(ID::EN, sig_en);
|
||||||
|
|
||||||
|
if (plain_cell->type.in(ID($assert), ID($assume)))
|
||||||
|
sig_a = module->Not(NEW_ID, sig_a);
|
||||||
|
|
||||||
|
SigBit combined_en = module->And(NEW_ID, sig_a, sig_en);
|
||||||
|
|
||||||
|
module->swap_names(cell, plain_cell);
|
||||||
|
|
||||||
|
if (cell->getPort(ID::ARGS).empty()) {
|
||||||
|
module->remove(cell);
|
||||||
|
} else {
|
||||||
|
cell->type = ID($print);
|
||||||
|
cell->setPort(ID::EN, combined_en);
|
||||||
|
cell->unsetPort(ID::A);
|
||||||
|
cell->unsetParam(ID(FLAVOR));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ struct ConnectPass : public Pass {
|
||||||
{
|
{
|
||||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" connect [-nomap] [-nounset] -set <lhs-expr> <rhs-expr>\n");
|
log(" connect [-nomap] [-nounset] -set <lhs-expr> <rhs-expr> [selection]\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log("Create a connection. This is equivalent to adding the statement 'assign\n");
|
log("Create a connection. This is equivalent to adding the statement 'assign\n");
|
||||||
log("<lhs-expr> = <rhs-expr>;' to the Verilog input. Per default, all existing\n");
|
log("<lhs-expr> = <rhs-expr>;' to the Verilog input. Per default, all existing\n");
|
||||||
|
@ -55,12 +55,12 @@ struct ConnectPass : public Pass {
|
||||||
log("the -nounset option.\n");
|
log("the -nounset option.\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" connect [-nomap] -unset <expr>\n");
|
log(" connect [-nomap] -unset <expr> [selection]\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log("Unconnect all existing drivers for the specified expression.\n");
|
log("Unconnect all existing drivers for the specified expression.\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" connect [-nomap] [-assert] -port <cell> <port> <expr>\n");
|
log(" connect [-nomap] [-assert] -port <cell> <port> <expr> [selection]\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log("Connect the specified cell port to the specified cell port.\n");
|
log("Connect the specified cell port to the specified cell port.\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
|
@ -80,17 +80,6 @@ struct ConnectPass : public Pass {
|
||||||
}
|
}
|
||||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||||
{
|
{
|
||||||
RTLIL::Module *module = nullptr;
|
|
||||||
for (auto mod : design->selected_modules()) {
|
|
||||||
if (module != nullptr)
|
|
||||||
log_cmd_error("Multiple modules selected: %s, %s\n", log_id(module->name), log_id(mod->name));
|
|
||||||
module = mod;
|
|
||||||
}
|
|
||||||
if (module == nullptr)
|
|
||||||
log_cmd_error("No modules selected.\n");
|
|
||||||
if (!module->processes.empty())
|
|
||||||
log_cmd_error("Found processes in selected module.\n");
|
|
||||||
|
|
||||||
bool flag_nounset = false, flag_nomap = false, flag_assert = false;
|
bool flag_nounset = false, flag_nomap = false, flag_assert = false;
|
||||||
std::string set_lhs, set_rhs, unset_expr;
|
std::string set_lhs, set_rhs, unset_expr;
|
||||||
std::string port_cell, port_port, port_expr;
|
std::string port_cell, port_port, port_expr;
|
||||||
|
@ -128,6 +117,18 @@ struct ConnectPass : public Pass {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
extra_args(args, argidx, design);
|
||||||
|
|
||||||
|
RTLIL::Module *module = nullptr;
|
||||||
|
for (auto mod : design->selected_modules()) {
|
||||||
|
if (module != nullptr)
|
||||||
|
log_cmd_error("Multiple modules selected: %s, %s\n", log_id(module->name), log_id(mod->name));
|
||||||
|
module = mod;
|
||||||
|
}
|
||||||
|
if (module == nullptr)
|
||||||
|
log_cmd_error("No modules selected.\n");
|
||||||
|
if (!module->processes.empty())
|
||||||
|
log_cmd_error("Found processes in selected module.\n");
|
||||||
|
|
||||||
SigMap sigmap;
|
SigMap sigmap;
|
||||||
if (!flag_nomap)
|
if (!flag_nomap)
|
||||||
|
|
|
@ -103,7 +103,6 @@ void load_plugin(std::string filename, std::vector<std::string> aliases)
|
||||||
|
|
||||||
loaded_plugins[orig_filename] = hdl;
|
loaded_plugins[orig_filename] = hdl;
|
||||||
Pass::init_register();
|
Pass::init_register();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,6 +65,7 @@ struct ShowWorker
|
||||||
bool enumerateIds;
|
bool enumerateIds;
|
||||||
bool abbreviateIds;
|
bool abbreviateIds;
|
||||||
bool notitle;
|
bool notitle;
|
||||||
|
bool href;
|
||||||
int page_counter;
|
int page_counter;
|
||||||
|
|
||||||
const std::vector<std::pair<std::string, RTLIL::Selection>> &color_selections;
|
const std::vector<std::pair<std::string, RTLIL::Selection>> &color_selections;
|
||||||
|
@ -432,9 +433,13 @@ struct ShowWorker
|
||||||
if (wire->port_input || wire->port_output)
|
if (wire->port_input || wire->port_output)
|
||||||
shape = "octagon";
|
shape = "octagon";
|
||||||
if (wire->name.isPublic()) {
|
if (wire->name.isPublic()) {
|
||||||
fprintf(f, "n%d [ shape=%s, label=\"%s\", %s ];\n",
|
std::string src_href;
|
||||||
|
if (href && wire->attributes.count(ID::src) > 0)
|
||||||
|
src_href = stringf(", href=\"%s\" ", escape(wire->attributes.at(ID::src).decode_string()));
|
||||||
|
fprintf(f, "n%d [ shape=%s, label=\"%s\", %s%s];\n",
|
||||||
id2num(wire->name), shape, findLabel(wire->name.str()),
|
id2num(wire->name), shape, findLabel(wire->name.str()),
|
||||||
nextColor(RTLIL::SigSpec(wire), "color=\"black\", fontcolor=\"black\"").c_str());
|
nextColor(RTLIL::SigSpec(wire), "color=\"black\", fontcolor=\"black\"").c_str(),
|
||||||
|
src_href.c_str());
|
||||||
if (wire->port_input)
|
if (wire->port_input)
|
||||||
all_sources.insert(stringf("n%d", id2num(wire->name)));
|
all_sources.insert(stringf("n%d", id2num(wire->name)));
|
||||||
else if (wire->port_output)
|
else if (wire->port_output)
|
||||||
|
@ -496,14 +501,18 @@ struct ShowWorker
|
||||||
conn.second, ct.cell_output(cell->type, conn.first));
|
conn.second, ct.cell_output(cell->type, conn.first));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string src_href;
|
||||||
|
if (href && cell->attributes.count(ID::src) > 0) {
|
||||||
|
src_href = stringf("%shref=\"%s\" ", (findColor(cell->name).empty() ? "" :" , "), escape(cell->attributes.at(ID::src).decode_string()));
|
||||||
|
}
|
||||||
#ifdef CLUSTER_CELLS_AND_PORTBOXES
|
#ifdef CLUSTER_CELLS_AND_PORTBOXES
|
||||||
if (!code.empty())
|
if (!code.empty())
|
||||||
fprintf(f, "subgraph cluster_c%d {\nc%d [ shape=record, label=\"%s\"%s ];\n%s}\n",
|
fprintf(f, "subgraph cluster_c%d {\nc%d [ shape=record, label=\"%s\"%s%s ];\n%s}\n",
|
||||||
id2num(cell->name), id2num(cell->name), label_string.c_str(), color.c_str(), code.c_str());
|
id2num(cell->name), id2num(cell->name), label_string.c_str(), color.c_str(), src_href.c_str(), code.c_str());
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
fprintf(f, "c%d [ shape=record, label=\"%s\", %s ];\n%s",
|
fprintf(f, "c%d [ shape=record, label=\"%s\", %s%s ];\n%s",
|
||||||
id2num(cell->name), label_string.c_str(), findColor(cell->name).c_str(), code.c_str());
|
id2num(cell->name), label_string.c_str(), findColor(cell->name).c_str(), src_href.c_str(), code.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &it : module->processes)
|
for (auto &it : module->processes)
|
||||||
|
@ -608,12 +617,12 @@ struct ShowWorker
|
||||||
}
|
}
|
||||||
|
|
||||||
ShowWorker(FILE *f, RTLIL::Design *design, std::vector<RTLIL::Design*> &libs, uint32_t colorSeed, bool genWidthLabels,
|
ShowWorker(FILE *f, RTLIL::Design *design, std::vector<RTLIL::Design*> &libs, uint32_t colorSeed, bool genWidthLabels,
|
||||||
bool genSignedLabels, bool stretchIO, bool enumerateIds, bool abbreviateIds, bool notitle,
|
bool genSignedLabels, bool stretchIO, bool enumerateIds, bool abbreviateIds, bool notitle, bool href,
|
||||||
const std::vector<std::pair<std::string, RTLIL::Selection>> &color_selections,
|
const std::vector<std::pair<std::string, RTLIL::Selection>> &color_selections,
|
||||||
const std::vector<std::pair<std::string, RTLIL::Selection>> &label_selections, RTLIL::IdString colorattr) :
|
const std::vector<std::pair<std::string, RTLIL::Selection>> &label_selections, RTLIL::IdString colorattr) :
|
||||||
f(f), design(design), currentColor(colorSeed), genWidthLabels(genWidthLabels),
|
f(f), design(design), currentColor(colorSeed), genWidthLabels(genWidthLabels),
|
||||||
genSignedLabels(genSignedLabels), stretchIO(stretchIO), enumerateIds(enumerateIds), abbreviateIds(abbreviateIds),
|
genSignedLabels(genSignedLabels), stretchIO(stretchIO), enumerateIds(enumerateIds), abbreviateIds(abbreviateIds),
|
||||||
notitle(notitle), color_selections(color_selections), label_selections(label_selections), colorattr(colorattr)
|
notitle(notitle), href(href), color_selections(color_selections), label_selections(label_selections), colorattr(colorattr)
|
||||||
{
|
{
|
||||||
ct.setup_internals();
|
ct.setup_internals();
|
||||||
ct.setup_internals_mem();
|
ct.setup_internals_mem();
|
||||||
|
@ -726,6 +735,10 @@ struct ShowPass : public Pass {
|
||||||
log(" don't run viewer in the background, IE wait for the viewer tool to\n");
|
log(" don't run viewer in the background, IE wait for the viewer tool to\n");
|
||||||
log(" exit before returning\n");
|
log(" exit before returning\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
|
log(" -href\n");
|
||||||
|
log(" adds href attribute to all items representing cells and wires, using\n");
|
||||||
|
log(" src attribute of origin\n");
|
||||||
|
log("\n");
|
||||||
log("When no <format> is specified, 'dot' is used. When no <format> and <viewer> is\n");
|
log("When no <format> is specified, 'dot' is used. When no <format> and <viewer> is\n");
|
||||||
log("specified, 'xdot' is used to display the schematic (POSIX systems only).\n");
|
log("specified, 'xdot' is used to display the schematic (POSIX systems only).\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
|
@ -763,6 +776,7 @@ struct ShowPass : public Pass {
|
||||||
bool flag_enum = false;
|
bool flag_enum = false;
|
||||||
bool flag_abbreviate = true;
|
bool flag_abbreviate = true;
|
||||||
bool flag_notitle = false;
|
bool flag_notitle = false;
|
||||||
|
bool flag_href = false;
|
||||||
bool custom_prefix = false;
|
bool custom_prefix = false;
|
||||||
std::string background = "&";
|
std::string background = "&";
|
||||||
RTLIL::IdString colorattr;
|
RTLIL::IdString colorattr;
|
||||||
|
@ -850,6 +864,10 @@ struct ShowPass : public Pass {
|
||||||
background= "";
|
background= "";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (arg == "-href") {
|
||||||
|
flag_href = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
extra_args(args, argidx, design);
|
extra_args(args, argidx, design);
|
||||||
|
@ -894,7 +912,7 @@ struct ShowPass : public Pass {
|
||||||
delete lib;
|
delete lib;
|
||||||
log_cmd_error("Can't open dot file `%s' for writing.\n", dot_file.c_str());
|
log_cmd_error("Can't open dot file `%s' for writing.\n", dot_file.c_str());
|
||||||
}
|
}
|
||||||
ShowWorker worker(f, design, libs, colorSeed, flag_width, flag_signed, flag_stretch, flag_enum, flag_abbreviate, flag_notitle, color_selections, label_selections, colorattr);
|
ShowWorker worker(f, design, libs, colorSeed, flag_width, flag_signed, flag_stretch, flag_enum, flag_abbreviate, flag_notitle, flag_href, color_selections, label_selections, colorattr);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
for (auto lib : libs)
|
for (auto lib : libs)
|
||||||
|
|
|
@ -28,6 +28,11 @@
|
||||||
USING_YOSYS_NAMESPACE
|
USING_YOSYS_NAMESPACE
|
||||||
PRIVATE_NAMESPACE_BEGIN
|
PRIVATE_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
struct cell_area_t {
|
||||||
|
double area;
|
||||||
|
bool is_sequential;
|
||||||
|
};
|
||||||
|
|
||||||
struct statdata_t
|
struct statdata_t
|
||||||
{
|
{
|
||||||
#define STAT_INT_MEMBERS X(num_wires) X(num_wire_bits) X(num_pub_wires) X(num_pub_wire_bits) \
|
#define STAT_INT_MEMBERS X(num_wires) X(num_wire_bits) X(num_pub_wires) X(num_pub_wire_bits) \
|
||||||
|
@ -39,6 +44,7 @@ struct statdata_t
|
||||||
STAT_INT_MEMBERS
|
STAT_INT_MEMBERS
|
||||||
#undef X
|
#undef X
|
||||||
double area;
|
double area;
|
||||||
|
double sequential_area;
|
||||||
string tech;
|
string tech;
|
||||||
|
|
||||||
std::map<RTLIL::IdString, int> techinfo;
|
std::map<RTLIL::IdString, int> techinfo;
|
||||||
|
@ -74,7 +80,7 @@ struct statdata_t
|
||||||
#undef X
|
#undef X
|
||||||
}
|
}
|
||||||
|
|
||||||
statdata_t(RTLIL::Design *design, RTLIL::Module *mod, bool width_mode, const dict<IdString, double> &cell_area, string techname)
|
statdata_t(RTLIL::Design *design, RTLIL::Module *mod, bool width_mode, const dict<IdString, cell_area_t> &cell_area, string techname)
|
||||||
{
|
{
|
||||||
tech = techname;
|
tech = techname;
|
||||||
|
|
||||||
|
@ -132,10 +138,16 @@ struct statdata_t
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cell_area.empty()) {
|
if (!cell_area.empty()) {
|
||||||
if (cell_area.count(cell_type))
|
if (cell_area.count(cell_type)) {
|
||||||
area += cell_area.at(cell_type);
|
cell_area_t cell_data = cell_area.at(cell_type);
|
||||||
else
|
if (cell_data.is_sequential) {
|
||||||
|
sequential_area += cell_data.area;
|
||||||
|
}
|
||||||
|
area += cell_data.area;
|
||||||
|
}
|
||||||
|
else {
|
||||||
unknown_cell_area.insert(cell_type);
|
unknown_cell_area.insert(cell_type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
num_cells++;
|
num_cells++;
|
||||||
|
@ -244,6 +256,7 @@ struct statdata_t
|
||||||
if (area != 0) {
|
if (area != 0) {
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" Chip area for %smodule '%s': %f\n", (top_mod) ? "top " : "", mod_name.c_str(), area);
|
log(" Chip area for %smodule '%s': %f\n", (top_mod) ? "top " : "", mod_name.c_str(), area);
|
||||||
|
log(" of which used for sequential elements: %f (%.2f%%)\n", sequential_area, 100.0*sequential_area/area);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tech == "xilinx")
|
if (tech == "xilinx")
|
||||||
|
@ -325,7 +338,7 @@ statdata_t hierarchy_worker(std::map<RTLIL::IdString, statdata_t> &mod_stat, RTL
|
||||||
return mod_data;
|
return mod_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void read_liberty_cellarea(dict<IdString, double> &cell_area, string liberty_file)
|
void read_liberty_cellarea(dict<IdString, cell_area_t> &cell_area, string liberty_file)
|
||||||
{
|
{
|
||||||
std::ifstream f;
|
std::ifstream f;
|
||||||
f.open(liberty_file.c_str());
|
f.open(liberty_file.c_str());
|
||||||
|
@ -341,8 +354,9 @@ void read_liberty_cellarea(dict<IdString, double> &cell_area, string liberty_fil
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
LibertyAst *ar = cell->find("area");
|
LibertyAst *ar = cell->find("area");
|
||||||
|
bool is_flip_flop = cell->find("ff") != nullptr;
|
||||||
if (ar != nullptr && !ar->value.empty())
|
if (ar != nullptr && !ar->value.empty())
|
||||||
cell_area["\\" + cell->args[0]] = atof(ar->value.c_str());
|
cell_area["\\" + cell->args[0]] = {/*area=*/atof(ar->value.c_str()), is_flip_flop};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -383,7 +397,7 @@ struct StatPass : public Pass {
|
||||||
bool width_mode = false, json_mode = false;
|
bool width_mode = false, json_mode = false;
|
||||||
RTLIL::Module *top_mod = nullptr;
|
RTLIL::Module *top_mod = nullptr;
|
||||||
std::map<RTLIL::IdString, statdata_t> mod_stat;
|
std::map<RTLIL::IdString, statdata_t> mod_stat;
|
||||||
dict<IdString, double> cell_area;
|
dict<IdString, cell_area_t> cell_area;
|
||||||
string techname;
|
string techname;
|
||||||
|
|
||||||
size_t argidx;
|
size_t argidx;
|
||||||
|
|
|
@ -339,6 +339,8 @@ struct EquivSimplePass : public Pass {
|
||||||
CellTypes ct;
|
CellTypes ct;
|
||||||
ct.setup_internals();
|
ct.setup_internals();
|
||||||
ct.setup_stdcells();
|
ct.setup_stdcells();
|
||||||
|
ct.setup_internals_ff();
|
||||||
|
ct.setup_stdcells_mem();
|
||||||
|
|
||||||
for (auto module : design->selected_modules())
|
for (auto module : design->selected_modules())
|
||||||
{
|
{
|
||||||
|
|
|
@ -671,7 +671,7 @@ bool set_keep_assert(std::map<RTLIL::Module*, bool> &cache, RTLIL::Module *mod)
|
||||||
if (cache.count(mod) == 0)
|
if (cache.count(mod) == 0)
|
||||||
for (auto c : mod->cells()) {
|
for (auto c : mod->cells()) {
|
||||||
RTLIL::Module *m = mod->design->module(c->type);
|
RTLIL::Module *m = mod->design->module(c->type);
|
||||||
if ((m != nullptr && set_keep_assert(cache, m)) || c->type.in(ID($assert), ID($assume), ID($live), ID($fair), ID($cover)))
|
if ((m != nullptr && set_keep_assert(cache, m)) || c->type.in(ID($check), ID($assert), ID($assume), ID($live), ID($fair), ID($cover)))
|
||||||
return cache[mod] = true;
|
return cache[mod] = true;
|
||||||
}
|
}
|
||||||
return cache[mod];
|
return cache[mod];
|
||||||
|
@ -1006,6 +1006,18 @@ struct HierarchyPass : public Pass {
|
||||||
if (mod->get_bool_attribute(ID::top))
|
if (mod->get_bool_attribute(ID::top))
|
||||||
top_mod = mod;
|
top_mod = mod;
|
||||||
|
|
||||||
|
if (top_mod == nullptr)
|
||||||
|
{
|
||||||
|
std::vector<IdString> abstract_ids;
|
||||||
|
for (auto module : design->modules())
|
||||||
|
if (module->name.begins_with("$abstract"))
|
||||||
|
abstract_ids.push_back(module->name);
|
||||||
|
for (auto abstract_id : abstract_ids)
|
||||||
|
design->module(abstract_id)->derive(design, {});
|
||||||
|
for (auto abstract_id : abstract_ids)
|
||||||
|
design->remove(design->module(abstract_id));
|
||||||
|
}
|
||||||
|
|
||||||
if (top_mod == nullptr && auto_top_mode) {
|
if (top_mod == nullptr && auto_top_mode) {
|
||||||
log_header(design, "Finding top of design hierarchy..\n");
|
log_header(design, "Finding top of design hierarchy..\n");
|
||||||
dict<Module*, int> db;
|
dict<Module*, int> db;
|
||||||
|
|
|
@ -39,6 +39,9 @@ struct MemoryCollectPass : public Pass {
|
||||||
log_header(design, "Executing MEMORY_COLLECT pass (generating $mem cells).\n");
|
log_header(design, "Executing MEMORY_COLLECT pass (generating $mem cells).\n");
|
||||||
extra_args(args, 1, design);
|
extra_args(args, 1, design);
|
||||||
for (auto module : design->selected_modules()) {
|
for (auto module : design->selected_modules()) {
|
||||||
|
if (module->has_processes_warn())
|
||||||
|
continue;
|
||||||
|
|
||||||
for (auto &mem : Mem::get_selected_memories(module)) {
|
for (auto &mem : Mem::get_selected_memories(module)) {
|
||||||
if (!mem.packed) {
|
if (!mem.packed) {
|
||||||
mem.packed = true;
|
mem.packed = true;
|
||||||
|
|
|
@ -2229,6 +2229,9 @@ struct MemoryLibMapPass : public Pass {
|
||||||
Library lib = parse_library(lib_files, defines);
|
Library lib = parse_library(lib_files, defines);
|
||||||
|
|
||||||
for (auto module : design->selected_modules()) {
|
for (auto module : design->selected_modules()) {
|
||||||
|
if (module->has_processes_warn())
|
||||||
|
continue;
|
||||||
|
|
||||||
MapWorker worker(module);
|
MapWorker worker(module);
|
||||||
auto mems = Mem::get_selected_memories(module);
|
auto mems = Mem::get_selected_memories(module);
|
||||||
for (auto &mem : mems)
|
for (auto &mem : mems)
|
||||||
|
|
|
@ -493,6 +493,9 @@ struct MemoryMapPass : public Pass {
|
||||||
extra_args(args, argidx, design);
|
extra_args(args, argidx, design);
|
||||||
|
|
||||||
for (auto mod : design->selected_modules()) {
|
for (auto mod : design->selected_modules()) {
|
||||||
|
if (mod->has_processes_warn())
|
||||||
|
continue;
|
||||||
|
|
||||||
MemoryMapWorker worker(design, mod);
|
MemoryMapWorker worker(design, mod);
|
||||||
worker.attr_icase = attr_icase;
|
worker.attr_icase = attr_icase;
|
||||||
worker.attributes = attributes;
|
worker.attributes = attributes;
|
||||||
|
|
|
@ -50,7 +50,7 @@ struct MemoryMemxPass : public Pass {
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override {
|
void execute(std::vector<std::string> args, RTLIL::Design *design) override {
|
||||||
log_header(design, "Executing MEMORY_MEMX pass (converting $mem cells to logic and flip-flops).\n");
|
log_header(design, "Executing MEMORY_MEMX pass (emit soft logic for out-of-bounds handling).\n");
|
||||||
extra_args(args, 1, design);
|
extra_args(args, 1, design);
|
||||||
|
|
||||||
for (auto module : design->selected_modules())
|
for (auto module : design->selected_modules())
|
||||||
|
|
|
@ -46,6 +46,9 @@ struct MemoryNarrowPass : public Pass {
|
||||||
extra_args(args, argidx, design);
|
extra_args(args, argidx, design);
|
||||||
|
|
||||||
for (auto module : design->selected_modules()) {
|
for (auto module : design->selected_modules()) {
|
||||||
|
if (module->has_processes_warn())
|
||||||
|
continue;
|
||||||
|
|
||||||
for (auto &mem : Mem::get_selected_memories(module))
|
for (auto &mem : Mem::get_selected_memories(module))
|
||||||
{
|
{
|
||||||
bool wide = false;
|
bool wide = false;
|
||||||
|
|
|
@ -558,8 +558,12 @@ struct MemorySharePass : public Pass {
|
||||||
extra_args(args, argidx, design);
|
extra_args(args, argidx, design);
|
||||||
MemoryShareWorker msw(design, flag_widen, flag_sat);
|
MemoryShareWorker msw(design, flag_widen, flag_sat);
|
||||||
|
|
||||||
for (auto module : design->selected_modules())
|
for (auto module : design->selected_modules()) {
|
||||||
|
if (module->has_processes_warn())
|
||||||
|
continue;
|
||||||
|
|
||||||
msw(module);
|
msw(module);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} MemorySharePass;
|
} MemorySharePass;
|
||||||
|
|
||||||
|
|
|
@ -35,10 +35,12 @@ struct keep_cache_t
|
||||||
{
|
{
|
||||||
Design *design;
|
Design *design;
|
||||||
dict<Module*, bool> cache;
|
dict<Module*, bool> cache;
|
||||||
|
bool purge_mode = false;
|
||||||
|
|
||||||
void reset(Design *design = nullptr)
|
void reset(Design *design = nullptr, bool purge_mode = false)
|
||||||
{
|
{
|
||||||
this->design = design;
|
this->design = design;
|
||||||
|
this->purge_mode = purge_mode;
|
||||||
cache.clear();
|
cache.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,12 +84,15 @@ struct keep_cache_t
|
||||||
if (!ignore_specify && cell->type.in(ID($specify2), ID($specify3), ID($specrule)))
|
if (!ignore_specify && cell->type.in(ID($specify2), ID($specify3), ID($specrule)))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (cell->type == ID($print))
|
if (cell->type == ID($print) || cell->type == ID($check))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (cell->has_keep_attr())
|
if (cell->has_keep_attr())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
if (!purge_mode && cell->type == ID($scopeinfo))
|
||||||
|
return true;
|
||||||
|
|
||||||
if (cell->module && cell->module->design)
|
if (cell->module && cell->module->design)
|
||||||
return query(cell->module->design->module(cell->type));
|
return query(cell->module->design->module(cell->type));
|
||||||
|
|
||||||
|
@ -236,10 +241,13 @@ int count_nontrivial_wire_attrs(RTLIL::Wire *w)
|
||||||
{
|
{
|
||||||
int count = w->attributes.size();
|
int count = w->attributes.size();
|
||||||
count -= w->attributes.count(ID::src);
|
count -= w->attributes.count(ID::src);
|
||||||
|
count -= w->attributes.count(ID::hdlname);
|
||||||
|
count -= w->attributes.count(ID(scopename));
|
||||||
count -= w->attributes.count(ID::unused_bits);
|
count -= w->attributes.count(ID::unused_bits);
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Should we pick `s2` over `s1` to represent a signal?
|
||||||
bool compare_signals(RTLIL::SigBit &s1, RTLIL::SigBit &s2, SigPool ®s, SigPool &conns, pool<RTLIL::Wire*> &direct_wires)
|
bool compare_signals(RTLIL::SigBit &s1, RTLIL::SigBit &s2, SigPool ®s, SigPool &conns, pool<RTLIL::Wire*> &direct_wires)
|
||||||
{
|
{
|
||||||
RTLIL::Wire *w1 = s1.wire;
|
RTLIL::Wire *w1 = s1.wire;
|
||||||
|
@ -292,9 +300,10 @@ bool check_public_name(RTLIL::IdString id)
|
||||||
|
|
||||||
bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbose)
|
bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbose)
|
||||||
{
|
{
|
||||||
|
// `register_signals` and `connected_signals` will help us decide later on
|
||||||
|
// on picking representatives out of groups of connected signals
|
||||||
SigPool register_signals;
|
SigPool register_signals;
|
||||||
SigPool connected_signals;
|
SigPool connected_signals;
|
||||||
|
|
||||||
if (!purge_mode)
|
if (!purge_mode)
|
||||||
for (auto &it : module->cells_) {
|
for (auto &it : module->cells_) {
|
||||||
RTLIL::Cell *cell = it.second;
|
RTLIL::Cell *cell = it.second;
|
||||||
|
@ -309,20 +318,27 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
|
||||||
}
|
}
|
||||||
|
|
||||||
SigMap assign_map(module);
|
SigMap assign_map(module);
|
||||||
pool<RTLIL::SigSpec> direct_sigs;
|
|
||||||
|
// construct a pool of wires which are directly driven by a known celltype,
|
||||||
|
// this will influence our choice of representatives
|
||||||
pool<RTLIL::Wire*> direct_wires;
|
pool<RTLIL::Wire*> direct_wires;
|
||||||
for (auto &it : module->cells_) {
|
{
|
||||||
RTLIL::Cell *cell = it.second;
|
pool<RTLIL::SigSpec> direct_sigs;
|
||||||
if (ct_all.cell_known(cell->type))
|
for (auto &it : module->cells_) {
|
||||||
for (auto &it2 : cell->connections())
|
RTLIL::Cell *cell = it.second;
|
||||||
if (ct_all.cell_output(cell->type, it2.first))
|
if (ct_all.cell_known(cell->type))
|
||||||
direct_sigs.insert(assign_map(it2.second));
|
for (auto &it2 : cell->connections())
|
||||||
}
|
if (ct_all.cell_output(cell->type, it2.first))
|
||||||
for (auto &it : module->wires_) {
|
direct_sigs.insert(assign_map(it2.second));
|
||||||
if (direct_sigs.count(assign_map(it.second)) || it.second->port_input)
|
}
|
||||||
direct_wires.insert(it.second);
|
for (auto &it : module->wires_) {
|
||||||
|
if (direct_sigs.count(assign_map(it.second)) || it.second->port_input)
|
||||||
|
direct_wires.insert(it.second);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// weight all options for representatives with `compare_signals`,
|
||||||
|
// the one that wins will be what `assign_map` maps to
|
||||||
for (auto &it : module->wires_) {
|
for (auto &it : module->wires_) {
|
||||||
RTLIL::Wire *wire = it.second;
|
RTLIL::Wire *wire = it.second;
|
||||||
for (int i = 0; i < wire->width; i++) {
|
for (int i = 0; i < wire->width; i++) {
|
||||||
|
@ -332,21 +348,30 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// we are removing all connections
|
||||||
module->connections_.clear();
|
module->connections_.clear();
|
||||||
|
|
||||||
|
// used signals sigmapped
|
||||||
SigPool used_signals;
|
SigPool used_signals;
|
||||||
|
// used signals pre-sigmapped
|
||||||
SigPool raw_used_signals;
|
SigPool raw_used_signals;
|
||||||
|
// used signals sigmapped, ignoring drivers (we keep track of this to set `unused_bits`)
|
||||||
SigPool used_signals_nodrivers;
|
SigPool used_signals_nodrivers;
|
||||||
|
|
||||||
|
// gather the usage information for cells
|
||||||
for (auto &it : module->cells_) {
|
for (auto &it : module->cells_) {
|
||||||
RTLIL::Cell *cell = it.second;
|
RTLIL::Cell *cell = it.second;
|
||||||
for (auto &it2 : cell->connections_) {
|
for (auto &it2 : cell->connections_) {
|
||||||
assign_map.apply(it2.second);
|
assign_map.apply(it2.second); // modify the cell connection in place
|
||||||
raw_used_signals.add(it2.second);
|
raw_used_signals.add(it2.second);
|
||||||
used_signals.add(it2.second);
|
used_signals.add(it2.second);
|
||||||
if (!ct_all.cell_output(cell->type, it2.first))
|
if (!ct_all.cell_output(cell->type, it2.first))
|
||||||
used_signals_nodrivers.add(it2.second);
|
used_signals_nodrivers.add(it2.second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// gather the usage information for ports, wires with `keep`,
|
||||||
|
// also gather init bits
|
||||||
dict<RTLIL::SigBit, RTLIL::State> init_bits;
|
dict<RTLIL::SigBit, RTLIL::State> init_bits;
|
||||||
for (auto &it : module->wires_) {
|
for (auto &it : module->wires_) {
|
||||||
RTLIL::Wire *wire = it.second;
|
RTLIL::Wire *wire = it.second;
|
||||||
|
@ -374,6 +399,7 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set init attributes on all wires of a connected group
|
||||||
for (auto wire : module->wires()) {
|
for (auto wire : module->wires()) {
|
||||||
bool found = false;
|
bool found = false;
|
||||||
Const val(State::Sx, wire->width);
|
Const val(State::Sx, wire->width);
|
||||||
|
@ -388,6 +414,7 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
|
||||||
wire->attributes[ID::init] = val;
|
wire->attributes[ID::init] = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// now decide for each wire if we should be deleting it
|
||||||
pool<RTLIL::Wire*> del_wires_queue;
|
pool<RTLIL::Wire*> del_wires_queue;
|
||||||
for (auto wire : module->wires())
|
for (auto wire : module->wires())
|
||||||
{
|
{
|
||||||
|
@ -418,6 +445,9 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
|
||||||
goto delete_this_wire;
|
goto delete_this_wire;
|
||||||
} else
|
} else
|
||||||
if (!used_signals.check_any(s2)) {
|
if (!used_signals.check_any(s2)) {
|
||||||
|
// this path shouldn't be possible: this wire is used directly (otherwise it would get cleaned up above), and indirectly
|
||||||
|
// used wires are a superset of those used directly
|
||||||
|
log_assert(false);
|
||||||
// delete wires that aren't used by anything indirectly, even though other wires may alias it
|
// delete wires that aren't used by anything indirectly, even though other wires may alias it
|
||||||
goto delete_this_wire;
|
goto delete_this_wire;
|
||||||
}
|
}
|
||||||
|
@ -638,7 +668,7 @@ struct OptCleanPass : public Pass {
|
||||||
}
|
}
|
||||||
extra_args(args, argidx, design);
|
extra_args(args, argidx, design);
|
||||||
|
|
||||||
keep_cache.reset(design);
|
keep_cache.reset(design, purge_mode);
|
||||||
|
|
||||||
ct_reg.setup_internals_mem();
|
ct_reg.setup_internals_mem();
|
||||||
ct_reg.setup_internals_anyinit();
|
ct_reg.setup_internals_anyinit();
|
||||||
|
|
|
@ -353,7 +353,7 @@ struct OptDffWorker
|
||||||
// Try a more complex conversion to plain async reset.
|
// Try a more complex conversion to plain async reset.
|
||||||
State val_neutral = ff.pol_set ? State::S0 : State::S1;
|
State val_neutral = ff.pol_set ? State::S0 : State::S1;
|
||||||
Const val_arst;
|
Const val_arst;
|
||||||
SigSpec sig_arst;
|
SigBit sig_arst;
|
||||||
if (ff.sig_clr[0] == val_neutral)
|
if (ff.sig_clr[0] == val_neutral)
|
||||||
sig_arst = ff.sig_set[0];
|
sig_arst = ff.sig_set[0];
|
||||||
else
|
else
|
||||||
|
|
|
@ -38,6 +38,7 @@ struct OptFfInvWorker
|
||||||
// - ... which has no other users
|
// - ... which has no other users
|
||||||
// - all users of FF are LUTs
|
// - all users of FF are LUTs
|
||||||
bool push_d_inv(FfData &ff) {
|
bool push_d_inv(FfData &ff) {
|
||||||
|
log_assert(ff.width == 1);
|
||||||
if (index.query_is_input(ff.sig_d))
|
if (index.query_is_input(ff.sig_d))
|
||||||
return false;
|
return false;
|
||||||
if (index.query_is_output(ff.sig_d))
|
if (index.query_is_output(ff.sig_d))
|
||||||
|
@ -90,7 +91,7 @@ struct OptFfInvWorker
|
||||||
int flip_mask = 0;
|
int flip_mask = 0;
|
||||||
SigSpec sig_a = lut->getPort(ID::A);
|
SigSpec sig_a = lut->getPort(ID::A);
|
||||||
for (int i = 0; i < GetSize(sig_a); i++) {
|
for (int i = 0; i < GetSize(sig_a); i++) {
|
||||||
if (index.sigmap(sig_a[i]) == index.sigmap(ff.sig_q)) {
|
if (index.sigmap(sig_a[i]) == index.sigmap(ff.sig_q[0])) {
|
||||||
flip_mask |= 1 << i;
|
flip_mask |= 1 << i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -167,7 +167,11 @@ struct OptLutWorker
|
||||||
legal = false;
|
legal = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (sigmap(lut_input[dlogic_conn.first]) != sigmap(lut_dlogic.second->getPort(dlogic_conn.second)))
|
|
||||||
|
if (lut_dlogic.second->getPort(dlogic_conn.second).size() != 1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (sigmap(lut_input[dlogic_conn.first]) != sigmap(lut_dlogic.second->getPort(dlogic_conn.second)[0]))
|
||||||
{
|
{
|
||||||
log_debug(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic.second->type.c_str(), log_id(module), log_id(lut_dlogic.second));
|
log_debug(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic.second->type.c_str(), log_id(module), log_id(lut_dlogic.second));
|
||||||
log_debug(" LUT input A[%d] (wire %s) not connected to %s port %s (wire %s).\n", dlogic_conn.first, log_signal(lut_input[dlogic_conn.first]), lut_dlogic.second->type.c_str(), dlogic_conn.second.c_str(), log_signal(lut_dlogic.second->getPort(dlogic_conn.second)));
|
log_debug(" LUT input A[%d] (wire %s) not connected to %s port %s (wire %s).\n", dlogic_conn.first, log_signal(lut_input[dlogic_conn.first]), lut_dlogic.second->type.c_str(), dlogic_conn.second.c_str(), log_signal(lut_dlogic.second->getPort(dlogic_conn.second)));
|
||||||
|
@ -314,7 +318,7 @@ struct OptLutWorker
|
||||||
|
|
||||||
auto lutA = worklist.pop();
|
auto lutA = worklist.pop();
|
||||||
SigSpec lutA_input = sigmap(lutA->getPort(ID::A));
|
SigSpec lutA_input = sigmap(lutA->getPort(ID::A));
|
||||||
SigSpec lutA_output = sigmap(lutA->getPort(ID::Y)[0]);
|
SigBit lutA_output = sigmap(lutA->getPort(ID::Y)[0]);
|
||||||
int lutA_width = lutA->getParam(ID::WIDTH).as_int();
|
int lutA_width = lutA->getParam(ID::WIDTH).as_int();
|
||||||
int lutA_arity = luts_arity[lutA];
|
int lutA_arity = luts_arity[lutA];
|
||||||
pool<int> &lutA_dlogic_inputs = luts_dlogic_inputs[lutA];
|
pool<int> &lutA_dlogic_inputs = luts_dlogic_inputs[lutA];
|
||||||
|
@ -529,12 +533,6 @@ struct OptLutPass : public Pass {
|
||||||
log("\n");
|
log("\n");
|
||||||
log("This pass combines cascaded $lut cells with unused inputs.\n");
|
log("This pass combines cascaded $lut cells with unused inputs.\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" -dlogic <type>:<cell-port>=<LUT-input>[:<cell-port>=<LUT-input>...]\n");
|
|
||||||
log(" preserve connections to dedicated logic cell <type> that has ports\n");
|
|
||||||
log(" <cell-port> connected to LUT inputs <LUT-input>. this includes\n");
|
|
||||||
log(" the case where both LUT and dedicated logic input are connected to\n");
|
|
||||||
log(" the same constant.\n");
|
|
||||||
log("\n");
|
|
||||||
log(" -tech ice40\n");
|
log(" -tech ice40\n");
|
||||||
log(" treat the design as a LUT-mapped circuit for the iCE40 architecture\n");
|
log(" treat the design as a LUT-mapped circuit for the iCE40 architecture\n");
|
||||||
log(" and preserve connections to SB_CARRY as appropriate\n");
|
log(" and preserve connections to SB_CARRY as appropriate\n");
|
||||||
|
|
|
@ -52,6 +52,9 @@ struct OptMemPass : public Pass {
|
||||||
|
|
||||||
int total_count = 0;
|
int total_count = 0;
|
||||||
for (auto module : design->selected_modules()) {
|
for (auto module : design->selected_modules()) {
|
||||||
|
if (module->has_processes_warn())
|
||||||
|
continue;
|
||||||
|
|
||||||
SigMap sigmap(module);
|
SigMap sigmap(module);
|
||||||
FfInitVals initvals(&sigmap, module);
|
FfInitVals initvals(&sigmap, module);
|
||||||
for (auto &mem : Mem::get_selected_memories(module)) {
|
for (auto &mem : Mem::get_selected_memories(module)) {
|
||||||
|
|
|
@ -272,6 +272,9 @@ struct OptMergeWorker
|
||||||
if ((!mode_share_all && !ct.cell_known(cell->type)) || !cell->known())
|
if ((!mode_share_all && !ct.cell_known(cell->type)) || !cell->known())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (cell->type == ID($scopeinfo))
|
||||||
|
continue;
|
||||||
|
|
||||||
uint64_t hash = hash_cell_parameters_and_connections(cell);
|
uint64_t hash = hash_cell_parameters_and_connections(cell);
|
||||||
auto r = sharemap.insert(std::make_pair(hash, cell));
|
auto r = sharemap.insert(std::make_pair(hash, cell));
|
||||||
if (!r.second) {
|
if (!r.second) {
|
||||||
|
|
|
@ -346,7 +346,7 @@ endmatch
|
||||||
code argQ argD
|
code argQ argD
|
||||||
{
|
{
|
||||||
if (clock != SigBit()) {
|
if (clock != SigBit()) {
|
||||||
if (port(ff, \CLK) != clock)
|
if (port(ff, \CLK)[0] != clock)
|
||||||
reject;
|
reject;
|
||||||
if (param(ff, \CLK_POLARITY).as_bool() != clock_pol)
|
if (param(ff, \CLK_POLARITY).as_bool() != clock_pol)
|
||||||
reject;
|
reject;
|
||||||
|
@ -393,7 +393,7 @@ endmatch
|
||||||
code argQ
|
code argQ
|
||||||
if (ff) {
|
if (ff) {
|
||||||
if (clock != SigBit()) {
|
if (clock != SigBit()) {
|
||||||
if (port(ff, \CLK) != clock)
|
if (port(ff, \CLK)[0] != clock)
|
||||||
reject;
|
reject;
|
||||||
if (param(ff, \CLK_POLARITY).as_bool() != clock_pol)
|
if (param(ff, \CLK_POLARITY).as_bool() != clock_pol)
|
||||||
reject;
|
reject;
|
||||||
|
|
|
@ -103,8 +103,20 @@ code
|
||||||
new_a.append(old_a);
|
new_a.append(old_a);
|
||||||
} else {
|
} else {
|
||||||
// data >> (...+c) transformed to data[MAX:c] >> (...)
|
// data >> (...+c) transformed to data[MAX:c] >> (...)
|
||||||
new_a.append(old_a.extract_end(offset));
|
if(offset < GetSize(old_a)) { // some signal bits left?
|
||||||
|
new_a.append(old_a.extract_end(offset));
|
||||||
|
} else {
|
||||||
|
// warn user in case data is empty (no bits left)
|
||||||
|
std::string location = shift->get_src_attribute();
|
||||||
|
if (location.empty())
|
||||||
|
location = shift->name.str();
|
||||||
|
if(shift->type.in($shiftx))
|
||||||
|
log_warning("at %s: result of indexed part-selection is always constant (selecting from '%s' with index '%s + %d')\n", \
|
||||||
|
location.c_str(), log_signal(old_a), log_signal(var_signal), offset);
|
||||||
|
else
|
||||||
|
log_warning("at %s: result of shift operation is always constant (shifting '%s' by '%s + %d'-bits)\n", \
|
||||||
|
location.c_str(), log_signal(old_a), log_signal(var_signal), offset);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SigSpec new_b = {var_signal, SigSpec(State::S0, log2scale)};
|
SigSpec new_b = {var_signal, SigSpec(State::S0, log2scale)};
|
||||||
|
|
|
@ -415,7 +415,7 @@ match ff
|
||||||
filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
|
filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
|
||||||
filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
|
filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
|
||||||
|
|
||||||
filter clock == SigBit() || port(ff, \CLK) == clock
|
filter clock == SigBit() || port(ff, \CLK)[0] == clock
|
||||||
endmatch
|
endmatch
|
||||||
|
|
||||||
code argQ
|
code argQ
|
||||||
|
@ -465,7 +465,7 @@ match ff
|
||||||
filter GetSize(port(ff, \D)) >= offset + GetSize(argD)
|
filter GetSize(port(ff, \D)) >= offset + GetSize(argD)
|
||||||
filter port(ff, \D).extract(offset, GetSize(argD)) == argD
|
filter port(ff, \D).extract(offset, GetSize(argD)) == argD
|
||||||
|
|
||||||
filter clock == SigBit() || port(ff, \CLK) == clock
|
filter clock == SigBit() || port(ff, \CLK)[0] == clock
|
||||||
endmatch
|
endmatch
|
||||||
|
|
||||||
code argQ
|
code argQ
|
||||||
|
|
|
@ -354,7 +354,7 @@ match ff
|
||||||
filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
|
filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
|
||||||
filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
|
filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
|
||||||
|
|
||||||
filter clock == SigBit() || port(ff, \CLK) == clock
|
filter clock == SigBit() || port(ff, \CLK)[0] == clock
|
||||||
endmatch
|
endmatch
|
||||||
|
|
||||||
code argQ
|
code argQ
|
||||||
|
@ -404,7 +404,7 @@ match ff
|
||||||
filter GetSize(port(ff, \D)) >= offset + GetSize(argD)
|
filter GetSize(port(ff, \D)) >= offset + GetSize(argD)
|
||||||
filter port(ff, \D).extract(offset, GetSize(argD)) == argD
|
filter port(ff, \D).extract(offset, GetSize(argD)) == argD
|
||||||
|
|
||||||
filter clock == SigBit() || port(ff, \CLK) == clock
|
filter clock == SigBit() || port(ff, \CLK)[0] == clock
|
||||||
endmatch
|
endmatch
|
||||||
|
|
||||||
code argQ
|
code argQ
|
||||||
|
|
|
@ -135,7 +135,7 @@ match ff
|
||||||
filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
|
filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
|
||||||
filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
|
filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
|
||||||
|
|
||||||
filter clock == SigBit() || port(ff, \CLK) == clock
|
filter clock == SigBit() || port(ff, \CLK)[0] == clock
|
||||||
endmatch
|
endmatch
|
||||||
|
|
||||||
code argQ
|
code argQ
|
||||||
|
|
|
@ -46,7 +46,7 @@ pattern xilinx_dsp_cascade
|
||||||
udata <std::function<SigSpec(const SigSpec&)>> unextend
|
udata <std::function<SigSpec(const SigSpec&)>> unextend
|
||||||
udata <vector<std::tuple<Cell*,int,int,int>>> chain longest_chain
|
udata <vector<std::tuple<Cell*,int,int,int>>> chain longest_chain
|
||||||
state <Cell*> next
|
state <Cell*> next
|
||||||
state <SigSpec> clock
|
state <SigBit> clock
|
||||||
state <int> AREG BREG
|
state <int> AREG BREG
|
||||||
|
|
||||||
// Variables used for subpatterns
|
// Variables used for subpatterns
|
||||||
|
@ -395,7 +395,7 @@ match ff
|
||||||
filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
|
filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
|
||||||
filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
|
filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
|
||||||
|
|
||||||
filter clock == SigBit() || port(ff, \CLK) == clock
|
filter clock == SigBit() || port(ff, \CLK)[0] == clock
|
||||||
endmatch
|
endmatch
|
||||||
|
|
||||||
code argQ
|
code argQ
|
||||||
|
|
|
@ -46,7 +46,7 @@ struct proc_dlatch_db_t
|
||||||
|
|
||||||
for (auto cell : module->cells())
|
for (auto cell : module->cells())
|
||||||
{
|
{
|
||||||
if (cell->type.in(ID($mux), ID($pmux)))
|
if (cell->type.in(ID($mux), ID($pmux), ID($bwmux)))
|
||||||
{
|
{
|
||||||
auto sig_y = sigmap(cell->getPort(ID::Y));
|
auto sig_y = sigmap(cell->getPort(ID::Y));
|
||||||
for (int i = 0; i < GetSize(sig_y); i++)
|
for (int i = 0; i < GetSize(sig_y); i++)
|
||||||
|
@ -186,6 +186,8 @@ struct proc_dlatch_db_t
|
||||||
Cell *cell = it->second.first;
|
Cell *cell = it->second.first;
|
||||||
int index = it->second.second;
|
int index = it->second.second;
|
||||||
|
|
||||||
|
log_assert(cell->type.in(ID($mux), ID($pmux), ID($bwmux)));
|
||||||
|
bool is_bwmux = (cell->type == ID($bwmux));
|
||||||
SigSpec sig_a = sigmap(cell->getPort(ID::A));
|
SigSpec sig_a = sigmap(cell->getPort(ID::A));
|
||||||
SigSpec sig_b = sigmap(cell->getPort(ID::B));
|
SigSpec sig_b = sigmap(cell->getPort(ID::B));
|
||||||
SigSpec sig_s = sigmap(cell->getPort(ID::S));
|
SigSpec sig_s = sigmap(cell->getPort(ID::S));
|
||||||
|
@ -200,12 +202,16 @@ struct proc_dlatch_db_t
|
||||||
sig[index] = State::Sx;
|
sig[index] = State::Sx;
|
||||||
cell->setPort(ID::A, sig);
|
cell->setPort(ID::A, sig);
|
||||||
}
|
}
|
||||||
for (int i = 0; i < GetSize(sig_s); i++)
|
if (!is_bwmux) {
|
||||||
n = make_inner(sig_s[i], State::S0, n);
|
for (int i = 0; i < GetSize(sig_s); i++)
|
||||||
|
n = make_inner(sig_s[i], State::S0, n);
|
||||||
|
} else {
|
||||||
|
n = make_inner(sig_s[index], State::S0, n);
|
||||||
|
}
|
||||||
children.insert(n);
|
children.insert(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < GetSize(sig_s); i++) {
|
for (int i = 0; i < (is_bwmux ? 1 : GetSize(sig_s)); i++) {
|
||||||
n = find_mux_feedback(sig_b[i*width + index], needle, set_undef);
|
n = find_mux_feedback(sig_b[i*width + index], needle, set_undef);
|
||||||
if (n != false_node) {
|
if (n != false_node) {
|
||||||
if (set_undef && sig_b[i*width + index] == needle) {
|
if (set_undef && sig_b[i*width + index] == needle) {
|
||||||
|
@ -213,7 +219,7 @@ struct proc_dlatch_db_t
|
||||||
sig[i*width + index] = State::Sx;
|
sig[i*width + index] = State::Sx;
|
||||||
cell->setPort(ID::B, sig);
|
cell->setPort(ID::B, sig);
|
||||||
}
|
}
|
||||||
children.insert(make_inner(sig_s[i], State::S1, n));
|
children.insert(make_inner(sig_s[is_bwmux ? index : i], State::S1, n));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -66,6 +66,11 @@ struct RomWorker
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lhs.empty()) {
|
||||||
|
log_debug("rejecting switch: lhs empty\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int swsigbits = 0;
|
int swsigbits = 0;
|
||||||
for (int i = 0; i < GetSize(sw->signal); i++)
|
for (int i = 0; i < GetSize(sw->signal); i++)
|
||||||
if (sw->signal[i] != State::S0)
|
if (sw->signal[i] != State::S0)
|
||||||
|
|
|
@ -41,31 +41,88 @@ struct Async2syncPass : public Pass {
|
||||||
log("reset value in the next cycle regardless of the data-in value at the time of\n");
|
log("reset value in the next cycle regardless of the data-in value at the time of\n");
|
||||||
log("the clock edge.\n");
|
log("the clock edge.\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
|
log(" -nolower\n");
|
||||||
|
log(" Do not automatically run 'chformal -lower' to lower $check cells.\n");
|
||||||
|
log("\n");
|
||||||
}
|
}
|
||||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||||
{
|
{
|
||||||
// bool flag_noinit = false;
|
bool flag_nolower = false;
|
||||||
|
|
||||||
log_header(design, "Executing ASYNC2SYNC pass.\n");
|
log_header(design, "Executing ASYNC2SYNC pass.\n");
|
||||||
|
|
||||||
size_t argidx;
|
size_t argidx;
|
||||||
for (argidx = 1; argidx < args.size(); argidx++)
|
for (argidx = 1; argidx < args.size(); argidx++)
|
||||||
{
|
{
|
||||||
// if (args[argidx] == "-noinit") {
|
if (args[argidx] == "-nolower") {
|
||||||
// flag_noinit = true;
|
flag_nolower = true;
|
||||||
// continue;
|
continue;
|
||||||
// }
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
extra_args(args, argidx, design);
|
extra_args(args, argidx, design);
|
||||||
|
|
||||||
|
bool have_check_cells = false;
|
||||||
|
|
||||||
for (auto module : design->selected_modules())
|
for (auto module : design->selected_modules())
|
||||||
{
|
{
|
||||||
SigMap sigmap(module);
|
SigMap sigmap(module);
|
||||||
FfInitVals initvals(&sigmap, module);
|
FfInitVals initvals(&sigmap, module);
|
||||||
|
|
||||||
|
SigBit initstate;
|
||||||
|
|
||||||
for (auto cell : vector<Cell*>(module->selected_cells()))
|
for (auto cell : vector<Cell*>(module->selected_cells()))
|
||||||
{
|
{
|
||||||
|
if (cell->type.in(ID($print), ID($check)))
|
||||||
|
{
|
||||||
|
if (cell->type == ID($check))
|
||||||
|
have_check_cells = true;
|
||||||
|
|
||||||
|
bool trg_enable = cell->getParam(ID(TRG_ENABLE)).as_bool();
|
||||||
|
if (!trg_enable)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int trg_width = cell->getParam(ID(TRG_WIDTH)).as_int();
|
||||||
|
|
||||||
|
if (trg_width > 1)
|
||||||
|
log_error("$check cell %s with TRG_WIDTH > 1 is not support by async2sync, use clk2fflogic.\n", log_id(cell));
|
||||||
|
|
||||||
|
if (trg_width == 0) {
|
||||||
|
if (initstate == State::S0)
|
||||||
|
initstate = module->Initstate(NEW_ID);
|
||||||
|
|
||||||
|
SigBit sig_en = cell->getPort(ID::EN);
|
||||||
|
cell->setPort(ID::EN, module->And(NEW_ID, sig_en, initstate));
|
||||||
|
} else {
|
||||||
|
SigBit sig_en = cell->getPort(ID::EN);
|
||||||
|
SigSpec sig_args = cell->getPort(ID::ARGS);
|
||||||
|
bool trg_polarity = cell->getParam(ID(TRG_POLARITY)).as_bool();
|
||||||
|
SigBit sig_trg = cell->getPort(ID::TRG);
|
||||||
|
Wire *sig_en_q = module->addWire(NEW_ID);
|
||||||
|
Wire *sig_args_q = module->addWire(NEW_ID, GetSize(sig_args));
|
||||||
|
sig_en_q->attributes.emplace(ID::init, State::S0);
|
||||||
|
module->addDff(NEW_ID, sig_trg, sig_en, sig_en_q, trg_polarity, cell->get_src_attribute());
|
||||||
|
module->addDff(NEW_ID, sig_trg, sig_args, sig_args_q, trg_polarity, cell->get_src_attribute());
|
||||||
|
cell->setPort(ID::EN, sig_en_q);
|
||||||
|
cell->setPort(ID::ARGS, sig_args_q);
|
||||||
|
if (cell->type == ID($check)) {
|
||||||
|
SigBit sig_a = cell->getPort(ID::A);
|
||||||
|
Wire *sig_a_q = module->addWire(NEW_ID);
|
||||||
|
sig_a_q->attributes.emplace(ID::init, State::S1);
|
||||||
|
module->addDff(NEW_ID, sig_trg, sig_a, sig_a_q, trg_polarity, cell->get_src_attribute());
|
||||||
|
cell->setPort(ID::A, sig_a_q);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cell->setPort(ID::TRG, SigSpec());
|
||||||
|
|
||||||
|
cell->setParam(ID::TRG_ENABLE, false);
|
||||||
|
cell->setParam(ID::TRG_WIDTH, 0);
|
||||||
|
cell->setParam(ID::TRG_POLARITY, false);
|
||||||
|
cell->set_bool_attribute(ID(trg_on_gclk));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!RTLIL::builtin_ff_cell_types().count(cell->type))
|
if (!RTLIL::builtin_ff_cell_types().count(cell->type))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -273,6 +330,12 @@ struct Async2syncPass : public Pass {
|
||||||
ff.emit();
|
ff.emit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (have_check_cells && !flag_nolower) {
|
||||||
|
log_push();
|
||||||
|
Pass::call(design, "chformal -lower");
|
||||||
|
log_pop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} Async2syncPass;
|
} Async2syncPass;
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,9 @@ struct Clk2fflogicPass : public Pass {
|
||||||
log("reset value in the next cycle regardless of the data-in value at the time of\n");
|
log("reset value in the next cycle regardless of the data-in value at the time of\n");
|
||||||
log("the clock edge.\n");
|
log("the clock edge.\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
|
log(" -nolower\n");
|
||||||
|
log(" Do not automatically run 'chformal -lower' to lower $check cells.\n");
|
||||||
|
log("\n");
|
||||||
}
|
}
|
||||||
// Active-high sampled and current value of a level-triggered control signal. Initial sampled values is low/non-asserted.
|
// Active-high sampled and current value of a level-triggered control signal. Initial sampled values is low/non-asserted.
|
||||||
SampledSig sample_control(Module *module, SigSpec sig, bool polarity, bool is_fine) {
|
SampledSig sample_control(Module *module, SigSpec sig, bool polarity, bool is_fine) {
|
||||||
|
@ -117,21 +120,23 @@ struct Clk2fflogicPass : public Pass {
|
||||||
}
|
}
|
||||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||||
{
|
{
|
||||||
// bool flag_noinit = false;
|
bool flag_nolower = false;
|
||||||
|
|
||||||
log_header(design, "Executing CLK2FFLOGIC pass (convert clocked FFs to generic $ff cells).\n");
|
log_header(design, "Executing CLK2FFLOGIC pass (convert clocked FFs to generic $ff cells).\n");
|
||||||
|
|
||||||
size_t argidx;
|
size_t argidx;
|
||||||
for (argidx = 1; argidx < args.size(); argidx++)
|
for (argidx = 1; argidx < args.size(); argidx++)
|
||||||
{
|
{
|
||||||
// if (args[argidx] == "-noinit") {
|
if (args[argidx] == "-nolower") {
|
||||||
// flag_noinit = true;
|
flag_nolower = true;
|
||||||
// continue;
|
continue;
|
||||||
// }
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
extra_args(args, argidx, design);
|
extra_args(args, argidx, design);
|
||||||
|
|
||||||
|
bool have_check_cells = false;
|
||||||
|
|
||||||
for (auto module : design->selected_modules())
|
for (auto module : design->selected_modules())
|
||||||
{
|
{
|
||||||
SigMap sigmap(module);
|
SigMap sigmap(module);
|
||||||
|
@ -194,79 +199,138 @@ struct Clk2fflogicPass : public Pass {
|
||||||
mem.emit();
|
mem.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SigBit initstate;
|
||||||
|
|
||||||
for (auto cell : vector<Cell*>(module->selected_cells()))
|
for (auto cell : vector<Cell*>(module->selected_cells()))
|
||||||
{
|
{
|
||||||
SigSpec qval;
|
if (cell->type.in(ID($print), ID($check)))
|
||||||
if (RTLIL::builtin_ff_cell_types().count(cell->type)) {
|
{
|
||||||
FfData ff(&initvals, cell);
|
if (cell->type == ID($check))
|
||||||
|
have_check_cells = true;
|
||||||
|
|
||||||
if (ff.has_gclk) {
|
bool trg_enable = cell->getParam(ID(TRG_ENABLE)).as_bool();
|
||||||
// Already a $ff or $_FF_ cell.
|
if (!trg_enable)
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
if (ff.has_clk) {
|
int trg_width = cell->getParam(ID(TRG_WIDTH)).as_int();
|
||||||
log("Replacing %s.%s (%s): CLK=%s, D=%s, Q=%s\n",
|
|
||||||
log_id(module), log_id(cell), log_id(cell->type),
|
if (trg_width == 0) {
|
||||||
log_signal(ff.sig_clk), log_signal(ff.sig_d), log_signal(ff.sig_q));
|
if (initstate == State::S0)
|
||||||
} else if (ff.has_aload) {
|
initstate = module->Initstate(NEW_ID);
|
||||||
log("Replacing %s.%s (%s): EN=%s, D=%s, Q=%s\n",
|
|
||||||
log_id(module), log_id(cell), log_id(cell->type),
|
SigBit sig_en = cell->getPort(ID::EN);
|
||||||
log_signal(ff.sig_aload), log_signal(ff.sig_ad), log_signal(ff.sig_q));
|
cell->setPort(ID::EN, module->And(NEW_ID, sig_en, initstate));
|
||||||
} else {
|
} else {
|
||||||
// $sr.
|
SigBit sig_en = cell->getPort(ID::EN);
|
||||||
log("Replacing %s.%s (%s): SET=%s, CLR=%s, Q=%s\n",
|
SigSpec sig_args = cell->getPort(ID::ARGS);
|
||||||
log_id(module), log_id(cell), log_id(cell->type),
|
Const trg_polarity = cell->getParam(ID(TRG_POLARITY));
|
||||||
log_signal(ff.sig_set), log_signal(ff.sig_clr), log_signal(ff.sig_q));
|
SigSpec sig_trg = cell->getPort(ID::TRG);
|
||||||
|
|
||||||
|
SigSpec sig_trg_sampled;
|
||||||
|
|
||||||
|
for (auto const &bit : sig_trg)
|
||||||
|
sig_trg_sampled.append(sample_control_edge(module, bit, trg_polarity[GetSize(sig_trg_sampled)] == State::S1, false));
|
||||||
|
SigSpec sig_args_sampled = sample_data(module, sig_args, Const(State::S0, GetSize(sig_args)), false, false).sampled;
|
||||||
|
SigBit sig_en_sampled = sample_data(module, sig_en, State::S0, false, false).sampled;
|
||||||
|
|
||||||
|
SigBit sig_trg_combined = module->ReduceOr(NEW_ID, sig_trg_sampled);
|
||||||
|
|
||||||
|
cell->setPort(ID::EN, module->And(NEW_ID, sig_en_sampled, sig_trg_combined));
|
||||||
|
cell->setPort(ID::ARGS, sig_args_sampled);
|
||||||
|
if (cell->type == ID($check)) {
|
||||||
|
SigBit sig_a = cell->getPort(ID::A);
|
||||||
|
SigBit sig_a_sampled = sample_data(module, sig_a, State::S1, false, false).sampled;
|
||||||
|
cell->setPort(ID::A, sig_a_sampled);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ff.remove();
|
cell->setPort(ID::TRG, SigSpec());
|
||||||
|
|
||||||
if (ff.has_clk)
|
cell->setParam(ID::TRG_ENABLE, false);
|
||||||
ff.unmap_ce_srst();
|
cell->setParam(ID::TRG_WIDTH, 0);
|
||||||
|
cell->setParam(ID::TRG_POLARITY, false);
|
||||||
|
cell->set_bool_attribute(ID(trg_on_gclk));
|
||||||
|
|
||||||
auto next_q = sample_data(module, ff.sig_q, ff.val_init, ff.is_fine, true).sampled;
|
continue;
|
||||||
|
|
||||||
if (ff.has_clk) {
|
|
||||||
// The init value for the sampled d is never used, so we can set it to fixed zero, reducing uninit'd FFs
|
|
||||||
auto sampled_d = sample_data(module, ff.sig_d, RTLIL::Const(State::S0, ff.width), ff.is_fine);
|
|
||||||
auto clk_edge = sample_control_edge(module, ff.sig_clk, ff.pol_clk, ff.is_fine);
|
|
||||||
next_q = mux(module, next_q, sampled_d.sampled, clk_edge, ff.is_fine);
|
|
||||||
}
|
|
||||||
|
|
||||||
SampledSig sampled_aload, sampled_ad, sampled_set, sampled_clr, sampled_arst;
|
|
||||||
// The check for a constant sig_aload is also done by opt_dff, but when using verific and running
|
|
||||||
// clk2fflogic before opt_dff (which does more and possibly unwanted optimizations) this check avoids
|
|
||||||
// generating a lot of extra logic.
|
|
||||||
bool has_nonconst_aload = ff.has_aload && ff.sig_aload != (ff.pol_aload ? State::S0 : State::S1);
|
|
||||||
if (has_nonconst_aload) {
|
|
||||||
sampled_aload = sample_control(module, ff.sig_aload, ff.pol_aload, ff.is_fine);
|
|
||||||
// The init value for the sampled ad is never used, so we can set it to fixed zero, reducing uninit'd FFs
|
|
||||||
sampled_ad = sample_data(module, ff.sig_ad, RTLIL::Const(State::S0, ff.width), ff.is_fine);
|
|
||||||
}
|
|
||||||
if (ff.has_sr) {
|
|
||||||
sampled_set = sample_control(module, ff.sig_set, ff.pol_set, ff.is_fine);
|
|
||||||
sampled_clr = sample_control(module, ff.sig_clr, ff.pol_clr, ff.is_fine);
|
|
||||||
}
|
|
||||||
if (ff.has_arst)
|
|
||||||
sampled_arst = sample_control(module, ff.sig_arst, ff.pol_arst, ff.is_fine);
|
|
||||||
|
|
||||||
// First perform updates using _only_ sampled values, then again using _only_ current values. Unlike the previous
|
|
||||||
// implementation, this approach correctly handles all the cases of multiple signals changing simultaneously.
|
|
||||||
for (int current = 0; current < 2; current++) {
|
|
||||||
if (has_nonconst_aload)
|
|
||||||
next_q = mux(module, next_q, sampled_ad[current], sampled_aload[current], ff.is_fine);
|
|
||||||
if (ff.has_sr)
|
|
||||||
next_q = bitwise_sr(module, next_q, sampled_set[current], sampled_clr[current], ff.is_fine);
|
|
||||||
if (ff.has_arst)
|
|
||||||
next_q = mux(module, next_q, ff.val_arst, sampled_arst[current], ff.is_fine);
|
|
||||||
}
|
|
||||||
|
|
||||||
module->connect(ff.sig_q, next_q);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!RTLIL::builtin_ff_cell_types().count(cell->type))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
FfData ff(&initvals, cell);
|
||||||
|
|
||||||
|
if (ff.has_gclk) {
|
||||||
|
// Already a $ff or $_FF_ cell.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ff.has_clk) {
|
||||||
|
log("Replacing %s.%s (%s): CLK=%s, D=%s, Q=%s\n",
|
||||||
|
log_id(module), log_id(cell), log_id(cell->type),
|
||||||
|
log_signal(ff.sig_clk), log_signal(ff.sig_d), log_signal(ff.sig_q));
|
||||||
|
} else if (ff.has_aload) {
|
||||||
|
log("Replacing %s.%s (%s): EN=%s, D=%s, Q=%s\n",
|
||||||
|
log_id(module), log_id(cell), log_id(cell->type),
|
||||||
|
log_signal(ff.sig_aload), log_signal(ff.sig_ad), log_signal(ff.sig_q));
|
||||||
|
} else {
|
||||||
|
// $sr.
|
||||||
|
log("Replacing %s.%s (%s): SET=%s, CLR=%s, Q=%s\n",
|
||||||
|
log_id(module), log_id(cell), log_id(cell->type),
|
||||||
|
log_signal(ff.sig_set), log_signal(ff.sig_clr), log_signal(ff.sig_q));
|
||||||
|
}
|
||||||
|
|
||||||
|
ff.remove();
|
||||||
|
|
||||||
|
if (ff.has_clk)
|
||||||
|
ff.unmap_ce_srst();
|
||||||
|
|
||||||
|
auto next_q = sample_data(module, ff.sig_q, ff.val_init, ff.is_fine, true).sampled;
|
||||||
|
|
||||||
|
if (ff.has_clk) {
|
||||||
|
// The init value for the sampled d is never used, so we can set it to fixed zero, reducing uninit'd FFs
|
||||||
|
auto sampled_d = sample_data(module, ff.sig_d, RTLIL::Const(State::S0, ff.width), ff.is_fine);
|
||||||
|
auto clk_edge = sample_control_edge(module, ff.sig_clk, ff.pol_clk, ff.is_fine);
|
||||||
|
next_q = mux(module, next_q, sampled_d.sampled, clk_edge, ff.is_fine);
|
||||||
|
}
|
||||||
|
|
||||||
|
SampledSig sampled_aload, sampled_ad, sampled_set, sampled_clr, sampled_arst;
|
||||||
|
// The check for a constant sig_aload is also done by opt_dff, but when using verific and running
|
||||||
|
// clk2fflogic before opt_dff (which does more and possibly unwanted optimizations) this check avoids
|
||||||
|
// generating a lot of extra logic.
|
||||||
|
bool has_nonconst_aload = ff.has_aload && ff.sig_aload != (ff.pol_aload ? State::S0 : State::S1);
|
||||||
|
if (has_nonconst_aload) {
|
||||||
|
sampled_aload = sample_control(module, ff.sig_aload, ff.pol_aload, ff.is_fine);
|
||||||
|
// The init value for the sampled ad is never used, so we can set it to fixed zero, reducing uninit'd FFs
|
||||||
|
sampled_ad = sample_data(module, ff.sig_ad, RTLIL::Const(State::S0, ff.width), ff.is_fine);
|
||||||
|
}
|
||||||
|
if (ff.has_sr) {
|
||||||
|
sampled_set = sample_control(module, ff.sig_set, ff.pol_set, ff.is_fine);
|
||||||
|
sampled_clr = sample_control(module, ff.sig_clr, ff.pol_clr, ff.is_fine);
|
||||||
|
}
|
||||||
|
if (ff.has_arst)
|
||||||
|
sampled_arst = sample_control(module, ff.sig_arst, ff.pol_arst, ff.is_fine);
|
||||||
|
|
||||||
|
// First perform updates using _only_ sampled values, then again using _only_ current values. Unlike the previous
|
||||||
|
// implementation, this approach correctly handles all the cases of multiple signals changing simultaneously.
|
||||||
|
for (int current = 0; current < 2; current++) {
|
||||||
|
if (has_nonconst_aload)
|
||||||
|
next_q = mux(module, next_q, sampled_ad[current], sampled_aload[current], ff.is_fine);
|
||||||
|
if (ff.has_sr)
|
||||||
|
next_q = bitwise_sr(module, next_q, sampled_set[current], sampled_clr[current], ff.is_fine);
|
||||||
|
if (ff.has_arst)
|
||||||
|
next_q = mux(module, next_q, ff.val_arst, sampled_arst[current], ff.is_fine);
|
||||||
|
}
|
||||||
|
|
||||||
|
module->connect(ff.sig_q, next_q);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (have_check_cells && !flag_nolower) {
|
||||||
|
log_push();
|
||||||
|
Pass::call(design, "chformal -lower");
|
||||||
|
log_pop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} Clk2fflogicPass;
|
} Clk2fflogicPass;
|
||||||
|
|
||||||
|
|
|
@ -813,7 +813,7 @@ struct SimInstance
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_ph3(bool check_assertions)
|
void update_ph3(bool gclk_trigger)
|
||||||
{
|
{
|
||||||
for (auto &it : ff_database)
|
for (auto &it : ff_database)
|
||||||
{
|
{
|
||||||
|
@ -858,49 +858,53 @@ struct SimInstance
|
||||||
Const en = get_state(cell->getPort(ID::EN));
|
Const en = get_state(cell->getPort(ID::EN));
|
||||||
Const args = get_state(cell->getPort(ID::ARGS));
|
Const args = get_state(cell->getPort(ID::ARGS));
|
||||||
|
|
||||||
if (!en.as_bool())
|
bool sampled = trg_en && trg.size() > 0;
|
||||||
goto update_print;
|
|
||||||
|
|
||||||
if (trg.size() > 0 && trg_en) {
|
if (sampled ? print.past_en.as_bool() : en.as_bool()) {
|
||||||
Const trg_pol = cell->getParam(ID::TRG_POLARITY);
|
if (sampled) {
|
||||||
for (int i = 0; i < trg.size(); i++) {
|
sampled = true;
|
||||||
bool pol = trg_pol[i] == State::S1;
|
Const trg_pol = cell->getParam(ID::TRG_POLARITY);
|
||||||
State curr = trg[i], past = print.past_trg[i];
|
for (int i = 0; i < trg.size(); i++) {
|
||||||
if (pol && curr == State::S1 && past == State::S0)
|
bool pol = trg_pol[i] == State::S1;
|
||||||
|
State curr = trg[i], past = print.past_trg[i];
|
||||||
|
if (pol && curr == State::S1 && past == State::S0)
|
||||||
|
triggered = true;
|
||||||
|
if (!pol && curr == State::S0 && past == State::S1)
|
||||||
|
triggered = true;
|
||||||
|
}
|
||||||
|
} else if (trg_en) {
|
||||||
|
// initial $print (TRG width = 0, TRG_ENABLE = true)
|
||||||
|
if (!print.initial_done && en != print.past_en)
|
||||||
triggered = true;
|
triggered = true;
|
||||||
if (!pol && curr == State::S0 && past == State::S1)
|
} else if (cell->get_bool_attribute(ID(trg_on_gclk))) {
|
||||||
|
// unified $print for cycle based FV semantics
|
||||||
|
triggered = gclk_trigger;
|
||||||
|
} else {
|
||||||
|
// always @(*) $print
|
||||||
|
if (args != print.past_args || en != print.past_en)
|
||||||
triggered = true;
|
triggered = true;
|
||||||
}
|
}
|
||||||
} else if (trg_en) {
|
|
||||||
// initial $print (TRG width = 0, TRG_ENABLE = true)
|
|
||||||
if (!print.initial_done && en != print.past_en)
|
|
||||||
triggered = true;
|
|
||||||
} else {
|
|
||||||
// always @(*) $print
|
|
||||||
if (args != print.past_args || en != print.past_en)
|
|
||||||
triggered = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (triggered) {
|
if (triggered) {
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
for (auto &part : print.fmt.parts) {
|
for (auto &part : print.fmt.parts) {
|
||||||
part.sig = args.extract(pos, part.sig.size());
|
part.sig = (sampled ? print.past_args : args).extract(pos, part.sig.size());
|
||||||
pos += part.sig.size();
|
pos += part.sig.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string rendered = print.fmt.render();
|
||||||
|
log("%s", rendered.c_str());
|
||||||
|
shared->display_output.emplace_back(shared->step, this, cell, rendered);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string rendered = print.fmt.render();
|
|
||||||
log("%s", rendered.c_str());
|
|
||||||
shared->display_output.emplace_back(shared->step, this, cell, rendered);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
update_print:
|
|
||||||
print.past_trg = trg;
|
print.past_trg = trg;
|
||||||
print.past_en = en;
|
print.past_en = en;
|
||||||
print.past_args = args;
|
print.past_args = args;
|
||||||
print.initial_done = true;
|
print.initial_done = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (check_assertions)
|
if (gclk_trigger)
|
||||||
{
|
{
|
||||||
for (auto cell : formal_database)
|
for (auto cell : formal_database)
|
||||||
{
|
{
|
||||||
|
@ -932,7 +936,7 @@ struct SimInstance
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto it : children)
|
for (auto it : children)
|
||||||
it.second->update_ph3(check_assertions);
|
it.second->update_ph3(gclk_trigger);
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_initstate_outputs(State state)
|
void set_initstate_outputs(State state)
|
||||||
|
|
|
@ -56,5 +56,5 @@ EXTRA_OBJS += passes/techmap/filterlib.o
|
||||||
|
|
||||||
$(PROGRAM_PREFIX)yosys-filterlib$(EXE): passes/techmap/filterlib.o
|
$(PROGRAM_PREFIX)yosys-filterlib$(EXE): passes/techmap/filterlib.o
|
||||||
$(Q) mkdir -p $(dir $@)
|
$(Q) mkdir -p $(dir $@)
|
||||||
$(P) $(LD) -o $(PROGRAM_PREFIX)yosys-filterlib$(EXE) $(LDFLAGS) $^ $(LDLIBS)
|
$(P) $(CXX) -o $(PROGRAM_PREFIX)yosys-filterlib$(EXE) $(LINKFLAGS) $^ $(LIBS)
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -115,7 +115,7 @@ static bool parse_pin(LibertyAst *cell, LibertyAst *attr, std::string &pin_name,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void find_cell(LibertyAst *ast, IdString cell_type, bool clkpol, bool has_reset, bool rstpol, bool rstval)
|
static void find_cell(LibertyAst *ast, IdString cell_type, bool clkpol, bool has_reset, bool rstpol, bool rstval, std::vector<std::string> &dont_use_cells)
|
||||||
{
|
{
|
||||||
LibertyAst *best_cell = nullptr;
|
LibertyAst *best_cell = nullptr;
|
||||||
std::map<std::string, char> best_cell_ports;
|
std::map<std::string, char> best_cell_ports;
|
||||||
|
@ -135,6 +135,18 @@ static void find_cell(LibertyAst *ast, IdString cell_type, bool clkpol, bool has
|
||||||
if (dn != nullptr && dn->value == "true")
|
if (dn != nullptr && dn->value == "true")
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
bool dont_use = false;
|
||||||
|
for (std::string &dont_use_cell : dont_use_cells)
|
||||||
|
{
|
||||||
|
if (patmatch(dont_use_cell.c_str(), cell->args[0].c_str()))
|
||||||
|
{
|
||||||
|
dont_use = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dont_use)
|
||||||
|
continue;
|
||||||
|
|
||||||
LibertyAst *ff = cell->find("ff");
|
LibertyAst *ff = cell->find("ff");
|
||||||
if (ff == nullptr)
|
if (ff == nullptr)
|
||||||
continue;
|
continue;
|
||||||
|
@ -227,7 +239,7 @@ static void find_cell(LibertyAst *ast, IdString cell_type, bool clkpol, bool has
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void find_cell_sr(LibertyAst *ast, IdString cell_type, bool clkpol, bool setpol, bool clrpol)
|
static void find_cell_sr(LibertyAst *ast, IdString cell_type, bool clkpol, bool setpol, bool clrpol, std::vector<std::string> &dont_use_cells)
|
||||||
{
|
{
|
||||||
LibertyAst *best_cell = nullptr;
|
LibertyAst *best_cell = nullptr;
|
||||||
std::map<std::string, char> best_cell_ports;
|
std::map<std::string, char> best_cell_ports;
|
||||||
|
@ -247,6 +259,18 @@ static void find_cell_sr(LibertyAst *ast, IdString cell_type, bool clkpol, bool
|
||||||
if (dn != nullptr && dn->value == "true")
|
if (dn != nullptr && dn->value == "true")
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
bool dont_use = false;
|
||||||
|
for (std::string &dont_use_cell : dont_use_cells)
|
||||||
|
{
|
||||||
|
if (patmatch(dont_use_cell.c_str(), cell->args[0].c_str()))
|
||||||
|
{
|
||||||
|
dont_use = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dont_use)
|
||||||
|
continue;
|
||||||
|
|
||||||
LibertyAst *ff = cell->find("ff");
|
LibertyAst *ff = cell->find("ff");
|
||||||
if (ff == nullptr)
|
if (ff == nullptr)
|
||||||
continue;
|
continue;
|
||||||
|
@ -414,7 +438,7 @@ struct DfflibmapPass : public Pass {
|
||||||
void help() override
|
void help() override
|
||||||
{
|
{
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" dfflibmap [-prepare] [-map-only] [-info] -liberty <file> [selection]\n");
|
log(" dfflibmap [-prepare] [-map-only] [-info] [-dont_use <cell_name>] -liberty <file> [selection]\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log("Map internal flip-flop cells to the flip-flop cells in the technology\n");
|
log("Map internal flip-flop cells to the flip-flop cells in the technology\n");
|
||||||
log("library specified in the given liberty file.\n");
|
log("library specified in the given liberty file.\n");
|
||||||
|
@ -435,6 +459,11 @@ struct DfflibmapPass : public Pass {
|
||||||
log("that would be passed to the dfflegalize pass. The design will not be\n");
|
log("that would be passed to the dfflegalize pass. The design will not be\n");
|
||||||
log("changed.\n");
|
log("changed.\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
|
log("When called with -dont_use, this command will not map to the specified cell\n");
|
||||||
|
log("name as an alternative to setting the dont_use property in the Liberty file.\n");
|
||||||
|
log("This argument can be called multiple times with different cell names. This\n");
|
||||||
|
log("argument also supports simple glob patterns in the cell name.\n");
|
||||||
|
log("\n");
|
||||||
}
|
}
|
||||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||||
{
|
{
|
||||||
|
@ -446,6 +475,8 @@ struct DfflibmapPass : public Pass {
|
||||||
bool map_only_mode = false;
|
bool map_only_mode = false;
|
||||||
bool info_mode = false;
|
bool info_mode = false;
|
||||||
|
|
||||||
|
std::vector<std::string> dont_use_cells;
|
||||||
|
|
||||||
size_t argidx;
|
size_t argidx;
|
||||||
for (argidx = 1; argidx < args.size(); argidx++)
|
for (argidx = 1; argidx < args.size(); argidx++)
|
||||||
{
|
{
|
||||||
|
@ -467,6 +498,10 @@ struct DfflibmapPass : public Pass {
|
||||||
info_mode = true;
|
info_mode = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (arg == "-dont_use" && argidx+1 < args.size()) {
|
||||||
|
dont_use_cells.push_back(args[++argidx]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
extra_args(args, argidx, design);
|
extra_args(args, argidx, design);
|
||||||
|
@ -491,26 +526,26 @@ struct DfflibmapPass : public Pass {
|
||||||
LibertyParser libparser(f);
|
LibertyParser libparser(f);
|
||||||
f.close();
|
f.close();
|
||||||
|
|
||||||
find_cell(libparser.ast, ID($_DFF_N_), false, false, false, false);
|
find_cell(libparser.ast, ID($_DFF_N_), false, false, false, false, dont_use_cells);
|
||||||
find_cell(libparser.ast, ID($_DFF_P_), true, false, false, false);
|
find_cell(libparser.ast, ID($_DFF_P_), true, false, false, false, dont_use_cells);
|
||||||
|
|
||||||
find_cell(libparser.ast, ID($_DFF_NN0_), false, true, false, false);
|
find_cell(libparser.ast, ID($_DFF_NN0_), false, true, false, false, dont_use_cells);
|
||||||
find_cell(libparser.ast, ID($_DFF_NN1_), false, true, false, true);
|
find_cell(libparser.ast, ID($_DFF_NN1_), false, true, false, true, dont_use_cells);
|
||||||
find_cell(libparser.ast, ID($_DFF_NP0_), false, true, true, false);
|
find_cell(libparser.ast, ID($_DFF_NP0_), false, true, true, false, dont_use_cells);
|
||||||
find_cell(libparser.ast, ID($_DFF_NP1_), false, true, true, true);
|
find_cell(libparser.ast, ID($_DFF_NP1_), false, true, true, true, dont_use_cells);
|
||||||
find_cell(libparser.ast, ID($_DFF_PN0_), true, true, false, false);
|
find_cell(libparser.ast, ID($_DFF_PN0_), true, true, false, false, dont_use_cells);
|
||||||
find_cell(libparser.ast, ID($_DFF_PN1_), true, true, false, true);
|
find_cell(libparser.ast, ID($_DFF_PN1_), true, true, false, true, dont_use_cells);
|
||||||
find_cell(libparser.ast, ID($_DFF_PP0_), true, true, true, false);
|
find_cell(libparser.ast, ID($_DFF_PP0_), true, true, true, false, dont_use_cells);
|
||||||
find_cell(libparser.ast, ID($_DFF_PP1_), true, true, true, true);
|
find_cell(libparser.ast, ID($_DFF_PP1_), true, true, true, true, dont_use_cells);
|
||||||
|
|
||||||
find_cell_sr(libparser.ast, ID($_DFFSR_NNN_), false, false, false);
|
find_cell_sr(libparser.ast, ID($_DFFSR_NNN_), false, false, false, dont_use_cells);
|
||||||
find_cell_sr(libparser.ast, ID($_DFFSR_NNP_), false, false, true);
|
find_cell_sr(libparser.ast, ID($_DFFSR_NNP_), false, false, true, dont_use_cells);
|
||||||
find_cell_sr(libparser.ast, ID($_DFFSR_NPN_), false, true, false);
|
find_cell_sr(libparser.ast, ID($_DFFSR_NPN_), false, true, false, dont_use_cells);
|
||||||
find_cell_sr(libparser.ast, ID($_DFFSR_NPP_), false, true, true);
|
find_cell_sr(libparser.ast, ID($_DFFSR_NPP_), false, true, true, dont_use_cells);
|
||||||
find_cell_sr(libparser.ast, ID($_DFFSR_PNN_), true, false, false);
|
find_cell_sr(libparser.ast, ID($_DFFSR_PNN_), true, false, false, dont_use_cells);
|
||||||
find_cell_sr(libparser.ast, ID($_DFFSR_PNP_), true, false, true);
|
find_cell_sr(libparser.ast, ID($_DFFSR_PNP_), true, false, true, dont_use_cells);
|
||||||
find_cell_sr(libparser.ast, ID($_DFFSR_PPN_), true, true, false);
|
find_cell_sr(libparser.ast, ID($_DFFSR_PPN_), true, true, false, dont_use_cells);
|
||||||
find_cell_sr(libparser.ast, ID($_DFFSR_PPP_), true, true, true);
|
find_cell_sr(libparser.ast, ID($_DFFSR_PPP_), true, true, true, dont_use_cells);
|
||||||
|
|
||||||
log(" final dff cell mappings:\n");
|
log(" final dff cell mappings:\n");
|
||||||
logmap_all();
|
logmap_all();
|
||||||
|
|
|
@ -281,7 +281,7 @@ struct ExtractFaWorker
|
||||||
void assign_new_driver(SigBit bit, SigBit new_driver)
|
void assign_new_driver(SigBit bit, SigBit new_driver)
|
||||||
{
|
{
|
||||||
Cell *cell = driver.at(bit);
|
Cell *cell = driver.at(bit);
|
||||||
if (sigmap(cell->getPort(ID::Y)) == bit) {
|
if (sigmap(cell->getPort(ID::Y)) == SigSpec(bit)) {
|
||||||
cell->setPort(ID::Y, module->addWire(NEW_ID));
|
cell->setPort(ID::Y, module->addWire(NEW_ID));
|
||||||
module->connect(bit, new_driver);
|
module->connect(bit, new_driver);
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,24 +46,6 @@ IdString map_name(RTLIL::Cell *cell, T *object)
|
||||||
return cell->module->uniquify(concat_name(cell, object->name));
|
return cell->module->uniquify(concat_name(cell, object->name));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
|
||||||
void map_attributes(RTLIL::Cell *cell, T *object, IdString orig_object_name)
|
|
||||||
{
|
|
||||||
if (object->has_attribute(ID::src))
|
|
||||||
object->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src));
|
|
||||||
|
|
||||||
// Preserve original names via the hdlname attribute, but only for objects with a fully public name.
|
|
||||||
if (cell->name[0] == '\\' && (object->has_attribute(ID::hdlname) || orig_object_name[0] == '\\')) {
|
|
||||||
std::vector<std::string> hierarchy;
|
|
||||||
if (object->has_attribute(ID::hdlname))
|
|
||||||
hierarchy = object->get_hdlname_attribute();
|
|
||||||
else
|
|
||||||
hierarchy.push_back(orig_object_name.str().substr(1));
|
|
||||||
hierarchy.insert(hierarchy.begin(), cell->name.str().substr(1));
|
|
||||||
object->set_hdlname_attribute(hierarchy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void map_sigspec(const dict<RTLIL::Wire*, RTLIL::Wire*> &map, RTLIL::SigSpec &sig, RTLIL::Module *into = nullptr)
|
void map_sigspec(const dict<RTLIL::Wire*, RTLIL::Wire*> &map, RTLIL::SigSpec &sig, RTLIL::Module *into = nullptr)
|
||||||
{
|
{
|
||||||
vector<SigChunk> chunks = sig;
|
vector<SigChunk> chunks = sig;
|
||||||
|
@ -76,6 +58,54 @@ void map_sigspec(const dict<RTLIL::Wire*, RTLIL::Wire*> &map, RTLIL::SigSpec &si
|
||||||
struct FlattenWorker
|
struct FlattenWorker
|
||||||
{
|
{
|
||||||
bool ignore_wb = false;
|
bool ignore_wb = false;
|
||||||
|
bool create_scopeinfo = true;
|
||||||
|
bool create_scopename = false;
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
void map_attributes(RTLIL::Cell *cell, T *object, IdString orig_object_name)
|
||||||
|
{
|
||||||
|
if (!create_scopeinfo && object->has_attribute(ID::src))
|
||||||
|
object->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src));
|
||||||
|
|
||||||
|
// Preserve original names via the hdlname attribute, but only for objects with a fully public name.
|
||||||
|
// If the '-scopename' option is used, also preserve the containing scope of private objects if their scope is fully public.
|
||||||
|
if (cell->name[0] == '\\') {
|
||||||
|
if (object->has_attribute(ID::hdlname) || orig_object_name[0] == '\\') {
|
||||||
|
std::string new_hdlname;
|
||||||
|
|
||||||
|
if (cell->has_attribute(ID::hdlname)) {
|
||||||
|
new_hdlname = cell->get_string_attribute(ID(hdlname));
|
||||||
|
} else {
|
||||||
|
log_assert(!cell->name.empty());
|
||||||
|
new_hdlname = cell->name.c_str() + 1;
|
||||||
|
}
|
||||||
|
new_hdlname += ' ';
|
||||||
|
|
||||||
|
if (object->has_attribute(ID::hdlname)) {
|
||||||
|
new_hdlname += object->get_string_attribute(ID(hdlname));
|
||||||
|
} else {
|
||||||
|
log_assert(!orig_object_name.empty());
|
||||||
|
new_hdlname += orig_object_name.c_str() + 1;
|
||||||
|
}
|
||||||
|
object->set_string_attribute(ID(hdlname), new_hdlname);
|
||||||
|
} else if (object->has_attribute(ID(scopename))) {
|
||||||
|
std::string new_scopename;
|
||||||
|
|
||||||
|
if (cell->has_attribute(ID::hdlname)) {
|
||||||
|
new_scopename = cell->get_string_attribute(ID(hdlname));
|
||||||
|
} else {
|
||||||
|
log_assert(!cell->name.empty());
|
||||||
|
new_scopename = cell->name.c_str() + 1;
|
||||||
|
}
|
||||||
|
new_scopename += ' ';
|
||||||
|
new_scopename += object->get_string_attribute(ID(scopename));
|
||||||
|
object->set_string_attribute(ID(scopename), new_scopename);
|
||||||
|
} else if (create_scopename) {
|
||||||
|
log_assert(!cell->name.empty());
|
||||||
|
object->set_string_attribute(ID(scopename), cell->name.c_str() + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void flatten_cell(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Cell *cell, RTLIL::Module *tpl, SigMap &sigmap, std::vector<RTLIL::Cell*> &new_cells)
|
void flatten_cell(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Cell *cell, RTLIL::Module *tpl, SigMap &sigmap, std::vector<RTLIL::Cell*> &new_cells)
|
||||||
{
|
{
|
||||||
|
@ -220,7 +250,33 @@ struct FlattenWorker
|
||||||
sigmap.add(new_conn.first, new_conn.second);
|
sigmap.add(new_conn.first, new_conn.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RTLIL::Cell *scopeinfo = nullptr;
|
||||||
|
RTLIL::IdString cell_name = cell->name;
|
||||||
|
|
||||||
|
if (create_scopeinfo && cell_name.isPublic())
|
||||||
|
{
|
||||||
|
// The $scopeinfo's name will be changed below after removing the flattened cell
|
||||||
|
scopeinfo = module->addCell(NEW_ID, ID($scopeinfo));
|
||||||
|
scopeinfo->setParam(ID::TYPE, RTLIL::Const("module"));
|
||||||
|
|
||||||
|
for (auto const &attr : cell->attributes)
|
||||||
|
{
|
||||||
|
if (attr.first == ID::hdlname)
|
||||||
|
scopeinfo->attributes.insert(attr);
|
||||||
|
else
|
||||||
|
scopeinfo->attributes.emplace(stringf("\\cell_%s", RTLIL::unescape_id(attr.first).c_str()), attr.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto const &attr : tpl->attributes)
|
||||||
|
scopeinfo->attributes.emplace(stringf("\\module_%s", RTLIL::unescape_id(attr.first).c_str()), attr.second);
|
||||||
|
|
||||||
|
scopeinfo->attributes.emplace(ID(module), RTLIL::unescape_id(tpl->name));
|
||||||
|
}
|
||||||
|
|
||||||
module->remove(cell);
|
module->remove(cell);
|
||||||
|
|
||||||
|
if (scopeinfo != nullptr)
|
||||||
|
module->rename(scopeinfo, cell_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void flatten_module(RTLIL::Design *design, RTLIL::Module *module, pool<RTLIL::Module*> &used_modules)
|
void flatten_module(RTLIL::Design *design, RTLIL::Module *module, pool<RTLIL::Module*> &used_modules)
|
||||||
|
@ -275,6 +331,20 @@ struct FlattenPass : public Pass {
|
||||||
log(" -wb\n");
|
log(" -wb\n");
|
||||||
log(" Ignore the 'whitebox' attribute on cell implementations.\n");
|
log(" Ignore the 'whitebox' attribute on cell implementations.\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
|
log(" -noscopeinfo\n");
|
||||||
|
log(" Do not create '$scopeinfo' cells that preserve attributes of cells and\n");
|
||||||
|
log(" modules that were removed during flattening. With this option, the\n");
|
||||||
|
log(" 'src' attribute of a given cell is merged into all objects replacing\n");
|
||||||
|
log(" that cell, with multiple distinct 'src' locations separated by '|'.\n");
|
||||||
|
log(" Without this option these 'src' locations can be found via the\n");
|
||||||
|
log(" cell_src' and 'module_src' attribute of '$scopeinfo' cells.\n");
|
||||||
|
log("\n");
|
||||||
|
log(" -scopename\n");
|
||||||
|
log(" Create 'scopename' attributes for objects with a private name. This\n");
|
||||||
|
log(" attribute records the 'hdlname' of the enclosing scope. For objects\n");
|
||||||
|
log(" with a public name the enclosing scope can be found via their\n");
|
||||||
|
log(" 'hdlname' attribute.\n");
|
||||||
|
log("\n");
|
||||||
}
|
}
|
||||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||||
{
|
{
|
||||||
|
@ -289,6 +359,14 @@ struct FlattenPass : public Pass {
|
||||||
worker.ignore_wb = true;
|
worker.ignore_wb = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (args[argidx] == "-noscopeinfo") {
|
||||||
|
worker.create_scopeinfo = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (args[argidx] == "-scopename") {
|
||||||
|
worker.create_scopename = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
extra_args(args, argidx, design);
|
extra_args(args, argidx, design);
|
||||||
|
|
|
@ -1803,11 +1803,12 @@ endmodule
|
||||||
|
|
||||||
module \$print (EN, TRG, ARGS);
|
module \$print (EN, TRG, ARGS);
|
||||||
|
|
||||||
|
parameter PRIORITY = 0;
|
||||||
|
|
||||||
parameter FORMAT = "";
|
parameter FORMAT = "";
|
||||||
parameter ARGS_WIDTH = 0;
|
parameter ARGS_WIDTH = 0;
|
||||||
parameter PRIORITY = 0;
|
|
||||||
parameter TRG_ENABLE = 1;
|
|
||||||
|
|
||||||
|
parameter TRG_ENABLE = 1;
|
||||||
parameter TRG_WIDTH = 0;
|
parameter TRG_WIDTH = 0;
|
||||||
parameter TRG_POLARITY = 0;
|
parameter TRG_POLARITY = 0;
|
||||||
|
|
||||||
|
@ -1817,6 +1818,27 @@ input [ARGS_WIDTH-1:0] ARGS;
|
||||||
|
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
|
// --------------------------------------------------------
|
||||||
|
|
||||||
|
module \$check (A, EN, TRG, ARGS);
|
||||||
|
|
||||||
|
parameter FLAVOR = "";
|
||||||
|
parameter PRIORITY = 0;
|
||||||
|
|
||||||
|
parameter FORMAT = "";
|
||||||
|
parameter ARGS_WIDTH = 0;
|
||||||
|
|
||||||
|
parameter TRG_ENABLE = 1;
|
||||||
|
parameter TRG_WIDTH = 0;
|
||||||
|
parameter TRG_POLARITY = 0;
|
||||||
|
|
||||||
|
input A;
|
||||||
|
input EN;
|
||||||
|
input [TRG_WIDTH-1:0] TRG;
|
||||||
|
input [ARGS_WIDTH-1:0] ARGS;
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
`ifndef SIMLIB_NOSR
|
`ifndef SIMLIB_NOSR
|
||||||
|
|
||||||
|
@ -2741,3 +2763,10 @@ assign Y = A;
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
|
|
||||||
|
(* noblackbox *)
|
||||||
|
module \$scopeinfo ();
|
||||||
|
|
||||||
|
parameter TYPE = "";
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
|
@ -60,7 +60,7 @@ struct SynthPass : public ScriptPass {
|
||||||
log(" do not run abc (as if yosys was compiled without ABC support)\n");
|
log(" do not run abc (as if yosys was compiled without ABC support)\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" -booth\n");
|
log(" -booth\n");
|
||||||
log(" run the booth pass to convert $mul to Booth encoded multipliers");
|
log(" run the booth pass to map $mul to Booth encoded multipliers\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" -noalumacc\n");
|
log(" -noalumacc\n");
|
||||||
log(" do not run 'alumacc' pass. i.e. keep arithmetic operators in\n");
|
log(" do not run 'alumacc' pass. i.e. keep arithmetic operators in\n");
|
||||||
|
@ -230,13 +230,13 @@ struct SynthPass : public ScriptPass {
|
||||||
|
|
||||||
if (check_label("coarse")) {
|
if (check_label("coarse")) {
|
||||||
run("proc");
|
run("proc");
|
||||||
if (help_mode || flatten)
|
if (flatten || help_mode)
|
||||||
run("flatten", " (if -flatten)");
|
run("flatten", " (if -flatten)");
|
||||||
run("opt_expr");
|
run("opt_expr");
|
||||||
run("opt_clean");
|
run("opt_clean");
|
||||||
run("check");
|
run("check");
|
||||||
run("opt -nodffe -nosdff");
|
run("opt -nodffe -nosdff");
|
||||||
if (!nofsm)
|
if (!nofsm || help_mode)
|
||||||
run("fsm" + fsm_opts, " (unless -nofsm)");
|
run("fsm" + fsm_opts, " (unless -nofsm)");
|
||||||
run("opt");
|
run("opt");
|
||||||
run("wreduce");
|
run("wreduce");
|
||||||
|
@ -246,8 +246,8 @@ struct SynthPass : public ScriptPass {
|
||||||
run("techmap -map +/cmp2lut.v -map +/cmp2lcu.v", " (if -lut)");
|
run("techmap -map +/cmp2lut.v -map +/cmp2lcu.v", " (if -lut)");
|
||||||
else if (lut)
|
else if (lut)
|
||||||
run(stringf("techmap -map +/cmp2lut.v -map +/cmp2lcu.v -D LUT_WIDTH=%d", lut));
|
run(stringf("techmap -map +/cmp2lut.v -map +/cmp2lcu.v -D LUT_WIDTH=%d", lut));
|
||||||
if (booth)
|
if (booth || help_mode)
|
||||||
run("booth");
|
run("booth", " (if -booth)");
|
||||||
if (!noalumacc)
|
if (!noalumacc)
|
||||||
run("alumacc", " (unless -noalumacc)");
|
run("alumacc", " (unless -noalumacc)");
|
||||||
if (!noshare)
|
if (!noshare)
|
||||||
|
@ -274,7 +274,7 @@ struct SynthPass : public ScriptPass {
|
||||||
}
|
}
|
||||||
run("opt -fast");
|
run("opt -fast");
|
||||||
|
|
||||||
if (!noabc && !flowmap) {
|
if ((!noabc && !flowmap) || help_mode) {
|
||||||
#ifdef YOSYS_ENABLE_ABC
|
#ifdef YOSYS_ENABLE_ABC
|
||||||
if (help_mode) {
|
if (help_mode) {
|
||||||
run(abc + " -fast", " (unless -noabc, unless -lut)");
|
run(abc + " -fast", " (unless -noabc, unless -lut)");
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
ram block $__GOWIN_SP_ {
|
ram block $__GOWIN_SP_ {
|
||||||
abits 14;
|
abits 14;
|
||||||
widths 1 2 4 9 18 36 per_port;
|
widths 1 2 4 9 18 36 per_port;
|
||||||
byte 9;
|
|
||||||
cost 128;
|
cost 128;
|
||||||
init no_undef;
|
init no_undef;
|
||||||
port srsw "A" {
|
port srsw "A" {
|
||||||
clock posedge;
|
clock posedge;
|
||||||
clken;
|
clken;
|
||||||
wrbe_separate;
|
|
||||||
option "RESET_MODE" "SYNC" {
|
option "RESET_MODE" "SYNC" {
|
||||||
rdsrst zero ungated;
|
rdsrst zero ungated;
|
||||||
}
|
}
|
||||||
|
@ -30,13 +28,11 @@ ram block $__GOWIN_SP_ {
|
||||||
ram block $__GOWIN_DP_ {
|
ram block $__GOWIN_DP_ {
|
||||||
abits 14;
|
abits 14;
|
||||||
widths 1 2 4 9 18 per_port;
|
widths 1 2 4 9 18 per_port;
|
||||||
byte 9;
|
|
||||||
cost 128;
|
cost 128;
|
||||||
init no_undef;
|
init no_undef;
|
||||||
port srsw "A" "B" {
|
port srsw "A" "B" {
|
||||||
clock posedge;
|
clock posedge;
|
||||||
clken;
|
clken;
|
||||||
wrbe_separate;
|
|
||||||
option "RESET_MODE" "SYNC" {
|
option "RESET_MODE" "SYNC" {
|
||||||
rdsrst zero ungated;
|
rdsrst zero ungated;
|
||||||
}
|
}
|
||||||
|
@ -59,7 +55,6 @@ ram block $__GOWIN_DP_ {
|
||||||
ram block $__GOWIN_SDP_ {
|
ram block $__GOWIN_SDP_ {
|
||||||
abits 14;
|
abits 14;
|
||||||
widths 1 2 4 9 18 36 per_port;
|
widths 1 2 4 9 18 36 per_port;
|
||||||
byte 9;
|
|
||||||
cost 128;
|
cost 128;
|
||||||
init no_undef;
|
init no_undef;
|
||||||
port sr "R" {
|
port sr "R" {
|
||||||
|
@ -76,6 +71,5 @@ ram block $__GOWIN_SDP_ {
|
||||||
port sw "W" {
|
port sw "W" {
|
||||||
clock posedge;
|
clock posedge;
|
||||||
clken;
|
clken;
|
||||||
wrbe_separate;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,8 +14,7 @@
|
||||||
`define x8_width(width) (width / 9 * 8 + width % 9)
|
`define x8_width(width) (width / 9 * 8 + width % 9)
|
||||||
`define x8_rd_data(data) {1'bx, data[31:24], 1'bx, data[23:16], 1'bx, data[15:8], 1'bx, data[7:0]}
|
`define x8_rd_data(data) {1'bx, data[31:24], 1'bx, data[23:16], 1'bx, data[15:8], 1'bx, data[7:0]}
|
||||||
`define x8_wr_data(data) {data[34:27], data[25:18], data[16:9], data[7:0]}
|
`define x8_wr_data(data) {data[34:27], data[25:18], data[16:9], data[7:0]}
|
||||||
`define wre(width, wr_en, wr_be) (width < 18 ? wr_en | wr_be[0] : wr_en)
|
`define addrbe_always(width, addr) (width < 18 ? addr : width == 18 ? {addr[13:4], 4'b0011} : {addr[13:5], 5'b01111})
|
||||||
`define addrbe(width, addr, wr_be) (width < 18 ? addr : {addr[13:4], wr_be})
|
|
||||||
|
|
||||||
|
|
||||||
`define INIT(func) \
|
`define INIT(func) \
|
||||||
|
@ -90,7 +89,6 @@ parameter INIT = 0;
|
||||||
parameter OPTION_RESET_MODE = "SYNC";
|
parameter OPTION_RESET_MODE = "SYNC";
|
||||||
|
|
||||||
parameter PORT_A_WIDTH = 36;
|
parameter PORT_A_WIDTH = 36;
|
||||||
parameter PORT_A_WR_BE_WIDTH = 4;
|
|
||||||
parameter PORT_A_OPTION_WRITE_MODE = 0;
|
parameter PORT_A_OPTION_WRITE_MODE = 0;
|
||||||
|
|
||||||
input PORT_A_CLK;
|
input PORT_A_CLK;
|
||||||
|
@ -99,15 +97,13 @@ input PORT_A_WR_EN;
|
||||||
input PORT_A_RD_SRST;
|
input PORT_A_RD_SRST;
|
||||||
input PORT_A_RD_ARST;
|
input PORT_A_RD_ARST;
|
||||||
input [13:0] PORT_A_ADDR;
|
input [13:0] PORT_A_ADDR;
|
||||||
input [PORT_A_WR_BE_WIDTH-1:0] PORT_A_WR_BE;
|
|
||||||
input [PORT_A_WIDTH-1:0] PORT_A_WR_DATA;
|
input [PORT_A_WIDTH-1:0] PORT_A_WR_DATA;
|
||||||
output [PORT_A_WIDTH-1:0] PORT_A_RD_DATA;
|
output [PORT_A_WIDTH-1:0] PORT_A_RD_DATA;
|
||||||
|
|
||||||
`DEF_FUNCS
|
`DEF_FUNCS
|
||||||
|
|
||||||
wire RST = OPTION_RESET_MODE == "SYNC" ? PORT_A_RD_SRST : PORT_A_RD_ARST;
|
wire RST = OPTION_RESET_MODE == "SYNC" ? PORT_A_RD_SRST : PORT_A_RD_ARST;
|
||||||
wire WRE = `wre(PORT_A_WIDTH, PORT_A_WR_EN, PORT_A_WR_BE);
|
wire [13:0] AD = `addrbe_always(PORT_A_WIDTH, PORT_A_ADDR);
|
||||||
wire [13:0] AD = `addrbe(PORT_A_WIDTH, PORT_A_ADDR, PORT_A_WR_BE);
|
|
||||||
|
|
||||||
generate
|
generate
|
||||||
|
|
||||||
|
@ -129,7 +125,7 @@ if (PORT_A_WIDTH < 9) begin
|
||||||
.BLKSEL(3'b000),
|
.BLKSEL(3'b000),
|
||||||
.CLK(PORT_A_CLK),
|
.CLK(PORT_A_CLK),
|
||||||
.CE(PORT_A_CLK_EN),
|
.CE(PORT_A_CLK_EN),
|
||||||
.WRE(WRE),
|
.WRE(PORT_A_WR_EN),
|
||||||
.RESET(RST),
|
.RESET(RST),
|
||||||
.OCE(1'b1),
|
.OCE(1'b1),
|
||||||
.AD(AD),
|
.AD(AD),
|
||||||
|
@ -155,7 +151,7 @@ end else begin
|
||||||
.BLKSEL(3'b000),
|
.BLKSEL(3'b000),
|
||||||
.CLK(PORT_A_CLK),
|
.CLK(PORT_A_CLK),
|
||||||
.CE(PORT_A_CLK_EN),
|
.CE(PORT_A_CLK_EN),
|
||||||
.WRE(WRE),
|
.WRE(PORT_A_WR_EN),
|
||||||
.RESET(RST),
|
.RESET(RST),
|
||||||
.OCE(1'b1),
|
.OCE(1'b1),
|
||||||
.AD(AD),
|
.AD(AD),
|
||||||
|
@ -176,11 +172,9 @@ parameter INIT = 0;
|
||||||
parameter OPTION_RESET_MODE = "SYNC";
|
parameter OPTION_RESET_MODE = "SYNC";
|
||||||
|
|
||||||
parameter PORT_A_WIDTH = 18;
|
parameter PORT_A_WIDTH = 18;
|
||||||
parameter PORT_A_WR_BE_WIDTH = 2;
|
|
||||||
parameter PORT_A_OPTION_WRITE_MODE = 0;
|
parameter PORT_A_OPTION_WRITE_MODE = 0;
|
||||||
|
|
||||||
parameter PORT_B_WIDTH = 18;
|
parameter PORT_B_WIDTH = 18;
|
||||||
parameter PORT_B_WR_BE_WIDTH = 2;
|
|
||||||
parameter PORT_B_OPTION_WRITE_MODE = 0;
|
parameter PORT_B_OPTION_WRITE_MODE = 0;
|
||||||
|
|
||||||
input PORT_A_CLK;
|
input PORT_A_CLK;
|
||||||
|
@ -189,7 +183,6 @@ input PORT_A_WR_EN;
|
||||||
input PORT_A_RD_SRST;
|
input PORT_A_RD_SRST;
|
||||||
input PORT_A_RD_ARST;
|
input PORT_A_RD_ARST;
|
||||||
input [13:0] PORT_A_ADDR;
|
input [13:0] PORT_A_ADDR;
|
||||||
input [PORT_A_WR_BE_WIDTH-1:0] PORT_A_WR_BE;
|
|
||||||
input [PORT_A_WIDTH-1:0] PORT_A_WR_DATA;
|
input [PORT_A_WIDTH-1:0] PORT_A_WR_DATA;
|
||||||
output [PORT_A_WIDTH-1:0] PORT_A_RD_DATA;
|
output [PORT_A_WIDTH-1:0] PORT_A_RD_DATA;
|
||||||
|
|
||||||
|
@ -199,7 +192,6 @@ input PORT_B_WR_EN;
|
||||||
input PORT_B_RD_SRST;
|
input PORT_B_RD_SRST;
|
||||||
input PORT_B_RD_ARST;
|
input PORT_B_RD_ARST;
|
||||||
input [13:0] PORT_B_ADDR;
|
input [13:0] PORT_B_ADDR;
|
||||||
input [PORT_A_WR_BE_WIDTH-1:0] PORT_B_WR_BE;
|
|
||||||
input [PORT_A_WIDTH-1:0] PORT_B_WR_DATA;
|
input [PORT_A_WIDTH-1:0] PORT_B_WR_DATA;
|
||||||
output [PORT_A_WIDTH-1:0] PORT_B_RD_DATA;
|
output [PORT_A_WIDTH-1:0] PORT_B_RD_DATA;
|
||||||
|
|
||||||
|
@ -207,10 +199,8 @@ output [PORT_A_WIDTH-1:0] PORT_B_RD_DATA;
|
||||||
|
|
||||||
wire RSTA = OPTION_RESET_MODE == "SYNC" ? PORT_A_RD_SRST : PORT_A_RD_ARST;
|
wire RSTA = OPTION_RESET_MODE == "SYNC" ? PORT_A_RD_SRST : PORT_A_RD_ARST;
|
||||||
wire RSTB = OPTION_RESET_MODE == "SYNC" ? PORT_B_RD_SRST : PORT_B_RD_ARST;
|
wire RSTB = OPTION_RESET_MODE == "SYNC" ? PORT_B_RD_SRST : PORT_B_RD_ARST;
|
||||||
wire WREA = `wre(PORT_A_WIDTH, PORT_A_WR_EN, PORT_A_WR_BE);
|
wire [13:0] ADA = `addrbe_always(PORT_A_WIDTH, PORT_A_ADDR);
|
||||||
wire WREB = `wre(PORT_B_WIDTH, PORT_B_WR_EN, PORT_B_WR_BE);
|
wire [13:0] ADB = `addrbe_always(PORT_B_WIDTH, PORT_B_ADDR);
|
||||||
wire [13:0] ADA = `addrbe(PORT_A_WIDTH, PORT_A_ADDR, PORT_A_WR_BE);
|
|
||||||
wire [13:0] ADB = `addrbe(PORT_B_WIDTH, PORT_B_ADDR, PORT_B_WR_BE);
|
|
||||||
|
|
||||||
generate
|
generate
|
||||||
|
|
||||||
|
@ -241,7 +231,7 @@ if (PORT_A_WIDTH < 9 || PORT_B_WIDTH < 9) begin
|
||||||
|
|
||||||
.CLKA(PORT_A_CLK),
|
.CLKA(PORT_A_CLK),
|
||||||
.CEA(PORT_A_CLK_EN),
|
.CEA(PORT_A_CLK_EN),
|
||||||
.WREA(WREA),
|
.WREA(PORT_A_WR_EN),
|
||||||
.RESETA(RSTA),
|
.RESETA(RSTA),
|
||||||
.OCEA(1'b1),
|
.OCEA(1'b1),
|
||||||
.ADA(ADA),
|
.ADA(ADA),
|
||||||
|
@ -250,7 +240,7 @@ if (PORT_A_WIDTH < 9 || PORT_B_WIDTH < 9) begin
|
||||||
|
|
||||||
.CLKB(PORT_B_CLK),
|
.CLKB(PORT_B_CLK),
|
||||||
.CEB(PORT_B_CLK_EN),
|
.CEB(PORT_B_CLK_EN),
|
||||||
.WREB(WREB),
|
.WREB(PORT_B_WR_EN),
|
||||||
.RESETB(RSTB),
|
.RESETB(RSTB),
|
||||||
.OCEB(1'b1),
|
.OCEB(1'b1),
|
||||||
.ADB(ADB),
|
.ADB(ADB),
|
||||||
|
@ -285,7 +275,7 @@ end else begin
|
||||||
|
|
||||||
.CLKA(PORT_A_CLK),
|
.CLKA(PORT_A_CLK),
|
||||||
.CEA(PORT_A_CLK_EN),
|
.CEA(PORT_A_CLK_EN),
|
||||||
.WREA(WREA),
|
.WREA(PORT_A_WR_EN),
|
||||||
.RESETA(RSTA),
|
.RESETA(RSTA),
|
||||||
.OCEA(1'b1),
|
.OCEA(1'b1),
|
||||||
.ADA(ADA),
|
.ADA(ADA),
|
||||||
|
@ -294,7 +284,7 @@ end else begin
|
||||||
|
|
||||||
.CLKB(PORT_B_CLK),
|
.CLKB(PORT_B_CLK),
|
||||||
.CEB(PORT_B_CLK_EN),
|
.CEB(PORT_B_CLK_EN),
|
||||||
.WREB(WREB),
|
.WREB(PORT_B_WR_EN),
|
||||||
.RESETB(RSTB),
|
.RESETB(RSTB),
|
||||||
.OCEB(1'b1),
|
.OCEB(1'b1),
|
||||||
.ADB(ADB),
|
.ADB(ADB),
|
||||||
|
@ -315,9 +305,7 @@ parameter INIT = 0;
|
||||||
parameter OPTION_RESET_MODE = "SYNC";
|
parameter OPTION_RESET_MODE = "SYNC";
|
||||||
|
|
||||||
parameter PORT_R_WIDTH = 18;
|
parameter PORT_R_WIDTH = 18;
|
||||||
|
|
||||||
parameter PORT_W_WIDTH = 18;
|
parameter PORT_W_WIDTH = 18;
|
||||||
parameter PORT_W_WR_BE_WIDTH = 2;
|
|
||||||
|
|
||||||
input PORT_R_CLK;
|
input PORT_R_CLK;
|
||||||
input PORT_R_CLK_EN;
|
input PORT_R_CLK_EN;
|
||||||
|
@ -330,14 +318,13 @@ input PORT_W_CLK;
|
||||||
input PORT_W_CLK_EN;
|
input PORT_W_CLK_EN;
|
||||||
input PORT_W_WR_EN;
|
input PORT_W_WR_EN;
|
||||||
input [13:0] PORT_W_ADDR;
|
input [13:0] PORT_W_ADDR;
|
||||||
input [PORT_W_WR_BE_WIDTH-1:0] PORT_W_WR_BE;
|
|
||||||
input [PORT_W_WIDTH-1:0] PORT_W_WR_DATA;
|
input [PORT_W_WIDTH-1:0] PORT_W_WR_DATA;
|
||||||
|
|
||||||
`DEF_FUNCS
|
`DEF_FUNCS
|
||||||
|
|
||||||
wire RST = OPTION_RESET_MODE == "SYNC" ? PORT_R_RD_SRST : PORT_R_RD_ARST;
|
wire RST = OPTION_RESET_MODE == "SYNC" ? PORT_R_RD_SRST : PORT_R_RD_ARST;
|
||||||
wire WRE = `wre(PORT_W_WIDTH, PORT_W_WR_EN, PORT_W_WR_BE);
|
wire [13:0] ADW = `addrbe_always(PORT_W_WIDTH, PORT_W_ADDR);
|
||||||
wire [13:0] ADW = `addrbe(PORT_W_WIDTH, PORT_W_ADDR, PORT_W_WR_BE);
|
wire WRE = PORT_W_CLK_EN & PORT_W_WR_EN;
|
||||||
|
|
||||||
generate
|
generate
|
||||||
|
|
||||||
|
@ -361,7 +348,7 @@ if (PORT_W_WIDTH < 9 || PORT_R_WIDTH < 9) begin
|
||||||
.BLKSELB(3'b000),
|
.BLKSELB(3'b000),
|
||||||
|
|
||||||
.CLKA(PORT_W_CLK),
|
.CLKA(PORT_W_CLK),
|
||||||
.CEA(PORT_W_CLK_EN),
|
.CEA(WRE),
|
||||||
.RESETA(1'b0),
|
.RESETA(1'b0),
|
||||||
.ADA(ADW),
|
.ADA(ADW),
|
||||||
.DI(DI),
|
.DI(DI),
|
||||||
|
@ -394,7 +381,7 @@ end else begin
|
||||||
.BLKSELB(3'b000),
|
.BLKSELB(3'b000),
|
||||||
|
|
||||||
.CLKA(PORT_W_CLK),
|
.CLKA(PORT_W_CLK),
|
||||||
.CEA(PORT_W_CLK_EN),
|
.CEA(WRE),
|
||||||
.RESETA(1'b0),
|
.RESETA(1'b0),
|
||||||
.ADA(ADW),
|
.ADA(ADW),
|
||||||
.DI(DI),
|
.DI(DI),
|
||||||
|
|
|
@ -107,7 +107,7 @@ reg [7:0] i = 0;
|
||||||
always @(posedge clk) begin
|
always @(posedge clk) begin
|
||||||
if (i < VECTORLEN) begin
|
if (i < VECTORLEN) begin
|
||||||
// FIXME: for some reason the first assert fails (despite comparing zero to zero)
|
// FIXME: for some reason the first assert fails (despite comparing zero to zero)
|
||||||
if (i > 0)
|
if (i > 0)
|
||||||
assert(y == y_expected);
|
assert(y == y_expected);
|
||||||
i <= i + 1;
|
i <= i + 1;
|
||||||
end
|
end
|
||||||
|
@ -117,4 +117,5 @@ EOF
|
||||||
read_verilog +/quicklogic/qlf_k6n10f/dsp_sim.v
|
read_verilog +/quicklogic/qlf_k6n10f/dsp_sim.v
|
||||||
hierarchy -top testbench
|
hierarchy -top testbench
|
||||||
proc
|
proc
|
||||||
|
async2sync
|
||||||
sim -assert -q -clock clk -n 20
|
sim -assert -q -clock clk -n 20
|
||||||
|
|
|
@ -36,7 +36,7 @@ blockram_tests: "list[tuple[list[tuple[str, int]], str, list[str]]]" = [
|
||||||
([("ADDRESS_WIDTH", 14), ("DATA_WIDTH", 2)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=2 %i"]),
|
([("ADDRESS_WIDTH", 14), ("DATA_WIDTH", 2)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=2 %i"]),
|
||||||
([("ADDRESS_WIDTH", 15), ("DATA_WIDTH", 1)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=1 %i"]),
|
([("ADDRESS_WIDTH", 15), ("DATA_WIDTH", 1)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=1 %i"]),
|
||||||
|
|
||||||
# 2x asymmetric (1024x36bit write / 2048x18bit read or vice versa = 1TDP36K)
|
# 2x asymmetric (1024x36bit write / 2048x18bit read or vice versa = 1TDP36K)
|
||||||
([("ADDRESS_WIDTH", 11), ("DATA_WIDTH", 18), ("SHIFT_VAL", 1)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]),
|
([("ADDRESS_WIDTH", 11), ("DATA_WIDTH", 18), ("SHIFT_VAL", 1)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]),
|
||||||
([("ADDRESS_WIDTH", 11), ("DATA_WIDTH", 16), ("SHIFT_VAL", 1)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]),
|
([("ADDRESS_WIDTH", 11), ("DATA_WIDTH", 16), ("SHIFT_VAL", 1)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]),
|
||||||
([("ADDRESS_WIDTH", 12), ("DATA_WIDTH", 9), ("SHIFT_VAL", 1)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]),
|
([("ADDRESS_WIDTH", 12), ("DATA_WIDTH", 9), ("SHIFT_VAL", 1)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]),
|
||||||
|
@ -131,6 +131,7 @@ read_verilog -defer -formal mem_tb.v
|
||||||
chparam{param_str} -set VECTORLEN {vectorlen} TB
|
chparam{param_str} -set VECTORLEN {vectorlen} TB
|
||||||
hierarchy -top TB -check
|
hierarchy -top TB -check
|
||||||
prep
|
prep
|
||||||
|
async2sync
|
||||||
log ** CHECKING SIMULATION FOR TEST {top} WITH PARAMS{param_str}
|
log ** CHECKING SIMULATION FOR TEST {top} WITH PARAMS{param_str}
|
||||||
sim -clock clk -n {vectorlen} -assert
|
sim -clock clk -n {vectorlen} -assert
|
||||||
"""
|
"""
|
||||||
|
@ -254,16 +255,16 @@ sim_tests: list[TestClass] = [
|
||||||
{"rq_a": 0x5678},
|
{"rq_a": 0x5678},
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
TestClass( # basic TDP test
|
TestClass( # basic TDP test
|
||||||
# note that the testbench uses ra and wa, while the common TDP model
|
# note that the testbench uses ra and wa, while the common TDP model
|
||||||
# uses a shared address
|
# uses a shared address
|
||||||
params={"ADDRESS_WIDTH": 10, "DATA_WIDTH": 36},
|
params={"ADDRESS_WIDTH": 10, "DATA_WIDTH": 36},
|
||||||
top="sync_ram_tdp",
|
top="sync_ram_tdp",
|
||||||
assertions=[],
|
assertions=[],
|
||||||
test_steps=[
|
test_steps=[
|
||||||
{"wce_a": 1, "ra_a": 0x0A, "wce_b": 1, "ra_b": 0xBA,
|
{"wce_a": 1, "ra_a": 0x0A, "wce_b": 1, "ra_b": 0xBA,
|
||||||
"wd_a": 0xdeadbeef, "wd_b": 0x5a5a5a5a},
|
"wd_a": 0xdeadbeef, "wd_b": 0x5a5a5a5a},
|
||||||
{"wce_a": 1, "ra_a": 0xFF,
|
{"wce_a": 1, "ra_a": 0xFF,
|
||||||
"wd_a": 0},
|
"wd_a": 0},
|
||||||
{"rce_a": 1, "ra_a": 0x0A, "rce_b": 1, "ra_b": 0x0A},
|
{"rce_a": 1, "ra_a": 0x0A, "rce_b": 1, "ra_b": 0x0A},
|
||||||
{"rq_a": 0xdeadbeef, "rq_b": 0xdeadbeef},
|
{"rq_a": 0xdeadbeef, "rq_b": 0xdeadbeef},
|
||||||
|
@ -276,9 +277,9 @@ sim_tests: list[TestClass] = [
|
||||||
top="sync_ram_tdp",
|
top="sync_ram_tdp",
|
||||||
assertions=[],
|
assertions=[],
|
||||||
test_steps=[
|
test_steps=[
|
||||||
{"wce_a": 1, "ra_a": 0x0F, "wce_b": 1, "ra_b": 0xBA,
|
{"wce_a": 1, "ra_a": 0x0F, "wce_b": 1, "ra_b": 0xBA,
|
||||||
"wd_a": 0xdeadbeef, "wd_b": 0x5a5a5a5a},
|
"wd_a": 0xdeadbeef, "wd_b": 0x5a5a5a5a},
|
||||||
{"wce_a": 1, "ra_a": 0xFF,
|
{"wce_a": 1, "ra_a": 0xFF,
|
||||||
"wd_a": 0},
|
"wd_a": 0},
|
||||||
{"rce_a": 1, "ra_a": 0x0F, "rce_b": 1, "ra_b": 0x0A},
|
{"rce_a": 1, "ra_a": 0x0F, "rce_b": 1, "ra_b": 0x0A},
|
||||||
{"rq_a": 0, "rq_b": 0x00005a5a},
|
{"rq_a": 0, "rq_b": 0x00005a5a},
|
||||||
|
@ -291,7 +292,7 @@ sim_tests: list[TestClass] = [
|
||||||
top="sync_ram_tdp",
|
top="sync_ram_tdp",
|
||||||
assertions=[],
|
assertions=[],
|
||||||
test_steps=[
|
test_steps=[
|
||||||
{"wce_a": 1, "ra_a": 0x0A, "wce_b": 1, "ra_b": 0xBA,
|
{"wce_a": 1, "ra_a": 0x0A, "wce_b": 1, "ra_b": 0xBA,
|
||||||
"wd_a": 0xdeadbeef, "wd_b": 0x5a5a5a5a},
|
"wd_a": 0xdeadbeef, "wd_b": 0x5a5a5a5a},
|
||||||
{"wce_a": 1, "ra_a": 0xBA, "rce_b": 1, "ra_b": 0xBA,
|
{"wce_a": 1, "ra_a": 0xBA, "rce_b": 1, "ra_b": 0xBA,
|
||||||
"wd_a": 0xa5a5a5a5},
|
"wd_a": 0xa5a5a5a5},
|
||||||
|
@ -409,7 +410,7 @@ for sim_test in sim_tests:
|
||||||
fn = f"t_mem{i}.ys"
|
fn = f"t_mem{i}.ys"
|
||||||
f = open(fn, mode="w")
|
f = open(fn, mode="w")
|
||||||
j = 0
|
j = 0
|
||||||
|
|
||||||
# output yosys script test file
|
# output yosys script test file
|
||||||
print(
|
print(
|
||||||
blockram_template.format(param_str=param_str, top=top),
|
blockram_template.format(param_str=param_str, top=top),
|
||||||
|
|
|
@ -10,5 +10,6 @@ select -assert-count 1 t:TDP36K a:is_split=0 %i
|
||||||
select -assert-count 1 t:TDP36K a:was_split_candidate=0 %i
|
select -assert-count 1 t:TDP36K a:was_split_candidate=0 %i
|
||||||
read_verilog +/quicklogic/common/cells_sim.v +/quicklogic/qlf_k6n10f/cells_sim.v +/quicklogic/qlf_k6n10f/brams_sim.v +/quicklogic/qlf_k6n10f/sram1024x18_mem.v +/quicklogic/qlf_k6n10f/ufifo_ctl.v +/quicklogic/qlf_k6n10f/TDP18K_FIFO.v
|
read_verilog +/quicklogic/common/cells_sim.v +/quicklogic/qlf_k6n10f/cells_sim.v +/quicklogic/qlf_k6n10f/brams_sim.v +/quicklogic/qlf_k6n10f/sram1024x18_mem.v +/quicklogic/qlf_k6n10f/ufifo_ctl.v +/quicklogic/qlf_k6n10f/TDP18K_FIFO.v
|
||||||
prep
|
prep
|
||||||
|
async2sync
|
||||||
hierarchy -top top
|
hierarchy -top top
|
||||||
sim -assert -q -n 12 -clock clk
|
sim -assert -q -n 12 -clock clk
|
||||||
|
|
|
@ -30,6 +30,7 @@ module top(output [42:0] P);
|
||||||
assert property (P == 42*42);
|
assert property (P == 42*42);
|
||||||
endmodule
|
endmodule
|
||||||
EOT
|
EOT
|
||||||
|
async2sync
|
||||||
techmap -map +/xilinx/xc7_dsp_map.v
|
techmap -map +/xilinx/xc7_dsp_map.v
|
||||||
verilog_defaults -add -D ALLOW_WHITEBOX_DSP48E1
|
verilog_defaults -add -D ALLOW_WHITEBOX_DSP48E1
|
||||||
synth_xilinx -abc9
|
synth_xilinx -abc9
|
||||||
|
|
|
@ -16,8 +16,8 @@ generate_target() {
|
||||||
# $ generate_ys_test ys_file [yosys_args]
|
# $ generate_ys_test ys_file [yosys_args]
|
||||||
generate_ys_test() {
|
generate_ys_test() {
|
||||||
ys_file=$1
|
ys_file=$1
|
||||||
yosys_args=${2:-}
|
yosys_args_=${2:-}
|
||||||
generate_target "$ys_file" "\"$YOSYS_BASEDIR/yosys\" -ql ${ys_file%.*}.log $yosys_args $ys_file"
|
generate_target "$ys_file" "\"$YOSYS_BASEDIR/yosys\" -ql ${ys_file%.*}.log $yosys_args_ $ys_file"
|
||||||
}
|
}
|
||||||
|
|
||||||
# $ generate_bash_test bash_file
|
# $ generate_bash_test bash_file
|
||||||
|
@ -75,7 +75,7 @@ generate_tests() {
|
||||||
if [[ $do_sv = true ]]; then
|
if [[ $do_sv = true ]]; then
|
||||||
for x in *.sv; do
|
for x in *.sv; do
|
||||||
if [ ! -f "${x%.sv}.ys" ]; then
|
if [ ! -f "${x%.sv}.ys" ]; then
|
||||||
generate_ys_test "$x" "-p \"prep -top top; sat -enable_undef -verify -prove-asserts\" $yosys_args"
|
generate_ys_test "$x" "-p \"prep -top top; async2sync; sat -enable_undef -verify -prove-asserts\" $yosys_args"
|
||||||
fi;
|
fi;
|
||||||
done
|
done
|
||||||
fi;
|
fi;
|
||||||
|
|
|
@ -186,4 +186,27 @@ design -stash preopt
|
||||||
equiv_opt -assert -run prepare: dummy
|
equiv_opt -assert -run prepare: dummy
|
||||||
|
|
||||||
|
|
||||||
|
design -reset
|
||||||
|
|
||||||
|
read_ilang <<EOT
|
||||||
|
|
||||||
|
module \m
|
||||||
|
wire width 3 input 1 \a
|
||||||
|
|
||||||
|
process \p
|
||||||
|
switch \a
|
||||||
|
case 3'000
|
||||||
|
case 3'001
|
||||||
|
case 3'010
|
||||||
|
case 3'011
|
||||||
|
case 3'100
|
||||||
|
case 3'101
|
||||||
|
case 3'110
|
||||||
|
case 3'111
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
EOT
|
||||||
|
|
||||||
|
proc_rom
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
read_verilog -sv asserts.v
|
read_verilog -sv asserts.v
|
||||||
hierarchy; proc; opt
|
hierarchy; proc; opt; async2sync
|
||||||
sat -verify -seq 1 -set-at 1 rst 1 -tempinduct -prove-asserts
|
sat -verify -seq 1 -set-at 1 rst 1 -tempinduct -prove-asserts
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
read_verilog -sv asserts_seq.v
|
read_verilog -sv asserts_seq.v
|
||||||
hierarchy; proc; opt
|
hierarchy; proc; opt; async2sync
|
||||||
|
|
||||||
sat -verify -prove-asserts -tempinduct -seq 1 test_001
|
sat -verify -prove-asserts -tempinduct -seq 1 test_001
|
||||||
sat -falsify -prove-asserts -tempinduct -seq 1 test_002
|
sat -falsify -prove-asserts -tempinduct -seq 1 test_002
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
read_verilog -sv initval.v
|
read_verilog -sv initval.v
|
||||||
proc;;
|
proc; async2sync;;
|
||||||
|
|
||||||
sat -seq 10 -prove-asserts
|
sat -seq 10 -prove-asserts
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue