diff --git a/.gitcommit b/.gitcommit index 46b7856fbc8..6828f88dcb0 100644 --- a/.gitcommit +++ b/.gitcommit @@ -1 +1 @@ -$Format:%h$ +$Format:%H$ diff --git a/.github/actions/setup-build-env/action.yml b/.github/actions/setup-build-env/action.yml index 345b6db8ada..c5a0d14abdf 100644 --- a/.github/actions/setup-build-env/action.yml +++ b/.github/actions/setup-build-env/action.yml @@ -14,7 +14,7 @@ runs: if: runner.os == 'macOS' shell: bash run: | - HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew install bison flex gawk libffi pkg-config bash autoconf + HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew install bison flex gawk libffi pkg-config bash autoconf llvm - name: Linux runtime environment if: runner.os == 'Linux' @@ -28,6 +28,7 @@ runs: shell: bash run: | echo "${{ github.workspace }}/.local/bin" >> $GITHUB_PATH + echo "$(brew --prefix llvm)/bin" >> $GITHUB_PATH echo "$(brew --prefix bison)/bin" >> $GITHUB_PATH echo "$(brew --prefix flex)/bin" >> $GITHUB_PATH echo "procs=$(sysctl -n hw.ncpu)" >> $GITHUB_ENV diff --git a/.github/workflows/test-build.yml b/.github/workflows/test-build.yml index bf95688a15c..0eb2c65c51c 100644 --- a/.github/workflows/test-build.yml +++ b/.github/workflows/test-build.yml @@ -56,7 +56,7 @@ jobs: mkdir build cd build make -f ../Makefile config-$CC - make -f ../Makefile -j$procs + make -f ../Makefile -j$procs ENABLE_LTO=1 - name: Log yosys-config output run: | diff --git a/.github/workflows/test-verific.yml b/.github/workflows/test-verific.yml index 5b6268a2eaf..e08f7bd2e0f 100644 --- a/.github/workflows/test-verific.yml +++ b/.github/workflows/test-verific.yml @@ -41,7 +41,7 @@ jobs: echo "ENABLE_VERIFIC_LIBERTY := 1" >> Makefile.conf echo "ENABLE_VERIFIC_YOSYSHQ_EXTENSIONS := 1" >> Makefile.conf echo "ENABLE_CCACHE := 1" >> Makefile.conf - make -j${{ env.procs }} + make -j${{ env.procs }} ENABLE_LTO=1 - name: Install Yosys run: | diff --git a/Brewfile b/Brewfile index b50c70a4b0e..18e4e2917e9 100644 --- a/Brewfile +++ b/Brewfile @@ -9,4 +9,5 @@ brew "python3" brew "tcl-tk" brew "xdot" brew "bash" -brew 'boost-python3' +brew "boost-python3" +brew "llvm" diff --git a/CHANGELOG b/CHANGELOG index 160f480e7b0..3d22bfe6008 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,9 +2,25 @@ List of major changes and improvements between releases ======================================================= -Yosys 0.43 .. Yosys 0.44-dev +Yosys 0.44 .. Yosys 0.45-dev -------------------------- +Yosys 0.43 .. Yosys 0.44 +-------------------------- + * Various + - Added ENABLE_LTO compile option to enable link time + optimizations. + - Build support for Haiku OS. + + * New commands and options + - Added "keep_hierarchy" pass to add attribute with + same name to modules based on cost. + - Added options "-noopt","-bloat" and "-check_cost" to + "test_cell" pass. + + * New back-ends + - Added initial PolarFire support. ( synth_microchip ) + Yosys 0.42 .. Yosys 0.43 -------------------------- * Various diff --git a/Dockerfile b/Dockerfile index 549c73c9712..9806696e051 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,6 +8,7 @@ RUN apt-get update -qq \ && DEBIAN_FRONTEND=noninteractive apt-get -y install --no-install-recommends \ ca-certificates \ clang \ + lld \ curl \ libffi-dev \ libreadline-dev \ diff --git a/Makefile b/Makefile index 2f1c9a01623..9fe5e01185c 100644 --- a/Makefile +++ b/Makefile @@ -34,7 +34,7 @@ ENABLE_PYOSYS := 0 ENABLE_GCOV := 0 ENABLE_GPROF := 0 ENABLE_DEBUG := 0 -ENABLE_NDEBUG := 0 +ENABLE_LTO := 0 ENABLE_CCACHE := 0 # sccache is not always a drop-in replacement for ccache in practice ENABLE_SCCACHE := 0 @@ -53,6 +53,11 @@ SANITIZER = # SANITIZER = undefined # SANITIZER = cfi +# Prefer using ENABLE_DEBUG over setting these +OPT_LEVEL := -O3 +GCC_LTO := +CLANG_LTO := -flto=thin + PROGRAM_PREFIX := OS := $(shell uname -s) @@ -143,14 +148,19 @@ LIBS += -lrt endif endif -YOSYS_VER := 0.43+0 +ifeq ($(OS), Haiku) +# Allow usage of non-posix vasprintf, mkstemps functions +CXXFLAGS += -D_DEFAULT_SOURCE +endif + +YOSYS_VER := 0.44+0 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo # will have this file in its unexpanded form tough, in which case we fall # back to calling git directly. TARBALL_GIT_REV := $(shell cat $(YOSYS_SRC)/.gitcommit) -ifeq ($(TARBALL_GIT_REV),$$Format:%h$$) +ifneq ($(findstring Format:,$(TARBALL_GIT_REV)),) GIT_REV := $(shell GIT_DIR=$(YOSYS_SRC)/.git git rev-parse --short=9 HEAD || echo UNKNOWN) else GIT_REV := $(TARBALL_GIT_REV) @@ -159,7 +169,7 @@ endif OBJS = kernel/version_$(GIT_REV).o bumpversion: - sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline ead4718.. | wc -l`/;" Makefile + sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 80ba43d.. | wc -l`/;" Makefile ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 ABC_USE_NAMESPACE=abc VERBOSE=$(Q) @@ -207,10 +217,17 @@ ifeq ($(OS), OpenBSD) ABC_ARCHFLAGS += "-DABC_NO_RLIMIT" endif +# This gets overridden later. +LTOFLAGS := $(GCC_LTO) + ifeq ($(CONFIG),clang) CXX = clang++ -CXXFLAGS += -std=$(CXXSTD) -Os +CXXFLAGS += -std=$(CXXSTD) $(OPT_LEVEL) +ifeq ($(ENABLE_LTO),1) +LINKFLAGS += -fuse-ld=lld +endif ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H $(ABC_ARCHFLAGS)" +LTOFLAGS := $(CLANG_LTO) ifneq ($(SANITIZER),) $(info [Clang Sanitizer] $(SANITIZER)) @@ -226,19 +243,20 @@ endif ifneq ($(findstring cfi,$(SANITIZER)),) CXXFLAGS += -flto LINKFLAGS += -flto +LTOFLAGS = endif endif else ifeq ($(CONFIG),gcc) CXX = g++ -CXXFLAGS += -std=$(CXXSTD) -Os +CXXFLAGS += -std=$(CXXSTD) $(OPT_LEVEL) ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H $(ABC_ARCHFLAGS)" else ifeq ($(CONFIG),gcc-static) LINKFLAGS := $(filter-out -rdynamic,$(LINKFLAGS)) -static LIBS := $(filter-out -lrt,$(LIBS)) CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS)) -CXXFLAGS += -std=$(CXXSTD) -Os +CXXFLAGS += -std=$(CXXSTD) $(OPT_LEVEL) ABCMKARGS = CC="$(CC)" CXX="$(CXX)" LD="$(CXX)" ABC_USE_LIBSTDCXX=1 LIBS="-lm -lpthread -static" OPTFLAGS="-O" \ ARCHFLAGS="-DABC_USE_STDINT_H -DABC_NO_DYNAMIC_LINKING=1 -Wno-unused-but-set-variable $(ARCHFLAGS)" ABC_USE_NO_READLINE=1 ifeq ($(DISABLE_ABC_THREADS),1) @@ -247,12 +265,12 @@ endif else ifeq ($(CONFIG),afl-gcc) CXX = AFL_QUIET=1 AFL_HARDEN=1 afl-gcc -CXXFLAGS += -std=$(CXXSTD) -Os +CXXFLAGS += -std=$(CXXSTD) $(OPT_LEVEL) ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H" else ifeq ($(CONFIG),cygwin) CXX = g++ -CXXFLAGS += -std=gnu++11 -Os +CXXFLAGS += -std=gnu++11 $(OPT_LEVEL) ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H" else ifeq ($(CONFIG),wasi) @@ -267,7 +285,7 @@ AR = $(WASI_SDK)/bin/ar RANLIB = $(WASI_SDK)/bin/ranlib WASIFLAGS := --sysroot $(WASI_SDK)/share/wasi-sysroot $(WASIFLAGS) endif -CXXFLAGS := $(WASIFLAGS) -std=$(CXXSTD) -Os -D_WASI_EMULATED_PROCESS_CLOCKS $(filter-out -fPIC,$(CXXFLAGS)) +CXXFLAGS := $(WASIFLAGS) -std=$(CXXSTD) $(OPT_LEVEL) -D_WASI_EMULATED_PROCESS_CLOCKS $(filter-out -fPIC,$(CXXFLAGS)) LINKFLAGS := $(WASIFLAGS) -Wl,-z,stack-size=1048576 $(filter-out -rdynamic,$(LINKFLAGS)) LIBS := -lwasi-emulated-process-clocks $(filter-out -lrt,$(LIBS)) ABCMKARGS += AR="$(AR)" RANLIB="$(RANLIB)" @@ -285,7 +303,7 @@ endif else ifeq ($(CONFIG),mxe) PKG_CONFIG = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-pkg-config CXX = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-g++ -CXXFLAGS += -std=$(CXXSTD) -Os -D_POSIX_SOURCE -DYOSYS_MXE_HACKS -Wno-attributes +CXXFLAGS += -std=$(CXXSTD) $(OPT_LEVEL) -D_POSIX_SOURCE -DYOSYS_MXE_HACKS -Wno-attributes CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS)) LINKFLAGS := $(filter-out -rdynamic,$(LINKFLAGS)) -s LIBS := $(filter-out -lrt,$(LIBS)) @@ -296,7 +314,7 @@ EXE = .exe else ifeq ($(CONFIG),msys2-32) CXX = i686-w64-mingw32-g++ -CXXFLAGS += -std=$(CXXSTD) -Os -D_POSIX_SOURCE -DYOSYS_WIN32_UNIX_DIR +CXXFLAGS += -std=$(CXXSTD) $(OPT_LEVEL) -D_POSIX_SOURCE -DYOSYS_WIN32_UNIX_DIR CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS)) LINKFLAGS := $(filter-out -rdynamic,$(LINKFLAGS)) -s LIBS := $(filter-out -lrt,$(LIBS)) @@ -306,7 +324,7 @@ EXE = .exe else ifeq ($(CONFIG),msys2-64) CXX = x86_64-w64-mingw32-g++ -CXXFLAGS += -std=$(CXXSTD) -Os -D_POSIX_SOURCE -DYOSYS_WIN32_UNIX_DIR +CXXFLAGS += -std=$(CXXSTD) $(OPT_LEVEL) -D_POSIX_SOURCE -DYOSYS_WIN32_UNIX_DIR CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS)) LINKFLAGS := $(filter-out -rdynamic,$(LINKFLAGS)) -s LIBS := $(filter-out -lrt,$(LIBS)) @@ -315,13 +333,20 @@ ABCMKARGS += LIBS="-lpthread -lshlwapi -s" ABC_USE_NO_READLINE=0 CC="x86_64-w64- EXE = .exe else ifeq ($(CONFIG),none) -CXXFLAGS += -std=$(CXXSTD) -Os +CXXFLAGS += -std=$(CXXSTD) $(OPT_LEVEL) ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H $(ABC_ARCHFLAGS)" +LTOFLAGS = else $(error Invalid CONFIG setting '$(CONFIG)'. Valid values: clang, gcc, mxe, msys2-32, msys2-64, none) endif + +ifeq ($(ENABLE_LTO),1) +CXXFLAGS += $(LTOFLAGS) +LINKFLAGS += $(LTOFLAGS) +endif + ifeq ($(ENABLE_LIBYOSYS),1) TARGETS += libyosys.so endif @@ -436,16 +461,8 @@ CXXFLAGS += -pg LINKFLAGS += -pg endif -ifeq ($(ENABLE_NDEBUG),1) -CXXFLAGS := -O3 -DNDEBUG $(filter-out -Os -ggdb,$(CXXFLAGS)) -endif - ifeq ($(ENABLE_DEBUG),1) -ifeq ($(CONFIG),clang) -CXXFLAGS := -O0 -DDEBUG $(filter-out -Os,$(CXXFLAGS)) -else -CXXFLAGS := -Og -DDEBUG $(filter-out -Os,$(CXXFLAGS)) -endif +CXXFLAGS := -Og -DDEBUG $(filter-out $(OPT_LEVEL),$(CXXFLAGS)) endif ifeq ($(ENABLE_ABC),1) @@ -574,6 +591,7 @@ S = endif $(eval $(call add_include_file,kernel/binding.h)) +$(eval $(call add_include_file,kernel/bitpattern.h)) $(eval $(call add_include_file,kernel/cellaigs.h)) $(eval $(call add_include_file,kernel/celledges.h)) $(eval $(call add_include_file,kernel/celltypes.h)) @@ -619,7 +637,7 @@ $(eval $(call add_include_file,backends/rtlil/rtlil_backend.h)) OBJS += kernel/driver.o kernel/register.o kernel/rtlil.o kernel/log.o kernel/calc.o kernel/yosys.o OBJS += kernel/binding.o -OBJS += kernel/cellaigs.o kernel/celledges.o kernel/satgen.o kernel/scopeinfo.o kernel/qcsat.o kernel/mem.o kernel/ffmerge.o kernel/ff.o kernel/yw.o kernel/json.o kernel/fmt.o +OBJS += kernel/cellaigs.o kernel/celledges.o kernel/cost.o kernel/satgen.o kernel/scopeinfo.o kernel/qcsat.o kernel/mem.o kernel/ffmerge.o kernel/ff.o kernel/yw.o kernel/json.o kernel/fmt.o ifeq ($(ENABLE_ZLIB),1) OBJS += kernel/fstdata.o endif @@ -773,10 +791,10 @@ check-git-abc: exit 1; \ elif git -C "$(YOSYS_SRC)" submodule status abc 2>/dev/null | grep -q '^ '; then \ exit 0; \ - elif [ -f "$(YOSYS_SRC)/abc/.gitcommit" ] && ! grep -q '\$$Format:%h\$$' "$(YOSYS_SRC)/abc/.gitcommit"; then \ + elif [ -f "$(YOSYS_SRC)/abc/.gitcommit" ] && ! grep -q '\$$Format:%[hH]\$$' "$(YOSYS_SRC)/abc/.gitcommit"; then \ echo "'abc' comes from a tarball. Continuing."; \ exit 0; \ - elif [ -f "$(YOSYS_SRC)/abc/.gitcommit" ] && grep -q '\$$Format:%h\$$' "$(YOSYS_SRC)/abc/.gitcommit"; then \ + elif [ -f "$(YOSYS_SRC)/abc/.gitcommit" ] && grep -q '\$$Format:%[hH]\$$' "$(YOSYS_SRC)/abc/.gitcommit"; then \ echo "Error: 'abc' is not configured as a git submodule."; \ echo "To resolve this:"; \ echo "1. Back up your changes: Save any modifications from the 'abc' directory to another location."; \ @@ -863,6 +881,7 @@ endif +cd tests/arch/quicklogic/pp3 && bash run-test.sh $(SEEDOPT) +cd tests/arch/quicklogic/qlf_k6n10f && bash run-test.sh $(SEEDOPT) +cd tests/arch/gatemate && bash run-test.sh $(SEEDOPT) + +cd tests/arch/microchip && bash run-test.sh $(SEEDOPT) +cd tests/rpc && bash run-test.sh +cd tests/memfile && bash run-test.sh +cd tests/verilog && bash run-test.sh diff --git a/README.md b/README.md index bb1c4d443e4..7437bb2832b 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ Xdot (graphviz) is used by the ``show`` command in yosys to display schematics. For example on Ubuntu Linux 16.04 LTS the following commands will install all prerequisites for building yosys: - $ sudo apt-get install build-essential clang bison flex \ + $ sudo apt-get install build-essential clang lld bison flex \ libreadline-dev gawk tcl-dev libffi-dev git \ graphviz xdot pkg-config python3 libboost-system-dev \ libboost-python-dev libboost-filesystem-dev zlib1g-dev @@ -116,6 +116,17 @@ unless `CXX` is assigned in the call to make, e.g. $ make CXX=$CXX +The Makefile has many variables influencing the build process. These can be +adjusted by modifying the Makefile.conf file which is created at the +`make config-...` step (see above), or they can be set by passing an option +to the make command directly. + +For example, if you have clang, and (a compatible version of) `ld.lld` +available in PATH, it's recommended to speed up incremental builds with +lld by enabling LTO: + + $ make ENABLE_LTO=1 + For other compilers and build configurations it might be necessary to make some changes to the config section of the Makefile. diff --git a/abc b/abc index 237d81397fc..28d955ca97a 160000 --- a/abc +++ b/abc @@ -1 +1 @@ -Subproject commit 237d81397fcc85dd3894bf1a449d2955cd3df02d +Subproject commit 28d955ca97a1c4be3aed4062aec0241a734fac5d diff --git a/docs/source/conf.py b/docs/source/conf.py index 1dd53e95411..2fdd43a31a0 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -5,7 +5,7 @@ project = 'YosysHQ Yosys' author = 'YosysHQ GmbH' copyright ='2024 YosysHQ GmbH' -yosys_ver = "0.43" +yosys_ver = "0.44" # select HTML theme html_theme = 'furo' diff --git a/docs/source/getting_started/installation.rst b/docs/source/getting_started/installation.rst index 1a89886089b..aea8b6a2dd2 100644 --- a/docs/source/getting_started/installation.rst +++ b/docs/source/getting_started/installation.rst @@ -94,7 +94,7 @@ Installing all prerequisites for Ubuntu 20.04: .. code:: console - sudo sudo apt-get install build-essential clang bison flex \ + sudo sudo apt-get install build-essential clang lld bison flex \ libreadline-dev gawk tcl-dev libffi-dev git make \ graphviz xdot pkg-config python3 libboost-system-dev \ libboost-python-dev libboost-filesystem-dev zlib1g-dev diff --git a/docs/source/using_yosys/more_scripting/interactive_investigation.rst b/docs/source/using_yosys/more_scripting/interactive_investigation.rst index db34f041bb9..03a1faefa73 100644 --- a/docs/source/using_yosys/more_scripting/interactive_investigation.rst +++ b/docs/source/using_yosys/more_scripting/interactive_investigation.rst @@ -115,7 +115,7 @@ leads us to the third diagram: Output of the third :cmd:ref:`show` command in :ref:`example_ys` -Here we see that the :cmd:ref:`proc` command not only has removed the artifacts +Here we see that the :cmd:ref:`opt` command not only has removed the artifacts left behind by :cmd:ref:`proc`, but also determined correctly that it can remove the first ``$mux`` cell without changing the behavior of the circuit. diff --git a/flake.nix b/flake.nix index 1e202324647..90fa5328c09 100644 --- a/flake.nix +++ b/flake.nix @@ -18,7 +18,7 @@ yosys = pkgs.clangStdenv.mkDerivation { name = "yosys"; src = ./. ; - buildInputs = with pkgs; [ clang bison flex libffi tcl readline python3 llvmPackages.libcxxClang zlib git pkg-configUpstream ]; + buildInputs = with pkgs; [ clang bison flex libffi tcl readline python3 llvmPackages.libcxxClang zlib git pkg-configUpstream llvmPackages.bintools ]; checkInputs = with pkgs; [ gtest ]; propagatedBuildInputs = [ abc-verifier ]; preConfigure = "make config-clang"; @@ -41,7 +41,7 @@ packages.default = yosys; defaultPackage = yosys; devShell = pkgs.mkShell { - buildInputs = with pkgs; [ clang bison flex libffi tcl readline python3 llvmPackages.libcxxClang zlib git gtest abc-verifier ]; + buildInputs = with pkgs; [ clang llvmPackages.bintools bison flex libffi tcl readline python3 llvmPackages.libcxxClang zlib git gtest abc-verifier ]; }; } ); diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index 996f6715d05..d21b8f02d90 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -20,10 +20,10 @@ * * This is the AST frontend library. * - * The AST frontend library is not a frontend on it's own but provides a - * generic abstract syntax tree (AST) abstraction for HDL code and can be - * used by HDL frontends. See "ast.h" for an overview of the API and the - * Verilog frontend for an usage example. + * The AST frontend library is not a frontend on its own but provides an + * abstract syntax tree (AST) abstraction for the open source Verilog frontend + * at frontends/verilog. + * * */ diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index 59a393e080e..6c4a1e15a65 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -17,7 +17,9 @@ * * --- * - * This is support code for the Verilog frontend at frontends/verilog + * The AST frontend library is not a frontend on its own but provides an + * abstract syntax tree (AST) abstraction for the open source Verilog frontend + * at frontends/verilog. * */ diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index 56e94489cba..2dd8aa09551 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -58,6 +58,7 @@ USING_YOSYS_NAMESPACE #ifdef VERIFIC_VHDL_SUPPORT #include "vhdl_file.h" +#include "VhdlIdDef.h" #include "VhdlUnits.h" #include "NameSpace.h" #endif @@ -2759,7 +2760,7 @@ void import_all(const char* work, std::map *nl_todo, Map * #endif } -std::set import_tops(const char* work, std::map *nl_todo, Map *parameters, bool show_message, std::string ppfile YS_MAYBE_UNUSED, std::vector &tops) +std::set import_tops(const char* work, std::map *nl_todo, Map *parameters, bool show_message, std::string ppfile YS_MAYBE_UNUSED, std::vector &tops, std::string *top = nullptr) { std::set top_mod_names; Array *netlists = nullptr; @@ -2817,6 +2818,12 @@ std::set import_tops(const char* work, std::mapId()->OrigName()) != 0) { + top_mod_names.erase(name); + top_mod_names.insert(vhdl_unit->Id()->OrigName()); + if (top && *top == name) + *top = vhdl_unit->Id()->OrigName(); + } continue; } #endif @@ -2957,7 +2964,7 @@ std::string verific_import(Design *design, const std::mapname)) + return mod_cost_cache_.at(mod->name); + + unsigned int module_cost = 1; + for (auto c : mod->cells()) { + unsigned int new_cost = module_cost + get(c); + module_cost = new_cost >= module_cost ? new_cost : INT_MAX; + } + + mod_cost_cache_[mod->name] = module_cost; + return module_cost; +} + +static unsigned int y_coef(RTLIL::IdString type) +{ + if ( + // equality + type.in(ID($bweqx), ID($nex), ID($eqx)) || + // basic logic + type.in(ID($and), ID($or), ID($xor), ID($xnor), ID($not)) || + // mux + type.in(ID($bwmux), ID($mux)) || + // others + type == ID($tribuf)) { + return 1; + } else if (type == ID($neg)) { + return 4; + } else if (type == ID($demux)) { + return 2; + } else if (type == ID($fa)) { + return 5; + } else if (type.in(ID($add), ID($sub), ID($alu))) { + // multi-bit adders + return 8; + } else if (type.in(ID($shl), ID($sshl))) { + // left shift + return 10; + } + return 0; +} + +static unsigned int max_inp_coef(RTLIL::IdString type) +{ + if ( + // binop reduce + type.in(ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool)) || + // others + type.in(ID($logic_not), ID($pmux), ID($bmux))) { + return 1; + } else if ( + // equality + type.in(ID($eq), ID($ne)) || + // logic + type.in(ID($logic_and), ID($logic_or))) { + return 2; + } else if (type == ID($lcu)) { + return 5; + } else if (type.in(ID($lt), ID($le), ID($ge), ID($gt))) { + // comparison + return 7; + } + return 0; +} + +static unsigned int sum_coef(RTLIL::IdString type) +{ + if (type.in(ID($shr), ID($sshr))) { + // right shift + return 4; + } else if (type.in(ID($shift), ID($shiftx))) { + // shift + return 8; + } + return 0; +} + +static unsigned int is_div_mod(RTLIL::IdString type) +{ + return (type == ID($div) || type == ID($divfloor) || type == ID($mod) || type == ID($modfloor)); +} + +static bool is_free(RTLIL::IdString type) +{ + return ( + // tags + type.in(ID($overwrite_tag), ID($set_tag), ID($original_tag), ID($get_tag)) || + // formal + type.in(ID($check), ID($equiv), ID($initstate), ID($assert), ID($assume), ID($live), ID($cover), ID($fair)) || + type.in(ID($allseq), ID($allconst), ID($anyseq), ID($anyconst), ID($anyinit)) || + // utilities + type.in(ID($scopeinfo), ID($print)) || + // real but free + type.in(ID($concat), ID($slice), ID($pos)) || + // specify + type.in(ID($specrule), ID($specify2), ID($specify3))); +} + +unsigned int max_inp_width(RTLIL::Cell *cell) +{ + unsigned int max = 0; + RTLIL::IdString input_width_params[] = { + ID::WIDTH, + ID::A_WIDTH, + ID::B_WIDTH, + ID::S_WIDTH, + }; + + if (cell->type == ID($bmux)) + return cell->getParam(ID::WIDTH).as_int() << cell->getParam(ID::S_WIDTH).as_int(); + + for (RTLIL::IdString param : input_width_params) + if (cell->hasParam(param)) + max = std::max(max, (unsigned int)cell->getParam(param).as_int()); + return max; +} + +unsigned int port_width_sum(RTLIL::Cell *cell) +{ + unsigned int sum = 0; + RTLIL::IdString port_width_params[] = { + ID::WIDTH, ID::A_WIDTH, ID::B_WIDTH, ID::S_WIDTH, ID::Y_WIDTH, + }; + + for (auto param : port_width_params) + if (cell->hasParam(param)) + sum += cell->getParam(param).as_int(); + + return sum; +} + +unsigned int CellCosts::get(RTLIL::Cell *cell) +{ + + // simple 1-bit cells + if (cmos_gate_cost().count(cell->type)) + return 1; + + if (design_ && design_->module(cell->type) && cell->parameters.empty()) { + log_debug("%s is a module, recurse\n", cell->name.c_str()); + return get(design_->module(cell->type)); + } else if (RTLIL::builtin_ff_cell_types().count(cell->type)) { + log_assert(cell->hasPort(ID::Q) && "Weird flip flop"); + log_debug("%s is ff\n", cell->name.c_str()); + return cell->getParam(ID::WIDTH).as_int(); + } else if (y_coef(cell->type)) { + // linear with Y_WIDTH or WIDTH + log_assert((cell->hasParam(ID::Y_WIDTH) || cell->hasParam(ID::WIDTH)) && "Unknown width"); + auto param = cell->hasParam(ID::Y_WIDTH) ? ID::Y_WIDTH : ID::WIDTH; + int width = cell->getParam(param).as_int(); + if (cell->type == ID($demux)) + width <<= cell->getParam(ID::S_WIDTH).as_int(); + log_debug("%s Y*coef %d * %d\n", cell->name.c_str(), width, y_coef(cell->type)); + return width * y_coef(cell->type); + } else if (sum_coef(cell->type)) { + // linear with sum of port widths + unsigned int sum = port_width_sum(cell); + log_debug("%s sum*coef %d * %d\n", cell->name.c_str(), sum, sum_coef(cell->type)); + return sum * sum_coef(cell->type); + } else if (max_inp_coef(cell->type)) { + // linear with largest input width + unsigned int max = max_inp_width(cell); + log_debug("%s max*coef %d * %d\n", cell->name.c_str(), max, max_inp_coef(cell->type)); + return max * max_inp_coef(cell->type); + } else if (is_div_mod(cell->type) || cell->type == ID($mul)) { + // quadratic with sum of port widths + unsigned int sum = port_width_sum(cell); + unsigned int coef = cell->type == ID($mul) ? 3 : 5; + log_debug("%s coef*(sum**2) %d * %d\n", cell->name.c_str(), coef, sum * sum); + return coef * sum * sum; + } else if (cell->type == ID($lut)) { + int width = cell->getParam(ID::WIDTH).as_int(); + unsigned int cost = 1U << (unsigned int)width; + log_debug("%s is 2**%d\n", cell->name.c_str(), width); + return cost; + } else if (cell->type == ID($sop)) { + int width = cell->getParam(ID::WIDTH).as_int(); + int depth = cell->getParam(ID::DEPTH).as_int(); + log_debug("%s is (2*%d + 1)*%d\n", cell->name.c_str(), width, depth); + return (2 * width + 1) * depth; + } else if (is_free(cell->type)) { + log_debug("%s is free\n", cell->name.c_str()); + return 0; + } + // TODO: $fsm $mem.* $macc + // ignored: $pow + + log_warning("Can't determine cost of %s cell (%d parameters).\n", log_id(cell->type), GetSize(cell->parameters)); + return 1; +} diff --git a/kernel/cost.h b/kernel/cost.h index b81420af756..15d74d7b30e 100644 --- a/kernel/cost.h +++ b/kernel/cost.h @@ -26,7 +26,17 @@ YOSYS_NAMESPACE_BEGIN struct CellCosts { + + private: + dict mod_cost_cache_; + Design *design_ = nullptr; + + public: + CellCosts(RTLIL::Design *design) : design_(design) { } + static const dict& default_gate_cost() { + // Default size heuristics for several common PDK standard cells + // used by abc and stat static const dict db = { { ID($_BUF_), 1 }, { ID($_NOT_), 2 }, @@ -43,12 +53,14 @@ struct CellCosts { ID($_AOI4_), 7 }, { ID($_OAI4_), 7 }, { ID($_MUX_), 4 }, - { ID($_NMUX_), 4 } + { ID($_NMUX_), 4 }, }; return db; } static const dict& cmos_gate_cost() { + // Estimated CMOS transistor counts for several common PDK standard cells + // used by stat and optionally by abc static const dict db = { { ID($_BUF_), 1 }, { ID($_NOT_), 2 }, @@ -65,50 +77,21 @@ struct CellCosts { ID($_AOI4_), 8 }, { ID($_OAI4_), 8 }, { ID($_MUX_), 12 }, - { ID($_NMUX_), 10 } + { ID($_NMUX_), 10 }, + { ID($_DFF_P_), 16 }, + { ID($_DFF_N_), 16 }, }; return db; } - dict mod_cost_cache; - const dict *gate_cost = nullptr; - Design *design = nullptr; - - int get(RTLIL::IdString type) const - { - if (gate_cost && gate_cost->count(type)) - return gate_cost->at(type); - - log_warning("Can't determine cost of %s cell.\n", log_id(type)); - return 1; - } - - int get(RTLIL::Cell *cell) - { - if (gate_cost && gate_cost->count(cell->type)) - return gate_cost->at(cell->type); - - if (design && design->module(cell->type) && cell->parameters.empty()) - { - RTLIL::Module *mod = design->module(cell->type); - - if (mod->attributes.count(ID(cost))) - return mod->attributes.at(ID(cost)).as_int(); - - if (mod_cost_cache.count(mod->name)) - return mod_cost_cache.at(mod->name); - - int module_cost = 1; - for (auto c : mod->cells()) - module_cost += get(c); - - mod_cost_cache[mod->name] = module_cost; - return module_cost; - } - - log_warning("Can't determine cost of %s cell (%d parameters).\n", log_id(cell->type), GetSize(cell->parameters)); - return 1; - } + // Get the cell cost for a cell based on its parameters. + // This cost is an *approximate* upper bound for the number of gates that + // the cell will get mapped to with "opt -fast; techmap" + // The intended usage is for flattening heuristics and similar situations + unsigned int get(RTLIL::Cell *cell); + // Sum up the cell costs of all cells in the module + // and all its submodules recursively + unsigned int get(RTLIL::Module *mod); }; YOSYS_NAMESPACE_END diff --git a/kernel/hashlib.h b/kernel/hashlib.h index e8ddddd3357..6b880f3a666 100644 --- a/kernel/hashlib.h +++ b/kernel/hashlib.h @@ -188,6 +188,7 @@ inline unsigned int mkhash(const T &v) { inline int hashtable_size(int min_size) { + // Primes as generated by https://oeis.org/A175953 static std::vector zero_and_some_primes = { 0, 23, 29, 37, 47, 59, 79, 101, 127, 163, 211, 269, 337, 431, 541, 677, 853, 1069, 1361, 1709, 2137, 2677, 3347, 4201, 5261, 6577, 8231, 10289, @@ -196,7 +197,9 @@ inline int hashtable_size(int min_size) 897133, 1121423, 1401791, 1752239, 2190299, 2737937, 3422429, 4278037, 5347553, 6684443, 8355563, 10444457, 13055587, 16319519, 20399411, 25499291, 31874149, 39842687, 49803361, 62254207, 77817767, 97272239, - 121590311, 151987889, 189984863, 237481091, 296851369, 371064217 + 121590311, 151987889, 189984863, 237481091, 296851369, 371064217, + 463830313, 579787991, 724735009, 905918777, 1132398479, 1415498113, + 1769372713 }; for (auto p : zero_and_some_primes) @@ -1129,6 +1132,11 @@ class idict const_iterator end() const { return const_iterator(*this, offset + size()); } }; +/** + * Union-find data structure with a promotion method + * mfp stands for "merge, find, promote" + * i-prefixed methods operate on indices in parents +*/ template class mfp { @@ -1142,13 +1150,18 @@ class mfp { } + // Finds a given element's index. If it isn't in the data structure, + // it is added as its own set int operator()(const K &key) const { int i = database(key); + // If the lookup caused the database to grow, + // also add a corresponding entry in parents initialized to -1 (no parent) parents.resize(database.size(), -1); return i; } + // Finds an element at given index const K &operator[](int index) const { return database[index]; @@ -1161,6 +1174,11 @@ class mfp while (parents[p] != -1) p = parents[p]; + // p is now the representative of i + // Now we traverse from i up to the representative again + // and make p the parent of all the nodes along the way. + // This is a side effect and doesn't affect the return value. + // It speeds up future find operations while (k != p) { int next_k = parents[k]; parents[k] = p; @@ -1170,6 +1188,7 @@ class mfp return p; } + // Merge sets if the given indices belong to different sets void imerge(int i, int j) { i = ifind(i); diff --git a/kernel/sigtools.h b/kernel/sigtools.h index 4ea43d74364..63f1b51ec84 100644 --- a/kernel/sigtools.h +++ b/kernel/sigtools.h @@ -229,6 +229,13 @@ using sort_by_name_id_guard = typename std::enable_if class SigSet> : public SigSet::type>> {}; +/** + * SigMap wraps a union-find "database" + * to map SigBits of a module to canonical representative SigBits. + * SigBits that are connected share a set in the underlying database. + * If a SigBit has a const state (impl: bit.wire is nullptr), + * it's promoted to a representative. + */ struct SigMap { mfp database; @@ -249,6 +256,7 @@ struct SigMap database.clear(); } + // Rebuild SigMap for all connections in module void set(RTLIL::Module *module) { int bitcount = 0; @@ -262,6 +270,7 @@ struct SigMap add(it.first, it.second); } + // Add connections from "from" to "to", bit-by-bit void add(const RTLIL::SigSpec& from, const RTLIL::SigSpec& to) { log_assert(GetSize(from) == GetSize(to)); @@ -287,6 +296,7 @@ struct SigMap } } + // Add sig as disconnected from anything void add(const RTLIL::SigBit &bit) { const auto &b = database.find(bit); @@ -302,6 +312,7 @@ struct SigMap inline void add(Wire *wire) { return add(RTLIL::SigSpec(wire)); } + // Modify bit to its representative void apply(RTLIL::SigBit &bit) const { bit = database.find(bit); @@ -332,6 +343,7 @@ struct SigMap return sig; } + // All non-const bits RTLIL::SigSpec allbits() const { RTLIL::SigSpec sig; diff --git a/kernel/yosys.cc b/kernel/yosys.cc index 57433d0d9c1..efdc54b796c 100644 --- a/kernel/yosys.cc +++ b/kernel/yosys.cc @@ -967,7 +967,7 @@ std::string proc_self_dirname() { return "/"; } -#elif defined(__OpenBSD__) +#elif defined(__OpenBSD__) || defined(__HAIKU__) char yosys_path[PATH_MAX]; char *yosys_argv0; @@ -1126,31 +1126,40 @@ bool run_frontend(std::string filename, std::string command, RTLIL::Design *desi design = yosys_design; if (command == "auto") { - std::string filename_trim = filename; - if (filename_trim.size() > 3 && filename_trim.compare(filename_trim.size()-3, std::string::npos, ".gz") == 0) - filename_trim.erase(filename_trim.size()-3); - if (filename_trim.size() > 2 && filename_trim.compare(filename_trim.size()-2, std::string::npos, ".v") == 0) - command = " -vlog2k"; - else if (filename_trim.size() > 2 && filename_trim.compare(filename_trim.size()-3, std::string::npos, ".sv") == 0) - command = " -sv"; - else if (filename_trim.size() > 3 && filename_trim.compare(filename_trim.size()-4, std::string::npos, ".vhd") == 0) - command = " -vhdl"; - else if (filename_trim.size() > 4 && filename_trim.compare(filename_trim.size()-5, std::string::npos, ".blif") == 0) - command = "blif"; - else if (filename_trim.size() > 5 && filename_trim.compare(filename_trim.size()-6, std::string::npos, ".eblif") == 0) - command = "blif"; - else if (filename_trim.size() > 4 && filename_trim.compare(filename_trim.size()-5, std::string::npos, ".json") == 0) - command = "json"; - else if (filename_trim.size() > 3 && filename_trim.compare(filename_trim.size()-3, std::string::npos, ".il") == 0) - command = "rtlil"; - else if (filename_trim.size() > 3 && filename_trim.compare(filename_trim.size()-3, std::string::npos, ".ys") == 0) - command = "script"; - else if (filename_trim.size() > 3 && filename_trim.compare(filename_trim.size()-4, std::string::npos, ".tcl") == 0) - command = "tcl"; - else if (filename == "-") - command = "script"; - else - log_error("Can't guess frontend for input file `%s' (missing -f option)!\n", filename.c_str()); + std::string filename_trim = filename; + + auto has_extension = [](const std::string& filename, const std::string& extension) { + if (filename.size() >= extension.size()) { + return filename.compare(filename.size() - extension.size(), extension.size(), extension) == 0; + } + return false; + }; + + if (has_extension(filename_trim, ".gz")) { + filename_trim.erase(filename_trim.size() - 3); + } + + if (has_extension(filename_trim, ".v")) { + command = " -vlog2k"; + } else if (has_extension(filename_trim, ".sv")) { + command = " -sv"; + } else if (has_extension(filename_trim, ".vhd") || has_extension(filename_trim, ".vhdl")) { + command = " -vhdl"; + } else if (has_extension(filename_trim, ".blif") || has_extension(filename_trim, ".eblif")) { + command = "blif"; + } else if (has_extension(filename_trim, ".json")) { + command = "json"; + } else if (has_extension(filename_trim, ".il")) { + command = "rtlil"; + } else if (has_extension(filename_trim, ".ys")) { + command = "script"; + } else if (has_extension(filename_trim, ".tcl")) { + command = "tcl"; + } else if (filename == "-") { + command = "script"; + } else { + log_error("Can't guess frontend for input file `%s' (missing -f option)!\n", filename.c_str()); + } } if (command == "script") diff --git a/misc/create_vcxsrc.sh b/misc/create_vcxsrc.sh index 5f8b35e8075..d7b516fd698 100644 --- a/misc/create_vcxsrc.sh +++ b/misc/create_vcxsrc.sh @@ -6,8 +6,8 @@ yosysver="$2" gitsha="$3" rm -rf YosysVS-Tpl-v2.zip YosysVS -wget https://yosyshq.net/yosys/nogit/YosysVS-Tpl-v2.zip -wget https://www.zlib.net/fossils/zlib-1.2.11.tar.gz +wget https://github.com/YosysHQ/yosys/releases/download/resources/YosysVS-Tpl-v2.zip +wget https://github.com/YosysHQ/yosys/releases/download/resources/zlib-1.2.11.tar.gz unzip YosysVS-Tpl-v2.zip rm -f YosysVS-Tpl-v2.zip diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index c2bb72f3bb3..379ce209790 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -230,8 +230,6 @@ struct statdata_t if (gate_costs.count(ctype)) tran_cnt += cnum * gate_costs.at(ctype); - else if (ctype.in(ID($_DFF_P_), ID($_DFF_N_))) - tran_cnt += cnum * 16; else *tran_cnt_exact = false; } diff --git a/passes/hierarchy/Makefile.inc b/passes/hierarchy/Makefile.inc index b3f139b72e7..3cd1b6180c6 100644 --- a/passes/hierarchy/Makefile.inc +++ b/passes/hierarchy/Makefile.inc @@ -2,4 +2,5 @@ OBJS += passes/hierarchy/hierarchy.o OBJS += passes/hierarchy/uniquify.o OBJS += passes/hierarchy/submod.o +OBJS += passes/hierarchy/keep_hierarchy.o diff --git a/passes/hierarchy/keep_hierarchy.cc b/passes/hierarchy/keep_hierarchy.cc new file mode 100644 index 00000000000..6b947e67370 --- /dev/null +++ b/passes/hierarchy/keep_hierarchy.cc @@ -0,0 +1,74 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/cost.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct KeepHierarchyPass : public Pass { + KeepHierarchyPass() : Pass("keep_hierarchy", "add the keep_hierarchy attribute") {} + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" keep_hierarchy [options]\n"); + log("\n"); + log("Add the keep_hierarchy attribute.\n"); + log("\n"); + log(" -min_cost \n"); + log(" only add the attribute to modules estimated to have more\n"); + log(" than gates after simple techmapping. Intended\n"); + log(" for tuning trade-offs between quality and yosys runtime.\n"); + } + void execute(std::vector args, RTLIL::Design *design) override + { + unsigned int min_cost = 0; + + log_header(design, "Executing KEEP_HIERARCHY pass.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-min_cost" && argidx+1 < args.size()) { + min_cost = std::stoi(args[++argidx].c_str()); + continue; + } + break; + } + extra_args(args, argidx, design); + + CellCosts costs(design); + + for (auto module : design->selected_modules()) { + if (min_cost) { + unsigned int cost = costs.get(module); + if (cost > min_cost) { + log("Marking %s (module too big: %d > %d).\n", log_id(module), cost, min_cost); + module->set_bool_attribute(ID::keep_hierarchy); + } + } else { + log("Marking %s.\n", log_id(module)); + module->set_bool_attribute(ID::keep_hierarchy); + } + } + } +} KeepHierarchyPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc index 3eadd35c6af..ae5a980b183 100644 --- a/passes/opt/opt_expr.cc +++ b/passes/opt/opt_expr.cc @@ -395,18 +395,10 @@ int get_highest_hot_index(RTLIL::SigSpec signal) void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool consume_x, bool mux_undef, bool mux_bool, bool do_fine, bool keepdc, bool noclkinv) { - CellTypes ct_combinational; - ct_combinational.setup_internals(); - ct_combinational.setup_stdcells(); - SigMap assign_map(module); dict invert_map; - TopoSort> cells; - dict> cell_to_inbit; - dict> outbit_to_cell; - - for (auto cell : module->cells()) + for (auto cell : module->cells()) { if (design->selected(module, cell) && cell->type[0] == '$') { if (cell->type.in(ID($_NOT_), ID($not), ID($logic_not)) && GetSize(cell->getPort(ID::A)) == 1 && GetSize(cell->getPort(ID::Y)) == 1) @@ -414,110 +406,122 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (cell->type.in(ID($mux), ID($_MUX_)) && cell->getPort(ID::A) == SigSpec(State::S1) && cell->getPort(ID::B) == SigSpec(State::S0)) invert_map[assign_map(cell->getPort(ID::Y))] = assign_map(cell->getPort(ID::S)); - if (ct_combinational.cell_known(cell->type)) - for (auto &conn : cell->connections()) { - RTLIL::SigSpec sig = assign_map(conn.second); - sig.remove_const(); - if (ct_combinational.cell_input(cell->type, conn.first)) - cell_to_inbit[cell].insert(sig.begin(), sig.end()); - if (ct_combinational.cell_output(cell->type, conn.first)) - for (auto &bit : sig) - outbit_to_cell[bit].insert(cell); - } - cells.node(cell); } + } - // Build the graph for the topological sort. - for (auto &it_right : cell_to_inbit) { - const int r_index = cells.node(it_right.first); - for (auto &it_sigbit : it_right.second) { - for (auto &it_left : outbit_to_cell[it_sigbit]) { - const int l_index = cells.node(it_left); - cells.edge(l_index, r_index); - } - } - } + CellTypes ct_memcells; + ct_memcells.setup_stdcells_mem(); - cells.sort(); + if (!noclkinv) + for (auto cell : module->cells()) + if (design->selected(module, cell)) { + if (cell->type.in(ID($dff), ID($dffe), ID($dffsr), ID($dffsre), ID($adff), ID($adffe), ID($aldff), ID($aldffe), ID($sdff), ID($sdffe), ID($sdffce), ID($fsm), ID($memrd), ID($memrd_v2), ID($memwr), ID($memwr_v2))) + handle_polarity_inv(cell, ID::CLK, ID::CLK_POLARITY, assign_map, invert_map); - for (auto cell : cells.sorted) - { -#define ACTION_DO(_p_, _s_) do { cover("opt.opt_expr.action_" S__LINE__); replace_cell(assign_map, module, cell, input.as_string(), _p_, _s_); goto next_cell; } while (0) -#define ACTION_DO_Y(_v_) ACTION_DO(ID::Y, RTLIL::SigSpec(RTLIL::State::S ## _v_)) + if (cell->type.in(ID($sr), ID($dffsr), ID($dffsre), ID($dlatchsr))) { + handle_polarity_inv(cell, ID::SET, ID::SET_POLARITY, assign_map, invert_map); + handle_polarity_inv(cell, ID::CLR, ID::CLR_POLARITY, assign_map, invert_map); + } - if (!noclkinv) - { - if (cell->type.in(ID($dff), ID($dffe), ID($dffsr), ID($dffsre), ID($adff), ID($adffe), ID($aldff), ID($aldffe), ID($sdff), ID($sdffe), ID($sdffce), ID($fsm), ID($memrd), ID($memrd_v2), ID($memwr), ID($memwr_v2))) - handle_polarity_inv(cell, ID::CLK, ID::CLK_POLARITY, assign_map, invert_map); + if (cell->type.in(ID($adff), ID($adffe), ID($adlatch))) + handle_polarity_inv(cell, ID::ARST, ID::ARST_POLARITY, assign_map, invert_map); - if (cell->type.in(ID($sr), ID($dffsr), ID($dffsre), ID($dlatchsr))) { - handle_polarity_inv(cell, ID::SET, ID::SET_POLARITY, assign_map, invert_map); - handle_polarity_inv(cell, ID::CLR, ID::CLR_POLARITY, assign_map, invert_map); - } + if (cell->type.in(ID($aldff), ID($aldffe))) + handle_polarity_inv(cell, ID::ALOAD, ID::ALOAD_POLARITY, assign_map, invert_map); - if (cell->type.in(ID($adff), ID($adffe), ID($adlatch))) - handle_polarity_inv(cell, ID::ARST, ID::ARST_POLARITY, assign_map, invert_map); + if (cell->type.in(ID($sdff), ID($sdffe), ID($sdffce))) + handle_polarity_inv(cell, ID::SRST, ID::SRST_POLARITY, assign_map, invert_map); - if (cell->type.in(ID($aldff), ID($aldffe))) - handle_polarity_inv(cell, ID::ALOAD, ID::ALOAD_POLARITY, assign_map, invert_map); + if (cell->type.in(ID($dffe), ID($adffe), ID($aldffe), ID($sdffe), ID($sdffce), ID($dffsre), ID($dlatch), ID($adlatch), ID($dlatchsr))) + handle_polarity_inv(cell, ID::EN, ID::EN_POLARITY, assign_map, invert_map); - if (cell->type.in(ID($sdff), ID($sdffe), ID($sdffce))) - handle_polarity_inv(cell, ID::SRST, ID::SRST_POLARITY, assign_map, invert_map); + if (!ct_memcells.cell_known(cell->type)) + continue; - if (cell->type.in(ID($dffe), ID($adffe), ID($aldffe), ID($sdffe), ID($sdffce), ID($dffsre), ID($dlatch), ID($adlatch), ID($dlatchsr))) - handle_polarity_inv(cell, ID::EN, ID::EN_POLARITY, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_SR_N?_", "$_SR_P?_", ID::S, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_SR_?N_", "$_SR_?P_", ID::R, assign_map, invert_map); - handle_clkpol_celltype_swap(cell, "$_SR_N?_", "$_SR_P?_", ID::S, assign_map, invert_map); - handle_clkpol_celltype_swap(cell, "$_SR_?N_", "$_SR_?P_", ID::R, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DFF_N_", "$_DFF_P_", ID::C, assign_map, invert_map); - handle_clkpol_celltype_swap(cell, "$_DFF_N_", "$_DFF_P_", ID::C, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DFFE_N?_", "$_DFFE_P?_", ID::C, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DFFE_?N_", "$_DFFE_?P_", ID::E, assign_map, invert_map); - handle_clkpol_celltype_swap(cell, "$_DFFE_N?_", "$_DFFE_P?_", ID::C, assign_map, invert_map); - handle_clkpol_celltype_swap(cell, "$_DFFE_?N_", "$_DFFE_?P_", ID::E, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DFF_N??_", "$_DFF_P??_", ID::C, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DFF_?N?_", "$_DFF_?P?_", ID::R, assign_map, invert_map); - handle_clkpol_celltype_swap(cell, "$_DFF_N??_", "$_DFF_P??_", ID::C, assign_map, invert_map); - handle_clkpol_celltype_swap(cell, "$_DFF_?N?_", "$_DFF_?P?_", ID::R, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DFFE_N???_", "$_DFFE_P???_", ID::C, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DFFE_?N??_", "$_DFFE_?P??_", ID::R, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DFFE_???N_", "$_DFFE_???P_", ID::E, assign_map, invert_map); - handle_clkpol_celltype_swap(cell, "$_DFFE_N???_", "$_DFFE_P???_", ID::C, assign_map, invert_map); - handle_clkpol_celltype_swap(cell, "$_DFFE_?N??_", "$_DFFE_?P??_", ID::R, assign_map, invert_map); - handle_clkpol_celltype_swap(cell, "$_DFFE_???N_", "$_DFFE_???P_", ID::E, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_SDFF_N??_", "$_SDFF_P??_", ID::C, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_SDFF_?N?_", "$_SDFF_?P?_", ID::R, assign_map, invert_map); - handle_clkpol_celltype_swap(cell, "$_SDFF_N??_", "$_SDFF_P??_", ID::C, assign_map, invert_map); - handle_clkpol_celltype_swap(cell, "$_SDFF_?N?_", "$_SDFF_?P?_", ID::R, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_SDFFE_N???_", "$_SDFFE_P???_", ID::C, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_SDFFE_?N??_", "$_SDFFE_?P??_", ID::R, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_SDFFE_???N_", "$_SDFFE_???P_", ID::E, assign_map, invert_map); - handle_clkpol_celltype_swap(cell, "$_SDFFE_N???_", "$_SDFFE_P???_", ID::C, assign_map, invert_map); - handle_clkpol_celltype_swap(cell, "$_SDFFE_?N??_", "$_SDFFE_?P??_", ID::R, assign_map, invert_map); - handle_clkpol_celltype_swap(cell, "$_SDFFE_???N_", "$_SDFFE_???P_", ID::E, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_SDFFCE_N???_", "$_SDFFCE_P???_", ID::C, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_SDFFCE_?N??_", "$_SDFFCE_?P??_", ID::R, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_SDFFCE_???N_", "$_SDFFCE_???P_", ID::E, assign_map, invert_map); - handle_clkpol_celltype_swap(cell, "$_SDFFCE_N???_", "$_SDFFCE_P???_", ID::C, assign_map, invert_map); - handle_clkpol_celltype_swap(cell, "$_SDFFCE_?N??_", "$_SDFFCE_?P??_", ID::R, assign_map, invert_map); - handle_clkpol_celltype_swap(cell, "$_SDFFCE_???N_", "$_SDFFCE_???P_", ID::E, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_ALDFF_N?_", "$_ALDFF_P?_", ID::C, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_ALDFF_?N_", "$_ALDFF_?P_", ID::L, assign_map, invert_map); - handle_clkpol_celltype_swap(cell, "$_ALDFF_N?_", "$_ALDFF_P?_", ID::C, assign_map, invert_map); - handle_clkpol_celltype_swap(cell, "$_ALDFF_?N_", "$_ALDFF_?P_", ID::L, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_ALDFFE_N??_", "$_ALDFFE_P??_", ID::C, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_ALDFFE_?N?_", "$_ALDFFE_?P?_", ID::L, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_ALDFFE_??N_", "$_ALDFFE_??P_", ID::E, assign_map, invert_map); - handle_clkpol_celltype_swap(cell, "$_ALDFFE_N??_", "$_ALDFFE_P??_", ID::C, assign_map, invert_map); - handle_clkpol_celltype_swap(cell, "$_ALDFFE_?N?_", "$_ALDFFE_?P?_", ID::L, assign_map, invert_map); - handle_clkpol_celltype_swap(cell, "$_ALDFFE_??N_", "$_ALDFFE_??P_", ID::E, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DFFSR_N??_", "$_DFFSR_P??_", ID::C, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DFFSR_?N?_", "$_DFFSR_?P?_", ID::S, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DFFSR_??N_", "$_DFFSR_??P_", ID::R, assign_map, invert_map); - handle_clkpol_celltype_swap(cell, "$_DFFSR_N??_", "$_DFFSR_P??_", ID::C, assign_map, invert_map); - handle_clkpol_celltype_swap(cell, "$_DFFSR_?N?_", "$_DFFSR_?P?_", ID::S, assign_map, invert_map); - handle_clkpol_celltype_swap(cell, "$_DFFSR_??N_", "$_DFFSR_??P_", ID::R, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DFFSRE_N???_", "$_DFFSRE_P???_", ID::C, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DFFSRE_?N??_", "$_DFFSRE_?P??_", ID::S, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DFFSRE_??N?_", "$_DFFSRE_??P?_", ID::R, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DFFSRE_???N_", "$_DFFSRE_???P_", ID::E, assign_map, invert_map); - handle_clkpol_celltype_swap(cell, "$_DFFSRE_N???_", "$_DFFSRE_P???_", ID::C, assign_map, invert_map); - handle_clkpol_celltype_swap(cell, "$_DFFSRE_?N??_", "$_DFFSRE_?P??_", ID::S, assign_map, invert_map); - handle_clkpol_celltype_swap(cell, "$_DFFSRE_??N?_", "$_DFFSRE_??P?_", ID::R, assign_map, invert_map); - handle_clkpol_celltype_swap(cell, "$_DFFSRE_???N_", "$_DFFSRE_???P_", ID::E, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DLATCH_N_", "$_DLATCH_P_", ID::E, assign_map, invert_map); - handle_clkpol_celltype_swap(cell, "$_DLATCH_N_", "$_DLATCH_P_", ID::E, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DLATCH_N??_", "$_DLATCH_P??_", ID::E, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DLATCH_?N?_", "$_DLATCH_?P?_", ID::R, assign_map, invert_map); - handle_clkpol_celltype_swap(cell, "$_DLATCH_N??_", "$_DLATCH_P??_", ID::E, assign_map, invert_map); - handle_clkpol_celltype_swap(cell, "$_DLATCH_?N?_", "$_DLATCH_?P?_", ID::R, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DLATCHSR_N??_", "$_DLATCHSR_P??_", ID::E, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DLATCHSR_?N?_", "$_DLATCHSR_?P?_", ID::S, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DLATCHSR_??N_", "$_DLATCHSR_??P_", ID::R, assign_map, invert_map); + } - handle_clkpol_celltype_swap(cell, "$_DLATCHSR_N??_", "$_DLATCHSR_P??_", ID::E, assign_map, invert_map); - handle_clkpol_celltype_swap(cell, "$_DLATCHSR_?N?_", "$_DLATCHSR_?P?_", ID::S, assign_map, invert_map); - handle_clkpol_celltype_swap(cell, "$_DLATCHSR_??N_", "$_DLATCHSR_??P_", ID::R, assign_map, invert_map); - } + TopoSort> cells; + dict outbit_to_cell; + + for (auto cell : module->cells()) + if (design->selected(module, cell) && yosys_celltypes.cell_evaluable(cell->type)) { + for (auto &conn : cell->connections()) + if (yosys_celltypes.cell_output(cell->type, conn.first)) + for (auto bit : assign_map(conn.second)) + outbit_to_cell[bit] = cell; + cells.node(cell); + } + + for (auto cell : module->cells()) + if (design->selected(module, cell) && yosys_celltypes.cell_evaluable(cell->type)) { + const int r_index = cells.node(cell); + for (auto &conn : cell->connections()) + if (yosys_celltypes.cell_input(cell->type, conn.first)) + for (auto bit : assign_map(conn.second)) + if (outbit_to_cell.count(bit)) + cells.edge(cells.node(outbit_to_cell.at(bit)), r_index); + } + + if (!cells.sort()) { + // There might be a combinational loop, or there might be constants on the output of cells. 'check' may find out more. + // ...unless this is a coarse-grained cell loop, but not a bit loop, in which case it won't, and all is good. + log("Couldn't topologically sort cells, optimizing module %s may take a longer time.\n", log_id(module)); + } + + for (auto cell : cells.sorted) + { +#define ACTION_DO(_p_, _s_) do { cover("opt.opt_expr.action_" S__LINE__); replace_cell(assign_map, module, cell, input.as_string(), _p_, _s_); goto next_cell; } while (0) +#define ACTION_DO_Y(_v_) ACTION_DO(ID::Y, RTLIL::SigSpec(RTLIL::State::S ## _v_)) bool detect_const_and = false; bool detect_const_or = false; diff --git a/passes/pmgen/Makefile.inc b/passes/pmgen/Makefile.inc index ed872b7212d..84d9ebaf121 100644 --- a/passes/pmgen/Makefile.inc +++ b/passes/pmgen/Makefile.inc @@ -37,6 +37,17 @@ $(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_cascade_pm.h)) # -------------------------------------- +OBJS += passes/pmgen/microchip_dsp.o +GENFILES += passes/pmgen/microchip_dsp_pm.h +GENFILES += passes/pmgen/microchip_dsp_CREG_pm.h +GENFILES += passes/pmgen/microchip_dsp_cascade_pm.h +passes/pmgen/microchip_dsp.o: passes/pmgen/microchip_dsp_pm.h passes/pmgen/microchip_dsp_CREG_pm.h passes/pmgen/microchip_dsp_cascade_pm.h +$(eval $(call add_extra_objs,passes/pmgen/microchip_dsp_pm.h)) +$(eval $(call add_extra_objs,passes/pmgen/microchip_dsp_CREG_pm.h)) +$(eval $(call add_extra_objs,passes/pmgen/microchip_dsp_cascade_pm.h)) + +# -------------------------------------- + OBJS += passes/pmgen/peepopt.o GENFILES += passes/pmgen/peepopt_pm.h passes/pmgen/peepopt.o: passes/pmgen/peepopt_pm.h diff --git a/passes/pmgen/microchip_dsp.cc b/passes/pmgen/microchip_dsp.cc new file mode 100644 index 00000000000..8207f4be360 --- /dev/null +++ b/passes/pmgen/microchip_dsp.cc @@ -0,0 +1,362 @@ +/* +ISC License + +Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries + +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/sigtools.h" +#include "kernel/yosys.h" +#include + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +#include "passes/pmgen/microchip_dsp_CREG_pm.h" +#include "passes/pmgen/microchip_dsp_cascade_pm.h" +#include "passes/pmgen/microchip_dsp_pm.h" + +void microchip_dsp_pack(microchip_dsp_pm &pm) +{ + auto &st = pm.st_microchip_dsp_pack; + + log("Analysing %s.%s for Microchip MACC_PA packing.\n", log_id(pm.module), log_id(st.dsp)); + + Cell *cell = st.dsp; + // pack pre-adder + if (st.preAdderStatic) { + SigSpec &pasub = cell->connections_.at(ID(PASUB)); + log(" static PASUB preadder %s (%s)\n", log_id(st.preAdderStatic), log_id(st.preAdderStatic->type)); + bool D_SIGNED = st.preAdderStatic->getParam(ID::B_SIGNED).as_bool(); + bool B_SIGNED = st.preAdderStatic->getParam(ID::A_SIGNED).as_bool(); + st.sigB.extend_u0(18, B_SIGNED); + st.sigD.extend_u0(18, D_SIGNED); + if (st.moveBtoA) { + cell->setPort(ID::A, st.sigA); // if pre-adder feeds into A, original sigB will be moved to port A + } + cell->setPort(ID::B, st.sigB); + cell->setPort(ID::D, st.sigD); + // MACC_PA supports both addition and subtraction with the pre-adder. + // Affects the sign of the 'D' port. + if (st.preAdderStatic->type == ID($add)) + pasub[0] = State::S0; + else if (st.preAdderStatic->type == ID($sub)) + pasub[0] = State::S1; + else + log_assert(!"strange pre-adder type"); + + pm.autoremove(st.preAdderStatic); + } + // pack post-adder + if (st.postAdderStatic) { + log(" postadder %s (%s)\n", log_id(st.postAdderStatic), log_id(st.postAdderStatic->type)); + SigSpec &sub = cell->connections_.at(ID(SUB)); + // Post-adder in MACC_PA also supports subtraction + // Determines the sign of the output from the multiplier. + if (st.postAdderStatic->type == ID($add)) + sub[0] = State::S0; + else if (st.postAdderStatic->type == ID($sub)) + sub[0] = State::S1; + else + log_assert(!"strange post-adder type"); + + if (st.useFeedBack) { + cell->setPort(ID(CDIN_FDBK_SEL), {State::S0, State::S1}); + } else { + st.sigC.extend_u0(48, st.postAdderStatic->getParam(ID::A_SIGNED).as_bool()); + cell->setPort(ID::C, st.sigC); + } + + pm.autoremove(st.postAdderStatic); + } + + // pack registers + if (st.clock != SigBit()) { + cell->setPort(ID::CLK, st.clock); + + // function to absorb a register + auto f = [&pm, cell](SigSpec &A, Cell *ff, IdString ceport, IdString rstport, IdString bypass) { + // input/output ports + SigSpec D = ff->getPort(ID::D); + SigSpec Q = pm.sigmap(ff->getPort(ID::Q)); + + if (!A.empty()) + A.replace(Q, D); + if (rstport != IdString()) { + if (ff->type.in(ID($sdff), ID($sdffe))) { + SigSpec srst = ff->getPort(ID::SRST); + bool rstpol_n = !ff->getParam(ID::SRST_POLARITY).as_bool(); + // active low sync rst + cell->setPort(rstport, rstpol_n ? srst : pm.module->Not(NEW_ID, srst)); + } else if (ff->type.in(ID($adff), ID($adffe))) { + SigSpec arst = ff->getPort(ID::ARST); + bool rstpol_n = !ff->getParam(ID::ARST_POLARITY).as_bool(); + // active low async rst + cell->setPort(rstport, rstpol_n ? arst : pm.module->Not(NEW_ID, arst)); + } else { + // active low async/sync rst + cell->setPort(rstport, State::S1); + } + } + if (ff->type.in(ID($dffe), ID($sdffe), ID($adffe))) { + SigSpec ce = ff->getPort(ID::EN); + bool cepol = ff->getParam(ID::EN_POLARITY).as_bool(); + // enables are all active high + cell->setPort(ceport, cepol ? ce : pm.module->Not(NEW_ID, ce)); + } else { + // enables are all active high + cell->setPort(ceport, State::S1); + } + + // bypass set to 0 + cell->setPort(bypass, State::S0); + + for (auto c : Q.chunks()) { + auto it = c.wire->attributes.find(ID::init); + if (it == c.wire->attributes.end()) + continue; + for (int i = c.offset; i < c.offset + c.width; i++) { + log_assert(it->second[i] == State::S0 || it->second[i] == State::Sx); + it->second[i] = State::Sx; + } + } + }; + + // NOTE: flops are not autoremoved because it is possible that they + // are only partially absorbed into DSP, or have fanouts. + if (st.ffA) { + SigSpec A = cell->getPort(ID::A); + if (st.ffA) { + f(A, st.ffA, ID(A_EN), ID(A_SRST_N), ID(A_BYPASS)); + } + pm.add_siguser(A, cell); + cell->setPort(ID::A, A); + } + if (st.ffB) { + SigSpec B = cell->getPort(ID::B); + if (st.ffB) { + f(B, st.ffB, ID(B_EN), ID(B_SRST_N), ID(B_BYPASS)); + } + pm.add_siguser(B, cell); + cell->setPort(ID::B, B); + } + if (st.ffD) { + SigSpec D = cell->getPort(ID::D); + if (st.ffD->type.in(ID($adff), ID($adffe))) { + f(D, st.ffD, ID(D_EN), ID(D_ARST_N), ID(D_BYPASS)); + } else { + f(D, st.ffD, ID(D_EN), ID(D_SRST_N), ID(D_BYPASS)); + } + + pm.add_siguser(D, cell); + cell->setPort(ID::D, D); + } + if (st.ffP) { + SigSpec P; // unused + f(P, st.ffP, ID(P_EN), ID(P_SRST_N), ID(P_BYPASS)); + st.ffP->connections_.at(ID::Q).replace(st.sigP, pm.module->addWire(NEW_ID, GetSize(st.sigP))); + } + + log(" clock: %s (%s)\n", log_signal(st.clock), "posedge"); + + if (st.ffA) + log(" \t ffA:%s\n", log_id(st.ffA)); + if (st.ffB) + log(" \t ffB:%s\n", log_id(st.ffB)); + if (st.ffD) + log(" \t ffD:%s\n", log_id(st.ffD)); + if (st.ffP) + log(" \t ffP:%s\n", log_id(st.ffP)); + } + log("\n"); + + SigSpec P = st.sigP; + if (GetSize(P) < 48) + P.append(pm.module->addWire(NEW_ID, 48 - GetSize(P))); + cell->setPort(ID::P, P); + + pm.blacklist(cell); +} + +// For packing cascaded DSPs +void microchip_dsp_packC(microchip_dsp_CREG_pm &pm) +{ + auto &st = pm.st_microchip_dsp_packC; + + log_debug("Analysing %s.%s for Microchip DSP packing (REG_C).\n", log_id(pm.module), log_id(st.dsp)); + log_debug("ffC: %s\n", log_id(st.ffC, "--")); + + Cell *cell = st.dsp; + + if (st.clock != SigBit()) { + cell->setPort(ID::CLK, st.clock); + + // same function as above, used for the last CREG we need to absorb + auto f = [&pm, cell](SigSpec &A, Cell *ff, IdString ceport, IdString rstport, IdString bypass) { + // input/output ports + SigSpec D = ff->getPort(ID::D); + SigSpec Q = pm.sigmap(ff->getPort(ID::Q)); + if (!A.empty()) + A.replace(Q, D); + if (rstport != IdString()) { + if (ff->type.in(ID($sdff), ID($sdffe))) { + SigSpec srst = ff->getPort(ID::SRST); + bool rstpol_n = !ff->getParam(ID::SRST_POLARITY).as_bool(); + // active low sync rst + cell->setPort(rstport, rstpol_n ? srst : pm.module->Not(NEW_ID, srst)); + } else if (ff->type.in(ID($adff), ID($adffe))) { + SigSpec arst = ff->getPort(ID::ARST); + bool rstpol_n = !ff->getParam(ID::ARST_POLARITY).as_bool(); + // active low async rst + cell->setPort(rstport, rstpol_n ? arst : pm.module->Not(NEW_ID, arst)); + } else { + // active low async/sync rst + cell->setPort(rstport, State::S1); + } + } + if (ff->type.in(ID($dffe), ID($sdffe), ID($adffe))) { + SigSpec ce = ff->getPort(ID::EN); + bool cepol = ff->getParam(ID::EN_POLARITY).as_bool(); + // enables are all active high + cell->setPort(ceport, cepol ? ce : pm.module->Not(NEW_ID, ce)); + } else { + // enables are all active high + cell->setPort(ceport, State::S1); + } + + // bypass set to 0 + cell->setPort(bypass, State::S0); + + for (auto c : Q.chunks()) { + auto it = c.wire->attributes.find(ID::init); + if (it == c.wire->attributes.end()) + continue; + for (int i = c.offset; i < c.offset + c.width; i++) { + log_assert(it->second[i] == State::S0 || it->second[i] == State::Sx); + it->second[i] = State::Sx; + } + } + }; + + if (st.ffC) { + SigSpec C = cell->getPort(ID::C); + + if (st.ffC->type.in(ID($adff), ID($adffe))) { + f(C, st.ffC, ID(C_EN), ID(C_ARST_N), ID(C_BYPASS)); + } else { + f(C, st.ffC, ID(C_EN), ID(C_SRST_N), ID(C_BYPASS)); + } + pm.add_siguser(C, cell); + cell->setPort(ID::C, C); + } + + log(" clock: %s (%s)", log_signal(st.clock), "posedge"); + + if (st.ffC) + log(" ffC:%s", log_id(st.ffC)); + log("\n"); + } + + pm.blacklist(cell); +} + +struct MicrochipDspPass : public Pass { + MicrochipDspPass() : Pass("microchip_dsp", "MICROCHIP: pack resources into DSPs") {} + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" microchip_dsp [options] [selection]\n"); + log("\n"); + log("Pack input registers 'A', 'B', 'C', and 'D' (with optional enable/reset),\n"); + log("output register 'P' (with optional enable/reset), pre-adder and/or post-adder into\n"); + log("Microchip DSP resources.\n"); + log("\n"); + log("Multiply-accumulate operations using the post-adder with feedback on the 'C'\n"); + log("input will be folded into the DSP. In this scenario only, the 'C' input can be\n"); + log("used to override the current accumulation result with a new value. This will\n"); + log("be added to the multiplier result to form the next accumulation result.\n"); + log("\n"); + log("Use of the dedicated 'PCOUT' -> 'PCIN' cascade path is detected for 'P' -> 'C'\n"); + log("connections (optionally, where 'P' is right-shifted by 17-bits and used as an\n"); + log("input to the post-adder. This pattern is common for summing partial products to\n"); + log("implement wide multipliers). Cascade chains are limited to a mazimum length \n"); + log("of 24 cells, corresponding to PolarFire (pf) devices.\n"); + log("\n"); + log("This pass is a no-op if the scratchpad variable 'microchip_dsp.multonly' is set\n"); + log("to 1.\n"); + log("\n"); + log("\n"); + log(" -family {polarfire}\n"); + log(" select the family to target\n"); + log(" default: polarfire\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) override + { + log_header(design, "Executing MICROCHIP_DSP pass (pack resources into DSPs).\n"); + + std::string family = "polarfire"; + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if ((args[argidx] == "-family") && argidx + 1 < args.size()) { + family = args[++argidx]; + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) { + + if (design->scratchpad_get_bool("microchip_dsp.multonly")) + continue; + + { + // For more details on PolarFire MACC_PA, consult + // the "PolarFire FPGA Macro Library Guide" + + // Main pattern matching step to capture a DSP cell. + // Match for pre-adder, post-adder, as well as + // registers 'A', 'B', 'D', and 'P'. Additionally, + // check for an accumulator pattern based on whether + // a post-adder and PREG are both present AND + // if PREG feeds into this post-adder. + microchip_dsp_pm pm(module, module->selected_cells()); + pm.run_microchip_dsp_pack(microchip_dsp_pack); + } + + // Separating out CREG packing is necessary since there + // is no guarantee that the cell ordering corresponds + // to the "expected" case (i.e. the order in which + // they appear in the source). There existed the possibility + // where a register got packed as a CREG into a + // downstream DSP that should have otherwise been a + // PREG of an upstream DSP that had not been visited + // yet + { + microchip_dsp_CREG_pm pm(module, module->selected_cells()); + pm.run_microchip_dsp_packC(microchip_dsp_packC); + } + + // Lastly, identify and utilise PCOUT -> PCIN chains + { + microchip_dsp_cascade_pm pm(module, module->selected_cells()); + pm.run_microchip_dsp_cascade(); + } + } + } +} MicrochipDspPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/pmgen/microchip_dsp.pmg b/passes/pmgen/microchip_dsp.pmg new file mode 100644 index 00000000000..9a6b9e1fa07 --- /dev/null +++ b/passes/pmgen/microchip_dsp.pmg @@ -0,0 +1,439 @@ +// ISC License +// +// Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +// This file describes the main pattern matcher setup (of three total) that +// forms the `microchip_dsp` pass described in microchip_dsp.cc +// At a high level, it works as follows: +// ( 1) Starting from a DSP cell. Capture DSP configurations as states +// ( 2) Match for pre-adder +// ( 3) Match for post-adder +// ( 4) Match register 'A', 'B', 'D', 'P' +// ( 5) If post-adder and PREG both present, check if PREG feeds into post-adder. +// This indicates an accumulator situation like the ASCII diagram below: +// +--------------------------------+ +// |_________ | +// | /-------\ +----+ | +// +----+ +-| post- |___|PREG|---+ 'P' +// |MULT|------ | adder | +----+ +// +----+ \-------/ + +pattern microchip_dsp_pack + +state clock +state sigA sigB sigC sigD sigP +state ffA ffB ffD ffP +state preAdderStatic postAdderStatic +state moveBtoA useFeedBack + +// static ports, used to detect dsp configuration +state bypassA bypassB bypassC bypassD bypassP +state bypassPASUB + +// Variables used for subpatterns +state argQ argD +udata allowAsync +udata dffD dffQ +udata dffclock +udata dff +udata u_preAdderStatic u_postAdderStatic +udata u_postAddAB +state postAddAB + +// (1) Starting from a DSP cell +match dsp + select dsp->type.in(\MACC_PA) +endmatch + +// detect existing signals connected to DSP +// detect configuration ports +code sigA sigB sigC sigD clock sigP + //helper function to remove unused bits + auto unextend = [](const SigSpec &sig) { + int i; + for (i = GetSize(sig)-1; i > 0; i--) + if (sig[i] != sig[i-1]) + break; + // Do not remove non-const sign bit + if (sig[i].wire) + ++i; + return sig.extract(0, i); + }; + + //unextend to remove unused bits + sigA = unextend(port(dsp, \A)); + sigB = unextend(port(dsp, \B)); + + //update signals + sigC = port(dsp, \C, SigSpec()); + sigD = port(dsp, \D, SigSpec()); + + + SigSpec P = port(dsp, \P); + // Only care about bits that are used + int i; + for (i = GetSize(P)-1; i >= 0; i--) + if (nusers(P[i]) > 1) + break; + i++; + log_assert(nusers(P.extract_end(i)) <= 1); + // This sigP could have no users if downstream sinks (e.g. $add) is + // narrower than $mul result, for example + if (i == 0) + reject; + sigP = P.extract(0, i); + clock = port(dsp, \CLK, SigBit()); + +endcode + +// capture static configuration ports +code bypassA bypassB bypassC bypassD bypassPASUB bypassP + bypassA = port(dsp, \A_BYPASS, SigSpec()); + bypassB = port(dsp, \B_BYPASS, SigSpec()); + bypassC = port(dsp, \C_BYPASS, SigSpec()); + bypassD = port(dsp, \D_BYPASS, SigSpec()); + bypassPASUB = port(dsp, \PASUB_BYPASS, SigSpec()); + bypassP = port(dsp, \P_BYPASS, SigSpec()); +endcode + +// (2) Match for pre-adder +// +code sigA sigB sigD preAdderStatic moveBtoA + subpattern(preAddMatching); + preAdderStatic = u_preAdderStatic; + moveBtoA = false; + + if (preAdderStatic) { + + if (port(preAdderStatic, \Y) == sigA) + { + //used for packing + moveBtoA = true; + + // sigA should be the input to the multiplier without the preAdd. sigB and sigD should be + //the preAdd inputs. If our "A" input into the multiplier is from the preAdd (not sigA), then + // we basically swap it. + sigA = sigB; + } + + // port B of preAdderStatic must be mapped to port D of DSP for subtraction + sigD = port(preAdderStatic, \B); + sigB = port(preAdderStatic, \A); + } +endcode + +// (3) Match for post-adder +// +code postAdderStatic sigP sigC + u_postAdderStatic = nullptr; + subpattern(postAddMatching); + postAdderStatic = u_postAdderStatic; + + if (postAdderStatic) { + //sigC will be whichever input to the postAdder that is NOT from the multiplier + // u_postAddAB is the input to the postAdder from the multiplier + sigC = port(postAdderStatic, u_postAddAB == \A ? \B : \A); + sigP = port(postAdderStatic, \Y); + } +endcode + + +// (4) Matching registers +// +// 'A' input for REG_A +code argQ bypassA sigA clock ffA + if (bypassA.is_fully_ones()){ + argQ = sigA; + allowAsync = false; + subpattern(in_dffe); + if (dff) { + ffA = dff; + clock = dffclock; + sigA = dffD; + } + } +endcode + +// 'B' input for REG_B +code argQ bypassB sigB clock ffB + if (bypassB.is_fully_ones()){ + argQ = sigB; + allowAsync = false; + subpattern(in_dffe); + if (dff) { + ffB = dff; + clock = dffclock; + sigB = dffD; + } + } +endcode + +// 'D' input for REG_D +code argQ bypassP sigD clock ffD + if (bypassD.is_fully_ones()){ + argQ = sigD; + allowAsync = true; + subpattern(in_dffe); + if (dff) { + ffD = dff; + clock = dffclock; + sigD = dffD; + } + } +endcode + +// 'P' output for REG_P +code argD ffP sigP clock bypassP + if (bypassP.is_fully_ones() && nusers(sigP) == 2) { + argD = sigP; + allowAsync = false; + subpattern(out_dffe); + if (dff) { + ffP = dff; + clock = dffclock; + sigP = dffQ; + } + } +endcode + +// (5) If post-adder and PREG both present, check if PREG feeds into post-adder via port C. +// This indicates an accumulator situation. Port C can be freed +// +--------------------------------+ +// |_________ | +// | /-------\ +----+ | +// +----+ +-| post- |___|PREG|---+ 'P' +// |MULT|------ | adder | +----+ +// +----+ \-------/ +code useFeedBack + useFeedBack = false; + if (postAdderStatic && ffP) { + if (sigC == sigP) { + useFeedBack = true; + } + } + +endcode + +// if any cells are absorbed, invoke the callback function +code + if (preAdderStatic || postAdderStatic) + accept; + if (ffA || ffB || ffD || ffP) + accept; +endcode + + +// ####################### +// Subpattern for matching against post-adder +// Match 'P' output that exclusively drives one of two inputs to an $add +// cell (post-adder). +// The other input to the adder is assumed to come in from the 'C' input + +subpattern postAddMatching +arg sigP + +match postAdd + + select postAdd->type.in($add, $sub) + select GetSize(port(postAdd, \Y)) <= 48 + + // AB is the port that connects MUL to ADD + choice AB {\A, \B} + select nusers(port(postAdd, AB)) == 2 + + // has one input coming from multiplier + index port(postAdd, AB)[0] === sigP[0] + filter GetSize(port(postAdd, AB)) >= GetSize(sigP) + filter port(postAdd, AB).extract(0, GetSize(sigP)) == sigP + // Check that remainder of AB is a sign- or zero-extension + filter port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(sigP[GetSize(sigP)-1], GetSize(port(postAdd, AB))-GetSize(sigP)) || port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(State::S0, GetSize(port(postAdd, AB))-GetSize(sigP)) + + set postAddAB AB + // optional +endmatch + +code + if (postAdd) + { + if (postAdd->type.in(ID($sub)) && postAddAB == \A) { + // if $sub, the multiplier output must match to $sub.B, otherwise no match + } else { + u_postAddAB = postAddAB; + u_postAdderStatic = postAdd; + } + + } +endcode + + +// ####################### +// Subpattern for matching against pre-adder +// support static PASUB only + +subpattern preAddMatching +arg sigA sigB sigD bypassB bypassD bypassPASUB + +code + u_preAdderStatic = nullptr; + + // Ensure that preAdder not already used + // Assume we can inspect port D to see if its all zeros. + if (!(sigD.empty() || sigD.is_fully_zero())) reject; + if (!bypassB.is_fully_ones()) reject; + if (!bypassD.is_fully_ones()) reject; + if (!bypassPASUB.is_fully_ones()) reject; +endcode + +match preAdd + + // can handle add or sub + select preAdd->type.in($add, $sub) + + // Output has to be 18 bits or less, and only has single fanout + select GetSize(port(preAdd, \Y)) <= 18 + select nusers(port(preAdd, \Y)) == 2 + + // Adder inputs must be 18 bits or less + select GetSize(port(preAdd, \A)) <= 18 + select GetSize(port(preAdd, \B)) <= 18 + + // Output feeds into one of multiplier input + filter port(preAdd, \Y) == sigB || port(preAdd, \Y) == sigA + + // optional +endmatch + +code + if (preAdd) + { + u_preAdderStatic = preAdd; + } +endcode + +// ####################### +// Subpattern for matching against input registers, based on knowledge of the +// 'Q' input. +subpattern in_dffe +arg argQ clock + +code + dff = nullptr; + if (argQ.empty()) + reject; + for (const auto &c : argQ.chunks()) { + // Abandon matches when 'Q' is a constant + if (!c.wire) + reject; + // Abandon matches when 'Q' has the keep attribute set + if (c.wire->get_bool_attribute(\keep)) + reject; + // Abandon matches when 'Q' has a non-zero init attribute set + Const init = c.wire->attributes.at(\init, Const()); + if (!init.empty()) + for (auto b : init.extract(c.offset, c.width)) + if (b != State::Sx && b != State::S0) + reject; + } +endcode + +match ff + // reg D has async rst + // reg A, B has sync rst + select ff->type.in($dff, $dffe, $sdff, $sdffe, $adff, $adffe) + // does not support clock inversion + select param(ff, \CLK_POLARITY).as_bool() + + // it is possible that only part of a dff output matches argQ + slice offset GetSize(port(ff, \D)) + index port(ff, \Q)[offset] === argQ[0] + + // Check that the rest of argQ is present + filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ) + filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ + + // only consider async rst flops when flag is set + filter !ff->type.in($adff, $adffe) || allowAsync + + // clock must be consistent + filter clock == SigBit() || port(ff, \CLK)[0] == clock +endmatch + +code argQ + // Check that reset value, if present, is fully 0. + bool noResetFlop = ff->type.in($dff, $dffe); + bool srstZero = ff->type.in($sdff, $sdffe) && param(ff, \SRST_VALUE).is_fully_zero(); + bool arstZero = ff->type.in($adff, $adffe) && param(ff, \ARST_VALUE).is_fully_zero(); + bool resetLegal = noResetFlop || srstZero || arstZero; + if (resetLegal) + { + SigSpec Q = port(ff, \Q); + dff = ff; + dffclock = port(ff, \CLK); + dffD = argQ; + SigSpec D = port(ff, \D); + argQ = Q; + dffD.replace(argQ, D); + } + +endcode +// ####################### + + +subpattern out_dffe +arg argD argQ clock + +code + dff = nullptr; + for (auto c : argD.chunks()) + // Abandon matches when 'D' has the keep attribute set + if (c.wire->get_bool_attribute(\keep)) + reject; +endcode + +match ff + select ff->type.in($dff, $dffe, $sdff, $sdffe) + // does not support clock inversion + select param(ff, \CLK_POLARITY).as_bool() + + slice offset GetSize(port(ff, \D)) + index port(ff, \D)[offset] === argD[0] + + // Check that the rest of argD is present + filter GetSize(port(ff, \D)) >= offset + GetSize(argD) + filter port(ff, \D).extract(offset, GetSize(argD)) == argD + + filter clock == SigBit() || port(ff, \CLK)[0] == clock +endmatch + +code argQ + SigSpec D = port(ff, \D); + SigSpec Q = port(ff, \Q); + argQ = argD; + argQ.replace(D, Q); + + // Abandon matches when 'Q' has a non-zero init attribute set + for (auto c : argQ.chunks()) { + Const init = c.wire->attributes.at(\init, Const()); + if (!init.empty()) + for (auto b : init.extract(c.offset, c.width)) + if (b != State::Sx && b != State::S0) + reject; + } + + dff = ff; + dffQ = argQ; + dffclock = port(ff, \CLK); +endcode diff --git a/passes/pmgen/microchip_dsp_CREG.pmg b/passes/pmgen/microchip_dsp_CREG.pmg new file mode 100644 index 00000000000..d1b15d460f5 --- /dev/null +++ b/passes/pmgen/microchip_dsp_CREG.pmg @@ -0,0 +1,168 @@ +// ISC License +// +// Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +// This file describes the second of three pattern matcher setups that +// forms the `microchip_dsp` pass described in microchip_dsp.cc +// At a high level, it works as follows: +// (1) Starting from a DSP cell that (a) doesn't have a CREG already, +// and (b) uses the 'C' port +// (2) Match the driver of the 'C' input to a possible $dff cell (CREG) +// (attached to at most two $mux cells that implement clock-enable or +// reset functionality, using a subpattern discussed below) +// Notes: +// - Running CREG packing after microchip_dsp_pack is necessary since there is no +// guarantee that the cell ordering corresponds to the "expected" case (i.e. +// the order in which they appear in the source) thus the possiblity existed +// where a register got packed as a CREG into a downstream DSP, while it should +// have otherwise been a PREG of an upstream DSP that had not been visited. +// yet. +// - The reason this is separated out from the microchip_dsp.pmg file is +// for efficiency --- each *.pmg file creates a class of the same basename, +// which when constructed, creates a custom database tailored to the +// pattern(s) contained within. Since the pattern in this file must be +// executed after the pattern contained in microchip_dsp.pmg, it is necessary +// to reconstruct this database. Separating the two patterns into +// independent files causes two smaller, more specific, databases. + +pattern microchip_dsp_packC + +udata > unextend +state clock +state sigC sigP +state ffC + +// Variables used for subpatterns +state argQ argD +state ffoffset +udata dffD dffQ +udata dffclock +udata dff + +// (1) Starting from a DSP cell that (a) doesn't have a CREG already, +// and (b) uses the 'C' port +match dsp + select dsp->type.in(\MACC_PA) + select port(dsp, \C_BYPASS, SigSpec()).is_fully_ones() + select nusers(port(dsp, \C, SigSpec())) > 1 +endmatch + +code sigC sigP clock + //helper function to remove unused bits + unextend = [](const SigSpec &sig) { + int i; + for (i = GetSize(sig)-1; i > 0; i--) + if (sig[i] != sig[i-1]) + break; + // Do not remove non-const sign bit + if (sig[i].wire) + ++i; + return sig.extract(0, i); + }; + sigC = unextend(port(dsp, \C, SigSpec())); + + SigSpec P = port(dsp, \P); + + // Only care about those bits that are used + int i; + for (i = GetSize(P)-1; i >= 0; i--) + if (nusers(P[i]) > 1) + break; + i++; + log_assert(nusers(P.extract_end(i)) <= 1); + sigP = P.extract(0, i); + + clock = port(dsp, \CLK, SigBit()); +endcode + +// (2) Match the driver of the 'C' input to a possible $dff cell (CREG) +// (attached to at most two $mux cells that implement clock-enable or +// reset functionality, using the in_dffe subpattern) +code argQ ffC sigC clock + argQ = sigC; + subpattern(in_dffe); + if (dff) { + ffC = dff; + clock = dffclock; + sigC = dffD; + } +endcode + +code + if (ffC) + accept; +endcode + +// ####################### + +// Subpattern for matching against input registers, based on knowledge of the +// 'Q' input. +subpattern in_dffe +arg argQ clock + +code + dff = nullptr; + if (argQ.empty()) + reject; + for (const auto &c : argQ.chunks()) { + // Abandon matches when 'Q' is a constant + if (!c.wire) + reject; + // Abandon matches when 'Q' has the keep attribute set + if (c.wire->get_bool_attribute(\keep)) + reject; + // Abandon matches when 'Q' has a non-zero init attribute set + Const init = c.wire->attributes.at(\init, Const()); + if (!init.empty()) + for (auto b : init.extract(c.offset, c.width)) + if (b != State::Sx && b != State::S0) + reject; + } +endcode + +match ff + select ff->type.in($dff, $dffe, $sdff, $sdffe, $adff, $adffe) + // does not support clock inversion + select param(ff, \CLK_POLARITY).as_bool() + + slice offset GetSize(port(ff, \D)) + index port(ff, \Q)[offset] === argQ[0] + + // Check that the rest of argQ is present + filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ) + filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ + + filter clock == SigBit() || port(ff, \CLK)[0] == clock +endmatch + +code argQ + // Check that reset value, if present, is fully 0. + bool noResetFlop = ff->type.in($dff, $dffe); + bool srstZero = ff->type.in($sdff, $sdffe) && param(ff, \SRST_VALUE).is_fully_zero(); + bool arstZero = ff->type.in($adff, $adffe) && param(ff, \ARST_VALUE).is_fully_zero(); + bool resetLegal = noResetFlop || srstZero || arstZero; + if (resetLegal) + { + SigSpec Q = port(ff, \Q); + dff = ff; + dffclock = port(ff, \CLK); + dffD = argQ; + SigSpec D = port(ff, \D); + argQ = Q; + dffD.replace(argQ, D); + } + +endcode diff --git a/passes/pmgen/microchip_dsp_cascade.pmg b/passes/pmgen/microchip_dsp_cascade.pmg new file mode 100644 index 00000000000..d7ea5911e09 --- /dev/null +++ b/passes/pmgen/microchip_dsp_cascade.pmg @@ -0,0 +1,236 @@ +// ISC License +// +// Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +// This file describes the third of three pattern matcher setups that +// forms the `microchip_dsp` pass described in microchip_dsp.cc +// At a high level, it works as follows: +// (1) Starting from a DSP cell that +// (a) CDIN_FDBK_SEL is set to default "00" +// (b) doesn't already use the 'PCOUT' port +// (2) Match another DSP cell that +// (a) does not have the CREG enabled, +// (b) 'C' port is driven by the 'P' output of the previous DSP cell +// (c) has its 'PCIN' port unused +// (3) Recursively go to (2) until no more matches possible, keeping track +// of the longest possible chain found +// (4) The longest chain is then divided into chunks of no more than +// MAX_DSP_CASCADE in length (to prevent long cascades that exceed the +// height of a DSP column) with each DSP in each chunk being rewritten +// to use [ABP]COUT -> [ABP]CIN cascading as appropriate + +pattern microchip_dsp_cascade + +udata > unextend +udata >> chain longest_chain +udata > visited +state next +state clock + +// Variables used for subpatterns +state argQ argD +state ffoffset +udata dffD dffQ +udata dffclock +udata dff + +// Maximum of 24 cascaded blocks +code +#define MAX_DSP_CASCADE 24 +endcode + +// NOTE: Chain vector +// +--------+ +--------+ +// | first |----> | next | ----> ... +// +--------+ +--------+ +// first.COUT cascades to next.CIN, so on and so forth + +// Helper function to remove unused bits +code + unextend = [](const SigSpec &sig) { + int i; + for (i = GetSize(sig)-1; i > 0; i--) + if (sig[i] != sig[i-1]) + break; + // Do not remove non-const sign bit + if (sig[i].wire) + ++i; + return sig.extract(0, i); + }; +endcode + +// (1) Starting from a DSP cell that +// (a) CDIN_FDBK_SEL is set to default "00" +// (b) doesn't already use the 'PCOUT' port +match first + select first->type.in(\MACC_PA) && port(first, \CDIN_FDBK_SEL, Const(0, 2)) == Const::from_string("00") + select nusers(port(first, \CDOUT, SigSpec())) <= 1 +endmatch + +// (4) The longest chain is then divided into chunks of no more than +// MAX_DSP_CASCADE in length (to prevent long cascades that exceed the +// height of a DSP column) with each DSP in each chunk being rewritten +// to use [ABP]COUT -> [ABP]CIN cascading as appropriate +code + visited.clear(); + visited.insert(first); + + longest_chain.clear(); + chain.emplace_back(first, -1); + subpattern(tail); +finally + + // longest cascade chain has been found with DSP "first" being the head of the chain + // do some post processing + + chain.pop_back(); + visited.clear(); + log_assert(chain.empty()); + + if (GetSize(longest_chain) > 1) { + Cell *dsp = std::get<0>(longest_chain.front()); + + Cell *dsp_pcin; + int SHIFT = -1; + for (int i = 1; i < GetSize(longest_chain); i++) { + log_assert(dsp->type.in(\MACC_PA)); + + std::tie(dsp_pcin,SHIFT) = longest_chain[i]; + + // Chain length exceeds the maximum cascade length, must split it up + if (i % MAX_DSP_CASCADE > 0) { + Wire *cascade = module->addWire(NEW_ID, 48); + + // zero port C and move wire to cascade + dsp_pcin->setPort(ID(C), Const(0, 48)); + dsp_pcin->setPort(ID(CDIN), cascade); + dsp->setPort(ID(CDOUT), cascade); + + // Configure wire to cascade the dsps + add_siguser(cascade, dsp_pcin); + add_siguser(cascade, dsp); + + // configure mux to use cascade for signal E + SigSpec cdin_fdbk_sel = port(dsp_pcin, \CDIN_FDBK_SEL, Const(0, 2)); + cdin_fdbk_sel[1] = State::S1; + dsp_pcin->setPort(\CDIN_FDBK_SEL, cdin_fdbk_sel); + + // check if shifting is required for wide multiplier implmentation + if (SHIFT == 17) + { + dsp_pcin->setPort(\ARSHFT17, State::S1); + } + + + log_debug("PCOUT -> PCIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin)); + + } else { + log_debug(" Blocking %s -> %s cascade (exceeds max: %d)\n", log_id(dsp), log_id(dsp_pcin), MAX_DSP_CASCADE); + } + + dsp = dsp_pcin; + } + + accept; + } +endcode + +// ------------------------------------------------------------------ + +subpattern tail +arg first +arg next + +// (2) Match another DSP cell that +// (a) does not have the CREG enabled, +// (b) 'C' port is driven by the 'P' output of the previous DSP cell +// (c) has its 'PCIN' port unused +match nextP + // find candidates where nextP.C port is driven (maybe partially) by chain's tail DSP.P port + // and with no registers in between (since cascade path cannot be pipelined) + + // reg C must not be used + select port(nextP, \C_BYPASS, SigSpec()).is_fully_ones() + + // must be same DSP type + select nextP->type.in(\MACC_PA) + + // port C should be driven by something + select nusers(port(nextP, \C, SigSpec())) > 1 + + // CIN must be unused + select nusers(port(nextP, \PCIN, SigSpec())) == 0 + + // should not have internal feedback connection + select port(nextP, \CDIN_FDBK_SEL, SigSpec()).is_fully_zero() + + // SHIFT should be unused + select port(nextP, \ARSHFT17_BYPASS).is_fully_ones() + select port(nextP, \ARSHFT17).is_fully_zero() + select nusers(port(nextP, \ARSHFT17, SigSpec())) == 0 + + // current DSP cell can be cascaded with the back of the cascade chain + // index port(nextP, \C)[0] === port(std::get<0>(chain.back()), \P)[0] || port(nextP, \C)[0] === port(std::get<0>(chain.back()), \P)[17] + filter port(nextP, \C)[0] == port(std::get<0>(chain.back()), \P)[0] || port(nextP, \C)[0] == port(std::get<0>(chain.back()), \P)[17] + + // semioptional + + optional +endmatch + +code next + next = nextP; + + // keep DSP type consistent in the chain + // currently since we only have one type anyways, this line is always false + if (next && next->type != first->type) reject; + + // break infinite recursion when there's a combinational loop + if (visited.count(next) > 0) reject; + +endcode + +// (3) Recursively go to (2) until no more matches possible, recording the +// longest possible chain +code + if (next) { + SigSpec driver_sigP = port(std::get<0>(chain.back()), \P); + int shift = 0; + if (port(next, \C)[0] == port(std::get<0>(chain.back()), \P)[17]) shift = 17; + + chain.emplace_back(next, shift); + visited.insert(next); + + SigSpec sigC = unextend(port(next, \C)); + + // Make sure driverDSP.P === DSP.C + if (GetSize(sigC) + shift <= GetSize(driver_sigP) && driver_sigP.extract(shift, GetSize(sigC)) == sigC) + { + subpattern(tail); + } + } else { + if (GetSize(chain) > GetSize(longest_chain)) + longest_chain = chain; + } +finally + if (next) + { + visited.erase(next); + chain.pop_back(); + } + + +endcode \ No newline at end of file diff --git a/passes/proc/proc_rom.cc b/passes/proc/proc_rom.cc index ebc2377aafa..ae67745479d 100644 --- a/passes/proc/proc_rom.cc +++ b/passes/proc/proc_rom.cc @@ -183,6 +183,12 @@ struct RomWorker mem.rd_ports.push_back(std::move(rd)); mem.emit(); + + if (sw->has_attribute(ID::src)) { + mem.inits[0].cell->attributes[ID::src] = sw->attributes[ID::src]; + mem.rd_ports[0].cell->attributes[ID::src] = sw->attributes[ID::src]; + } + for (auto cs: sw->cases) delete cs; sw->cases.clear(); diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc index aeed16b9784..dd17a2eba9d 100644 --- a/passes/sat/sim.cc +++ b/passes/sat/sim.cc @@ -2317,6 +2317,23 @@ struct SimWorker : SimShared } }; +std::string form_vcd_name(const char *name, int size, Wire *w) +{ + std::string full_name = name; + bool have_bracket = strchr(name, '['); + if (w) { + if (have_bracket || !(w->start_offset==0 && w->width==1)) { + full_name += stringf(" [%d:%d]", + w->upto ? w->start_offset : w->start_offset + w->width - 1, + w->upto ? w->start_offset + w->width - 1 : w->start_offset); + } + } else { + // Special handling for memories + full_name += have_bracket ? stringf(" [%d:0]", size - 1) : std::string(); + } + return full_name; +} + struct VCDWriter : public OutputWriter { VCDWriter(SimWorker *worker, std::string filename) : OutputWriter(worker) { @@ -2342,16 +2359,14 @@ struct VCDWriter : public OutputWriter worker->top->write_output_header( [this](IdString name) { vcdfile << stringf("$scope module %s $end\n", log_id(name)); }, [this]() { vcdfile << stringf("$upscope $end\n");}, - [this,use_signal](const char *name, int size, Wire *, int id, bool is_reg) { - if (use_signal.at(id)) { - // Works around gtkwave trying to parse everything past the last [ in a signal - // name. While the emitted range doesn't necessarily match the wire's range, - // this is consistent with the range gtkwave makes up if it doesn't find a - // range - std::string range = strchr(name, '[') ? stringf("[%d:0]", size - 1) : std::string(); - vcdfile << stringf("$var %s %d n%d %s%s%s $end\n", is_reg ? "reg" : "wire", size, id, name[0] == '$' ? "\\" : "", name, range.c_str()); - - } + [this,use_signal](const char *name, int size, Wire *w, int id, bool is_reg) { + if (!use_signal.at(id)) return; + // Works around gtkwave trying to parse everything past the last [ in a signal + // name. While the emitted range doesn't necessarily match the wire's range, + // this is consistent with the range gtkwave makes up if it doesn't find a + // range + std::string full_name = form_vcd_name(name, size, w); + vcdfile << stringf("$var %s %d n%d %s%s $end\n", is_reg ? "reg" : "wire", size, id, name[0] == '$' ? "\\" : "", full_name.c_str()); } ); @@ -2410,10 +2425,11 @@ struct FSTWriter : public OutputWriter worker->top->write_output_header( [this](IdString name) { fstWriterSetScope(fstfile, FST_ST_VCD_MODULE, stringf("%s",log_id(name)).c_str(), nullptr); }, [this]() { fstWriterSetUpscope(fstfile); }, - [this,use_signal](const char *name, int size, Wire *, int id, bool is_reg) { + [this,use_signal](const char *name, int size, Wire *w, int id, bool is_reg) { if (!use_signal.at(id)) return; + std::string full_name = form_vcd_name(name, size, w); fstHandle fst_id = fstWriterCreateVar(fstfile, is_reg ? FST_VT_VCD_REG : FST_VT_VCD_WIRE, FST_VD_IMPLICIT, size, - name, 0); + full_name.c_str(), 0); mapping.emplace(id, fst_id); } ); diff --git a/passes/sat/synthprop.cc b/passes/sat/synthprop.cc index 25416b6c4c3..5553abec2cc 100644 --- a/passes/sat/synthprop.cc +++ b/passes/sat/synthprop.cc @@ -185,35 +185,25 @@ struct SyntProperties : public Pass { log("\n"); log(" synthprop [options]\n"); log("\n"); - log("This creates synthesizable properties for selected module.\n"); + log("This creates synthesizable properties for the selected module.\n"); log("\n"); log("\n"); log(" -name \n"); - log("\n"); - log("Name output port for assertions (default: assertions).\n"); - log("\n"); + log(" name of the output port for assertions (default: assertions).\n"); log("\n"); log(" -map \n"); - log("\n"); - log("Write port mapping for synthesizable properties.\n"); - log("\n"); + log(" write the port mapping for synthesizable properties into the given file.\n"); log("\n"); log(" -or_outputs\n"); - log("\n"); - log("Or all outputs together to create a single output that goes high when any\n"); - log("property is violated, instead of generating individual output bits.\n"); - log("\n"); + log(" Or all outputs together to create a single output that goes high when\n"); + log(" any property is violated, instead of generating individual output bits.\n"); log("\n"); log(" -reset \n"); - log("\n"); - log("Name of top-level reset input. Latch a high state on the generated outputs\n"); - log("until an asynchronous top-level reset input is activated.\n"); - log("\n"); + log(" name of the top-level reset input. Latch a high state on the generated\n"); + log(" outputs until an asynchronous top-level reset input is activated.\n"); log("\n"); log(" -resetn \n"); - log("\n"); - log("Name of top-level reset input (inverse polarity). Latch a high state on the\n"); - log("generated outputs until an asynchronous top-level reset input is activated.\n"); + log(" like above but with inverse polarity\n"); log("\n"); log("\n"); } diff --git a/passes/tests/test_cell.cc b/passes/tests/test_cell.cc index e21ec452cc8..fc2aac2f83d 100644 --- a/passes/tests/test_cell.cc +++ b/passes/tests/test_cell.cc @@ -23,11 +23,13 @@ #include "kernel/consteval.h" #include "kernel/celledges.h" #include "kernel/macc.h" +#include "kernel/cost.h" #include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN +static int bloat_factor = 1; static uint32_t xorshift32_state = 123456789; static uint32_t xorshift32(uint32_t limit) { @@ -37,7 +39,7 @@ static uint32_t xorshift32(uint32_t limit) { return xorshift32_state % limit; } -static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, std::string cell_type_flags, bool constmode, bool muxdiv) +static RTLIL::Cell* create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, std::string cell_type_flags, bool constmode, bool muxdiv) { RTLIL::Module *module = design->addModule(ID(gold)); RTLIL::Cell *cell = module->addCell(ID(UUT), cell_type); @@ -45,7 +47,7 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, if (cell_type.in(ID($mux), ID($pmux))) { - int width = 1 + xorshift32(8); + int width = 1 + xorshift32(8 * bloat_factor); int swidth = cell_type == ID($mux) ? 1 : 1 + xorshift32(8); wire = module->addWire(ID::A); @@ -71,8 +73,8 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, if (cell_type == ID($bmux)) { - int width = 1 + xorshift32(8); - int swidth = 1 + xorshift32(4); + int width = 1 + xorshift32(8 * bloat_factor); + int swidth = 1 + xorshift32(4 * bloat_factor); wire = module->addWire(ID::A); wire->width = width << swidth; @@ -92,8 +94,8 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, if (cell_type == ID($demux)) { - int width = 1 + xorshift32(8); - int swidth = 1 + xorshift32(6); + int width = 1 + xorshift32(8 * bloat_factor); + int swidth = 1 + xorshift32(6 * bloat_factor); wire = module->addWire(ID::A); wire->width = width; @@ -113,7 +115,7 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, if (cell_type == ID($fa)) { - int width = 1 + xorshift32(8); + int width = 1 + xorshift32(8 * bloat_factor); wire = module->addWire(ID::A); wire->width = width; @@ -143,7 +145,7 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, if (cell_type == ID($lcu)) { - int width = 1 + xorshift32(8); + int width = 1 + xorshift32(8 * bloat_factor); wire = module->addWire(ID::P); wire->width = width; @@ -168,7 +170,7 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, if (cell_type == ID($macc)) { Macc macc; - int width = 1 + xorshift32(8); + int width = 1 + xorshift32(8 * bloat_factor); int depth = 1 + xorshift32(6); int mulbits_a = 0, mulbits_b = 0; @@ -215,7 +217,7 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, if (cell_type == ID($lut)) { - int width = 1 + xorshift32(6); + int width = 1 + xorshift32(6 * bloat_factor); wire = module->addWire(ID::A); wire->width = width; @@ -235,8 +237,8 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, if (cell_type == ID($sop)) { - int width = 1 + xorshift32(8); - int depth = 1 + xorshift32(8); + int width = 1 + xorshift32(8 * bloat_factor); + int depth = 1 + xorshift32(8 * bloat_factor); wire = module->addWire(ID::A); wire->width = width; @@ -270,7 +272,7 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, if (cell_type_flags.find('A') != std::string::npos) { wire = module->addWire(ID::A); - wire->width = 1 + xorshift32(8); + wire->width = 1 + xorshift32(8 * bloat_factor); wire->port_input = true; cell->setPort(ID::A, wire); } @@ -278,9 +280,9 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, if (cell_type_flags.find('B') != std::string::npos) { wire = module->addWire(ID::B); if (cell_type_flags.find('h') != std::string::npos) - wire->width = 1 + xorshift32(6); + wire->width = 1 + xorshift32(6 * bloat_factor); else - wire->width = 1 + xorshift32(8); + wire->width = 1 + xorshift32(8 * bloat_factor); wire->port_input = true; cell->setPort(ID::B, wire); } @@ -301,7 +303,7 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, if (cell_type_flags.find('Y') != std::string::npos) { wire = module->addWire(ID::Y); - wire->width = 1 + xorshift32(8); + wire->width = 1 + xorshift32(8 * bloat_factor); wire->port_output = true; cell->setPort(ID::Y, wire); } @@ -380,6 +382,7 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, module->fixup_ports(); cell->fixup_parameters(); cell->check(); + return cell; } static void run_edges_test(RTLIL::Design *design, bool verbose) @@ -752,6 +755,9 @@ struct TestCellPass : public Pass { log(" -noeval\n"); log(" do not check const-eval models\n"); log("\n"); + log(" -noopt\n"); + log(" do not opt tecchmapped design\n"); + log("\n"); log(" -edges\n"); log(" test cell edges db creator against sat-based implementation\n"); log("\n"); @@ -760,6 +766,11 @@ struct TestCellPass : public Pass { log("\n"); log(" -vlog {filename}\n"); log(" create a Verilog test bench to test simlib and write_verilog\n"); + log(" -bloat {factor}\n"); + log(" increase cell size limits b{factor} times where possible\n"); + log(" -check_cost\n"); + log(" check if the estimated cell cost is a valid upper bound for\n"); + log(" the techmapped cell count \n"); log("\n"); } void execute(std::vector args, RTLIL::Design*) override @@ -774,7 +785,9 @@ struct TestCellPass : public Pass { bool constmode = false; bool nosat = false; bool noeval = false; + bool noopt = false; bool edges = false; + bool check_cost = false; int argidx; for (argidx = 1; argidx < GetSize(args); argidx++) @@ -828,6 +841,10 @@ struct TestCellPass : public Pass { noeval = true; continue; } + if (args[argidx] == "-noopt") { + noopt = true; + continue; + } if (args[argidx] == "-edges") { edges = true; continue; @@ -842,6 +859,14 @@ struct TestCellPass : public Pass { log_cmd_error("Failed to open output file `%s'.\n", args[argidx].c_str()); continue; } + if (args[argidx] == "-bloat" && argidx+1 < GetSize(args)) { + bloat_factor = atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-check_cost") { + check_cost = true; + continue; + } break; } @@ -965,21 +990,30 @@ struct TestCellPass : public Pass { std::vector uut_names; - for (auto cell_type : selected_cell_types) + for (auto cell_type : selected_cell_types) { + // Cells that failed cell cost check + int failed = 0; + // How much bigger is the worst offender than estimated? + int worst_abs = 0; + // How many times is it bigger than estimated? + float worst_rel = 0.0; for (int i = 0; i < num_iter; i++) { + Cell* uut = nullptr; RTLIL::Design *design = new RTLIL::Design; if (cell_type == ID(rtlil)) Frontend::frontend_call(design, NULL, std::string(), "rtlil " + rtlil_file); else - create_gold_module(design, cell_type, cell_types.at(cell_type), constmode, muxdiv); + uut = create_gold_module(design, cell_type, cell_types.at(cell_type), constmode, muxdiv); if (!write_prefix.empty()) { Pass::call(design, stringf("write_rtlil %s_%s_%05d.il", write_prefix.c_str(), cell_type.c_str()+1, i)); } else if (edges) { Pass::call(design, "dump gold"); run_edges_test(design, verbose); } else { - Pass::call(design, stringf("copy gold gate; cd gate; %s; cd ..; opt -fast gate", techmap_cmd.c_str())); + Pass::call(design, stringf("copy gold gate; cd gate; %s; cd ..", techmap_cmd.c_str())); + if (!noopt) + Pass::call(design, "opt -fast gate"); if (!nosat) Pass::call(design, "miter -equiv -flatten -make_outputs -ignore_gold_x gold gate miter"); if (verbose) @@ -997,10 +1031,44 @@ struct TestCellPass : public Pass { } if (!noeval) run_eval_test(design, verbose, nosat, uut_name, vlog_file); + if (check_cost && uut) { + Pass::call(design, "select gate"); + int num_cells = 0; + for (auto mod : design->selected_modules()) { + // Expected to run once + for (auto cell : mod->selected_cells()) { + (void) cell; + num_cells++; + } + } + CellCosts costs(design); + Pass::call(design, "select gold"); + for (auto mod : design->selected_modules()) { + log_assert(mod->name.str() == "\\gold"); + // Expected to run once + int num_cells_estimate = costs.get(uut); + if (num_cells <= num_cells_estimate) { + log_debug("Correct upper bound for %s: %d <= %d\n", cell_type.c_str(), num_cells, num_cells_estimate); + } else { + failed++; + if (worst_abs < num_cells - num_cells_estimate) { + worst_abs = num_cells - num_cells_estimate; + worst_rel = (float)(num_cells - num_cells_estimate) / (float)num_cells_estimate; + } + log_warning("Upper bound violated for %s: %d > %d\n", cell_type.c_str(), num_cells, num_cells_estimate); + } + } + } } delete design; } - + if (check_cost && failed) { + log_warning("Cell type %s cost underestimated in %.1f%% cases " + "with worst offender being by %d (%.1f%%)\n", + cell_type.c_str(), 100 * (float)failed / (float)num_iter, + worst_abs, 100 * worst_rel); + } + } if (vlog_file.is_open()) { vlog_file << "\nmodule testbench;\n"; for (auto &uut : uut_names) diff --git a/techlibs/gowin/cells_sim.v b/techlibs/gowin/cells_sim.v index b268690803f..3edf6c8fcd9 100644 --- a/techlibs/gowin/cells_sim.v +++ b/techlibs/gowin/cells_sim.v @@ -867,8 +867,12 @@ module ODDRC(D0, D1, CLEAR, TX, CLK, Q0, Q1); parameter INIT = 0; endmodule +(* blackbox, keep *) module GSR (input GSRI); - wire GSRO = GSRI; +endmodule + +(* blackbox, keep *) +module BANDGAP (input BGEN); endmodule (* abc9_box, lib_whitebox *) @@ -1901,3 +1905,14 @@ output OSCOUT; parameter FREQ_DIV = 100; parameter REGULATOR_EN = 1'b0; endmodule + +(* blackbox *) +module DCS (CLK0, CLK1, CLK2, CLK3, CLKSEL, SELFORCE, CLKOUT); +input CLK0, CLK1, CLK2, CLK3, SELFORCE; +input [3:0] CLKSEL; +output CLKOUT; +parameter DCS_MODE = "RISING"; +endmodule + + + diff --git a/techlibs/gowin/cells_xtra.py b/techlibs/gowin/cells_xtra.py index 4d117e428c3..224ece02f17 100644 --- a/techlibs/gowin/cells_xtra.py +++ b/techlibs/gowin/cells_xtra.py @@ -13,7 +13,7 @@ class State(Enum): IN_MODULE = auto() IN_PARAMETER = auto() -_skip = { 'ALU', 'DFF', 'DFFC', 'DFFCE', 'DFFE', 'DFFN', 'DFFNC', 'DFFNCE', +_skip = { 'ALU', 'BANDGAP', 'DFF', 'DFFC', 'DFFCE', 'DFFE', 'DFFN', 'DFFNC', 'DFFNCE', 'DFFNE', 'DFFNP', 'DFFNPE', 'DFFNR', 'DFFNRE', 'DFFNS', 'DFFNSE', 'DFFP', 'DFFPE', 'DFFR', 'DFFRE', 'DFFS', 'DFFSE', 'DP', 'DPX9', 'ELVDS_OBUF', 'GND', 'GSR', 'IBUF', 'IDDR', 'IDDRC', 'IDES10', diff --git a/techlibs/gowin/cells_xtra.v b/techlibs/gowin/cells_xtra.v index 4b89b8098aa..6ab50a097d1 100644 --- a/techlibs/gowin/cells_xtra.v +++ b/techlibs/gowin/cells_xtra.v @@ -1564,12 +1564,6 @@ parameter IDLE = 4'd0, RD_S2 = 4'd12; endmodule -module DCS (...); -input CLK0, CLK1, CLK2, CLK3, SELFORCE; -input [3:0] CLKSEL; -output CLKOUT; -endmodule - module DQCE (...); input CLKIN; input CE; @@ -1687,10 +1681,6 @@ endmodule module ADC (...); endmodule -module BANDGAP (...); -input BGEN; -endmodule - module CLKDIV2 (...); parameter GSREN = "false"; input HCLKIN, RESETN; diff --git a/techlibs/microchip/LSRAM.txt b/techlibs/microchip/LSRAM.txt new file mode 100644 index 00000000000..9c22e4f302e --- /dev/null +++ b/techlibs/microchip/LSRAM.txt @@ -0,0 +1,191 @@ +# ISC License +# +# Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries +# +# 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. + + + + +# LSRAM true dual-port +ram block $__LSRAM_TDP_ { + + # Cost of a given cell is assumed to be: + # (cost-widthscale) + [widthscale * (used_bits/14)] + cost 129; + + # INIT is supported + init any; + + # port A and port B are allowed to have different widths, but they MUST have + # WIDTH values of the same set. + # Example: Port A has a Data Width of 1. Then Port B's Data Width must be either + # 1, 2, 4, 8, or 16 (both values are in the 'WIDTH_1' set). + # WIDTH_1 = {1, 2, 4, 8, 16} + # WIDTH_2 = {5, 10, 20} + + # "byte" specifies how many data bits correspond to one write enable bit. + # "byte" must be larger than width, or width must be a multipler of "byte" + # if "byte" > WIDTH, a single enable wire is inferred + # otherwise, WIDTH/byte number of enable wires are inferred + # + # WIDTH = {1, 2, 4, 5, 8, 10} requires 1 enable wire + # WIDTH = {16, 20} requires 2 enable wire + + option "WIDTH_CONFIG" "REGULAR" { + + # Data-Width| Address bits + # 1 | 14 + # 2 | 13 + # 4 | 12 + # 8 | 11 + # 16 | 10 + + # 14 address bits + abits 14; + + widths 1 2 4 8 16 per_port; + byte 8; + } + option "WIDTH_CONFIG" "ALIGN" { + + # Data-Width| Address bits + # 5 | 12 + # 10 | 11 + # 20 | 10 + + # Quick "hack" to fix address bit alignment by setting address bits to 12. + # If abits=14, tool will think there are 14 bits for width=5, 13 bits for width=10, 12 bits for width=20 + # THe LSRAM_map.v file detects if this option is being used, and adjusts the address port alignments accordingly. + abits 12; + + widths 5 10 20 per_port; + byte 10; + } + + + + port srsw "A" "B" { + + # read & write width must be same + width tied; + + # clock polarity is rising + clock posedge; + + # A/B read-enable + rden; + + + # initial value of read port data (not supported) + rdinit none; + + # write modes (_WMODE) + # 1. Simple Write: read-data port holds prev value (similar to "NO_CHANGE" for RAMB18E1) + # 2. Feed-through: read-data port takes new write value (similar to "WRITE_FIRST" for RAMB18E1) + # 3. Read-Before-Write: read-data port holds old value while being written (similar to "READ_FIRST" for RAMB18E1) + + portoption "WRITE_MODE" "NO_CHANGE" { + + # Read-write interaction + rdwr no_change; + + # Write transparency: + # For write ports, define behaviour when another synchronous read port + # reads from the same memory cell that said write port is writing to at the same time. + wrtrans all old; + } + portoption "WRITE_MODE" "WRITE_FIRST" { + # bits corresponding to high A/B_WEN are updated + rdwr new_only; + wrtrans all new; + } + portoption "WRITE_MODE" "READ_FIRST" { + rdwr old; + + wrtrans all old; + } + + # generate params to indicate if read or write is used for each port + optional_rw; + } +} + +# two-port configuration +ram block $__LSRAM_SDP_ { + + # since two-port configuration is dedicated for wide-read/write, + # we want to prioritize this configuration over TDP to avoid tool picking multiple TDP RAMs + # inplace of a single SDP RAM for wide read/write. This means the cost of a single SDP should + # be less than 2 TDP. + cost 129; + init any; + + option "WIDTH_CONFIG" "REGULAR" { + + # Data-Width| Address bits + # 1 | 14 + # 2 | 13 + # 4 | 12 + # 8 | 11 + # 16 | 10 + # 32 | 9 + + abits 14; + + widths 1 2 4 8 16 32 per_port; + + # width = 32, byte-write size is 8, ignore other widths + byte 8; + + } + option "WIDTH_CONFIG" "ALIGN" { + + # Data-Width| Address bits + # 5 | 12 + # 10 | 11 + # 20 | 10 + # 40 | 9 + + # Same trick as TSP RAM for alignment + abits 12; + widths 5 10 20 40 per_port; + byte 10; + } + + port sw "W" { + + # only consider wide write + + option "WIDTH_CONFIG" "REGULAR" width 32; + option "WIDTH_CONFIG" "ALIGN" width 40; + + clock posedge; + + # only simple write supported for two-port mode + wrtrans all old; + + optional; + } + port sr "R" { + + option "WIDTH_CONFIG" "REGULAR" width 32; + option "WIDTH_CONFIG" "ALIGN" width 40; + + + clock posedge; + rden; + rdinit none; + optional; + } +} diff --git a/techlibs/microchip/LSRAM_map.v b/techlibs/microchip/LSRAM_map.v new file mode 100644 index 00000000000..c84b5dd1935 --- /dev/null +++ b/techlibs/microchip/LSRAM_map.v @@ -0,0 +1,320 @@ +/* +ISC License + +Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries + +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. +*/ + +// See document PolarFire Family Fabric User Guide +// section 4.1 for port list. + + +//LSRAM true dual-port +module $__LSRAM_TDP_ (...); + +parameter INIT = 0; +parameter ADDR_BITS = 14; + +parameter OPTION_WIDTH_CONFIG = "A"; + +parameter PORT_A_WIDTH = 1; +parameter PORT_A_WR_EN_WIDTH = 1; +parameter PORT_A_RD_USED = 0; +parameter PORT_A_WR_USED = 0; +parameter PORT_A_OPTION_WRITE_MODE = "NO_CHANGE"; + +parameter PORT_B_WIDTH = 1; +parameter PORT_B_WR_EN_WIDTH = 1; +parameter PORT_B_RD_USED = 0; +parameter PORT_B_WR_USED = 0; +parameter PORT_B_OPTION_WRITE_MODE = "NO_CHANGE"; + + +input PORT_A_CLK; +input PORT_A_RD_EN; +input [ADDR_BITS-1:0] PORT_A_ADDR; +input [PORT_A_WIDTH-1:0] PORT_A_WR_DATA; +input [PORT_A_WR_EN_WIDTH-1:0] PORT_A_WR_EN; +output [PORT_A_WIDTH-1:0] PORT_A_RD_DATA; + + +input PORT_B_CLK; +input PORT_B_RD_EN; +input [ADDR_BITS-1:0] PORT_B_ADDR; +input [PORT_B_WIDTH-1:0] PORT_B_WR_DATA; +input [PORT_B_WR_EN_WIDTH-1:0] PORT_B_WR_EN; +output [PORT_B_WIDTH-1:0] PORT_B_RD_DATA; + + +`include "brams_defs.vh" + +// address wires +wire [ADDR_BITS-1:0] A_address; +wire [ADDR_BITS-1:0] B_address; +assign A_address = (OPTION_WIDTH_CONFIG == "REGULAR") ? PORT_A_ADDR : {PORT_A_ADDR, 2'b00}; +assign B_address = (OPTION_WIDTH_CONFIG == "REGULAR") ? PORT_B_ADDR : {PORT_B_ADDR, 2'b00}; + +// if port is not used, set block sel to 0 to disable it (read-data output is set to 0) +parameter PORT_A_RD_USED = 0; +parameter PORT_A_WR_USED = 0; +wire [2:0] A_BLK_SEL = (PORT_A_RD_USED == 1 || PORT_A_WR_USED == 1) ? 3'b111 : 3'b000; +wire [2:0] B_BLK_SEL = (PORT_B_RD_USED == 1 || PORT_B_WR_USED == 1) ? 3'b111 : 3'b000; + +// wires for write data +generate + wire [19:0] A_write_data; + wire [19:0] B_write_data; + if (PORT_A_WIDTH == 16) begin + assign A_write_data[7:0] = PORT_A_WR_DATA[7:0]; + assign A_write_data[17:10] = PORT_A_WR_DATA[15:8]; + assign A_write_data[9:8] = 2'b0; + assign A_write_data[19:18] = 2'b0; + end else begin + assign A_write_data[PORT_A_WIDTH-1:0] = PORT_A_WR_DATA; + end + + if (PORT_B_WIDTH == 16) begin + assign B_write_data[7:0] = PORT_B_WR_DATA[7:0]; + assign B_write_data[17:10] = PORT_B_WR_DATA[15:8]; + assign B_write_data[9:8] = 2'b0; + assign B_write_data[19:18] = 2'b0; + end else begin + assign B_write_data[PORT_B_WIDTH-1:0] = PORT_B_WR_DATA; + end +endgenerate + +// wires for read data +wire [19:0] A_read_data; +assign PORT_A_RD_DATA = A_read_data[PORT_A_WIDTH-1:0]; +wire [19:0] B_read_data; +assign PORT_B_RD_DATA = B_read_data[PORT_B_WIDTH-1:0]; + +// byte-write enables +wire [1:0] A_write_EN = (PORT_A_WR_EN_WIDTH == 1) ? {1'b0, PORT_A_WR_EN} : PORT_A_WR_EN; +wire [1:0] B_write_EN = (PORT_B_WR_EN_WIDTH == 1) ? {1'b0, PORT_B_WR_EN} : PORT_B_WR_EN; + +// port width +wire [2:0] A_width = (PORT_A_WIDTH == 1) ? 3'b000 : + (PORT_A_WIDTH == 2) ? 3'b001 : + (PORT_A_WIDTH == 4 || PORT_A_WIDTH == 5) ? 3'b010 : + (PORT_A_WIDTH == 8 || PORT_A_WIDTH == 10) ? 3'b011 : 3'b100; +wire [2:0] B_width = (PORT_B_WIDTH == 1) ? 3'b000 : + (PORT_B_WIDTH == 2) ? 3'b001 : + (PORT_B_WIDTH == 4 || PORT_B_WIDTH == 5) ? 3'b010 : + (PORT_B_WIDTH == 8 || PORT_B_WIDTH == 10) ? 3'b011 : 3'b100; + +// write modes +wire [1:0] A_write_mode = PORT_A_OPTION_WRITE_MODE == "NO_CHANGE" ? 2'b00 : + PORT_A_OPTION_WRITE_MODE == "WRITE_FIRST" ? 2'b01 : 2'b10; +wire [1:0] B_write_mode = PORT_B_OPTION_WRITE_MODE == "NO_CHANGE" ? 2'b00 : + PORT_B_OPTION_WRITE_MODE == "WRITE_FIRST" ? 2'b01 : 2'b10; + +RAM1K20 #( + `PARAMS_INIT_LSRAM +) _TECHMAP_REPLACE_ ( + + // port A + .A_ADDR(A_address), + .A_BLK_EN(A_BLK_SEL), + .A_CLK(PORT_A_CLK), + .A_DIN(A_write_data), + .A_DOUT(A_read_data), + .A_WEN(A_write_EN), + .A_REN(PORT_A_RD_EN), + .A_WIDTH(A_width), + .A_WMODE(A_write_mode), + .A_BYPASS(1'b1), + .A_DOUT_EN(1'b1), + .A_DOUT_SRST_N(1'b1), + .A_DOUT_ARST_N(1'b1), + + // port B + .B_ADDR(B_address), + .B_BLK_EN(B_BLK_SEL), + .B_CLK(PORT_B_CLK), + .B_DIN(B_write_data), + .B_DOUT(B_read_data), + .B_WEN(B_write_EN), + .B_REN(PORT_B_RD_EN), + .B_WIDTH(B_width), + .B_WMODE(B_write_mode), + .B_BYPASS(1'b1), + .B_DOUT_EN(1'b1), + .B_DOUT_SRST_N(1'b1), + .B_DOUT_ARST_N(1'b1), + + // Disable ECC for TDP + .ECC_EN(1'b0), + .ECC_BYPASS(1'b1), + .BUSY_FB(1'b0) + +); + +endmodule + +// single dual port configuration +module $__LSRAM_SDP_ (...); + +parameter INIT = 0; +parameter OPTION_WIDTH_CONFIG = "REGULAR"; +parameter ADDR_BITS = 14; + +parameter PORT_W_WIDTH = 1; +parameter PORT_W_WR_EN_WIDTH = 4; +parameter PORT_W_USED = 1; + +parameter PORT_R_WIDTH = 1; +parameter PORT_R_USED = 0; + +input PORT_W_CLK; +input [ADDR_BITS-1:0] PORT_W_ADDR; +input [PORT_W_WIDTH-1:0] PORT_W_WR_DATA; +input [PORT_W_WR_EN_WIDTH-1:0] PORT_W_WR_EN; + +input PORT_R_CLK; +input PORT_R_RD_EN; +input [ADDR_BITS-1:0] PORT_R_ADDR; +output [PORT_R_WIDTH-1:0] PORT_R_RD_DATA; +input PORT_R_RD_SRST; + +`include "brams_defs.vh" + + +// address wires +wire [ADDR_BITS-1:0] A_address; +wire [ADDR_BITS-1:0] B_address; +assign A_address = (OPTION_WIDTH_CONFIG == "REGULAR") ? PORT_R_ADDR : {PORT_R_ADDR, 2'b00}; +assign B_address = (OPTION_WIDTH_CONFIG == "REGULAR") ? PORT_W_ADDR : {PORT_W_ADDR, 2'b00}; + +// if port is not used, set block sel to 0 to disable it (read-data output is set to 0) +// port A is for read, port B for write +parameter PORT_W_USED = 0; +parameter PORT_R_USED = 0; +wire [2:0] A_BLK_SEL = (PORT_R_USED == 1) ? 3'b111 : 3'b000; +wire [2:0] B_BLK_SEL = (PORT_W_USED == 1) ? 3'b111 : 3'b000; + +// read/write data & write enables +// Currently support only wide write, width = {32, 40} +generate + wire [19:0] A_write_data; + wire [19:0] B_write_data; + wire [1:0] A_write_EN; + wire [1:0] B_write_EN; + + // write port (A provides MSB) + if (PORT_W_WIDTH == 32) begin + + assign B_write_data[3:0] = PORT_W_WR_DATA[3:0]; + assign B_write_data[8:5] = PORT_W_WR_DATA[7:4]; + assign B_write_data[13:10] = PORT_W_WR_DATA[11:8]; + assign B_write_data[18:15] = PORT_W_WR_DATA[15:12]; + assign B_write_data[4] = 1'b0; + assign B_write_data[9] = 1'b0; + assign B_write_data[14] = 1'b0; + assign B_write_data[19] = 1'b0; + + assign A_write_data[3:0] = PORT_W_WR_DATA[19:16]; + assign A_write_data[8:5] = PORT_W_WR_DATA[23:20]; + assign A_write_data[13:10] = PORT_W_WR_DATA[27:24]; + assign A_write_data[18:15] = PORT_W_WR_DATA[31:28]; + assign A_write_data[4] = 1'b0; + assign A_write_data[9] = 1'b0; + assign A_write_data[14] = 1'b0; + assign A_write_data[19] = 1'b0; + + end else if (PORT_W_WIDTH == 40) begin + assign B_write_data = PORT_W_WR_DATA[19:0]; + assign A_write_data = PORT_W_WR_DATA[39:20]; + end + + // byte-write enables + assign A_write_EN = PORT_W_WR_EN[1:0]; + assign B_write_EN = PORT_W_WR_EN[3:2]; + + // read ports (A provides MSB) + wire [19:0] A_read_data; + wire [19:0] B_read_data; + if (PORT_R_WIDTH == 32) begin + assign PORT_R_RD_DATA[3:0] = B_read_data[3:0]; + assign PORT_R_RD_DATA[8:5] = B_read_data[7:4]; + assign PORT_R_RD_DATA[13:10] = B_read_data[11:8]; + assign PORT_R_RD_DATA[18:15] = B_read_data[15:12]; + + assign PORT_R_RD_DATA[19:16] = A_read_data[3:0]; + assign PORT_R_RD_DATA[23:20] = A_read_data[8:5]; + assign PORT_R_RD_DATA[27:24] = A_read_data[13:10]; + assign PORT_R_RD_DATA[31:28] = A_read_data[18:15]; + end else if (PORT_R_WIDTH == 40) begin + assign PORT_R_RD_DATA[19:0] = B_read_data[19:0]; + assign PORT_R_RD_DATA[39:20] = A_read_data[19:0]; + end +endgenerate + +// port width +wire [2:0] A_width = (PORT_R_WIDTH == 1) ? 3'b000 : + (PORT_R_WIDTH == 2) ? 3'b001 : + (PORT_R_WIDTH == 4 || PORT_R_WIDTH == 5) ? 3'b010 : + (PORT_R_WIDTH == 8 || PORT_R_WIDTH == 10) ? 3'b011 : + (PORT_R_WIDTH == 16 || PORT_R_WIDTH == 20) ? 3'b100 : 3'b101; +wire [2:0] B_width = (PORT_W_WIDTH == 1) ? 3'b000 : + (PORT_W_WIDTH == 2) ? 3'b001 : + (PORT_W_WIDTH == 4 || PORT_W_WIDTH == 5) ? 3'b010 : + (PORT_W_WIDTH == 8 || PORT_W_WIDTH == 10) ? 3'b011 : + (PORT_W_WIDTH == 16 || PORT_W_WIDTH == 20) ? 3'b100 : 3'b101; + +// write modes +wire [1:0] A_write_mode = 2'b00; +wire [1:0] B_write_mode = 2'b00; + +RAM1K20 #( + `PARAMS_INIT_LSRAM +) _TECHMAP_REPLACE_ ( + // port A - read + .A_ADDR(A_address), + .A_BLK_EN(A_BLK_SEL), + .A_CLK(PORT_R_CLK), + .A_DIN(A_write_data), + .A_DOUT(A_read_data), + .A_WEN(A_write_EN), + .A_REN(PORT_R_RD_EN), + .A_WIDTH(A_width), + .A_WMODE(A_write_mode), + .A_BYPASS(1'b1), + .A_DOUT_EN(1'b1), + .A_DOUT_SRST_N(1'b1), + .A_DOUT_ARST_N(1'b1), + + // port B - write + .B_ADDR(B_address), + .B_BLK_EN(B_BLK_SEL), + .B_CLK(PORT_W_CLK), + .B_DIN(B_write_data), + .B_DOUT(B_read_data), + .B_WEN(B_write_EN), + .B_REN(PORT_R_RD_EN), + .B_WIDTH(B_width), + .B_WMODE(B_write_mode), + .B_BYPASS(1'b1), + .B_DOUT_EN(1'b1), + .B_DOUT_SRST_N(1'b1), + .B_DOUT_ARST_N(1'b1), + + // Disable ECC for SDP + .ECC_EN(1'b0), + .ECC_BYPASS(1'b1), + .BUSY_FB(1'b0) +); + + +endmodule diff --git a/techlibs/microchip/Makefile.inc b/techlibs/microchip/Makefile.inc new file mode 100644 index 00000000000..b1905e2cfee --- /dev/null +++ b/techlibs/microchip/Makefile.inc @@ -0,0 +1,31 @@ +# ISC License +# +# Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries +# +# 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. + + + +OBJS += techlibs/microchip/synth_microchip.o +OBJS += techlibs/microchip/microchip_dffopt.o + +$(eval $(call add_share_file,share/microchip,techlibs/microchip/arith_map.v)) +$(eval $(call add_share_file,share/microchip,techlibs/microchip/cells_map.v)) +$(eval $(call add_share_file,share/microchip,techlibs/microchip/cells_sim.v)) +$(eval $(call add_share_file,share/microchip,techlibs/microchip/polarfire_dsp_map.v)) + +$(eval $(call add_share_file,share/microchip,techlibs/microchip/brams_defs.vh)) +$(eval $(call add_share_file,share/microchip,techlibs/microchip/LSRAM_map.v)) +$(eval $(call add_share_file,share/microchip,techlibs/microchip/LSRAM.txt)) +$(eval $(call add_share_file,share/microchip,techlibs/microchip/uSRAM_map.v)) +$(eval $(call add_share_file,share/microchip,techlibs/microchip/uSRAM.txt)) diff --git a/techlibs/microchip/arith_map.v b/techlibs/microchip/arith_map.v new file mode 100644 index 00000000000..6b105a1f3cc --- /dev/null +++ b/techlibs/microchip/arith_map.v @@ -0,0 +1,105 @@ +/* +ISC License + +Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries + +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. +*/ + +// Based on Macro Library for PolarFire https://coredocs.s3.amazonaws.com/Libero/2021_2/Tool/pf_mlg.pdf +// NOTE: prefix module names with \$__ so that mapping prioritizes these cells over internal Yosys cells + + +(* techmap_celltype = "$_MUX4_" *) +module \$__microchip_MUX4_ (A, B, C, D, S, T, Y); + input A, B, C, D, S, T; + output Y; + MX4 _TECHMAP_REPLACE_.MUX4(.D3(D), .D2(C), .D1(B), .D0(A), .S1(T), .S0(S), .Y(Y)); + +endmodule + + +(* techmap_celltype = "$reduce_xor" *) +module \$__microchip_XOR8_ (A, Y); + parameter A_SIGNED = 1; + parameter A_WIDTH = 8; + parameter Y_WIDTH = 1; + + input [A_WIDTH-1:0] A; + output [Y_WIDTH-1:0] Y; + + // check if mapping should proceed + generate + if (A_WIDTH != 8 || A_SIGNED || Y_WIDTH != 1) begin + wire _TECHMAP_FAIL_ = 1; + end + endgenerate + + + XOR8 _TECHMAP_REPLACE_.XOR8 (.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]), .E(A[4]), .F(A[5]), .G(A[6]), .H(A[7]), .Y(Y)); + + +endmodule + +(* techmap_celltype = "$alu" *) +module \$__SF2_ALU (A, B, CI, BI, X, Y, CO); + parameter A_SIGNED = 0; + parameter B_SIGNED = 0; + parameter A_WIDTH = 1; + parameter B_WIDTH = 1; + parameter Y_WIDTH = 1; + + (* force_downto *) + input [A_WIDTH-1:0] A; + (* force_downto *) + input [B_WIDTH-1:0] B; + (* force_downto *) + output [Y_WIDTH-1:0] X, Y; + + input CI, BI; + (* force_downto *) + output [Y_WIDTH-1:0] CO; + + wire _TECHMAP_FAIL_ = Y_WIDTH <= 2; + + (* force_downto *) + wire [Y_WIDTH-1:0] AA, BB; + \$pos #(.A_SIGNED(A_SIGNED), .A_WIDTH(A_WIDTH), .Y_WIDTH(Y_WIDTH)) A_conv (.A(A), .Y(AA)); + \$pos #(.A_SIGNED(B_SIGNED), .A_WIDTH(B_WIDTH), .Y_WIDTH(Y_WIDTH)) B_conv (.A(B), .Y(BB)); + + (* force_downto *) + wire [Y_WIDTH-1:0] C = {CO, CI}; + + genvar i; + generate for (i = 0; i < Y_WIDTH; i = i + 1) begin:slice + ARI1 #( + // See section 1.4 of PolarFire Macro Library + + // G = F1 = A[i] & (B[i]^BI) + // Y = F0 = A[i]^B[i]^BI + // P = Y + // ADCB + .INIT(20'b 01_11_0010_1000_1001_0110) + ) carry ( + .A(1'b0), + .B(AA[i]), + .C(BB[i]), + .D(BI), + .FCI(C[i]), + .Y(X[i]), + .S(Y[i]), + .FCO(CO[i]) + ); + end endgenerate +endmodule + diff --git a/techlibs/microchip/brams_defs.vh b/techlibs/microchip/brams_defs.vh new file mode 100644 index 00000000000..e02d2a83db1 --- /dev/null +++ b/techlibs/microchip/brams_defs.vh @@ -0,0 +1,69 @@ +/* +ISC License + +Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries + +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. +*/ + +`define PARAMS_INIT_LSRAM \ + .INIT0(slice_init_LSRAM(00)), \ + .INIT1(slice_init_LSRAM(01)), \ + .INIT2(slice_init_LSRAM(02)), \ + .INIT3(slice_init_LSRAM(03)), \ + .INIT4(slice_init_LSRAM(04)), \ + .INIT5(slice_init_LSRAM(05)), \ + .INIT6(slice_init_LSRAM(06)), \ + .INIT7(slice_init_LSRAM(07)), \ + .INIT8(slice_init_LSRAM(08)), \ + .INIT9(slice_init_LSRAM(09)), \ + .INIT10(slice_init_LSRAM(10)), \ + .INIT11(slice_init_LSRAM(11)), \ + .INIT12(slice_init_LSRAM(12)), \ + .INIT13(slice_init_LSRAM(13)), \ + .INIT14(slice_init_LSRAM(14)), \ + .INIT15(slice_init_LSRAM(15)), \ + .INIT16(slice_init_LSRAM(16)), \ + .INIT17(slice_init_LSRAM(17)), \ + .INIT18(slice_init_LSRAM(18)), \ + .INIT19(slice_init_LSRAM(19)) + +`define PARAMS_INIT_uSRAM \ + .INIT0(slice_init_uSRAM(00)), \ + .INIT1(slice_init_uSRAM(01)), \ + .INIT2(slice_init_uSRAM(02)), \ + .INIT3(slice_init_uSRAM(03)), \ + .INIT4(slice_init_uSRAM(04)), \ + .INIT5(slice_init_uSRAM(05)), \ + .INIT6(slice_init_uSRAM(06)), \ + .INIT7(slice_init_uSRAM(07)), \ + .INIT8(slice_init_uSRAM(08)), \ + .INIT9(slice_init_uSRAM(09)), \ + .INIT10(slice_init_uSRAM(10)), \ + .INIT11(slice_init_uSRAM(11)) \ + +// Helper function for initializing the LSRAM +function [1023:0] slice_init_LSRAM; + input integer slice_idx; + integer i; + for (i = 0; i < 1024; i = i + 1) + slice_init_LSRAM[i] = INIT[(slice_idx * 1024 + i)]; +endfunction + +// Helper function for initializing the uSRAM +function [63:0] slice_init_uSRAM; + input integer slice_idx; + integer i; + for (i = 0; i < 64; i = i + 1) + slice_init_uSRAM[i] = INIT[(slice_idx * 64 + i)]; +endfunction \ No newline at end of file diff --git a/techlibs/microchip/cells_map.v b/techlibs/microchip/cells_map.v new file mode 100644 index 00000000000..6425d594ff8 --- /dev/null +++ b/techlibs/microchip/cells_map.v @@ -0,0 +1,104 @@ +/* +ISC License + +Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries + +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. +*/ + +// DFFs +module \$_DFFE_PN0P_ (input D, C, R, E, output Q); + SLE _TECHMAP_REPLACE_ (.D(D), .CLK(C), .EN(E), .ALn(R), .ADn(1'b1), .SLn(1'b1), .SD(1'b0), .LAT(1'b0), .Q(Q)); +endmodule + +module \$_DFFE_PN1P_ (input D, C, R, E, output Q); + SLE _TECHMAP_REPLACE_ (.D(D), .CLK(C), .EN(E), .ALn(R), .ADn(1'b0), .SLn(1'b1), .SD(1'b0), .LAT(1'b0), .Q(Q)); +endmodule + +// for sync set/reset registers, we can pass them into ABC9. So we need to follow the simplification idiom +// and map to intermediate cell types +module \$_SDFFCE_PN0P_ (input D, C, R, E, output Q); + MICROCHIP_SYNC_RESET_DFF _TECHMAP_REPLACE_ (.D(D), .CLK(C), .Reset(R), .En(E), .Q(Q)); +endmodule + +module \$_SDFFCE_PN1P_ (input D, C, R, E, output Q); + MICROCHIP_SYNC_SET_DFF _TECHMAP_REPLACE_ (.D(D), .CLK(C), .Set(R), .En(E), .Q(Q)); +endmodule + + +// LATCHES + +module \$_DLATCH_PN0_ (input D, R, E, output Q); + SLE _TECHMAP_REPLACE_ (.D(D), .CLK(E), .EN(1'b1), .ALn(R), .ADn(1'b1), .SLn(1'b1), .SD(1'b0), .LAT(1'b1), .Q(Q)); +endmodule + +module \$_DLATCH_PN1_ (input D, R, E, output Q); + SLE _TECHMAP_REPLACE_ (.D(D), .CLK(E), .EN(1'b1), .ALn(R), .ADn(1'b0), .SLn(1'b1), .SD(1'b0), .LAT(1'b1), .Q(Q)); +endmodule + +module \$_DLATCH_P_ (input D, E, output Q); + SLE _TECHMAP_REPLACE_ (.D(D), .CLK(E), .EN(1'b1), .ALn(1'b1), .ADn(1'b0), .SLn(1'b1), .SD(1'b0), .LAT(1'b1), .Q(Q)); +endmodule + +// map intermediate flops to SLE +`ifdef FINAL_MAP +module MICROCHIP_SYNC_SET_DFF( + input D, + input CLK, + input Set, + input En, + output Q); + SLE _TECHMAP_REPLACE_ (.D(D), .CLK(CLK), .EN(En), .ALn(1'b1), .ADn(1'b0), .SLn(Set), .SD(1'b1), .LAT(1'b0), .Q(Q)); +endmodule + +module MICROCHIP_SYNC_RESET_DFF( + input D, + input CLK, + input Reset, + input En, + output Q); + SLE _TECHMAP_REPLACE_ (.D(D), .CLK(CLK), .EN(En), .ALn(1'b1), .ADn(1'b0), .SLn(Reset), .SD(1'b0), .LAT(1'b0), .Q(Q)); +endmodule +`endif + + +// LUT + +`ifndef NO_LUT +module \$lut (A, Y); + parameter WIDTH = 0; + parameter LUT = 0; + + (* force_downto *) + input [WIDTH-1:0] A; + output Y; + + generate + if (WIDTH == 1) begin + CFG1 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.Y(Y), .A(A[0])); + end else + if (WIDTH == 2) begin + CFG2 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.Y(Y), .A(A[0]), .B(A[1])); + end else + if (WIDTH == 3) begin + CFG3 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.Y(Y), .A(A[0]), .B(A[1]), .C(A[2])); + end else + if (WIDTH == 4) begin + CFG4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.Y(Y), .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + end else begin + wire _TECHMAP_FAIL_ = 1; + end + endgenerate +endmodule +`endif + diff --git a/techlibs/microchip/cells_sim.v b/techlibs/microchip/cells_sim.v new file mode 100644 index 00000000000..de4e3607f2a --- /dev/null +++ b/techlibs/microchip/cells_sim.v @@ -0,0 +1,719 @@ +/* +ISC License + +Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries + +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. +*/ + +// Macro Library for PolarFire https://coredocs.s3.amazonaws.com/Libero/2021_2/Tool/pf_mlg.pdf + +module AND2 ( + input A, B, + output Y +); + assign Y = A & B; +endmodule + +module AND3 ( + input A, B, C, + output Y +); + assign Y = A & B & C; +endmodule + +module AND4 ( + input A, B, C, D, + output Y +); + assign Y = A & B & C & D; +endmodule + +(* abc9_lut=1 *) +module CFG1 ( + output Y, + input A +); + parameter [1:0] INIT = 2'h0; + assign Y = INIT >> A; + specify + (A => Y) = 127; + endspecify +endmodule + +(* abc9_lut=1 *) +module CFG2 ( + output Y, + input A, + input B +); + parameter [3:0] INIT = 4'h0; + assign Y = INIT >> {B, A}; + specify + (A => Y) = 238; + (B => Y) = 127; + endspecify +endmodule + +(* abc9_lut=1 *) +module CFG3 ( + output Y, + input A, + input B, + input C +); + parameter [7:0] INIT = 8'h0; + assign Y = INIT >> {C, B, A}; + specify + (A => Y) = 407; + (B => Y) = 238; + (C => Y) = 127; + endspecify +endmodule + +(* abc9_lut=1 *) +module CFG4 ( + output Y, + input A, + input B, + input C, + input D +); + parameter [15:0] INIT = 16'h0; + assign Y = INIT >> {D, C, B, A}; + specify + (A => Y) = 472; + (B => Y) = 407; + (C => Y) = 238; + (D => Y) = 127; + endspecify +endmodule + +module BUFF ( + input A, + output Y +); + assign Y = A; +endmodule + +module BUFD ( + input A, + output Y +); + assign Y = A; +endmodule + +module CLKINT ( + input A, + (* clkbuf_driver *) + output Y +); + assign Y = A; +endmodule + +module CLKINT_PRESERVE ( + input A, + (* clkbuf_driver *) + output Y +); + assign Y = A; +endmodule + +module GCLKINT ( + input A, EN, + (* clkbuf_driver *) + output Y +); + assign Y = A & EN; +endmodule + +module RCLKINT ( + input A, + (* clkbuf_driver *) + output Y +); + assign Y = A; +endmodule + +module RGCLKINT ( + input A, EN, + (* clkbuf_driver *) + output Y +); + assign Y = A & EN; +endmodule + +// sequential elements + +// MICROCHIP_SYNC_SET_DFF and MICROCHIP_SYNC_RESET_DFF are intermediate cell types to implement the simplification idiom for abc9 flow +// see: https://yosyshq.readthedocs.io/projects/yosys/en/latest/yosys_internals/extending_yosys/abc_flow.html + +(* abc9_flop, lib_whitebox *) +module MICROCHIP_SYNC_SET_DFF( + input D, + input CLK, + input Set, + input En, + output reg Q); + parameter [0:0] INIT = 1'b0; // unused + + always @(posedge CLK) begin + if (En == 1) begin + if (Set == 0) + Q <= 1; + else + Q <= D; + end + end + + specify + $setup(D , posedge CLK &&& En && Set, 0); // neg setup not supported? + $setup(En, posedge CLK, 109); + $setup(Set, posedge CLK &&& En, 404); + if (En && !Set) (posedge CLK => (Q : 1'b1)) = 303; + if (En && Set) (posedge CLK => (Q : D)) = 303; + endspecify +endmodule + +(* abc9_flop, lib_whitebox *) +module MICROCHIP_SYNC_RESET_DFF( + input D, + input CLK, + input Reset, + input En, + output reg Q); + parameter [0:0] INIT = 1'b0; // unused + + always @(posedge CLK) begin + if (En == 1) begin + if (Reset == 0) + Q <= 0; + else + Q <= D; + end + end + + specify + $setup(D , posedge CLK &&& En && Reset, 0); // neg setup not supported? + $setup(En, posedge CLK, 109); + $setup(Reset, posedge CLK &&& En, 404); + if (En && !Reset) (posedge CLK => (Q : 1'b0)) = 303; + if (En && Reset) (posedge CLK => (Q : D)) = 303; + endspecify +endmodule + +module SLE ( + output Q, + input ADn, + input ALn, + (* clkbuf_sink *) + input CLK, + input D, + input LAT, + input SD, + input EN, + input SLn +); + reg q_latch, q_ff; + + always @(posedge CLK, negedge ALn) begin + if (!ALn) begin + q_ff <= !ADn; + end else if (EN) begin + if (!SLn) + q_ff <= SD; + else + q_ff <= D; + end + end + + always @* begin + if (!ALn) begin + q_latch <= !ADn; + end else if (CLK && EN) begin + if (!SLn) + q_ff <= SD; + else + q_ff <= D; + end + end + + assign Q = LAT ? q_latch : q_ff; +endmodule + +(* abc9_box, lib_whitebox *) +module ARI1 ( + (* abc9_carry *) + input FCI, + (* abc9_carry *) + output FCO, + + input A, B, C, D, + output Y, S +); + parameter [19:0] INIT = 20'h0; + wire [2:0] Fsel = {D, C, B}; + wire F0 = INIT[Fsel]; + wire F1 = INIT[8 + Fsel]; + wire Yout = A ? F1 : F0; + assign Y = Yout; + assign S = FCI ^ Yout; + wire G = INIT[16] ? (INIT[17] ? F1 : F0) : INIT[17]; + wire P = INIT[19] ? 1'b1 : (INIT[18] ? Yout : 1'b0); + assign FCO = P ? FCI : G; + + specify + //pin to pin path delay + (A => Y ) = 472; + (B => Y ) = 407; + (C => Y ) = 238; + (D => Y ) = 127; + (A => S ) = 572; + (B => S ) = 507; + (C => S ) = 338; + (D => S ) = 227; + (FCI => S ) = 100; + (A => FCO ) = 522; + (B => FCO ) = 457; + (C => FCO ) = 288; + (D => FCO ) = 177; + (FCI => FCO ) = 50; + endspecify +endmodule + +(* blackbox *) +module GCLKBUF ( + (* iopad_external_pin *) + input PAD, + input EN, + (* clkbuf_driver *) + output Y +); +endmodule + +(* blackbox *) +module GCLKBUF_DIFF ( + (* iopad_external_pin *) + input PADP, + (* iopad_external_pin *) + input PADN, + input EN, + (* clkbuf_driver *) + output Y +); +endmodule + +module INV ( + input A, + output Y +); + assign Y = !A; +endmodule + +module INVD ( + input A, + output Y +); + assign Y = !A; +endmodule + +module MX2 ( + input A, B, S, + output Y +); + assign Y = S ? B : A; +endmodule + +module MX4 ( + input D0, D1, D2, D3, S0, S1, + output Y +); + assign Y = S1 ? (S0 ? D3 : D2) : (S0 ? D1 : D0); +endmodule + +module NAND2 ( + input A, B, + output Y +); + assign Y = !(A & B); +endmodule + +module NAND3 ( + input A, B, C, + output Y +); + assign Y = !(A & B & C); +endmodule + +module NAND4 ( + input A, B, C, D, + output Y +); + assign Y = !(A & B & C & D); +endmodule + +module NOR2 ( + input A, B, + output Y +); + assign Y = !(A | B); +endmodule + +module NOR3 ( + input A, B, C, + output Y +); + assign Y = !(A | B | C); +endmodule + +module NOR4 ( + input A, B, C, D, + output Y +); + assign Y = !(A | B | C | D); +endmodule + +module OR2 ( + input A, B, + output Y +); + assign Y = A | B; +endmodule + +module OR3 ( + input A, B, C, + output Y +); + assign Y = A | B | C; +endmodule + +module OR4 ( + input A, B, C, D, + output Y +); + assign Y = A | B | C | D; +endmodule + +module XOR2 ( + input A, B, + output Y +); + assign Y = A ^ B; +endmodule + +module XOR3 ( + input A, B, C, + output Y +); + assign Y = A ^ B ^ C; +endmodule + +module XOR4 ( + input A, B, C, D, + output Y +); + assign Y = A ^ B ^ C ^ D; +endmodule + +module XOR8 ( + input A, B, C, D, E, F, G, H, + output Y +); + assign Y = A ^ B ^ C ^ D ^ E ^ F ^ G ^ H; +endmodule + +// module UJTAG + +module BIBUF ( + input D, + input E, + (* iopad_external_pin *) + inout PAD, + output Y +); + parameter IOSTD = ""; + assign PAD = E ? D : 1'bz; + assign Y = PAD; +endmodule + +(* blackbox *) +module BIBUF_DIFF ( + input D, + input E, + (* iopad_external_pin *) + inout PADP, + (* iopad_external_pin *) + inout PADN, + output Y +); + parameter IOSTD = ""; +endmodule + +module CLKBIBUF ( + input D, + input E, + (* iopad_external_pin *) + inout PAD, + (* clkbuf_driver *) + output Y +); + parameter IOSTD = ""; + assign PAD = E ? D : 1'bz; + assign Y = PAD; +endmodule + +module CLKBUF ( + (* iopad_external_pin *) + input PAD, + (* clkbuf_driver *) + output Y +); + parameter IOSTD = ""; + assign Y = PAD; + specify + (PAD => Y) = 50; + endspecify +endmodule + +(* blackbox *) +module CLKBUF_DIFF ( + (* iopad_external_pin *) + input PADP, + (* iopad_external_pin *) + input PADN, + (* clkbuf_driver *) + output Y +); + parameter IOSTD = ""; +endmodule + +module INBUF ( + (* iopad_external_pin *) + input PAD, + output Y +); + parameter IOSTD = ""; + assign Y = PAD; +endmodule + +(* blackbox *) +module INBUF_DIFF ( + (* iopad_external_pin *) + input PADP, + (* iopad_external_pin *) + input PADN, + output Y +); + parameter IOSTD = ""; +endmodule + +module OUTBUF ( + input D, + (* iopad_external_pin *) + output PAD +); + parameter IOSTD = ""; + assign PAD = D; +endmodule + +(* blackbox *) +module OUTBUF_DIFF ( + input D, + (* iopad_external_pin *) + output PADP, + (* iopad_external_pin *) + output PADN +); + parameter IOSTD = ""; +endmodule + +module TRIBUFF ( + input D, + input E, + (* iopad_external_pin *) + output PAD +); + parameter IOSTD = ""; + assign PAD = E ? D : 1'bz; +endmodule + +(* blackbox *) +module TRIBUFF_DIFF ( + input D, + input E, + (* iopad_external_pin *) + output PADP, + (* iopad_external_pin *) + output PADN +); + parameter IOSTD = ""; +endmodule + +(* blackbox *) +module MACC_PA ( + input DOTP, + input SIMD, + input OVFL_CARRYOUT_SEL, + input CLK, + input AL_N, + input [17:0] A, + input A_BYPASS, + input A_SRST_N, + input A_EN, + input [17:0] B, + input B_BYPASS, + input B_SRST_N, + input B_EN, + input [17:0] D, + input D_BYPASS, + input D_ARST_N, + input D_SRST_N, + input D_EN, + input CARRYIN, + input [47:0] C, + input C_BYPASS, + input C_ARST_N, + input C_SRST_N, + input C_EN, + input [47:0] CDIN, + output [47:0] P, + output OVFL_CARRYOUT, + input P_BYPASS, + input P_SRST_N, + input P_EN, + output [47:0] CDOUT, + input PASUB, + input PASUB_BYPASS, + input PASUB_AD_N, + input PASUB_SL_N, + input PASUB_SD_N, + input PASUB_EN, + input [1:0] CDIN_FDBK_SEL, + input CDIN_FDBK_SEL_BYPASS, + input [1:0] CDIN_FDBK_SEL_AD_N, + input CDIN_FDBK_SEL_SL_N, + input [1:0] CDIN_FDBK_SEL_SD_N, + input CDIN_FDBK_SEL_EN, + input ARSHFT17, + input ARSHFT17_BYPASS, + input ARSHFT17_AD_N, + input ARSHFT17_SL_N, + input ARSHFT17_SD_N, + input ARSHFT17_EN, + input SUB, + input SUB_BYPASS, + input SUB_AD_N, + input SUB_SL_N, + input SUB_SD_N, + input SUB_EN +); +endmodule + +(* blackbox *) +module RAM1K20 ( + input [13:0] A_ADDR, + input [2:0] A_BLK_EN, + input A_CLK, + input [19:0] A_DIN, + output [19:0] A_DOUT, + input [1:0] A_WEN, + input A_REN, + input [2:0] A_WIDTH, + input [1:0] A_WMODE, + input A_BYPASS, + input A_DOUT_EN, + input A_DOUT_SRST_N, + input A_DOUT_ARST_N, + input [13:0] B_ADDR, + input [2:0] B_BLK_EN, + input B_CLK, + input [19:0] B_DIN, + output [19:0] B_DOUT, + input [1:0] B_WEN, + input B_REN, + input [2:0] B_WIDTH, + input [1:0] B_WMODE, + input B_BYPASS, + input B_DOUT_EN, + input B_DOUT_SRST_N, + input B_DOUT_ARST_N, + input ECC_EN, + input ECC_BYPASS, + output SB_CORRECT, + output DB_DETECT, + input BUSY_FB, + output ACCESS_BUSY +); +parameter INIT0 = 1024'h0; +parameter INIT1 = 1024'h0; +parameter INIT2 = 1024'h0; +parameter INIT3 = 1024'h0; +parameter INIT4 = 1024'h0; +parameter INIT5 = 1024'h0; +parameter INIT6 = 1024'h0; +parameter INIT7 = 1024'h0; +parameter INIT8 = 1024'h0; +parameter INIT9 = 1024'h0; +parameter INIT10 = 1024'h0; +parameter INIT11 = 1024'h0; +parameter INIT12 = 1024'h0; +parameter INIT13 = 1024'h0; +parameter INIT14 = 1024'h0; +parameter INIT15 = 1024'h0; +parameter INIT16 = 1024'h0; +parameter INIT17 = 1024'h0; +parameter INIT18 = 1024'h0; +parameter INIT19 = 1024'h0; +endmodule + +(* blackbox *) +module RAM64x12 ( + input R_CLK, + input [5:0] R_ADDR, + input R_ADDR_BYPASS, + input R_ADDR_EN, + input R_ADDR_SL_N, + input R_ADDR_SD, + input R_ADDR_AL_N, + input R_ADDR_AD_N, + input BLK_EN, + output [11:0] R_DATA, + input R_DATA_BYPASS, + input R_DATA_EN, + input R_DATA_SL_N, + input R_DATA_SD, + input R_DATA_AL_N, + input R_DATA_AD_N, + + input W_CLK, + input [5:0] W_ADDR, + input [11:0]W_DATA, + input W_EN, + + input BUSY_FB, + output ACCESS_BUSY +); +parameter INIT0 = 64'h0; +parameter INIT1 = 64'h0; +parameter INIT2 = 64'h0; +parameter INIT3 = 64'h0; +parameter INIT4 = 64'h0; +parameter INIT5 = 64'h0; +parameter INIT6 = 64'h0; +parameter INIT7 = 64'h0; +parameter INIT8 = 64'h0; +parameter INIT9 = 64'h0; +parameter INIT10 = 64'h0; +parameter INIT11 = 64'h0; + +endmodule \ No newline at end of file diff --git a/techlibs/microchip/microchip_dffopt.cc b/techlibs/microchip/microchip_dffopt.cc new file mode 100644 index 00000000000..41a74fdc168 --- /dev/null +++ b/techlibs/microchip/microchip_dffopt.cc @@ -0,0 +1,343 @@ +/* +ISC License + +Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries + +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/sigtools.h" +#include "kernel/yosys.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +typedef std::pair> LutData; + +// Compute a LUT implementing (select ^ select_inv) ? alt_data : data. Returns true if successful. +bool merge_lut(LutData &result, const LutData &data, const LutData select, bool select_inv, SigBit alt_data, int max_lut_size) +{ + // First, gather input signals -- insert new signals at the beginning + // of the vector, so they don't disturb the likely-critical D LUT input + // timings. + result.second = data.second; + // D lut inputs initially start at 0. + int idx_data = 0; + // Now add the control input LUT inputs. + std::vector idx_sel; + for (auto bit : select.second) { + int idx = -1; + for (int i = 0; i < GetSize(result.second); i++) + if (result.second[i] == bit) + idx = i; + if (idx == -1) { + idx = 0; + // Insert new signal at the beginning and bump all indices. + result.second.insert(result.second.begin(), bit); + idx_data++; + for (int &sidx : idx_sel) + sidx++; + } + idx_sel.push_back(idx); + } + // Insert the Q signal, if any, to the slowest input -- it will have + // no problem meeting timing. + // This is to emulate CLK_EN, where output data is retained + int idx_alt = -1; + if (alt_data.wire) { + // Check if we already have it. + for (int i = 0; i < GetSize(result.second); i++) + if (result.second[i] == alt_data) + idx_alt = i; + // If not, add it. + if (idx_alt == -1) { + idx_alt = 0; + result.second.insert(result.second.begin(), alt_data); + idx_data++; + for (int &sidx : idx_sel) + sidx++; + } + } + + // If LUT would be too large, bail. + if (GetSize(result.second) > max_lut_size) + return false; + + // Okay, we're doing it — compute the LUT mask. + result.first = Const(0, 1 << GetSize(result.second)); + for (int i = 0; i < GetSize(result.first); i++) { + int sel_lut_idx = 0; + for (int j = 0; j < GetSize(select.second); j++) + if (i & 1 << idx_sel[j]) + sel_lut_idx |= 1 << j; + bool select_val = (select.first.bits[sel_lut_idx] == State::S1); + bool new_bit; + if (select_val ^ select_inv) { + // Use alt_data. + if (alt_data.wire) + new_bit = (i & 1 << idx_alt) != 0; + else + new_bit = alt_data.data == State::S1; + } else { + // Use original LUT. + int lut_idx = i >> idx_data & ((1 << GetSize(data.second)) - 1); + new_bit = data.first.bits[lut_idx] == State::S1; + } + result.first.bits[i] = new_bit ? State::S1 : State::S0; + } + return true; +} + +struct MicrochipDffOptPass : public Pass { + MicrochipDffOptPass() : Pass("microchip_dffopt", "MICROCHIP: optimize FF control signal usage") {} + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" microchip_dffopt [options] [selection]\n"); + log("\n"); + log("Converts hardware clock enable and set/reset signals on FFs to emulation\n"); + log("using LUTs, if doing so would improve area. Operates on post-techmap LUT, DFF\n"); + log("cells. \n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) override + { + log_header(design, "Executing MICROCHIP_DFFOPT pass (optimize FF control signal usage).\n"); + + size_t argidx = 1; + int max_lut_size = 4; + + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) { + log("Optimizing FFs in %s.\n", log_id(module)); + + SigMap sigmap(module); + dict> bit_to_lut; + dict bit_uses; + + // Gather LUTs. + for (auto cell : module->selected_cells()) { + for (auto port : cell->connections()) + for (auto bit : port.second) + bit_uses[sigmap(bit)]++; + if (cell->get_bool_attribute(ID::keep)) + continue; + if (cell->type == ID(INV)) { + SigBit sigout = sigmap(cell->getPort(ID::Y)); + SigBit sigin = sigmap(cell->getPort(ID::A)); + bit_to_lut[sigout] = make_pair(LutData(Const(1, 2), {sigin}), cell); // INIT = 01 + } else if (cell->type.in(ID(CFG1), ID(CFG2), ID(CFG3), ID(CFG4))) { + SigBit sigout = sigmap(cell->getPort(ID::Y)); + const Const &init = cell->getParam(ID::INIT); + std::vector sigin; + sigin.push_back(sigmap(cell->getPort(ID(A)))); + if (cell->type == ID(CFG1)) + goto lut_sigin_done; + sigin.push_back(sigmap(cell->getPort(ID(B)))); + if (cell->type == ID(CFG2)) + goto lut_sigin_done; + sigin.push_back(sigmap(cell->getPort(ID(C)))); + if (cell->type == ID(CFG3)) + goto lut_sigin_done; + sigin.push_back(sigmap(cell->getPort(ID(D)))); + + lut_sigin_done: + bit_to_lut[sigout] = make_pair(LutData(init, sigin), cell); + } + } + for (auto wire : module->wires()) + if (wire->port_output || wire->port_input) + for (int i = 0; i < GetSize(wire); i++) + bit_uses[sigmap(SigBit(wire, i))]++; + + // Iterate through FFs. + for (auto cell : module->selected_cells()) { + + if (!cell->type.in(ID(SLE))) // not a SLE + continue; + if (cell->getPort(ID(LAT)).is_fully_ones()) // skip latch + continue; + if (cell->get_bool_attribute(ID::keep)) // keep attribute + continue; + if (!cell->getPort(ID(ALn)).is_fully_ones()) // async FF + continue; + + const bool hasSyncLoad = cell->getPort(ID(SLn)).is_wire(); + const bool has_s = hasSyncLoad && cell->getPort(ID(SD)).is_fully_ones(); + const bool has_r = hasSyncLoad && cell->getPort(ID(SD)).is_fully_zero(); + + // SLE cannot have both synchronous set and reset implemented at the same time + log_assert(!(has_s && has_r)); + + // Don't bother if D has more than one use. + SigBit sig_D = sigmap(cell->getPort(ID::D)); + if (bit_uses[sig_D] > 2) + continue; + + // Find the D LUT. + auto it_D = bit_to_lut.find(sig_D); + if (it_D == bit_to_lut.end()) + continue; + LutData lut_d = it_D->second.first; + Cell *cell_d = it_D->second.second; + + LutData lut_d_post_ce; + LutData lut_d_post_s; + LutData lut_d_post_r; + bool worthy_post_ce = false; + bool worthy_post_s = false; + bool worthy_post_r = false; + + // First, unmap CE. + SigBit sig_Q = sigmap(cell->getPort(ID::Q)); + SigBit sig_CE = sigmap(cell->getPort(ID(EN))); + LutData lut_ce = LutData(Const(2, 2), {sig_CE}); // INIT = 10 + auto it_CE = bit_to_lut.find(sig_CE); + if (it_CE != bit_to_lut.end()) + lut_ce = it_CE->second.first; + if (sig_CE.wire) { + // Merge CE LUT and D LUT into one. If it cannot be done, nothing to do about this FF. + if (!merge_lut(lut_d_post_ce, lut_d, lut_ce, true, sig_Q, max_lut_size)) + continue; + + // If this gets rid of a CE LUT, it's worth it. If not, it still may be worth it, if we can remove set/reset + // as well. + if (it_CE != bit_to_lut.end()) + worthy_post_ce = true; + } else if (sig_CE.data != State::S1) { + // Strange. Should not happen in a reasonable flow, so bail. + log_assert(false); // This DFF is always off + continue; + } else { + lut_d_post_ce = lut_d; + } + + // Second, unmap S, if any. + lut_d_post_s = lut_d_post_ce; + if (has_s) { + SigBit sig_S = sigmap(cell->getPort(ID(SLn))); + LutData lut_s = LutData(Const(2, 2), {sig_S}); // INIT = 10 + bool inv_s = true; // active low + auto it_S = bit_to_lut.find(sig_S); + if (it_S != bit_to_lut.end()) + lut_s = it_S->second.first; + if (sig_S.wire) { + // Merge S LUT and D LUT into one. If it cannot be done, try to at least merge CE. + if (!merge_lut(lut_d_post_s, lut_d_post_ce, lut_s, inv_s, SigBit(State::S1), max_lut_size)) + goto unmap; + // If this gets rid of an S LUT, it's worth it. + if (it_S != bit_to_lut.end()) + worthy_post_s = true; + } else if (sig_S.data != (inv_s ? State::S1 : State::S0)) { + // Strange. Should not happen in a reasonable flow, so bail. + log_assert(false); // DFF is always in set mode + continue; + } + } + + // Third, unmap R, if any. + lut_d_post_r = lut_d_post_s; + if (has_r) { + SigBit sig_R = sigmap(cell->getPort(ID(SLn))); + LutData lut_r = LutData(Const(2, 2), {sig_R}); // INIT = 10 + bool inv_r = true; // active low + auto it_R = bit_to_lut.find(sig_R); + if (it_R != bit_to_lut.end()) + lut_r = it_R->second.first; + if (sig_R.wire) { + // Merge R LUT and D LUT into one. If it cannot be done, try to at least merge CE/S. + if (!merge_lut(lut_d_post_r, lut_d_post_s, lut_r, inv_r, SigBit(State::S0), max_lut_size)) + goto unmap; + // If this gets rid of an S LUT, it's worth it. + if (it_R != bit_to_lut.end()) + worthy_post_r = true; + } else if (sig_R.data != (inv_r ? State::S1 : State::S0)) { + // Strange. Should not happen in a reasonable flow, so bail. + log_assert(false); // DFF is always in reset mode + continue; + } + } + + unmap: + + // SLE cannot have both synchronous set and reset implemented at the same time + log_assert(!(worthy_post_r && worthy_post_s)); + + LutData final_lut; + if (worthy_post_r) { + final_lut = lut_d_post_r; + } else if (worthy_post_s) { + final_lut = lut_d_post_s; + } else if (worthy_post_ce) { + final_lut = lut_d_post_ce; + } else { + // Nothing to do here. + continue; + } + + std::string ports; + if (worthy_post_r) + ports += " + R"; + if (worthy_post_s) + ports += " + S"; + if (worthy_post_ce) + ports += " + CE"; + log(" Merging D%s LUTs for %s/%s (%d -> %d)\n", ports.c_str(), log_id(cell), log_id(sig_Q.wire), + GetSize(lut_d.second), GetSize(final_lut.second)); + + // Okay, we're doing it. Unmap ports. + if ((has_s && worthy_post_s) || worthy_post_r) { + cell->setPort(ID(SLn), Const(1, 1)); + } + + // if we made it this far, clk enable is always merged into D + cell->setPort(ID(EN), Const(1, 1)); + + // Create the new LUT. + Cell *lut_cell = nullptr; + switch (GetSize(final_lut.second)) { + case 1: + lut_cell = module->addCell(NEW_ID, ID(CFG1)); + break; + case 2: + lut_cell = module->addCell(NEW_ID, ID(CFG2)); + break; + case 3: + lut_cell = module->addCell(NEW_ID, ID(CFG3)); + break; + case 4: + lut_cell = module->addCell(NEW_ID, ID(CFG4)); + break; + default: + log_assert(!"unknown lut size"); + } + lut_cell->attributes = cell_d->attributes; + Wire *lut_out = module->addWire(NEW_ID); + lut_cell->setParam(ID::INIT, final_lut.first); + cell->setPort(ID::D, lut_out); + lut_cell->setPort(ID::Y, lut_out); + lut_cell->setPort(ID(A), final_lut.second[0]); + if (GetSize(final_lut.second) >= 2) + lut_cell->setPort(ID(B), final_lut.second[1]); + if (GetSize(final_lut.second) >= 3) + lut_cell->setPort(ID(C), final_lut.second[2]); + if (GetSize(final_lut.second) >= 4) + lut_cell->setPort(ID(D), final_lut.second[3]); + } + } + } +} MicrochipDffOptPass; + +PRIVATE_NAMESPACE_END diff --git a/techlibs/microchip/polarfire_dsp_map.v b/techlibs/microchip/polarfire_dsp_map.v new file mode 100644 index 00000000000..b416841fb41 --- /dev/null +++ b/techlibs/microchip/polarfire_dsp_map.v @@ -0,0 +1,95 @@ +/* +ISC License + +Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries + +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. +*/ + +module \$__MUL18X18 (input [17:0] A, input [17:0] B, output [35:0] Y); + parameter A_SIGNED = 0; + parameter B_SIGNED = 0; + parameter A_WIDTH = 0; + parameter B_WIDTH = 0; + parameter Y_WIDTH = 0; + + wire [47:0] P_48; + // For pin descriptions, see Section 9 of PolarFire FPGA Macro Library Guide: + // https://coredocs.s3.amazonaws.com/Libero/2021_2/Tool/pf_mlg.pdf + MACC_PA _TECHMAP_REPLACE_ ( + .DOTP(1'b0), + .SIMD(1'b0), + .OVFL_CARRYOUT_SEL(1'b0), + + .AL_N(1'b1), + .A(A), + .A_BYPASS(1'b1), + .A_SRST_N(1'b1), + .A_EN(1'b1), + + .B(B), + .B_BYPASS(1'b1), + .B_SRST_N(1'b1), + .B_EN(1'b1), + + .D(18'b0), + .D_BYPASS(1'b1), + .D_ARST_N(1'b1), + .D_SRST_N(1'b1), + .D_EN(1'b1), + + .CARRYIN(1'b0), + .C(48'b0), + .C_BYPASS(1'b1), + .C_ARST_N(1'b1), + .C_SRST_N(1'b1), + .C_EN(1'b1), + + + .P(P_48), + + .P_BYPASS(1'b1), + .P_SRST_N(1'b1), + .P_EN(1'b1), + + .PASUB(1'b0), + .PASUB_BYPASS(1'b1), + .PASUB_AD_N(1'b0), + .PASUB_SL_N(1'b1), + .PASUB_SD_N(1'b0), + .PASUB_EN(1'b1), + + .CDIN_FDBK_SEL(2'b00), + .CDIN_FDBK_SEL_BYPASS(1'b1), + .CDIN_FDBK_SEL_AD_N(2'b00), + .CDIN_FDBK_SEL_SL_N(1'b1), + .CDIN_FDBK_SEL_SD_N(2'b00), + .CDIN_FDBK_SEL_EN(1'b1), + + .ARSHFT17(1'b0), + .ARSHFT17_BYPASS(1'b1), + .ARSHFT17_AD_N(1'b0), + .ARSHFT17_SL_N(1'b1), + .ARSHFT17_SD_N(1'b0), + .ARSHFT17_EN(1'b1), + + .SUB(1'b0), + .SUB_BYPASS(1'b1), + .SUB_AD_N(1'b0), + .SUB_SL_N(1'b1), + .SUB_SD_N(1'b0), + .SUB_EN(1'b1) + + ); + assign Y = P_48; +endmodule diff --git a/techlibs/microchip/synth_microchip.cc b/techlibs/microchip/synth_microchip.cc new file mode 100644 index 00000000000..727bf9ac65a --- /dev/null +++ b/techlibs/microchip/synth_microchip.cc @@ -0,0 +1,553 @@ +/* +ISC License + +Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries + +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/celltypes.h" +#include "kernel/log.h" +#include "kernel/register.h" +#include "kernel/rtlil.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct SynthMicrochipPass : public ScriptPass { + SynthMicrochipPass() : ScriptPass("synth_microchip", "synthesis for Microchip FPGAs") {} + + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" synth_microchip [options]\n"); + log("\n"); + log("This command runs synthesis for Microchip FPGAs. This command creates \n"); + log("netlists that are compatible with Microchip PolarFire devices. \n"); + log("\n"); + log(" -top \n"); + log(" use the specified module as the top module\n"); + log("\n"); + log(" -family \n"); + log(" Run synthesis for the specified Microchip architecture. \n"); + log(" Generate the synthesis netlist for the specified family.\n"); + log(" supported values:\n"); + log(" - polarfire: PolarFire\n"); + log("\n"); + log(" -edif \n"); + log(" Write the design to the specified edif file. Writing of an output file\n"); + log(" is omitted if this parameter is not specified.\n"); + log("\n"); + log(" -blif \n"); + log(" Write the design to the specified BLIF file. Writing of an output file\n"); + log(" is omitted if this parameter is not specified.\n"); + log("\n"); + log(" -vlog \n"); + log(" write the design to the specified Verilog file. writing of an output\n"); + log(" file is omitted if this parameter is not specified.\n"); + log(" -nobram\n"); + log(" Do not use block RAM cells in output netlist\n"); + log("\n"); + log(" -nocarry\n"); + log(" Do not use ARI1 cells in output netlist\n"); + log("\n"); + log(" -nodsp\n"); + log(" Do not use MATH blocks to implement multipliers and associated logic\n"); + log("\n"); + log(" -noiopad\n"); + log(" Disable I/O buffer insertion (useful for hierarchical or \n"); + log(" out-of-context flows)\n"); + log("\n"); + log(" -noclkbuf\n"); + log(" Disable automatic clock buffer insertion\n"); + log("\n"); + log(" -run :\n"); + log(" Only run the commands between the labels (see below). an empty\n"); + log(" 'from_label' is synonymous to 'begin', and empty 'to_label' is\n"); + log(" synonymous to the end of the command list.\n"); + log("\n"); + log(" -noflatten\n"); + log(" do not flatten design before synthesis\n"); + log("\n"); + log(" -dff\n"); + log(" Run 'abc'/'abc9' with -dff option\n"); + log("\n"); + log(" -retime\n"); + log(" Run 'abc' with '-D 1' option to enable flip-flop retiming.\n"); + log(" implies -dff.\n"); + log("\n"); + log(" -noabc9\n"); + log(" Use classic ABC flow instead of ABC9\n"); + log("\n"); + log(" -discard-ffinit\n"); + log(" discard FF init value instead of emitting an error\n"); + log("\n"); + log("\n"); + log("The following commands are executed by this synthesis command:\n"); + help_script(); + log("\n"); + } + + std::string top_opt, edif_file, blif_file, vlog_file, family; + bool flatten, retime, noiopad, noclkbuf, nobram, nocarry, nowidelut, nodsp; + bool abc9, dff; + bool discard_ffinit; + int lut_size; + + // debug dump switches + bool debug_memory, debug_carry; + + void clear_flags() override + { + top_opt = "-auto-top"; + edif_file.clear(); + blif_file.clear(); + vlog_file.clear(); + family = "polarfire"; + flatten = true; + retime = false; + noiopad = false; + noclkbuf = false; + nocarry = false; + nobram = false; + nowidelut = false; + nodsp = false; + abc9 = true; + dff = false; + lut_size = 4; + discard_ffinit = false; + + debug_memory = false; + debug_carry = false; + } + + void execute(std::vector args, RTLIL::Design *design) override + { + std::string run_from, run_to; + clear_flags(); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-top" && argidx + 1 < args.size()) { + top_opt = "-top " + args[++argidx]; + continue; + } + if ((args[argidx] == "-family" || args[argidx] == "-arch") && argidx + 1 < args.size()) { + family = args[++argidx]; + continue; + } + if (args[argidx] == "-edif" && argidx + 1 < args.size()) { + edif_file = args[++argidx]; + continue; + } + if (args[argidx] == "-blif" && argidx + 1 < args.size()) { + blif_file = args[++argidx]; + continue; + } + if (args[argidx] == "-vlog" && argidx + 1 < args.size()) { + vlog_file = args[++argidx]; + continue; + } + if (args[argidx] == "-run" && argidx + 1 < args.size()) { + size_t pos = args[argidx + 1].find(':'); + if (pos == std::string::npos) + break; + run_from = args[++argidx].substr(0, pos); + run_to = args[argidx].substr(pos + 1); + continue; + } + if (args[argidx] == "-noflatten") { + flatten = false; + continue; + } + if (args[argidx] == "-retime") { + dff = true; + retime = true; + continue; + } + if (args[argidx] == "-nocarry") { + nocarry = true; + continue; + } + if (args[argidx] == "-nowidelut") { + nowidelut = true; + continue; + } + if (args[argidx] == "-iopad") { + continue; + } + if (args[argidx] == "-noiopad") { + noiopad = true; + continue; + } + if (args[argidx] == "-noclkbuf") { + noclkbuf = true; + continue; + } + if (args[argidx] == "-nocarry") { + nocarry = true; + continue; + } + if (args[argidx] == "-nobram") { + nobram = true; + continue; + } + if (args[argidx] == "-noabc9") { + abc9 = false; + continue; + } + if (args[argidx] == "-nodsp") { + nodsp = true; + continue; + } + if (args[argidx] == "-dff") { + dff = true; + continue; + } + if (args[argidx] == "-debug_memory") { + debug_memory = true; + continue; + } + if (args[argidx] == "-debug_carry") { + debug_carry = true; + continue; + } + if (args[argidx] == "-discard-ffinit") { + discard_ffinit = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + if (family == "polarfire") { + lut_size = 4; + } else { + log_cmd_error("Invalid Microchip -family setting: '%s'.\n", family.c_str()); + } + + if (!design->full_selection()) + log_cmd_error("This command only operates on fully selected designs!\n"); + + if (abc9 && retime) + log_cmd_error("-retime option not currently compatible with -abc9!\n"); + + log_header(design, "Executing SYNTH_MICROCHIP pass.\n"); + log_push(); + + run_script(design, run_from, run_to); + + log_pop(); + } + + void script() override + { + std::string lut_size_s = std::to_string(lut_size); + if (help_mode) + lut_size_s = "[4]"; + + if (check_label("begin")) { + std::string read_args; + read_args += " -lib -specify +/microchip/cells_sim.v"; + run("read_verilog" + read_args); + + run(stringf("hierarchy -check %s", top_opt.c_str())); + } + + if (check_label("prepare")) { + run("proc"); + if (flatten || help_mode) + run("flatten", "(with '-flatten')"); + if (active_design) + active_design->scratchpad_unset("tribuf.added_something"); + run("tribuf -logic"); + if (noiopad && active_design && active_design->scratchpad_get_bool("tribuf.added_something")) + log_error("Tristate buffers are unsupported without the '-iopad' option.\n"); + run("deminout"); + run("opt_expr"); + run("opt_clean"); + run("check"); + run("opt -nodffe -nosdff"); + run("fsm"); + run("opt"); + + run("wreduce"); + run("peepopt"); + run("opt_clean"); + } + + if (check_label("map_dsp", "(skip if '-nodsp')")) { + if (!nodsp || help_mode) { + run("memory_dff"); // microchip_dsp will merge registers, reserve memory port registers first + if (help_mode) + run("techmap -map +/mul2dsp.v -map +/microchip/{family}_dsp_map.v {options}"); + else if (family == "polarfire") // Microchip - map multipliers to DSP + run("techmap -map +/mul2dsp.v -map +/microchip/polarfire_dsp_map.v -D DSP_A_MAXWIDTH=18 -D DSP_B_MAXWIDTH=18 " + "-D DSP_A_MAXWIDTH_PARTIAL=18 " // Partial multipliers are intentionally + // limited to 18x18 in order to take + // advantage of the (PCOUT >> 17) -> PCIN + // dedicated cascade chain capability + "-D DSP_A_MINWIDTH=2 -D DSP_B_MINWIDTH=2 " // Blocks Nx1 multipliers + "-D DSP_Y_MINWIDTH=9 " + "-D DSP_SIGNEDONLY=1 -D DSP_NAME=$__MUL18X18"); + + run("select a:mul2dsp"); + run("setattr -unset mul2dsp"); + run("opt_expr -fine"); + run("wreduce"); + run("select -clear"); + if (help_mode) + run("microchip_dsp -family "); + else if (family == "polarfire") // Microchip - absorb cells into DSP + run("microchip_dsp -family " + family); + + run("chtype -set $mul t:$__soft_mul"); + } + } + + if (check_label("coarse")) { + run("techmap -map +/cmp2lut.v -map +/cmp2lcu.v -D LUT_WIDTH=" + lut_size_s); + run("alumacc"); + run("share"); + run("opt"); + run("memory -nomap"); + run("opt_clean"); + if (discard_ffinit || help_mode) + run("attrmap -remove init", "(only if -discard-ffinit)"); + } + + if (check_label("map_memory")) { + std::string params = ""; + std::string LSRAM_map = "+/microchip/LSRAM_map.v"; + std::string uSRAM_map = "+/microchip/uSRAM_map.v"; + if (debug_memory) + run("write_verilog -noexpr memory_map_pre.vm"); + if (help_mode) { + params = " [...]"; + } else { + + if (family == "polarfire") { + // cost of a single bit for memory lowered to soft logic + params += " -logic-cost-rom 0.015625"; + + params += " -lib +/microchip/LSRAM.txt"; + params += " -lib +/microchip/uSRAM.txt"; + LSRAM_map = "+/microchip/LSRAM_map.v"; + uSRAM_map = "+/microchip/uSRAM_map.v"; + } + if (nobram) + params += " -no-auto-block"; + } + + // transform memories into intermediate cells + // Cost based transformation. The cost is assigned by us for each cell. + run("memory_libmap" + params); + if (debug_memory) + run("write_verilog -noexpr memory_map_libmap.vm"); + + // map intermediate cells to actual RAM macros + // NOTE: order doesnt matter here + run("techmap -map " + LSRAM_map); + run("techmap -map " + uSRAM_map); + if (debug_memory) + run("write_verilog -noexpr memory_map_final.vm"); + } + + if (check_label("map_ffram")) { + run("opt -fast -full"); + + // blast unmapped RAM to flops or LUTs + run("memory_map"); + } + + if (check_label("fine")) { + run("opt -full"); + + if (debug_carry) + run("write_verilog -noexpr ARI1_cells.vm"); + + if (!nocarry) { + // converts $mux -> $_MUX_ to allow muxcover to work + run("simplemap t:$mux"); + + // converts $and/$or/$xor to gate representation for extract_reduce to work + run("simplemap t:$xor"); // only mapping reduce_xor + + // mapping based on Yosys internal gates + if (debug_carry) + run("write_verilog -noexpr ARI1_pre.vm"); + + // collapse $_AND_/$_OR_/$_XOR_ chains into reduction cells + run("extract_reduce"); + + if (debug_carry) + run("write_verilog -noexpr ARI1_extract_reduce.vm"); + + // pack mux trees into $_MUX4_ + run("muxcover -nodecode -mux4=220"); + + if (debug_carry) + run("write_verilog -noexpr ARI1_muxcover.vm"); + + run("techmap -map +/microchip/arith_map.v"); + if (debug_carry) + run("write_verilog -noexpr ARI1_post.vm"); + } + + // convert all remaining cells to gates + run("techmap -map +/techmap.v"); + + run("opt -fast"); + } + + if (check_label("map_cells")) { + // Needs to be done before logic optimization, so that inverters (inserted + // here because of negative-polarity output enable) are handled. + if (help_mode || !noiopad) { + run("iopadmap -bits -inpad INBUF Y:PAD -outpad OUTBUF D:PAD -toutpad TRIBUFF E:D:PAD -tinoutpad BIBUF E:Y:D:PAD", + "(unless -noiobs)"); + } + + std::string techmap_args = "-map +/techmap.v -map +/microchip/cells_map.v"; + run("techmap " + techmap_args); + run("clean"); + } + + if (check_label("map_ffs")) { + // dfflegalize : Converts FFs to types supported by the target + // this can convert less capable cells into more capable cells (e.g. dff -> dffe) + + // Based on PolarFire® FPGA Macro Library Guide + // D-flop: + // active high enable + // active low clear or active low set + // Latch: + // active low clear or active low set + // SLE (can implement D-flop/Latch): + // active high EN + // active low async load (set/reset) with static load configuration via ADn (Q = ~ADn) + // active low sync load (set/reset) with static load configuration via SD + // static latch configuration bit + // init not supported + + // Yosys internal cell description + // see: https://yosyshq.readthedocs.io/projects/yosys/en/latest/yosys_internals/formats/cell_library.html + // see: common/simcells.v + // $_DFF_[NP]_ (regular dff) + // $_DFFE_[NP][NP]_ (enable) + // $_DFF_[NP][NP][01]_ (async reset to 0/1) + // $_DFFE_[NP][NP][01][NP]_ (async reset to 0/1 + enable) + // $_ALDFF_[NP][NP]_ (async load) + // $_ALDFFE_[NP][NP][NP]_ (async load + enable) + // $_DFFSR_[NP][NP][NP]_ (async set & reset) + // $_DFFSRE_[NP][NP][NP][NP]_ (async set & reset + enable) + // $_SDFF_[NP][NP][01]_ (sync reset to 0/1) + // $_SDFFE_[NP][NP][01][NP]_ (sync reset to 0/1 + enable, reset prioritize over enable) + // $_SDFFCE_[NP][NP][01][NP]_ (sync reset to 0/1 + enable, enable prioritize over reset) + // $_SR_[NP][NP]_ (set/reset latch) + // $_DLATCH_[NP]_ (D-latch) + // $_DLATCH_[NP][NP][01]_ (D-latch + reset to 0/1) + // $_DLATCHSR_[NP][NP][NP]_ (D-latch + set + reset) + + if (family == "polarfire") { + std::string params = ""; + + // D-flop with async reset and enable + // posedge CLK, active low reset to 1 or 0, active high EN + params += " -cell $_DFFE_PN?P_ x"; + + // D-flop with sync reset and enable, enable takes priority over reset + // posedge CLK, active low reset to 1 or 0, active high EN + params += " -cell $_SDFFCE_PN?P_ x"; + + // D-latch + reset to 0/1 + // posedge CLK, active low reset to 1 or 0 + params += " -cell $_DLATCH_PN?_ x"; + + run("dfflegalize" + params, "(Converts FFs to supported types)"); + } + + if (abc9 || help_mode) { + if (dff || help_mode) + run("zinit -all w:* t:$_SDFFCE_*", "('-dff' only)"); + run("techmap -D NO_LUT -map +/microchip/cells_map.v", "('-abc9' only)"); + } + } + + if (check_label("map_luts")) { + run("opt_expr -mux_undef -noclkinv"); + if (help_mode) + run("abc -luts 2:2,3,6:5[,10,20] [-dff] [-D 1]", "(option for '-nowidelut', '-dff', '-retime')"); + else if (abc9) { + + std::string abc9_opts; + // for the if command in abc to specify wire delay between adjacent LUTs (default = 0) + // NOTE: should not have 0 wire delay between LUTs, + // otherwise abc might use LUT2+LUT3 instead of single LUT4 + abc9_opts += " -W 300"; + if (nowidelut) + abc9_opts += stringf(" -maxlut %d", lut_size); + if (dff) + abc9_opts += " -dff"; + run("abc9" + abc9_opts); + } else { + std::string abc_opts = " -lut " + lut_size_s; + if (dff) + abc_opts += " -dff"; + if (retime) + abc_opts += " -D 1"; + run("abc" + abc_opts); + } + run("clean"); + + if (help_mode || !abc9) + run("techmap -D NO_LUT -map +/microchip/cells_map.v", "(only if not '-abc9')"); + std::string techmap_args = "-map +/microchip/cells_map.v -D FINAL_MAP"; + techmap_args += " -D LUT_WIDTH=" + lut_size_s; + run("techmap " + techmap_args); + + if (help_mode || lut_size == 4) + run("microchip_dffopt"); + } + + run("clkbufmap -buf CLKINT Y:A -inpad CLKBUF Y:PAD"); + + run("clean -purge"); + + if (check_label("check")) { + run("hierarchy -check"); + run("stat"); + run("check -noinit"); + run("blackbox =A:whitebox"); + } + + if (check_label("edif")) { + if (!edif_file.empty() || help_mode) + run(stringf("write_edif -pvector bra %s", edif_file.c_str())); + } + + if (check_label("blif")) { + if (!blif_file.empty() || help_mode) + run(stringf("write_blif %s", blif_file.c_str())); + } + + if (check_label("vlog")) + { + if (!vlog_file.empty() || help_mode) + run(stringf("write_verilog %s", help_mode ? "" : vlog_file.c_str())); + } + } +} SynthMicrochipPass; + +PRIVATE_NAMESPACE_END diff --git a/techlibs/microchip/uSRAM.txt b/techlibs/microchip/uSRAM.txt new file mode 100644 index 00000000000..10f9a143530 --- /dev/null +++ b/techlibs/microchip/uSRAM.txt @@ -0,0 +1,69 @@ +# ISC License +# +# Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries +# +# 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. + +# asynchronous read +ram block $__uSRAM_AR_ { + + #(LSRAM cost)/3 + cost 43; + + # INIT supported + init any; + + abits 6; + widths 12 per_port; + + # single write enable wire + port sw "W" { + clock posedge; + + # collision not supported, but write takes precedence and read data is invalid while writing to + # the same address + wrtrans all new; + + optional; + } + port ar "R" { + optional; + } +} + +# synchronous read +# NOTE: synchronous read can be realized by the address pipeline register or data pipeline register. +# This assumes address is synchronized +ram block $__uSRAM_SR_ { + + cost 42; + + init any; + abits 6; +widths 12 per_port; + + port sw "W" { + clock posedge; + + # collision not supported + wrtrans all new; + + optional; + } + port sr "R" { + clock posedge; + rden; + rdinit none; + optional; + } +} diff --git a/techlibs/microchip/uSRAM_map.v b/techlibs/microchip/uSRAM_map.v new file mode 100644 index 00000000000..5750a780f33 --- /dev/null +++ b/techlibs/microchip/uSRAM_map.v @@ -0,0 +1,126 @@ +/* +ISC License + +Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries + +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. +*/ + +// See document PolarFire Family Fabric User Guide +// section 4.2 for port list. + +// Asynchronous read +module $__uSRAM_AR_ (...); + +parameter INIT = 0; +parameter ADDR_BITS = 6; + +parameter PORT_W_WIDTH = 12; +parameter PORT_R_WIDTH = 12; +parameter PORT_R_USED = 0; +parameter PORT_W_USED = 0; + +input PORT_W_CLK; +input [ADDR_BITS-1:0] PORT_W_ADDR; +input [PORT_W_WIDTH-1:0] PORT_W_WR_DATA; +input PORT_W_WR_EN; + +input [ADDR_BITS-1:0] PORT_R_ADDR; +output [PORT_R_WIDTH-1:0] PORT_R_RD_DATA; + +`include "brams_defs.vh" + +RAM64x12 #( + `PARAMS_INIT_uSRAM +) _TECHMAP_REPLACE_ ( + .R_ADDR(PORT_R_ADDR), + .R_ADDR_BYPASS(1'b1), + .R_ADDR_EN(1'b0), + .R_ADDR_SL_N(1'b1), + .R_ADDR_SD(1'b0), + .R_ADDR_AL_N(1'b1), + .R_ADDR_AD_N(1'b0), + .BLK_EN(PORT_R_USED ? 1'b1 : 1'b0), + .R_DATA(PORT_R_RD_DATA), + .R_DATA_BYPASS(1'b1), + .R_DATA_EN(1'b0), + .R_DATA_SL_N(1'b1), + .R_DATA_SD(1'b0), + .R_DATA_AL_N(1'b1), + .R_DATA_AD_N(1'b0), + + .W_CLK(PORT_W_CLK), + .W_ADDR(PORT_W_ADDR), + .W_DATA(PORT_W_WR_DATA), + .W_EN(PORT_W_WR_EN), + + .BUSY_FB(1'b0) +); + +endmodule + +// Synchronous read +module $__uSRAM_SR_ (...); + +parameter INIT = 0; +parameter ADDR_BITS = 6; + +parameter PORT_W_WIDTH = 12; +parameter PORT_R_WIDTH = 12; +parameter PORT_R_USED = 0; +parameter PORT_W_USED = 0; + +input PORT_W_CLK; +input [ADDR_BITS-1:0] PORT_W_ADDR; +input [PORT_W_WIDTH-1:0] PORT_W_WR_DATA; +input PORT_W_WR_EN; + +// Read port clock and enable signal +// that async read uSRAM doesn't have +input PORT_R_CLK; +input PORT_R_RD_EN; +input [ADDR_BITS-1:0] PORT_R_ADDR; +output [PORT_R_WIDTH-1:0] PORT_R_RD_DATA; + +`include "brams_defs.vh" + +RAM64x12 #( + `PARAMS_INIT_uSRAM +) _TECHMAP_REPLACE_ ( + .R_CLK(PORT_R_CLK), + .R_ADDR(PORT_R_ADDR), + .R_ADDR_BYPASS(1'b0), + .R_ADDR_EN(PORT_R_RD_EN), + .R_ADDR_SL_N(1'b1), + .R_ADDR_SD(1'b0), + .R_ADDR_AL_N(1'b1), + .R_ADDR_AD_N(1'b0), + .BLK_EN(PORT_R_USED ? 1'b1 : 1'b0), + .R_DATA(PORT_R_RD_DATA), + .R_DATA_BYPASS(1'b1), + .R_DATA_EN(1'b0), + .R_DATA_SL_N(1'b1), + .R_DATA_SD(1'b0), + .R_DATA_AL_N(1'b1), + .R_DATA_AD_N(1'b0), + + .W_CLK(PORT_W_CLK), + .W_ADDR(PORT_W_ADDR), + .W_DATA(PORT_W_WR_DATA), + .W_EN(PORT_W_WR_EN), + + .BUSY_FB(1'b0) +); + +endmodule + diff --git a/tests/arch/microchip/.gitignore b/tests/arch/microchip/.gitignore new file mode 100644 index 00000000000..9c0a77944a9 --- /dev/null +++ b/tests/arch/microchip/.gitignore @@ -0,0 +1,4 @@ +*.log +/run-test.mk +*.vm + diff --git a/tests/arch/microchip/dff.ys b/tests/arch/microchip/dff.ys new file mode 100644 index 00000000000..ee377bb4028 --- /dev/null +++ b/tests/arch/microchip/dff.ys @@ -0,0 +1,76 @@ +# ISC License +# +# Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries +# +# 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. + + +# active low async reset with enable +read_verilog <