diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 57cbe5010..2a046703b 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -14,7 +14,7 @@ jobs: run: sudo apt-get install bison flex libreadline-dev tcl-dev libffi-dev - name: Checkout repository - uses: actions/checkout@v3.0.0 + uses: actions/checkout@v3 - name: Initialize CodeQL uses: github/codeql-action/init@v2 diff --git a/.github/workflows/deprecated.yml b/.github/workflows/deprecated.yml new file mode 100644 index 000000000..073b9ded3 --- /dev/null +++ b/.github/workflows/deprecated.yml @@ -0,0 +1,21 @@ +name: Deprecated compilers + +on: [push, pull_request] + +jobs: + gcc48: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Build GCC 4.8 + run: | + docker run --rm -v $(pwd):/work yosyshq/deprecated-compilers:1.0 make config-gcc-4.8 + docker run --rm -v $(pwd):/work yosyshq/deprecated-compilers:1.0 make -j8 + + clang39: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Build Clang 3.9 + run: | + docker run --rm -v $(pwd):/work yosyshq/deprecated-compilers:1.0 make CC=clang-3.9 CXX=clang-3.9 LD=clang-3.9 -j8 diff --git a/.github/workflows/emcc.yml b/.github/workflows/emcc.yml index f1cc4b3e7..7a9a6064f 100644 --- a/.github/workflows/emcc.yml +++ b/.github/workflows/emcc.yml @@ -7,10 +7,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: mymindstorm/setup-emsdk@v11 - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Cache sources id: cache-sources - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: . key: cache-yosys @@ -18,7 +18,7 @@ jobs: run: | make config-emcc make YOSYS_VER=latest - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: name: yosysjs path: yosysjs-latest.zip diff --git a/.github/workflows/test-linux.yml b/.github/workflows/test-linux.yml index e27ea37d2..eee556794 100644 --- a/.github/workflows/test-linux.yml +++ b/.github/workflows/test-linux.yml @@ -25,12 +25,6 @@ jobs: - os: { id: ubuntu-20.04, name: focal } compiler: 'gcc-10' cpp_std: 'c++11' - - os: { id: ubuntu-18.04, name: bionic } - compiler: 'clang-3.9' - cpp_std: 'c++11' - - os: { id: ubuntu-18.04, name: bionic } - compiler: 'gcc-4.8' - cpp_std: 'c++11' fail-fast: false steps: - name: Install Dependencies @@ -84,7 +78,7 @@ jobs: $CXX --version - name: Checkout Yosys - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Get iverilog shell: bash @@ -93,7 +87,7 @@ jobs: - name: Cache iverilog id: cache-iverilog - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: .local/ key: ${{ matrix.os.id }}-${{ hashFiles('iverilog/.git/refs/heads/master') }} @@ -109,15 +103,7 @@ jobs: make -j${{ env.procs }} make install - - name: Build yosys (gcc-4.8) - if: matrix.compiler == 'gcc-4.8' - shell: bash - run: | - make config-${{ matrix.compiler }} - make -j${{ env.procs }} CCXXSTD=${{ matrix.cpp_std }} CC=$CC CXX=$CC LD=$CC - - name: Build yosys - if: matrix.compiler != 'gcc-4.8' shell: bash run: | make config-${CC%%-*} diff --git a/.github/workflows/test-macos.yml b/.github/workflows/test-macos.yml index 22cf5e658..048457234 100644 --- a/.github/workflows/test-macos.yml +++ b/.github/workflows/test-macos.yml @@ -35,7 +35,7 @@ jobs: cc --version - name: Checkout Yosys - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Get iverilog shell: bash @@ -44,7 +44,7 @@ jobs: - name: Cache iverilog id: cache-iverilog - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: .local/ key: ${{ matrix.os.id }}-${{ hashFiles('iverilog/.git/refs/heads/master') }} diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml index c6f4da30d..c2a1756e9 100644 --- a/.github/workflows/version.yml +++ b/.github/workflows/version.yml @@ -10,15 +10,15 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 - name: Take last commit id: log - run: echo "::set-output name=message::$(git log --no-merges -1 --oneline)" + run: echo "message=$(git log --no-merges -1 --oneline)" >> $GITHUB_OUTPUT - name: Take repository id: repo - run: echo "::set-output name=message::$GITHUB_REPOSITORY" + run: echo "message=$GITHUB_REPOSITORY" >> $GITHUB_OUTPUT - name: Bump version if: "!contains(steps.log.outputs.message, 'Bump version') && contains(steps.repo.outputs.message, 'YosysHQ/yosys')" run: | diff --git a/.github/workflows/vs.yml b/.github/workflows/vs.yml index 79a8401d6..744ad5677 100644 --- a/.github/workflows/vs.yml +++ b/.github/workflows/vs.yml @@ -6,16 +6,16 @@ jobs: yosys-vcxsrc: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Cache sources id: cache-sources - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: . key: cache-yosys - name: Build run: make vcxsrc YOSYS_VER=latest - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: name: vcxsrc path: yosys-win32-vcxsrc-latest.zip @@ -24,7 +24,7 @@ jobs: runs-on: windows-2019 needs: yosys-vcxsrc steps: - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v3 with: name: vcxsrc path: . diff --git a/CHANGELOG b/CHANGELOG index bb55c0c59..5fb698c90 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,9 +2,27 @@ List of major changes and improvements between releases ======================================================= -Yosys 0.22 .. Yosys 0.22-dev +Yosys 0.23 .. Yosys 0.23-dev -------------------------- + +Yosys 0.22 .. Yosys 0.23 +-------------------------- + * New commands and options + - Added option "-cross" to "miter" pass. + - Added option "-nocheck" to "equiv_opt" pass. + + * Formal Verification + - yosys-smtbmc: Added "--detect-loops" option for checking if states are + unique in temporal induction counter examples. + + * Verific support + - Added support for reading Liberty files using Verific library. + (Optinally enabled with ENABLE_VERIFIC_LIBERTY) + - Added option "-cells" to "verific -import" enabling import of + all cells from verific design. + * Various + - MinGW build (Windows) plugin support. - Added YOSYS_ABORT_ON_LOG_ERROR environment variable for debugging. Setting it to 1 causes abort() to be called when Yosys terminates with an error message. diff --git a/Makefile b/Makefile index 0acf55a72..f6259886f 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,7 @@ ENABLE_EDITLINE := 0 ENABLE_GHDL := 0 ENABLE_VERIFIC := 0 ENABLE_VERIFIC_EDIF := 0 +ENABLE_VERIFIC_LIBERTY := 0 DISABLE_VERIFIC_EXTENSIONS := 0 DISABLE_VERIFIC_VHDL := 0 ENABLE_COVER := 1 @@ -56,6 +57,9 @@ PROGRAM_PREFIX := OS := $(shell uname -s) PREFIX ?= /usr/local INSTALL_SUDO := +ifneq ($(filter MINGW%,$(OS)),) +OS := MINGW +endif ifneq ($(wildcard Makefile.conf),) include Makefile.conf @@ -91,6 +95,13 @@ CXXSTD ?= c++11 CXXFLAGS := $(CXXFLAGS) -Wall -Wextra -ggdb -I. -I"$(YOSYS_SRC)" -MD -MP -D_YOSYS_ -fPIC -I$(PREFIX)/include LDLIBS := $(LDLIBS) -lstdc++ -lm PLUGIN_LDFLAGS := +PLUGIN_LDLIBS := +EXE_LDFLAGS := +ifeq ($(OS), MINGW) +EXE_LDFLAGS := -Wl,--export-all-symbols -Wl,--out-implib,libyosys_exe.a +PLUGIN_LDFLAGS += -L"$(LIBDIR)" +PLUGIN_LDLIBS := -lyosys_exe +endif PKG_CONFIG ?= pkg-config SED ?= sed @@ -131,7 +142,7 @@ LDLIBS += -lrt endif endif -YOSYS_VER := 0.22+37 +YOSYS_VER := 0.23+0 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo @@ -147,7 +158,7 @@ endif OBJS = kernel/version_$(GIT_REV).o bumpversion: - sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline f109fa3.. | wc -l`/;" Makefile + sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 7ce5011.. | wc -l`/;" Makefile # set 'ABCREV = default' to use abc/ as it is # @@ -436,8 +447,11 @@ endif ifeq ($(ENABLE_PLUGINS),1) CXXFLAGS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --cflags libffi) -DYOSYS_ENABLE_PLUGINS +ifeq ($(OS), MINGW) +CXXFLAGS += -Ilibs/dlfcn-win32 +endif LDLIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --libs libffi || echo -lffi) -ifneq ($(OS), $(filter $(OS),FreeBSD OpenBSD NetBSD)) +ifneq ($(OS), $(filter $(OS),FreeBSD OpenBSD NetBSD MINGW)) LDLIBS += -ldl endif endif @@ -531,6 +545,10 @@ ifeq ($(ENABLE_VERIFIC_EDIF),1) VERIFIC_COMPONENTS += edif CXXFLAGS += -DVERIFIC_EDIF_SUPPORT endif +ifeq ($(ENABLE_VERIFIC_LIBERTY),1) +VERIFIC_COMPONENTS += synlib +CXXFLAGS += -DVERIFIC_LIBERTY_SUPPORT +endif ifneq ($(DISABLE_VERIFIC_EXTENSIONS),1) VERIFIC_COMPONENTS += extensions CXXFLAGS += -DYOSYSHQ_VERIFIC_EXTENSIONS @@ -646,6 +664,11 @@ OBJS += kernel/cellaigs.o kernel/celledges.o kernel/satgen.o kernel/qcsat.o kern ifeq ($(ENABLE_ZLIB),1) OBJS += kernel/fstdata.o endif +ifeq ($(ENABLE_PLUGINS),1) +ifeq ($(OS), MINGW) +OBJS += libs/dlfcn-win32/dlfcn.o +endif +endif kernel/log.o: CXXFLAGS += -DYOSYS_SRC='"$(YOSYS_SRC)"' kernel/yosys.o: CXXFLAGS += -DYOSYS_DATDIR='"$(DATDIR)"' -DYOSYS_PROGRAM_PREFIX='"$(PROGRAM_PREFIX)"' @@ -724,7 +747,7 @@ yosys.js: $(filter-out yosysjs-$(YOSYS_VER).zip,$(EXTRA_TARGETS)) endif $(PROGRAM_PREFIX)yosys$(EXE): $(OBJS) - $(P) $(LD) -o $(PROGRAM_PREFIX)yosys$(EXE) $(LDFLAGS) $(OBJS) $(LDLIBS) + $(P) $(LD) -o $(PROGRAM_PREFIX)yosys$(EXE) $(EXE_LDFLAGS) $(LDFLAGS) $(OBJS) $(LDLIBS) libyosys.so: $(filter-out kernel/driver.o,$(OBJS)) ifeq ($(OS), Darwin) @@ -767,8 +790,8 @@ LDLIBS_NOVERIFIC = $(LDLIBS) endif $(PROGRAM_PREFIX)yosys-config: misc/yosys-config.in - $(P) $(SED) -e 's#@CXXFLAGS@#$(subst -I. -I"$(YOSYS_SRC)",-I"$(DATDIR)/include",$(strip $(CXXFLAGS_NOVERIFIC)))#;' \ - -e 's#@CXX@#$(strip $(CXX))#;' -e 's#@LDFLAGS@#$(strip $(LDFLAGS) $(PLUGIN_LDFLAGS))#;' -e 's#@LDLIBS@#$(strip $(LDLIBS_NOVERIFIC))#;' \ + $(P) $(SED) -e 's#@CXXFLAGS@#$(subst -Ilibs/dlfcn-win32,,$(subst -I. -I"$(YOSYS_SRC)",-I"$(DATDIR)/include",$(strip $(CXXFLAGS_NOVERIFIC))))#;' \ + -e 's#@CXX@#$(strip $(CXX))#;' -e 's#@LDFLAGS@#$(strip $(LDFLAGS) $(PLUGIN_LDFLAGS))#;' -e 's#@LDLIBS@#$(strip $(LDLIBS_NOVERIFIC) $(PLUGIN_LDLIBS))#;' \ -e 's#@BINDIR@#$(strip $(BINDIR))#;' -e 's#@DATDIR@#$(strip $(DATDIR))#;' < $< > $(PROGRAM_PREFIX)yosys-config $(Q) chmod +x $(PROGRAM_PREFIX)yosys-config @@ -916,6 +939,12 @@ ifeq ($(ENABLE_PYOSYS),1) $(INSTALL_SUDO) cp misc/__init__.py $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys/ endif endif +ifeq ($(ENABLE_PLUGINS),1) +ifeq ($(OS), MINGW) + $(INSTALL_SUDO) mkdir -p $(DESTDIR)$(LIBDIR) + $(INSTALL_SUDO) cp libyosys_exe.a $(DESTDIR)$(LIBDIR)/ +endif +endif uninstall: $(INSTALL_SUDO) rm -vf $(addprefix $(DESTDIR)$(BINDIR)/,$(notdir $(TARGETS))) @@ -1040,12 +1069,10 @@ config-mxe: clean config-msys2-32: clean echo 'CONFIG := msys2-32' > Makefile.conf - echo 'ENABLE_PLUGINS := 0' >> Makefile.conf echo "PREFIX := $(MINGW_PREFIX)" >> Makefile.conf config-msys2-64: clean echo 'CONFIG := msys2-64' > Makefile.conf - echo 'ENABLE_PLUGINS := 0' >> Makefile.conf echo "PREFIX := $(MINGW_PREFIX)" >> Makefile.conf config-cygwin: clean diff --git a/backends/smt2/smtbmc.py b/backends/smt2/smtbmc.py index 4c1f07229..cb21eb3aa 100644 --- a/backends/smt2/smtbmc.py +++ b/backends/smt2/smtbmc.py @@ -453,7 +453,6 @@ assert topmod in smt.modinfo if cexfile is not None: if not got_topt: - assume_skipped = 0 skip_steps = 0 num_steps = 0 @@ -499,7 +498,6 @@ if aimfile is not None: latch_map = dict() if not got_topt: - assume_skipped = 0 skip_steps = 0 num_steps = 0 @@ -633,7 +631,6 @@ if aimfile is not None: if inywfile is not None: if not got_topt: - assume_skipped = 0 skip_steps = 0 num_steps = 0 diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index 71b87755d..6ef563929 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -57,6 +57,11 @@ USING_YOSYS_NAMESPACE #include "edif_file.h" #endif +#ifdef VERIFIC_LIBERTY_SUPPORT +#include "synlib_file.h" +#include "SynlibGroup.h" +#endif + #include "VerificStream.h" #include "FileSystem.h" @@ -2358,6 +2363,9 @@ void verific_import(Design *design, const std::map &par #endif #ifdef VERIFIC_EDIF_SUPPORT edif_file::Reset(); +#endif +#ifdef VERIFIC_LIBERTY_SUPPORT + synlib_file::Reset(); #endif Libset::Reset(); Message::Reset(); @@ -2426,6 +2434,18 @@ struct VerificPass : public Pass { log("Load the specified EDIF files into Verific.\n"); log("\n"); log("\n"); +#endif +#ifdef VERIFIC_LIBERTY_SUPPORT + log(" verific {-liberty} ..\n"); + log("\n"); + log("Load the specified Liberty files into Verific.\n"); + log("Default library when -work is not present is one specified in liberty file.\n"); + log("To use from SystemVerilog or VHDL use -L to specify liberty library."); + log("\n"); + log(" -lib\n"); + log(" only create empty blackbox modules\n"); + log("\n"); + log("\n"); #endif log(" verific {-f|-F} [-vlog95|-vlog2k|-sv2005|-sv2009|\n"); log(" -sv2012|-sv|-formal] \n"); @@ -2535,7 +2555,7 @@ struct VerificPass : public Pass { log("\n"); log(" -cells\n"); log(" Import all cell definitions from Verific loaded libraries even if they are\n"); - log(" unused in design. Useful with \"-edif\" option.\n"); + log(" unused in design. Useful with \"-edif\" and \"-liberty\" option.\n"); log("\n"); log(" -chparam name value \n"); log(" Elaborate the specified top modules (all modules when -all given) using\n"); @@ -2729,6 +2749,7 @@ struct VerificPass : public Pass { int argidx = 1; std::string work = "work"; + bool is_work_set = false; veri_file::RegisterCallBackVerificStream(&verific_read_cb); if (GetSize(args) > argidx && (args[argidx] == "-set-error" || args[argidx] == "-set-warning" || @@ -2813,6 +2834,7 @@ struct VerificPass : public Pass { { if (args[argidx] == "-work" && argidx+1 < GetSize(args)) { work = args[++argidx]; + is_work_set = true; continue; } if (args[argidx] == "-L" && argidx+1 < GetSize(args)) { @@ -3001,6 +3023,42 @@ struct VerificPass : public Pass { } goto check_error; } +#endif +#ifdef VERIFIC_LIBERTY_SUPPORT + if (GetSize(args) > argidx && args[argidx] == "-liberty") { + bool flag_lib = false; + for (argidx++; argidx < GetSize(args); argidx++) { + if (args[argidx] == "-lib") { + flag_lib = true; + continue; + } + if (args[argidx].compare(0, 1, "-") == 0) { + cmd_error(args, argidx, "unknown option"); + goto check_error; + } + break; + } + + while (argidx < GetSize(args)) { + std::string filename = frontent_rewrite(args, argidx, tmp_files); + if (!synlib_file::Read(filename.c_str(), is_work_set ? work.c_str() : nullptr)) + log_cmd_error("Reading `%s' in LIBERTY mode failed.\n", filename.c_str()); + SynlibLibrary *lib = synlib_file::GetLastLibraryAnalyzed(); + if (lib && flag_lib) { + MapIter mi ; + Verific::Cell *c ; + FOREACH_CELL_OF_LIBRARY(lib->GetLibrary(),mi,c) { + MapIter ni ; + Netlist *nl; + FOREACH_NETLIST_OF_CELL(c, ni, nl) { + if (nl) + nl->MakeBlackBox(); + } + } + } + } + goto check_error; + } #endif if (argidx < GetSize(args) && args[argidx] == "-pp") { @@ -3293,6 +3351,9 @@ struct VerificPass : public Pass { #endif #ifdef VERIFIC_EDIF_SUPPORT edif_file::Reset(); +#endif +#ifdef VERIFIC_LIBERTY_SUPPORT + synlib_file::Reset(); #endif Libset::Reset(); Message::Reset(); @@ -3427,6 +3488,14 @@ struct ReadPass : public Pass { log("\n"); log("\n"); #endif + log(" read {-liberty} ..\n"); + log("\n"); + log("Load the specified Liberty files.\n"); + log("\n"); + log(" -lib\n"); + log(" only create empty blackbox modules\n"); + log("\n"); + log("\n"); log(" read {-f|-F} \n"); log("\n"); log("Load and execute the specified command file. (Requires Verific.)\n"); @@ -3531,6 +3600,15 @@ struct ReadPass : public Pass { return; } #endif + if (args[1] == "-liberty") { + if (use_verific) { + args[0] = "verific"; + } else { + args[0] = "read_liberty"; + } + Pass::call(design, args); + return; + } if (args[1] == "-f" || args[1] == "-F") { if (use_verific) { args[0] = "verific"; diff --git a/kernel/calc.cc b/kernel/calc.cc index 0865db526..32e8a49fe 100644 --- a/kernel/calc.cc +++ b/kernel/calc.cc @@ -609,6 +609,36 @@ RTLIL::Const RTLIL::const_neg(const RTLIL::Const &arg1, const RTLIL::Const&, boo return RTLIL::const_sub(zero, arg1_ext, true, signed1, result_len); } +RTLIL::Const RTLIL::const_mux(const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3) +{ + log_assert(arg2.size() == arg1.size()); + if (arg3[0] == State::S0) + return arg1; + else if (arg3[0] == State::S1) + return arg2; + + RTLIL::Const ret = arg1; + for (int i = 0; i < ret.size(); i++) + if (ret[i] != arg2[i]) + ret[i] = State::Sx; + return ret; +} + +RTLIL::Const RTLIL::const_pmux(const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3) +{ + if (arg3.is_fully_zero()) + return arg1; + + if (!arg3.is_onehot()) + return RTLIL::Const(State::Sx, arg1.size()); + + for (int i = 0; i < arg3.size(); i++) + if (arg3[i] == State::S1) + return RTLIL::Const(std::vector(arg2.bits.begin() + i*arg1.bits.size(), arg2.bits.begin() + (i+1)*arg1.bits.size())); + + log_abort(); // unreachable +} + RTLIL::Const RTLIL::const_bmux(const RTLIL::Const &arg1, const RTLIL::Const &arg2) { std::vector t = arg1.bits; diff --git a/kernel/celltypes.h b/kernel/celltypes.h index d62ba1506..e617b4603 100644 --- a/kernel/celltypes.h +++ b/kernel/celltypes.h @@ -488,16 +488,10 @@ struct CellTypes static RTLIL::Const eval(RTLIL::Cell *cell, const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3, bool *errp = nullptr) { - if (cell->type.in(ID($mux), ID($pmux), ID($_MUX_))) { - RTLIL::Const ret = arg1; - for (size_t i = 0; i < arg3.bits.size(); i++) - if (arg3.bits[i] == RTLIL::State::S1) { - std::vector bits(arg2.bits.begin() + i*arg1.bits.size(), arg2.bits.begin() + (i+1)*arg1.bits.size()); - ret = RTLIL::Const(bits); - } - return ret; - } - + if (cell->type.in(ID($mux), ID($_MUX_))) + return const_mux(arg1, arg2, arg3); + if (cell->type == ID($pmux)) + return const_pmux(arg1, arg2, arg3); if (cell->type == ID($_AOI3_)) return eval_not(const_or(const_and(arg1, arg2, false, false, 1), arg3, false, false, 1)); if (cell->type == ID($_OAI3_)) diff --git a/kernel/fstdata.cc b/kernel/fstdata.cc index b2e574b02..1b8043f9a 100644 --- a/kernel/fstdata.cc +++ b/kernel/fstdata.cc @@ -78,7 +78,18 @@ uint64_t FstData::getStartTime() { return fstReaderGetStartTime(ctx); } uint64_t FstData::getEndTime() { return fstReaderGetEndTime(ctx); } +static void normalize_brackets(std::string &str) +{ + for (auto &c : str) { + if (c == '<') + c = '['; + else if (c == '>') + c = ']'; + } +} + fstHandle FstData::getHandle(std::string name) { + normalize_brackets(name); if (name_to_handle.find(name) != name_to_handle.end()) return name_to_handle[name]; else @@ -120,6 +131,7 @@ void FstData::extractVarNames() var.is_reg = (fstVarType)h->u.var.typ == FST_VT_VCD_REG; var.name = remove_spaces(h->u.var.name); var.scope = fst_scope_name; + normalize_brackets(var.scope); var.width = h->u.var.length; vars.push_back(var); if (!var.is_alias) @@ -134,35 +146,34 @@ void FstData::extractVarNames() if (clean_name[0]=='\\') clean_name = clean_name.substr(1); size_t pos = clean_name.find_last_of("<"); - if (pos != std::string::npos) { + if (pos != std::string::npos && clean_name.back() == '>') { std::string mem_cell = clean_name.substr(0, pos); + normalize_brackets(mem_cell); std::string addr = clean_name.substr(pos+1); addr.pop_back(); // remove closing bracket char *endptr; int mem_addr = strtol(addr.c_str(), &endptr, 16); if (*endptr) { - log_warning("Error parsing memory address in : %s\n", clean_name.c_str()); + log_debug("Error parsing memory address in : %s\n", clean_name.c_str()); } else { memory_to_handle[var.scope+"."+mem_cell][mem_addr] = var.id; - name_to_handle[stringf("%s.%s[%d]",var.scope.c_str(),mem_cell.c_str(),mem_addr)] = h->u.var.handle; - continue; } } pos = clean_name.find_last_of("["); - if (pos != std::string::npos) { + if (pos != std::string::npos && clean_name.back() == ']') { std::string mem_cell = clean_name.substr(0, pos); + normalize_brackets(mem_cell); std::string addr = clean_name.substr(pos+1); addr.pop_back(); // remove closing bracket char *endptr; int mem_addr = strtol(addr.c_str(), &endptr, 10); if (*endptr) { - log_warning("Error parsing memory address in : %s\n", clean_name.c_str()); + log_debug("Error parsing memory address in : %s\n", clean_name.c_str()); } else { memory_to_handle[var.scope+"."+mem_cell][mem_addr] = var.id; - name_to_handle[stringf("%s.%s[%d]",var.scope.c_str(),mem_cell.c_str(),mem_addr)] = h->u.var.handle; - continue; } } + normalize_brackets(clean_name); name_to_handle[var.scope+"."+clean_name] = h->u.var.handle; break; } @@ -241,6 +252,7 @@ void FstData::reconstructAllAtTimes(std::vector &signal, uint64_t sta past_data = last_data; callback(last_time); } + past_data = last_data; callback(end_time); } diff --git a/kernel/log.h b/kernel/log.h index 8ef6e6d0e..822816cb4 100644 --- a/kernel/log.h +++ b/kernel/log.h @@ -393,6 +393,11 @@ void log_dump_val_worker(RTLIL::IdString v); void log_dump_val_worker(RTLIL::SigSpec v); void log_dump_val_worker(RTLIL::State v); +template static inline void log_dump_val_worker(dict &v); +template static inline void log_dump_val_worker(pool &v); +template static inline void log_dump_val_worker(std::vector &v); +template static inline void log_dump_val_worker(T *ptr); + template static inline void log_dump_val_worker(dict &v) { log("{"); diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 27ffdff1f..755abf534 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -500,6 +500,8 @@ namespace RTLIL RTLIL::Const const_pos (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); RTLIL::Const const_neg (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); + RTLIL::Const const_mux (const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3); + RTLIL::Const const_pmux (const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3); RTLIL::Const const_bmux (const RTLIL::Const &arg1, const RTLIL::Const &arg2); RTLIL::Const const_demux (const RTLIL::Const &arg1, const RTLIL::Const &arg2); diff --git a/libs/dlfcn-win32/dlfcn.cc b/libs/dlfcn-win32/dlfcn.cc new file mode 100644 index 000000000..03ed34dba --- /dev/null +++ b/libs/dlfcn-win32/dlfcn.cc @@ -0,0 +1,820 @@ +/* + * dlfcn-win32 + * Copyright (c) 2007 Ramiro Polla + * Copyright (c) 2015 Tiancheng "Timothy" Gu + * Copyright (c) 2019 Pali Rohár + * Copyright (c) 2020 Ralf Habacker + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifdef _DEBUG +#define _CRTDBG_MAP_ALLOC +#include +#include +#endif +#include +#include +#include + +/* Older versions do not have this type */ +#if _WIN32_WINNT < 0x0500 +typedef ULONG ULONG_PTR; +#endif + +/* Older SDK versions do not have these macros */ +#ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS +#define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 0x4 +#endif +#ifndef GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT +#define GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT 0x2 +#endif + +#ifdef _MSC_VER +/* https://docs.microsoft.com/en-us/cpp/intrinsics/returnaddress */ +#pragma intrinsic( _ReturnAddress ) +#else +/* https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html */ +#ifndef _ReturnAddress +#define _ReturnAddress( ) ( __builtin_extract_return_addr( __builtin_return_address( 0 ) ) ) +#endif +#endif + +#ifdef DLFCN_WIN32_SHARED +#define DLFCN_WIN32_EXPORTS +#endif +#include "dlfcn.h" + +#if defined( _MSC_VER ) && _MSC_VER >= 1300 +/* https://docs.microsoft.com/en-us/cpp/cpp/noinline */ +#define DLFCN_NOINLINE __declspec( noinline ) +#elif defined( __GNUC__ ) && ( ( __GNUC__ > 3 ) || ( __GNUC__ == 3 && __GNUC_MINOR__ >= 1 ) ) +/* https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html */ +#define DLFCN_NOINLINE __attribute__(( noinline )) +#else +#define DLFCN_NOINLINE +#endif + +/* Note: + * MSDN says these functions are not thread-safe. We make no efforts to have + * any kind of thread safety. + */ + +typedef struct local_object { + HMODULE hModule; + struct local_object *previous; + struct local_object *next; +} local_object; + +static local_object first_object; + +/* These functions implement a double linked list for the local objects. */ +static local_object *local_search( HMODULE hModule ) +{ + local_object *pobject; + + if( hModule == NULL ) + return NULL; + + for( pobject = &first_object; pobject; pobject = pobject->next ) + if( pobject->hModule == hModule ) + return pobject; + + return NULL; +} + +static BOOL local_add( HMODULE hModule ) +{ + local_object *pobject; + local_object *nobject; + + if( hModule == NULL ) + return TRUE; + + pobject = local_search( hModule ); + + /* Do not add object again if it's already on the list */ + if( pobject != NULL ) + return TRUE; + + for( pobject = &first_object; pobject->next; pobject = pobject->next ); + + nobject = (local_object *) malloc( sizeof( local_object ) ); + + if( !nobject ) + return FALSE; + + pobject->next = nobject; + nobject->next = NULL; + nobject->previous = pobject; + nobject->hModule = hModule; + + return TRUE; +} + +static void local_rem( HMODULE hModule ) +{ + local_object *pobject; + + if( hModule == NULL ) + return; + + pobject = local_search( hModule ); + + if( pobject == NULL ) + return; + + if( pobject->next ) + pobject->next->previous = pobject->previous; + if( pobject->previous ) + pobject->previous->next = pobject->next; + + free( pobject ); +} + +/* POSIX says dlerror( ) doesn't have to be thread-safe, so we use one + * static buffer. + * MSDN says the buffer cannot be larger than 64K bytes, so we set it to + * the limit. + */ +static char error_buffer[65535]; +static BOOL error_occurred; + +static void save_err_str( const char *str, DWORD dwMessageId ) +{ + DWORD ret; + size_t pos, len; + + len = strlen( str ); + if( len > sizeof( error_buffer ) - 5 ) + len = sizeof( error_buffer ) - 5; + + /* Format error message to: + * "": + */ + pos = 0; + error_buffer[pos++] = '"'; + memcpy( error_buffer + pos, str, len ); + pos += len; + error_buffer[pos++] = '"'; + error_buffer[pos++] = ':'; + error_buffer[pos++] = ' '; + + ret = FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwMessageId, + MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), + error_buffer + pos, (DWORD) ( sizeof( error_buffer ) - pos ), NULL ); + pos += ret; + + /* When FormatMessageA() fails it returns zero and does not touch buffer + * so add trailing null byte */ + if( ret == 0 ) + error_buffer[pos] = '\0'; + + if( pos > 1 ) + { + /* POSIX says the string must not have trailing */ + if( error_buffer[pos-2] == '\r' && error_buffer[pos-1] == '\n' ) + error_buffer[pos-2] = '\0'; + } + + error_occurred = TRUE; +} + +static void save_err_ptr_str( const void *ptr, DWORD dwMessageId ) +{ + char ptr_buf[2 + 2 * sizeof( ptr ) + 1]; + char num; + size_t i; + + ptr_buf[0] = '0'; + ptr_buf[1] = 'x'; + + for( i = 0; i < 2 * sizeof( ptr ); i++ ) + { + num = (char) ( ( ( (ULONG_PTR) ptr ) >> ( 8 * sizeof( ptr ) - 4 * ( i + 1 ) ) ) & 0xF ); + ptr_buf[2 + i] = num + ( ( num < 0xA ) ? '0' : ( 'A' - 0xA ) ); + } + + ptr_buf[2 + 2 * sizeof( ptr )] = 0; + + save_err_str( ptr_buf, dwMessageId ); +} + +static UINT MySetErrorMode( UINT uMode ) +{ + static BOOL (WINAPI *SetThreadErrorModePtr)(DWORD, DWORD *) = NULL; + static BOOL failed = FALSE; + HMODULE kernel32; + DWORD oldMode; + + if( !failed && SetThreadErrorModePtr == NULL ) + { + kernel32 = GetModuleHandleA( "Kernel32.dll" ); + if( kernel32 != NULL ) + SetThreadErrorModePtr = (BOOL (WINAPI *)(DWORD, DWORD *)) (LPVOID) GetProcAddress( kernel32, "SetThreadErrorMode" ); + if( SetThreadErrorModePtr == NULL ) + failed = TRUE; + } + + if( !failed ) + { + if( !SetThreadErrorModePtr( uMode, &oldMode ) ) + return 0; + else + return oldMode; + } + else + { + return SetErrorMode( uMode ); + } +} + +static HMODULE MyGetModuleHandleFromAddress( const void *addr ) +{ + static BOOL (WINAPI *GetModuleHandleExAPtr)(DWORD, LPCSTR, HMODULE *) = NULL; + static BOOL failed = FALSE; + HMODULE kernel32; + HMODULE hModule; + MEMORY_BASIC_INFORMATION info; + SIZE_T sLen; + + if( !failed && GetModuleHandleExAPtr == NULL ) + { + kernel32 = GetModuleHandleA( "Kernel32.dll" ); + if( kernel32 != NULL ) + GetModuleHandleExAPtr = (BOOL (WINAPI *)(DWORD, LPCSTR, HMODULE *)) (LPVOID) GetProcAddress( kernel32, "GetModuleHandleExA" ); + if( GetModuleHandleExAPtr == NULL ) + failed = TRUE; + } + + if( !failed ) + { + /* If GetModuleHandleExA is available use it with GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS */ + if( !GetModuleHandleExAPtr( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (const char *)addr, &hModule ) ) + return NULL; + } + else + { + /* To get HMODULE from address use undocumented hack from https://stackoverflow.com/a/2396380 + * The HMODULE of a DLL is the same value as the module's base address. + */ + sLen = VirtualQuery( addr, &info, sizeof( info ) ); + if( sLen != sizeof( info ) ) + return NULL; + hModule = (HMODULE) info.AllocationBase; + } + + return hModule; +} + +/* Load Psapi.dll at runtime, this avoids linking caveat */ +static BOOL MyEnumProcessModules( HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded ) +{ + static BOOL (WINAPI *EnumProcessModulesPtr)(HANDLE, HMODULE *, DWORD, LPDWORD) = NULL; + static BOOL failed = FALSE; + UINT uMode; + HMODULE psapi; + + if( failed ) + return FALSE; + + if( EnumProcessModulesPtr == NULL ) + { + /* Windows 7 and newer versions have K32EnumProcessModules in Kernel32.dll which is always pre-loaded */ + psapi = GetModuleHandleA( "Kernel32.dll" ); + if( psapi != NULL ) + EnumProcessModulesPtr = (BOOL (WINAPI *)(HANDLE, HMODULE *, DWORD, LPDWORD)) (LPVOID) GetProcAddress( psapi, "K32EnumProcessModules" ); + + /* Windows Vista and older version have EnumProcessModules in Psapi.dll which needs to be loaded */ + if( EnumProcessModulesPtr == NULL ) + { + /* Do not let Windows display the critical-error-handler message box */ + uMode = MySetErrorMode( SEM_FAILCRITICALERRORS ); + psapi = LoadLibraryA( "Psapi.dll" ); + if( psapi != NULL ) + { + EnumProcessModulesPtr = (BOOL (WINAPI *)(HANDLE, HMODULE *, DWORD, LPDWORD)) (LPVOID) GetProcAddress( psapi, "EnumProcessModules" ); + if( EnumProcessModulesPtr == NULL ) + FreeLibrary( psapi ); + } + MySetErrorMode( uMode ); + } + + if( EnumProcessModulesPtr == NULL ) + { + failed = TRUE; + return FALSE; + } + } + + return EnumProcessModulesPtr( hProcess, lphModule, cb, lpcbNeeded ); +} + +DLFCN_EXPORT +void *dlopen( const char *file, int mode ) +{ + HMODULE hModule; + UINT uMode; + + error_occurred = FALSE; + + /* Do not let Windows display the critical-error-handler message box */ + uMode = MySetErrorMode( SEM_FAILCRITICALERRORS ); + + if( file == NULL ) + { + /* POSIX says that if the value of file is NULL, a handle on a global + * symbol object must be provided. That object must be able to access + * all symbols from the original program file, and any objects loaded + * with the RTLD_GLOBAL flag. + * The return value from GetModuleHandle( ) allows us to retrieve + * symbols only from the original program file. EnumProcessModules() is + * used to access symbols from other libraries. For objects loaded + * with the RTLD_LOCAL flag, we create our own list later on. They are + * excluded from EnumProcessModules() iteration. + */ + hModule = GetModuleHandle( NULL ); + + if( !hModule ) + save_err_str( "(null)", GetLastError( ) ); + } + else + { + HANDLE hCurrentProc; + DWORD dwProcModsBefore, dwProcModsAfter; + char lpFileName[MAX_PATH]; + size_t i, len; + + len = strlen( file ); + + if( len >= sizeof( lpFileName ) ) + { + save_err_str( file, ERROR_FILENAME_EXCED_RANGE ); + hModule = NULL; + } + else + { + /* MSDN says backslashes *must* be used instead of forward slashes. */ + for( i = 0; i < len; i++ ) + { + if( file[i] == '/' ) + lpFileName[i] = '\\'; + else + lpFileName[i] = file[i]; + } + lpFileName[len] = '\0'; + + hCurrentProc = GetCurrentProcess( ); + + if( MyEnumProcessModules( hCurrentProc, NULL, 0, &dwProcModsBefore ) == 0 ) + dwProcModsBefore = 0; + + /* POSIX says the search path is implementation-defined. + * LOAD_WITH_ALTERED_SEARCH_PATH is used to make it behave more closely + * to UNIX's search paths (start with system folders instead of current + * folder). + */ + hModule = LoadLibraryExA( lpFileName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH ); + + if( !hModule ) + { + save_err_str( lpFileName, GetLastError( ) ); + } + else + { + if( MyEnumProcessModules( hCurrentProc, NULL, 0, &dwProcModsAfter ) == 0 ) + dwProcModsAfter = 0; + + /* If the object was loaded with RTLD_LOCAL, add it to list of local + * objects, so that its symbols cannot be retrieved even if the handle for + * the original program file is passed. POSIX says that if the same + * file is specified in multiple invocations, and any of them are + * RTLD_GLOBAL, even if any further invocations use RTLD_LOCAL, the + * symbols will remain global. If number of loaded modules was not + * changed after calling LoadLibraryEx(), it means that library was + * already loaded. + */ + if( (mode & RTLD_LOCAL) && dwProcModsBefore != dwProcModsAfter ) + { + if( !local_add( hModule ) ) + { + save_err_str( lpFileName, ERROR_NOT_ENOUGH_MEMORY ); + FreeLibrary( hModule ); + hModule = NULL; + } + } + else if( !(mode & RTLD_LOCAL) && dwProcModsBefore == dwProcModsAfter ) + { + local_rem( hModule ); + } + } + } + } + + /* Return to previous state of the error-mode bit flags. */ + MySetErrorMode( uMode ); + + return (void *) hModule; +} + +DLFCN_EXPORT +int dlclose( void *handle ) +{ + HMODULE hModule = (HMODULE) handle; + BOOL ret; + + error_occurred = FALSE; + + ret = FreeLibrary( hModule ); + + /* If the object was loaded with RTLD_LOCAL, remove it from list of local + * objects. + */ + if( ret ) + local_rem( hModule ); + else + save_err_ptr_str( handle, GetLastError( ) ); + + /* dlclose's return value in inverted in relation to FreeLibrary's. */ + ret = !ret; + + return (int) ret; +} + +DLFCN_NOINLINE /* Needed for _ReturnAddress() */ +DLFCN_EXPORT +void *dlsym( void *handle, const char *name ) +{ + FARPROC symbol; + HMODULE hCaller; + HMODULE hModule; + DWORD dwMessageId; + + error_occurred = FALSE; + + symbol = NULL; + hCaller = NULL; + hModule = GetModuleHandle( NULL ); + dwMessageId = 0; + + if( handle == RTLD_DEFAULT ) + { + /* The symbol lookup happens in the normal global scope; that is, + * a search for a symbol using this handle would find the same + * definition as a direct use of this symbol in the program code. + * So use same lookup procedure as when filename is NULL. + */ + handle = hModule; + } + else if( handle == RTLD_NEXT ) + { + /* Specifies the next object after this one that defines name. + * This one refers to the object containing the invocation of dlsym(). + * The next object is the one found upon the application of a load + * order symbol resolution algorithm. To get caller function of dlsym() + * use _ReturnAddress() intrinsic. To get HMODULE of caller function + * use MyGetModuleHandleFromAddress() which calls either standard + * GetModuleHandleExA() function or hack via VirtualQuery(). + */ + hCaller = MyGetModuleHandleFromAddress( _ReturnAddress( ) ); + + if( hCaller == NULL ) + { + dwMessageId = ERROR_INVALID_PARAMETER; + goto end; + } + } + + if( handle != RTLD_NEXT ) + { + symbol = GetProcAddress( (HMODULE) handle, name ); + + if( symbol != NULL ) + goto end; + } + + /* If the handle for the original program file is passed, also search + * in all globally loaded objects. + */ + + if( hModule == handle || handle == RTLD_NEXT ) + { + HANDLE hCurrentProc; + HMODULE *modules; + DWORD cbNeeded; + DWORD dwSize; + size_t i; + + hCurrentProc = GetCurrentProcess( ); + + /* GetModuleHandle( NULL ) only returns the current program file. So + * if we want to get ALL loaded module including those in linked DLLs, + * we have to use EnumProcessModules( ). + */ + if( MyEnumProcessModules( hCurrentProc, NULL, 0, &dwSize ) != 0 ) + { + modules = (HMODULE *)malloc( dwSize ); + if( modules ) + { + if( MyEnumProcessModules( hCurrentProc, modules, dwSize, &cbNeeded ) != 0 && dwSize == cbNeeded ) + { + for( i = 0; i < dwSize / sizeof( HMODULE ); i++ ) + { + if( handle == RTLD_NEXT && hCaller ) + { + /* Next modules can be used for RTLD_NEXT */ + if( hCaller == modules[i] ) + hCaller = NULL; + continue; + } + if( local_search( modules[i] ) ) + continue; + symbol = GetProcAddress( modules[i], name ); + if( symbol != NULL ) + { + free( modules ); + goto end; + } + } + + } + free( modules ); + } + else + { + dwMessageId = ERROR_NOT_ENOUGH_MEMORY; + goto end; + } + } + } + +end: + if( symbol == NULL ) + { + if( !dwMessageId ) + dwMessageId = ERROR_PROC_NOT_FOUND; + save_err_str( name, dwMessageId ); + } + + return *(void **) (&symbol); +} + +DLFCN_EXPORT +char *dlerror( void ) +{ + /* If this is the second consecutive call to dlerror, return NULL */ + if( !error_occurred ) + return NULL; + + /* POSIX says that invoking dlerror( ) a second time, immediately following + * a prior invocation, shall result in NULL being returned. + */ + error_occurred = FALSE; + + return error_buffer; +} + +/* See https://docs.microsoft.com/en-us/archive/msdn-magazine/2002/march/inside-windows-an-in-depth-look-into-the-win32-portable-executable-file-format-part-2 + * for details */ + +/* Get specific image section */ +static BOOL get_image_section( HMODULE module, int index, void **ptr, DWORD *size ) +{ + IMAGE_DOS_HEADER *dosHeader; + IMAGE_NT_HEADERS *ntHeaders; + IMAGE_OPTIONAL_HEADER *optionalHeader; + + dosHeader = (IMAGE_DOS_HEADER *) module; + + if( dosHeader->e_magic != IMAGE_DOS_SIGNATURE ) + return FALSE; + + ntHeaders = (IMAGE_NT_HEADERS *) ( (BYTE *) dosHeader + dosHeader->e_lfanew ); + + if( ntHeaders->Signature != IMAGE_NT_SIGNATURE ) + return FALSE; + + optionalHeader = &ntHeaders->OptionalHeader; + + if( optionalHeader->Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC ) + return FALSE; + + if( index < 0 || index > IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR ) + return FALSE; + + if( optionalHeader->DataDirectory[index].Size == 0 || optionalHeader->DataDirectory[index].VirtualAddress == 0 ) + return FALSE; + + if( size != NULL ) + *size = optionalHeader->DataDirectory[index].Size; + + *ptr = (void *)( (BYTE *) module + optionalHeader->DataDirectory[index].VirtualAddress ); + + return TRUE; +} + +/* Return symbol name for a given address from export table */ +static const char *get_export_symbol_name( HMODULE module, IMAGE_EXPORT_DIRECTORY *ied, const void *addr, void **func_address ) +{ + DWORD i; + void *candidateAddr = NULL; + int candidateIndex = -1; + BYTE *base = (BYTE *) module; + DWORD *functionAddressesOffsets = (DWORD *) (base + ied->AddressOfFunctions); + DWORD *functionNamesOffsets = (DWORD *) (base + ied->AddressOfNames); + USHORT *functionNameOrdinalsIndexes = (USHORT *) (base + ied->AddressOfNameOrdinals); + + for( i = 0; i < ied->NumberOfFunctions; i++ ) + { + if( (void *) ( base + functionAddressesOffsets[i] ) > addr || candidateAddr >= (void *) ( base + functionAddressesOffsets[i] ) ) + continue; + + candidateAddr = (void *) ( base + functionAddressesOffsets[i] ); + candidateIndex = i; + } + + if( candidateIndex == -1 ) + return NULL; + + *func_address = candidateAddr; + + for( i = 0; i < ied->NumberOfNames; i++ ) + { + if( functionNameOrdinalsIndexes[i] == candidateIndex ) + return (const char *) ( base + functionNamesOffsets[i] ); + } + + return NULL; +} + +static BOOL is_valid_address( const void *addr ) +{ + MEMORY_BASIC_INFORMATION info; + SIZE_T result; + + if( addr == NULL ) + return FALSE; + + /* check valid pointer */ + result = VirtualQuery( addr, &info, sizeof( info ) ); + + if( result == 0 || info.AllocationBase == NULL || info.AllocationProtect == 0 || info.AllocationProtect == PAGE_NOACCESS ) + return FALSE; + + return TRUE; +} + +/* Return state if address points to an import thunk + * + * An import thunk is setup with a 'jmp' instruction followed by an + * absolute address (32bit) or relative offset (64bit) pointing into + * the import address table (iat), which is partially maintained by + * the runtime linker. + */ +static BOOL is_import_thunk( const void *addr ) +{ + return *(short *) addr == 0x25ff ? TRUE : FALSE; +} + +/* Return adress from the import address table (iat), + * if the original address points to a thunk table entry. + */ +static void *get_address_from_import_address_table( void *iat, DWORD iat_size, const void *addr ) +{ + BYTE *thkp = (BYTE *) addr; + /* Get offset from thunk table (after instruction 0xff 0x25) + * 4018c8 <_VirtualQuery>: ff 25 4a 8a 00 00 + */ + ULONG offset = *(ULONG *)( thkp + 2 ); +#ifdef _WIN64 + /* On 64 bit the offset is relative + * 4018c8: ff 25 4a 8a 00 00 jmpq *0x8a4a(%rip) # 40a318 <__imp_VirtualQuery> + * And can be also negative (MSVC in WDK) + * 100002f20: ff 25 3a e1 ff ff jmpq *-0x1ec6(%rip) # 0x100001060 + * So cast to signed LONG type + */ + BYTE *ptr = (BYTE *)( thkp + 6 + (LONG) offset ); +#else + /* On 32 bit the offset is absolute + * 4019b4: ff 25 90 71 40 00 jmp *0x40719 + */ + BYTE *ptr = (BYTE *) offset; +#endif + + if( !is_valid_address( ptr ) || ptr < (BYTE *) iat || ptr > (BYTE *) iat + iat_size ) + return NULL; + + return *(void **) ptr; +} + +/* Holds module filename */ +static char module_filename[2*MAX_PATH]; + +static BOOL fill_info( const void *addr, Dl_info *info ) +{ + HMODULE hModule; + DWORD dwSize; + IMAGE_EXPORT_DIRECTORY *ied; + void *funcAddress = NULL; + + /* Get module of the specified address */ + hModule = MyGetModuleHandleFromAddress( addr ); + + if( hModule == NULL ) + return FALSE; + + dwSize = GetModuleFileNameA( hModule, module_filename, sizeof( module_filename ) ); + + if( dwSize == 0 || dwSize == sizeof( module_filename ) ) + return FALSE; + + info->dli_fname = module_filename; + info->dli_fbase = (void *) hModule; + + /* Find function name and function address in module's export table */ + if( get_image_section( hModule, IMAGE_DIRECTORY_ENTRY_EXPORT, (void **) &ied, NULL ) ) + info->dli_sname = get_export_symbol_name( hModule, ied, addr, &funcAddress ); + else + info->dli_sname = NULL; + + info->dli_saddr = info->dli_sname == NULL ? NULL : funcAddress != NULL ? funcAddress : (void *) addr; + + return TRUE; +} + +DLFCN_EXPORT +int dladdr( const void *addr, Dl_info *info ) +{ + if( info == NULL ) + return 0; + + if( !is_valid_address( addr ) ) + return 0; + + if( is_import_thunk( addr ) ) + { + void *iat; + DWORD iatSize; + HMODULE hModule; + + /* Get module of the import thunk address */ + hModule = MyGetModuleHandleFromAddress( addr ); + + if( hModule == NULL ) + return 0; + + if( !get_image_section( hModule, IMAGE_DIRECTORY_ENTRY_IAT, &iat, &iatSize ) ) + { + /* Fallback for cases where the iat is not defined, + * for example i586-mingw32msvc-gcc */ + IMAGE_IMPORT_DESCRIPTOR *iid; + DWORD iidSize; + + if( !get_image_section( hModule, IMAGE_DIRECTORY_ENTRY_IMPORT, (void **) &iid, &iidSize ) ) + return 0; + + if( iid == NULL || iid->Characteristics == 0 || iid->FirstThunk == 0 ) + return 0; + + iat = (void *)( (BYTE *) hModule + iid->FirstThunk ); + /* We assume that in this case iid and iat's are in linear order */ + iatSize = iidSize - (DWORD) ( (BYTE *) iat - (BYTE *) iid ); + } + + addr = get_address_from_import_address_table( iat, iatSize, addr ); + + if( !is_valid_address( addr ) ) + return 0; + } + + if( !fill_info( addr, info ) ) + return 0; + + return 1; +} + +#ifdef DLFCN_WIN32_SHARED +BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved ) +{ + (void) hinstDLL; + (void) fdwReason; + (void) lpvReserved; + return TRUE; +} +#endif diff --git a/libs/dlfcn-win32/dlfcn.h b/libs/dlfcn-win32/dlfcn.h new file mode 100644 index 000000000..bf5c7d4d1 --- /dev/null +++ b/libs/dlfcn-win32/dlfcn.h @@ -0,0 +1,94 @@ +/* + * dlfcn-win32 + * Copyright (c) 2007 Ramiro Polla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef DLFCN_H +#define DLFCN_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(DLFCN_WIN32_SHARED) +#if defined(DLFCN_WIN32_EXPORTS) +# define DLFCN_EXPORT __declspec(dllexport) +#else +# define DLFCN_EXPORT __declspec(dllimport) +#endif +#else +# define DLFCN_EXPORT +#endif + +/* Relocations are performed when the object is loaded. */ +#define RTLD_NOW 0 + +/* Relocations are performed at an implementation-defined time. + * Windows API does not support lazy symbol resolving (when first reference + * to a given symbol occurs). So RTLD_LAZY implementation is same as RTLD_NOW. + */ +#define RTLD_LAZY RTLD_NOW + +/* All symbols are available for relocation processing of other modules. */ +#define RTLD_GLOBAL (1 << 1) + +/* All symbols are not made available for relocation processing by other modules. */ +#define RTLD_LOCAL (1 << 2) + +/* These two were added in The Open Group Base Specifications Issue 6. + * Note: All other RTLD_* flags in any dlfcn.h are not standard compliant. + */ + +/* The symbol lookup happens in the normal global scope. */ +#define RTLD_DEFAULT ((void *)0) + +/* Specifies the next object after this one that defines name. */ +#define RTLD_NEXT ((void *)-1) + +/* Structure filled in by dladdr() */ +typedef struct dl_info +{ + const char *dli_fname; /* Filename of defining object (thread unsafe and reused on every call to dladdr) */ + void *dli_fbase; /* Load address of that object */ + const char *dli_sname; /* Name of nearest lower symbol */ + void *dli_saddr; /* Exact value of nearest symbol */ +} Dl_info; + +/* Open a symbol table handle. */ +DLFCN_EXPORT void *dlopen(const char *file, int mode); + +/* Close a symbol table handle. */ +DLFCN_EXPORT int dlclose(void *handle); + +/* Get the address of a symbol from a symbol table handle. */ +DLFCN_EXPORT void *dlsym(void *handle, const char *name); + +/* Get diagnostic information. */ +DLFCN_EXPORT char *dlerror(void); + +/* Translate address to symbolic information (no POSIX standard) */ +DLFCN_EXPORT int dladdr(const void *addr, Dl_info *info); + +#ifdef __cplusplus +} +#endif + +#endif /* DLFCN_H */ diff --git a/libs/fst/fstapi.cc b/libs/fst/fstapi.cc index b79470db3..da0d959a0 100644 --- a/libs/fst/fstapi.cc +++ b/libs/fst/fstapi.cc @@ -3607,6 +3607,7 @@ static int fstReaderRecreateHierFile(struct fstReaderContext *xc) fflush(xc->f); #endif zfd = dup(fileno(xc->f)); + lseek(zfd, ftell(xc->f), SEEK_SET); zhandle = gzdopen(zfd, "rb"); if (!zhandle) { close(zfd); @@ -4272,6 +4273,7 @@ int fstReaderInit(struct fstReaderContext *xc) #endif zfd = dup(fileno(xc->f)); + lseek(zfd, ftell(xc->f), SEEK_SET); zhandle = gzdopen(zfd, "rb"); if (zhandle) { for (offpnt = 0; offpnt < uclen; offpnt += FST_GZIO_LEN) { diff --git a/manual/command-reference-manual.tex b/manual/command-reference-manual.tex index 671fabb81..17689c141 100644 --- a/manual/command-reference-manual.tex +++ b/manual/command-reference-manual.tex @@ -902,6 +902,11 @@ widening it to 1-bit, or removing the cell altogether. This command replaces clocked flip-flops with generic $ff cells that use the implicit global clock. This is useful for formal verification of designs with multiple clocks. + +This pass assumes negative hold time for the async FF inputs. For example when +a reset deasserts with the clock edge, then the FF output will still drive the +reset value in the next cycle regardless of the data-in value at the time of +the clock edge. \end{lstlisting} \section{clkbufmap -- insert clock buffers on clock networks} @@ -1554,12 +1559,17 @@ after an optimization pass. -undef enable modelling of undef states during equiv_induct. + -nocheck + disable running check before and after the command under test. + The following commands are executed by this verification command: run_pass: hierarchy -auto-top design -save preopt + check -assert (unless -nocheck) [command] + check -assert (unless -nocheck) design -stash postopt prepare: @@ -3122,6 +3132,12 @@ detected. call 'flatten -wb; opt_expr -keepdc -undriven;;' on the miter circuit. + -cross + allow output ports on the gold module to match input ports on the + gate module. This is useful when the gold module contains additional + logic to drive some of the gate module inputs. + + miter -assert [options] module [miter_name] Creates a miter circuit for property checking. All input ports are kept, @@ -3989,6 +4005,19 @@ the language version (and before file names) to set additional verilog defines. Load the specified VHDL files. (Requires Verific.) + read {-edif} .. + +Load the specified EDIF files. (Requires Verific.) + + + read {-liberty} .. + +Load the specified Liberty files. + + -lib + only create empty blackbox modules + + read {-f|-F} Load and execute the specified command file. (Requires Verific.) @@ -7926,6 +7955,20 @@ Like -sv, but define FORMAL instead of SYNTHESIS. Load the specified VHDL files into Verific. + verific {-edif} .. + +Load the specified EDIF files into Verific. + + + verific {-liberty} .. + +Load the specified Liberty files into Verific. +Default library when -work is not present is one specified in liberty file. +To use from SystemVerilog or VHDL use -L to specify liberty library. + -lib + only create empty blackbox modules + + verific {-f|-F} [-vlog95|-vlog2k|-sv2005|-sv2009| -sv2012|-sv|-formal] @@ -8032,6 +8075,10 @@ Import options: -fullinit Keep all register initializations, even those for non-FF registers. + -cells + Import all cell definitions from Verific loaded libraries even if they are + unused in design. Useful with "-edif" and "-liberty" option. + -chparam name value Elaborate the specified top modules (all modules when -all given) using this parameter value. Modules on which this parameter does not exist will diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc index be0cd470b..9cc0170dc 100644 --- a/passes/opt/opt_expr.cc +++ b/passes/opt/opt_expr.cc @@ -1494,7 +1494,7 @@ skip_identity: RTLIL::SigSpec input = assign_map(cell->getPort(ID::S)); RTLIL::SigSpec inA = assign_map(cell->getPort(ID::A)); RTLIL::SigSpec inB = assign_map(cell->getPort(ID::B)); - if (input.is_fully_const()) + if (input.is_fully_const() && (!keepdc || input.is_fully_def())) ACTION_DO(ID::Y, input.as_bool() ? cell->getPort(ID::B) : cell->getPort(ID::A)); else if (inA == inB) ACTION_DO(ID::Y, cell->getPort(ID::A)); diff --git a/passes/sat/miter.cc b/passes/sat/miter.cc index 37efadfbd..bf33c9ce3 100644 --- a/passes/sat/miter.cc +++ b/passes/sat/miter.cc @@ -31,6 +31,7 @@ void create_miter_equiv(struct Pass *that, std::vector args, RTLIL: bool flag_make_outcmp = false; bool flag_make_assert = false; bool flag_flatten = false; + bool flag_cross = false; log_header(design, "Executing MITER pass (creating miter circuit).\n"); @@ -57,6 +58,10 @@ void create_miter_equiv(struct Pass *that, std::vector args, RTLIL: flag_flatten = true; continue; } + if (args[argidx] == "-cross") { + flag_cross = true; + continue; + } break; } if (argidx+3 != args.size() || args[argidx].compare(0, 1, "-") == 0) @@ -75,6 +80,7 @@ void create_miter_equiv(struct Pass *that, std::vector args, RTLIL: RTLIL::Module *gold_module = design->module(gold_name); RTLIL::Module *gate_module = design->module(gate_name); + pool gold_cross_ports; for (auto gold_wire : gold_module->wires()) { if (gold_wire->port_id == 0) @@ -82,12 +88,17 @@ void create_miter_equiv(struct Pass *that, std::vector args, RTLIL: RTLIL::Wire *gate_wire = gate_module->wire(gold_wire->name); if (gate_wire == nullptr) goto match_gold_port_error; + if (gold_wire->width != gate_wire->width) + goto match_gold_port_error; + if (flag_cross && !gold_wire->port_input && gold_wire->port_output && + gate_wire->port_input && !gate_wire->port_output) { + gold_cross_ports.insert(gold_wire); + continue; + } if (gold_wire->port_input != gate_wire->port_input) goto match_gold_port_error; if (gold_wire->port_output != gate_wire->port_output) goto match_gold_port_error; - if (gold_wire->width != gate_wire->width) - goto match_gold_port_error; continue; match_gold_port_error: log_cmd_error("No matching port in gate module was found for %s!\n", gold_wire->name.c_str()); @@ -99,12 +110,15 @@ void create_miter_equiv(struct Pass *that, std::vector args, RTLIL: RTLIL::Wire *gold_wire = gold_module->wire(gate_wire->name); if (gold_wire == nullptr) goto match_gate_port_error; + if (gate_wire->width != gold_wire->width) + goto match_gate_port_error; + if (flag_cross && !gold_wire->port_input && gold_wire->port_output && + gate_wire->port_input && !gate_wire->port_output) + continue; if (gate_wire->port_input != gold_wire->port_input) goto match_gate_port_error; if (gate_wire->port_output != gold_wire->port_output) goto match_gate_port_error; - if (gate_wire->width != gold_wire->width) - goto match_gate_port_error; continue; match_gate_port_error: log_cmd_error("No matching port in gold module was found for %s!\n", gate_wire->name.c_str()); @@ -123,6 +137,14 @@ void create_miter_equiv(struct Pass *that, std::vector args, RTLIL: for (auto gold_wire : gold_module->wires()) { + if (gold_cross_ports.count(gold_wire)) + { + RTLIL::Wire *w = miter_module->addWire("\\cross_" + RTLIL::unescape_id(gold_wire->name), gold_wire->width); + gold_cell->setPort(gold_wire->name, w); + gate_cell->setPort(gold_wire->name, w); + continue; + } + if (gold_wire->port_input) { RTLIL::Wire *w = miter_module->addWire("\\in_" + RTLIL::unescape_id(gold_wire->name), gold_wire->width); @@ -384,6 +406,12 @@ struct MiterPass : public Pass { log(" call 'flatten -wb; opt_expr -keepdc -undriven;;' on the miter circuit.\n"); log("\n"); log("\n"); + log(" -cross\n"); + log(" allow output ports on the gold module to match input ports on the\n"); + log(" gate module. This is useful when the gold module contains additional\n"); + log(" logic to drive some of the gate module inputs.\n"); + log("\n"); + log("\n"); log(" miter -assert [options] module [miter_name]\n"); log("\n"); log("Creates a miter circuit for property checking. All input ports are kept,\n"); diff --git a/passes/sat/sat.cc b/passes/sat/sat.cc index df2725b3c..e10517d72 100644 --- a/passes/sat/sat.cc +++ b/passes/sat/sat.cc @@ -758,6 +758,7 @@ struct SatHelper if (last_timestep == -2) log(" no model variables selected for display.\n"); + fprintf(f, "#%d\n", last_timestep+1); fclose(f); } diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc index b68783f20..3ac431223 100644 --- a/passes/sat/sim.cc +++ b/passes/sat/sim.cc @@ -813,18 +813,6 @@ struct SimInstance std::string v = shared->fst->valueOf(item.second); did_something |= set_state(item.first, Const::from_string(v)); } - for (auto &it : ff_database) - { - ff_state_t &ff = it.second; - SigSpec dsig = it.second.data.sig_d; - Const value = get_state(dsig); - if (dsig.is_wire()) { - ff.past_d = value; - if (ff.data.has_aload) - ff.past_ad = value; - did_something |= true; - } - } for (auto cell : module->cells()) { if (cell->is_mem_cell()) { @@ -1019,6 +1007,16 @@ struct SimWorker : SimShared top->update_ph3(); } + void initialize_stable_past() + { + if (debug) + log("\n-- ph1 (initialize) --\n"); + top->update_ph1(); + if (debug) + log("\n-- ph3 (initialize) --\n"); + top->update_ph3(); + } + void set_inports(pool ports, State value) { for (auto portname : ports) @@ -1191,6 +1189,7 @@ struct SimWorker : SimShared if (initial) { did_something |= top->setInitState(); + initialize_stable_past(); initial = false; } if (did_something) diff --git a/techlibs/common/Makefile.inc b/techlibs/common/Makefile.inc index 607e772a2..47f1ed604 100644 --- a/techlibs/common/Makefile.inc +++ b/techlibs/common/Makefile.inc @@ -22,6 +22,7 @@ kernel/register.o: techlibs/common/simlib_help.inc techlibs/common/simcells_help $(eval $(call add_share_file,share,techlibs/common/simlib.v)) $(eval $(call add_share_file,share,techlibs/common/simcells.v)) $(eval $(call add_share_file,share,techlibs/common/techmap.v)) +$(eval $(call add_share_file,share,techlibs/common/smtmap.v)) $(eval $(call add_share_file,share,techlibs/common/pmux2mux.v)) $(eval $(call add_share_file,share,techlibs/common/adff2dff.v)) $(eval $(call add_share_file,share,techlibs/common/dff2ff.v)) diff --git a/techlibs/common/simlib.v b/techlibs/common/simlib.v index ab9bd7e1d..e64697efb 100644 --- a/techlibs/common/simlib.v +++ b/techlibs/common/simlib.v @@ -1279,14 +1279,9 @@ parameter WIDTH = 0; input [WIDTH-1:0] A, B; input S; -output reg [WIDTH-1:0] Y; +output [WIDTH-1:0] Y; -always @* begin - if (S) - Y = B; - else - Y = A; -end +assign Y = S ? B : A; endmodule diff --git a/techlibs/common/smtmap.v b/techlibs/common/smtmap.v new file mode 100644 index 000000000..8c7503dc8 --- /dev/null +++ b/techlibs/common/smtmap.v @@ -0,0 +1,28 @@ +(* techmap_celltype = "$pmux" *) +module smt_pmux (A, B, S, Y); + parameter WIDTH = 1; + parameter S_WIDTH = 1; + + (* force_downto *) + input [WIDTH-1:0] A; + (* force_downto *) + input [WIDTH*S_WIDTH-1:0] B; + (* force_downto *) + input [S_WIDTH-1:0] S; + (* force_downto *) + output [WIDTH-1:0] Y; + + (* force_downto *) + wire [WIDTH-1:0] Y_B; + + genvar i, j; + generate + (* force_downto *) + wire [WIDTH*(S_WIDTH+1)-1:0] C; + + assign C[WIDTH-1:0] = A; + for (i = 0; i < S_WIDTH; i = i + 1) + assign C[WIDTH*(i+2)-1:WIDTH*(i+1)] = S[i] ? B[WIDTH*(i+1)-1:WIDTH*i] : C[WIDTH*(i+1)-1:WIDTH*i]; + assign Y = C[WIDTH*(S_WIDTH+1)-1:WIDTH*S_WIDTH]; + endgenerate +endmodule diff --git a/tests/sim/run-test.sh b/tests/sim/run-test.sh index d34d1f3c9..a5120e77e 100755 --- a/tests/sim/run-test.sh +++ b/tests/sim/run-test.sh @@ -3,7 +3,7 @@ set -eu source ../gen-tests-makefile.sh echo "Generate FST for sim models" find tb/* -name tb*.v | while read name; do - test_name=$(basename -s .v $name) + test_name=$(basename $name .v) echo "Test $test_name" verilog_name=${test_name:3}.v iverilog -o tb/$test_name.out $name $verilog_name