diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 66c0b19715a..e4c776ed914 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -43,7 +43,7 @@ body: attributes: value: > When providing steps to reproduce the issue, please ensure that the issue - is reproducible in the current git master of Yosys. Also ensure to + is reproducible in the current git main of Yosys. Also ensure to provide all necessary source files needed. diff --git a/.github/workflows/test-verific.yml b/.github/workflows/test-verific.yml new file mode 100644 index 00000000000..b4383eba462 --- /dev/null +++ b/.github/workflows/test-verific.yml @@ -0,0 +1,77 @@ +name: Build and run tests with Verific (Linux) + +on: [push, pull_request] + +jobs: + test-verific: + runs-on: [self-hosted, linux, x64] + steps: + - name: Checkout Yosys + uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Runtime environment + run: | + echo "procs=$(nproc)" >> $GITHUB_ENV + + - name: Build Yosys + run: | + make config-clang + echo "ENABLE_VERIFIC := 1" >> Makefile.conf + echo "ENABLE_VERIFIC_EDIF := 1" >> Makefile.conf + echo "ENABLE_VERIFIC_LIBERTY := 1" >> Makefile.conf + echo "ENABLE_CCACHE := 1" >> Makefile.conf + make -j${{ env.procs }} + + - name: Install Yosys + run: | + make install DESTDIR=${GITHUB_WORKSPACE}/.local PREFIX= + + - name: Checkout Documentation + if: ${{ github.ref == 'refs/heads/main' }} + uses: actions/checkout@v4 + with: + path: 'yosys-cmd-ref' + repository: 'YosysHQ-Docs/yosys-cmd-ref' + fetch-depth: 0 + token: ${{ secrets.CI_DOCS_UPDATE_PAT }} + persist-credentials: true + + - name: Update documentation + if: ${{ github.ref == 'refs/heads/main' }} + run: | + make docs + rm -rf docs/build + cd yosys-cmd-ref + rm -rf * + git checkout README.md + cp -R ../docs/* . + rm -rf util/__pycache__ + git add -A . + git diff-index --quiet HEAD || git commit -m "Update" + git push + + - name: Checkout SBY + uses: actions/checkout@v4 + with: + repository: 'YosysHQ/sby' + path: 'sby' + + - name: Build SBY + run: | + make -C sby install DESTDIR=${GITHUB_WORKSPACE}/.local PREFIX= + + - name: Run Yosys tests + run: | + make -j${{ env.procs }} test + + - name: Run Verific specific Yosys tests + run: | + make -C tests/sva + cd tests/svtypes && bash run-test.sh + + - name: Run SBY tests + if: ${{ github.ref == 'refs/heads/main' }} + run: | + make -C sby run_ci diff --git a/CHANGELOG b/CHANGELOG index b172988d5db..f42eaca27e1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,9 +2,23 @@ List of major changes and improvements between releases ======================================================= -Yosys 0.39 .. Yosys 0.40-dev +Yosys 0.40 .. Yosys 0.41-dev -------------------------- +Yosys 0.39 .. Yosys 0.40 +-------------------------- + * New commands and options + - Added option "-vhdl2019" to "read" and "verific" pass. + + * Various + - Major documentation overhaul. + - Added port statistics to "stat" command. + - Added new formatting features to cxxrtl backend. + + * Verific support + - Added better support for VHDL constants import. + - Added support for VHDL 2009. + Yosys 0.38 .. Yosys 0.39 -------------------------- * New commands and options diff --git a/CODEOWNERS b/CODEOWNERS index 7d680e9f2e4..879bb8dee21 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -10,6 +10,7 @@ # PATH (can use glob) USERNAME(S) +CODEOWNERS @nakengelhardt passes/cmds/scratchpad.cc @nakengelhardt frontends/rpc/ @whitequark backends/cxxrtl/ @whitequark @@ -19,7 +20,7 @@ passes/opt/opt_lut.cc @whitequark passes/techmap/abc9*.cc @eddiehung @Ravenslofty backends/aiger/xaiger.cc @eddiehung docs/ @KrystalDelusion - +.github/workflows/*.yml @mmicko ## External Contributors # Only users with write permission to the repository get review diff --git a/Makefile b/Makefile index af6a6c1756c..dde82f13331 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ -CONFIG := clang +CONFIG := none +# CONFIG := clang # CONFIG := gcc # CONFIG := afl-gcc # CONFIG := emcc @@ -141,7 +142,7 @@ LIBS += -lrt endif endif -YOSYS_VER := 0.39+124 +YOSYS_VER := 0.40+25 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo @@ -157,7 +158,7 @@ endif OBJS = kernel/version_$(GIT_REV).o bumpversion: - sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 0033808.. | wc -l`/;" Makefile + sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline a1bb025.. | wc -l`/;" Makefile # set 'ABCREV = default' to use abc/ as it is # @@ -217,7 +218,7 @@ endif ifeq ($(CONFIG),clang) CXX = clang++ CXXFLAGS += -std=$(CXXSTD) -Os -ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -Wno-c++11-narrowing $(ABC_ARCHFLAGS)" +ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H $(ABC_ARCHFLAGS)" ifneq ($(SANITIZER),) $(info [Clang Sanitizer] $(SANITIZER)) @@ -265,7 +266,7 @@ ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H" else ifeq ($(CONFIG),emcc) CXX = emcc CXXFLAGS := -std=$(CXXSTD) $(filter-out -fPIC -ggdb,$(CXXFLAGS)) -ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DABC_MEMALIGN=8 -Wno-c++11-narrowing" +ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DABC_MEMALIGN=8" EMCC_CXXFLAGS := -Os -Wno-warn-absolute-paths EMCC_LINKFLAGS := --embed-file share EMCC_LINKFLAGS += -s NO_EXIT_RUNTIME=1 @@ -317,7 +318,7 @@ CXXFLAGS := $(WASIFLAGS) -std=$(CXXSTD) -Os -D_WASI_EMULATED_PROCESS_CLOCKS $(fi LINKFLAGS := $(WASIFLAGS) -Wl,-z,stack-size=1048576 $(filter-out -rdynamic,$(LINKFLAGS)) LIBS := -lwasi-emulated-process-clocks $(filter-out -lrt,$(LIBS)) ABCMKARGS += AR="$(AR)" RANLIB="$(RANLIB)" -ABCMKARGS += ARCHFLAGS="$(WASIFLAGS) -D_WASI_EMULATED_PROCESS_CLOCKS -DABC_USE_STDINT_H -DABC_NO_DYNAMIC_LINKING -DABC_NO_RLIMIT -Wno-c++11-narrowing" +ABCMKARGS += ARCHFLAGS="$(WASIFLAGS) -D_WASI_EMULATED_PROCESS_CLOCKS -DABC_USE_STDINT_H -DABC_NO_DYNAMIC_LINKING -DABC_NO_RLIMIT" ABCMKARGS += OPTFLAGS="-Os" EXE = .wasm @@ -360,8 +361,12 @@ ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC ABCMKARGS += LIBS="-lpthread -lshlwapi -s" ABC_USE_NO_READLINE=0 CC="x86_64-w64-mingw32-gcc" CXX="$(CXX)" EXE = .exe -else ifneq ($(CONFIG),none) -$(error Invalid CONFIG setting '$(CONFIG)'. Valid values: clang, gcc, emcc, mxe, msys2-32, msys2-64) +else ifeq ($(CONFIG),none) +CXXFLAGS += -std=$(CXXSTD) -Os +ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H $(ABC_ARCHFLAGS)" + +else +$(error Invalid CONFIG setting '$(CONFIG)'. Valid values: clang, gcc, emcc, mxe, msys2-32, msys2-64, none) endif ifeq ($(ENABLE_LIBYOSYS),1) @@ -624,6 +629,7 @@ $(eval $(call add_include_file,kernel/sigtools.h)) $(eval $(call add_include_file,kernel/timinginfo.h)) $(eval $(call add_include_file,kernel/utils.h)) $(eval $(call add_include_file,kernel/yosys.h)) +$(eval $(call add_include_file,kernel/yosys_common.h)) $(eval $(call add_include_file,kernel/yw.h)) $(eval $(call add_include_file,libs/ezsat/ezsat.h)) $(eval $(call add_include_file,libs/ezsat/ezminisat.h)) @@ -979,16 +985,27 @@ docs/gen_images: $(Q) $(MAKE) -C docs images DOCS_GUIDELINE_FILES := GettingStarted CodingStyle -docs/guidelines: - $(Q) mkdir -p docs/source/temp - $(Q) cp -f $(addprefix guidelines/,$(DOCS_GUIDELINE_FILES)) docs/source/temp +docs/guidelines docs/source/generated: + $(Q) mkdir -p docs/source/generated + $(Q) cp -f $(addprefix guidelines/,$(DOCS_GUIDELINE_FILES)) docs/source/generated + +# some commands return an error and print the usage text to stderr +define DOC_USAGE_STDERR +docs/source/generated/$(1): $(PROGRAM_PREFIX)$(1) docs/source/generated + -$(Q) ./$$< --help 2> $$@ +endef +DOCS_USAGE_STDERR := yosys-config yosys-filterlib yosys-abc +$(foreach usage,$(DOCS_USAGE_STDERR),$(eval $(call DOC_USAGE_STDERR,$(usage)))) + +# others print to stdout +define DOC_USAGE_STDOUT +docs/source/generated/$(1): $(PROGRAM_PREFIX)$(1) docs/source/generated + $(Q) ./$$< --help > $$@ +endef +DOCS_USAGE_STDOUT := yosys yosys-smtbmc yosys-witness +$(foreach usage,$(DOCS_USAGE_STDOUT),$(eval $(call DOC_USAGE_STDOUT,$(usage)))) -# many of these will return an error which can be safely ignored, so we prefix -# the command with a '-' -DOCS_USAGE_PROGS := yosys yosys-config yosys-filterlib yosys-abc yosys-smtbmc yosys-witness -docs/usage: $(addprefix docs/source/temp/,$(DOCS_USAGE_PROGS)) -docs/source/temp/%: docs/guidelines - -$(Q) ./$(PROGRAM_PREFIX)$* --help > $@ 2>&1 +docs/usage: $(addprefix docs/source/generated/,$(DOCS_USAGE_STDOUT) $(DOCS_USAGE_STDERR)) docs/reqs: $(Q) $(MAKE) -C docs reqs @@ -1128,6 +1145,9 @@ echo-git-rev: echo-abc-rev: @echo "$(ABCREV)" +echo-cxx: + @echo "$(CXX)" + -include libs/*/*.d -include frontends/*/*.d -include passes/*/*.d diff --git a/README.md b/README.md index c330d4f74f0..4647efbe9d1 100644 --- a/README.md +++ b/README.md @@ -105,11 +105,17 @@ For Cygwin use the following command to install all prerequisites, or select the setup-x86_64.exe -q --packages=bison,flex,gcc-core,gcc-g++,git,libffi-devel,libreadline-devel,make,pkg-config,python3,tcl-devel,boost-build,zlib-devel -To configure the build system to use a specific compiler, use one of +The environment variable `CXX` can be used to control the C++ compiler used, or +run one of the following: $ make config-clang $ make config-gcc +Note that these will result in `make` ignoring the `CXX` environment variable, +unless `CXX` is assigned in the call to make, e.g. + + $ make CXX=$CXX + For other compilers and build configurations it might be necessary to make some changes to the config section of the Makefile. diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h index b834cd12019..8546a841189 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h @@ -625,11 +625,11 @@ struct value : public expr_base> { value remainder; value dividend = sext(); value divisor = other.template sext(); - if (dividend.is_neg()) dividend = dividend.neg(); - if (divisor.is_neg()) divisor = divisor.neg(); + if (is_neg()) dividend = dividend.neg(); + if (other.is_neg()) divisor = divisor.neg(); std::tie(quotient, remainder) = dividend.udivmod(divisor); - if (dividend.is_neg() != divisor.is_neg()) quotient = quotient.neg(); - if (dividend.is_neg()) remainder = remainder.neg(); + if (is_neg() != other.is_neg()) quotient = quotient.neg(); + if (is_neg()) remainder = remainder.neg(); return {quotient.template trunc(), remainder.template trunc()}; } }; @@ -1010,22 +1010,24 @@ struct observer { // Default member initializers would make this a non-aggregate-type in C++11, so they are commented out. struct fmt_part { enum { - STRING = 0, + LITERAL = 0, INTEGER = 1, - CHARACTER = 2, - VLOG_TIME = 3, + STRING = 2, + UNICHAR = 3, + VLOG_TIME = 4, } type; - // STRING type + // LITERAL type std::string str; - // INTEGER/CHARACTER types + // INTEGER/STRING/UNICHAR types // + value val; - // INTEGER/CHARACTER/VLOG_TIME types + // INTEGER/STRING/VLOG_TIME types enum { RIGHT = 0, LEFT = 1, + NUMERIC = 2, } justify; // = RIGHT; char padding; // = '\0'; size_t width; // = 0; @@ -1033,7 +1035,14 @@ struct fmt_part { // INTEGER type unsigned base; // = 10; bool signed_; // = false; - bool plus; // = false; + enum { + MINUS = 0, + PLUS_MINUS = 1, + SPACE_MINUS = 2, + } sign; // = MINUS; + bool hex_upper; // = false; + bool show_base; // = false; + bool group; // = false; // VLOG_TIME type bool realtime; // = false; @@ -1049,11 +1058,12 @@ struct fmt_part { // We might want to replace some of these bit() calls with direct // chunk access if it turns out to be slow enough to matter. std::string buf; + std::string prefix; switch (type) { - case STRING: + case LITERAL: return str; - case CHARACTER: { + case STRING: { buf.reserve(Bits/8); for (int i = 0; i < Bits; i += 8) { char ch = 0; @@ -1067,35 +1077,76 @@ struct fmt_part { break; } + case UNICHAR: { + uint32_t codepoint = val.template get(); + if (codepoint >= 0x10000) + buf += (char)(0xf0 | (codepoint >> 18)); + else if (codepoint >= 0x800) + buf += (char)(0xe0 | (codepoint >> 12)); + else if (codepoint >= 0x80) + buf += (char)(0xc0 | (codepoint >> 6)); + else + buf += (char)codepoint; + if (codepoint >= 0x10000) + buf += (char)(0x80 | ((codepoint >> 12) & 0x3f)); + if (codepoint >= 0x800) + buf += (char)(0x80 | ((codepoint >> 6) & 0x3f)); + if (codepoint >= 0x80) + buf += (char)(0x80 | ((codepoint >> 0) & 0x3f)); + break; + } + case INTEGER: { - size_t width = Bits; + bool negative = signed_ && val.is_neg(); + if (negative) { + prefix = "-"; + val = val.neg(); + } else { + switch (sign) { + case MINUS: break; + case PLUS_MINUS: prefix = "+"; break; + case SPACE_MINUS: prefix = " "; break; + } + } + + size_t val_width = Bits; if (base != 10) { - width = 0; + val_width = 1; for (size_t index = 0; index < Bits; index++) if (val.bit(index)) - width = index + 1; + val_width = index + 1; } if (base == 2) { - for (size_t i = width; i > 0; i--) - buf += (val.bit(i - 1) ? '1' : '0'); + if (show_base) + prefix += "0b"; + for (size_t index = 0; index < val_width; index++) { + if (group && index > 0 && index % 4 == 0) + buf += '_'; + buf += (val.bit(index) ? '1' : '0'); + } } else if (base == 8 || base == 16) { + if (show_base) + prefix += (base == 16) ? (hex_upper ? "0X" : "0x") : "0o"; size_t step = (base == 16) ? 4 : 3; - for (size_t index = 0; index < width; index += step) { + for (size_t index = 0; index < val_width; index += step) { + if (group && index > 0 && index % (4 * step) == 0) + buf += '_'; uint8_t value = val.bit(index) | (val.bit(index + 1) << 1) | (val.bit(index + 2) << 2); if (step == 4) value |= val.bit(index + 3) << 3; - buf += "0123456789abcdef"[value]; + buf += (hex_upper ? "0123456789ABCDEF" : "0123456789abcdef")[value]; } - std::reverse(buf.begin(), buf.end()); } else if (base == 10) { - bool negative = signed_ && val.is_neg(); - if (negative) - val = val.neg(); + if (show_base) + prefix += "0d"; if (val.is_zero()) buf += '0'; value<(Bits > 4 ? Bits : 4)> xval = val.template zext<(Bits > 4 ? Bits : 4)>(); + size_t index = 0; while (!xval.is_zero()) { + if (group && index > 0 && index % 3 == 0) + buf += '_'; value<(Bits > 4 ? Bits : 4)> quotient, remainder; if (Bits >= 4) std::tie(quotient, remainder) = xval.udivmod(value<(Bits > 4 ? Bits : 4)>{10u}); @@ -1103,11 +1154,18 @@ struct fmt_part { std::tie(quotient, remainder) = std::make_pair(value<(Bits > 4 ? Bits : 4)>{0u}, xval); buf += '0' + remainder.template trunc<4>().template get(); xval = quotient; + index++; } - if (negative || plus) - buf += negative ? '-' : '+'; - std::reverse(buf.begin(), buf.end()); } else assert(false && "Unsupported base for fmt_part"); + if (justify == NUMERIC && group && padding == '0') { + int group_size = base == 10 ? 3 : 4; + while (prefix.size() + buf.size() < width) { + if (buf.size() % (group_size + 1) == group_size) + buf += '_'; + buf += '0'; + } + } + std::reverse(buf.begin(), buf.end()); break; } @@ -1123,17 +1181,29 @@ struct fmt_part { std::string str; assert(width == 0 || padding != '\0'); - if (justify == RIGHT && buf.size() < width) { - size_t pad_width = width - buf.size(); - if (padding == '0' && (buf.front() == '+' || buf.front() == '-')) { - str += buf.front(); - buf.erase(0, 1); - } - str += std::string(pad_width, padding); + if (prefix.size() + buf.size() < width) { + size_t pad_width = width - prefix.size() - buf.size(); + switch (justify) { + case LEFT: + str += prefix; + str += buf; + str += std::string(pad_width, padding); + break; + case RIGHT: + str += std::string(pad_width, padding); + str += prefix; + str += buf; + break; + case NUMERIC: + str += prefix; + str += std::string(pad_width, padding); + str += buf; + break; + } + } else { + str += prefix; + str += buf; } - str += buf; - if (justify == LEFT && buf.size() < width) - str += std::string(width - buf.size(), padding); return str; } }; diff --git a/backends/jny/jny.cc b/backends/jny/jny.cc index 9989feed599..1c163dba52e 100644 --- a/backends/jny/jny.cc +++ b/backends/jny/jny.cc @@ -124,7 +124,7 @@ struct JnyWriter design->sort(); f << "{\n"; - f << " \"$schema\": \"https://raw.githubusercontent.com/YosysHQ/yosys/master/misc/jny.schema.json\",\n"; + f << " \"$schema\": \"https://raw.githubusercontent.com/YosysHQ/yosys/main/misc/jny.schema.json\",\n"; f << stringf(" \"generator\": \"%s\",\n", escape_string(yosys_version_str).c_str()); f << " \"version\": \"0.0.1\",\n"; f << " \"invocation\": \"" << escape_string(invk) << "\",\n"; @@ -426,7 +426,7 @@ struct JnyBackend : public Backend { log(" Don't include property information in the netlist output.\n"); log("\n"); log("The JSON schema for JNY output files is located in the \"jny.schema.json\" file\n"); - log("which is located at \"https://raw.githubusercontent.com/YosysHQ/yosys/master/misc/jny.schema.json\"\n"); + log("which is located at \"https://raw.githubusercontent.com/YosysHQ/yosys/main/misc/jny.schema.json\"\n"); log("\n"); } diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index 05b7c6c4008..31bbc996ffa 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -1070,7 +1070,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) f << stringf(";\n"); return true; } - + if (cell->type == ID($_BUF_)) { f << stringf("%s" "assign ", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Y)); @@ -2014,22 +2014,29 @@ void dump_sync_effect(std::ostream &f, std::string indent, const RTLIL::SigSpec void dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right) { - if (simple_lhs) { + bool all_chunks_wires = true; + for (auto &chunk : left.chunks()) + if (chunk.is_wire() && reg_wires.count(chunk.wire->name)) + all_chunks_wires = false; + if (!simple_lhs && all_chunks_wires) { + f << stringf("%s" "assign ", indent.c_str()); + dump_sigspec(f, left); + f << stringf(" = "); + dump_sigspec(f, right); + f << stringf(";\n"); + } else { int offset = 0; for (auto &chunk : left.chunks()) { - f << stringf("%s" "assign ", indent.c_str()); + if (chunk.is_wire() && reg_wires.count(chunk.wire->name)) + f << stringf("%s" "always%s\n%s ", indent.c_str(), systemverilog ? "_comb" : " @*", indent.c_str()); + else + f << stringf("%s" "assign ", indent.c_str()); dump_sigspec(f, chunk); f << stringf(" = "); dump_sigspec(f, right.extract(offset, GetSize(chunk))); f << stringf(";\n"); offset += GetSize(chunk); } - } else { - f << stringf("%s" "assign ", indent.c_str()); - dump_sigspec(f, left); - f << stringf(" = "); - dump_sigspec(f, right); - f << stringf(";\n"); } } @@ -2276,11 +2283,15 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) active_initdata[sig[i]] = val[i]; } - if (!module->processes.empty()) - log_warning("Module %s contains unmapped RTLIL processes. RTLIL processes\n" - "can't always be mapped directly to Verilog always blocks. Unintended\n" - "changes in simulation behavior are possible! Use \"proc\" to convert\n" - "processes to logic networks and registers.\n", log_id(module)); + bool has_sync_rules = false; + for (auto process : module->processes) + if (!process.second->syncs.empty()) + has_sync_rules = true; + if (has_sync_rules) + log_warning("Module %s contains RTLIL processes with sync rules. Such RTLIL " + "processes can't always be mapped directly to Verilog always blocks. " + "unintended changes in simulation behavior are possible! Use \"proc\" " + "to convert processes to logic networks and registers.\n", log_id(module)); f << stringf("\n"); for (auto it = module->processes.begin(); it != module->processes.end(); ++it) diff --git a/docs/.gitignore b/docs/.gitignore index d7bfd8e956a..65bbcdeaeb8 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1,6 +1,6 @@ /build/ /source/cmd -/source/temp +/source/generated /source/_images/**/*.log /source/_images/**/*.aux /source/_images/**/*.pdf diff --git a/docs/Makefile b/docs/Makefile index a5b2fed6b7d..701157ee6c3 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -48,6 +48,7 @@ help: clean: clean-examples rm -rf $(BUILDDIR)/* rm -rf source/cmd util/__pycache__ + rm -rf source/generated $(MAKE) -C source/_images clean .PHONY: html diff --git a/docs/source/appendix/auxprogs.rst b/docs/source/appendix/auxprogs.rst index b6defbe802e..c239136b64e 100644 --- a/docs/source/appendix/auxprogs.rst +++ b/docs/source/appendix/auxprogs.rst @@ -11,7 +11,7 @@ The ``yosys-config`` tool (an auto-generated shell-script) can be used to query compiler options and other information needed for building loadable modules for Yosys. See :doc:`/yosys_internals/extending_yosys/extensions` for details. -.. literalinclude:: /temp/yosys-config +.. literalinclude:: /generated/yosys-config :start-at: Usage .. _sec:filterlib: @@ -25,7 +25,7 @@ The ``yosys-filterlib`` tool is a small utility that can be used to strip or extract information from a Liberty file. This can be useful for removing sensitive or proprietary information such as timing or other trade secrets. -.. literalinclude:: /temp/yosys-filterlib +.. literalinclude:: /generated/yosys-filterlib :start-at: Usage yosys-abc @@ -36,9 +36,8 @@ been accepted upstream. Not all versions of Yosys work with all versions of ABC. So Yosys comes with its own yosys-abc to avoid compatibility issues between the two. -.. literalinclude:: /temp/yosys-abc +.. literalinclude:: /generated/yosys-abc :start-at: usage - :end-before: UC Berkeley yosys-smtbmc ------------ @@ -46,7 +45,7 @@ yosys-smtbmc The ``yosys-smtbmc`` tool is a utility used by SBY for interacting with smt solvers. -.. literalinclude:: /temp/yosys-smtbmc +.. literalinclude:: /generated/yosys-smtbmc yosys-witness ------------- @@ -55,7 +54,7 @@ yosys-witness This is used in SBY and SCY for producing traces in a consistent format independent of the solver. -.. literalinclude:: /temp/yosys-witness +.. literalinclude:: /generated/yosys-witness :start-at: Usage .. note:: ``yosys-witness`` requires `click`_ Python package for use. diff --git a/docs/source/cmd_ref.rst b/docs/source/cmd_ref.rst index f0c4eaaf927..acf2d1d4141 100644 --- a/docs/source/cmd_ref.rst +++ b/docs/source/cmd_ref.rst @@ -4,7 +4,7 @@ Command line reference ================================================================================ -.. literalinclude:: /temp/yosys +.. literalinclude:: /generated/yosys :start-at: Usage .. toctree:: diff --git a/docs/source/code_examples/extensions/.gitignore b/docs/source/code_examples/extensions/.gitignore index ccdd6bd5c68..675a532eda6 100644 --- a/docs/source/code_examples/extensions/.gitignore +++ b/docs/source/code_examples/extensions/.gitignore @@ -1,2 +1,3 @@ my_cmd.so my_cmd.d +*.log diff --git a/docs/source/code_examples/extensions/Makefile b/docs/source/code_examples/extensions/Makefile index 288983ed356..8b5fa106af1 100644 --- a/docs/source/code_examples/extensions/Makefile +++ b/docs/source/code_examples/extensions/Makefile @@ -13,18 +13,18 @@ my_cmd.so: my_cmd.cc $(YOSYS)-config --exec --cxx $(subst $(DATDIR),../../../../share,$(CXXFLAGS)) --ldflags -o my_cmd.so -shared my_cmd.cc --ldlibs test0.log: my_cmd.so - $(YOSYS) -Ql test0.log_new -m ./my_cmd.so -p 'my_cmd foo bar' absval_ref.v + $(YOSYS) -QTl test0.log_new -m ./my_cmd.so -p 'my_cmd foo bar' -f verilog absval_ref.v mv test0.log_new test0.log test1.log: my_cmd.so - $(YOSYS) -Ql test1.log_new -m ./my_cmd.so -p 'clean; test1; dump' absval_ref.v + $(YOSYS) -QTl test1.log_new -m ./my_cmd.so -p 'clean; test1; dump' -f verilog absval_ref.v mv test1.log_new test1.log test1.dot: my_cmd.so $(YOSYS) -m ./my_cmd.so -p 'test1; show -format dot -prefix test1' test2.log: my_cmd.so - $(YOSYS) -Ql test2.log_new -m ./my_cmd.so -p 'hierarchy -top test; test2' sigmap_test.v + $(YOSYS) -QTl test2.log_new -m ./my_cmd.so -p 'hierarchy -top test; test2' -f verilog sigmap_test.v mv test2.log_new test2.log .PHONY: clean diff --git a/docs/source/getting_started/example_synth.rst b/docs/source/getting_started/example_synth.rst index 799b4ec48b4..916bef9fabd 100644 --- a/docs/source/getting_started/example_synth.rst +++ b/docs/source/getting_started/example_synth.rst @@ -633,9 +633,9 @@ with the mapping to ``SB_RAM40_4K`` done by :cmd:ref:`techmap` using into flip flops (the ``logic fallback``) with :cmd:ref:`memory_map`. .. |techlibs/ice40/brams.txt| replace:: :file:`techlibs/ice40/brams.txt` -.. _techlibs/ice40/brams.txt: https://github.com/YosysHQ/yosys/tree/master/techlibs/ice40/brams.txt +.. _techlibs/ice40/brams.txt: https://github.com/YosysHQ/yosys/tree/main/techlibs/ice40/brams.txt .. |techlibs/ice40/brams_map.v| replace:: :file:`techlibs/ice40/brams_map.v` -.. _techlibs/ice40/brams_map.v: https://github.com/YosysHQ/yosys/tree/master/techlibs/ice40/brams_map.v +.. _techlibs/ice40/brams_map.v: https://github.com/YosysHQ/yosys/tree/main/techlibs/ice40/brams_map.v .. literalinclude:: /cmd/synth_ice40.rst :language: yoscrypt diff --git a/docs/source/test_suites.rst b/docs/source/test_suites.rst index 2edb0e67d1a..7a6b7497774 100644 --- a/docs/source/test_suites.rst +++ b/docs/source/test_suites.rst @@ -16,8 +16,8 @@ Automatic testing .. _Yosys Git repo: https://github.com/YosysHQ/yosys -.. |test-linux| image:: https://github.com/YosysHQ/yosys/actions/workflows/test-linux.yml/badge.svg?branch=master -.. |test-macos| image:: https://github.com/YosysHQ/yosys/actions/workflows/test-macos.yml/badge.svg?branch=master +.. |test-linux| image:: https://github.com/YosysHQ/yosys/actions/workflows/test-linux.yml/badge.svg?branch=main +.. |test-macos| image:: https://github.com/YosysHQ/yosys/actions/workflows/test-macos.yml/badge.svg?branch=main For up to date information, including OS versions, refer to `the git actions page`_. diff --git a/docs/source/using_yosys/more_scripting/interactive_investigation.rst b/docs/source/using_yosys/more_scripting/interactive_investigation.rst index ed798d6b694..f56543bebbb 100644 --- a/docs/source/using_yosys/more_scripting/interactive_investigation.rst +++ b/docs/source/using_yosys/more_scripting/interactive_investigation.rst @@ -18,7 +18,7 @@ in the circuit diagrams generated by it. The code used is included in the Yosys code base under |code_examples/show|_. .. |code_examples/show| replace:: :file:`docs/source/code_examples/show` -.. _code_examples/show: https://github.com/YosysHQ/yosys/tree/master/docs/source/code_examples/show +.. _code_examples/show: https://github.com/YosysHQ/yosys/tree/main/docs/source/code_examples/show A simple circuit ^^^^^^^^^^^^^^^^ @@ -337,7 +337,7 @@ The code used is included in the Yosys code base under |code_examples/scrambler|_. .. |code_examples/scrambler| replace:: :file:`docs/source/code_examples/scrambler` -.. _code_examples/scrambler: https://github.com/YosysHQ/yosys/tree/master/docs/source/code_examples/scrambler +.. _code_examples/scrambler: https://github.com/YosysHQ/yosys/tree/main/docs/source/code_examples/scrambler Changing design hierarchy ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/source/using_yosys/more_scripting/model_checking.rst b/docs/source/using_yosys/more_scripting/model_checking.rst index 0b97d384e63..92a9d85ce67 100644 --- a/docs/source/using_yosys/more_scripting/model_checking.rst +++ b/docs/source/using_yosys/more_scripting/model_checking.rst @@ -29,7 +29,7 @@ Let's take a look at an example included in the Yosys code base under |code_examples/synth_flow|_: .. |code_examples/synth_flow| replace:: :file:`docs/source/code_examples/synth_flow` -.. _code_examples/synth_flow: https://github.com/YosysHQ/yosys/tree/master/docs/source/code_examples/synth_flow +.. _code_examples/synth_flow: https://github.com/YosysHQ/yosys/tree/main/docs/source/code_examples/synth_flow .. literalinclude:: /code_examples/synth_flow/techmap_01_map.v :language: verilog @@ -81,7 +81,7 @@ The code used in this section is included in the Yosys code base under |code_examples/axis|_. .. |code_examples/axis| replace:: :file:`docs/source/code_examples/axis` -.. _code_examples/axis: https://github.com/YosysHQ/yosys/tree/master/docs/source/code_examples/axis +.. _code_examples/axis: https://github.com/YosysHQ/yosys/tree/main/docs/source/code_examples/axis The following AXI4 Stream Master has a bug. But the bug is not exposed if the slave keeps ``tready`` asserted all the time. (Something a test bench might do.) diff --git a/docs/source/using_yosys/more_scripting/selections.rst b/docs/source/using_yosys/more_scripting/selections.rst index d4def881cc9..6aa3465cc99 100644 --- a/docs/source/using_yosys/more_scripting/selections.rst +++ b/docs/source/using_yosys/more_scripting/selections.rst @@ -405,7 +405,7 @@ those cases selection variables must be used to capture more complex selections. Example code from |code_examples/selections|_: .. |code_examples/selections| replace:: :file:`docs/source/code_examples/selections` -.. _code_examples/selections: https://github.com/YosysHQ/yosys/tree/master/docs/source/code_examples/selections +.. _code_examples/selections: https://github.com/YosysHQ/yosys/tree/main/docs/source/code_examples/selections .. literalinclude:: /code_examples/selections/select.v :language: verilog diff --git a/docs/source/using_yosys/synthesis/cell_libs.rst b/docs/source/using_yosys/synthesis/cell_libs.rst index a723845379a..476269abfc9 100644 --- a/docs/source/using_yosys/synthesis/cell_libs.rst +++ b/docs/source/using_yosys/synthesis/cell_libs.rst @@ -21,7 +21,7 @@ detail in the :doc:`/getting_started/example_synth` document. To learn more about these commands, check out :ref:`interactive_show`. -.. _example project: https://github.com/YosysHQ/yosys/tree/master/docs/source/code_examples/intro +.. _example project: https://github.com/YosysHQ/yosys/tree/main/docs/source/code_examples/intro A simple counter ~~~~~~~~~~~~~~~~ diff --git a/docs/source/using_yosys/synthesis/extract.rst b/docs/source/using_yosys/synthesis/extract.rst index 678efba868e..bbe1870dfb3 100644 --- a/docs/source/using_yosys/synthesis/extract.rst +++ b/docs/source/using_yosys/synthesis/extract.rst @@ -15,7 +15,7 @@ The extract pass Example code can be found in |code_examples/macc|_. .. |code_examples/macc| replace:: :file:`docs/source/code_examples/macc` -.. _code_examples/macc: https://github.com/YosysHQ/yosys/tree/master/docs/source/code_examples/macc +.. _code_examples/macc: https://github.com/YosysHQ/yosys/tree/main/docs/source/code_examples/macc .. literalinclude:: /code_examples/macc/macc_simple_test.ys diff --git a/docs/source/using_yosys/synthesis/memory.rst b/docs/source/using_yosys/synthesis/memory.rst index 7df75fb8809..3dbafeaab7e 100644 --- a/docs/source/using_yosys/synthesis/memory.rst +++ b/docs/source/using_yosys/synthesis/memory.rst @@ -36,7 +36,7 @@ Example |code_examples/synth_flow|_. .. |code_examples/synth_flow| replace:: :file:`docs/source/code_examples/synth_flow` -.. _code_examples/synth_flow: https://github.com/YosysHQ/yosys/tree/master/docs/source/code_examples/synth_flow +.. _code_examples/synth_flow: https://github.com/YosysHQ/yosys/tree/main/docs/source/code_examples/synth_flow .. figure:: /_images/code_examples/synth_flow/memory_01.* :class: width-helper @@ -92,7 +92,7 @@ leftover memory cells unable to be converted are then picked up by For more on the lib format for :cmd:ref:`memory_libmap`, see `passes/memory/memlib.md -`_ +`_ Supported memory patterns ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/source/using_yosys/synthesis/proc.rst b/docs/source/using_yosys/synthesis/proc.rst index 785be7adc7c..b4983438021 100644 --- a/docs/source/using_yosys/synthesis/proc.rst +++ b/docs/source/using_yosys/synthesis/proc.rst @@ -31,7 +31,7 @@ Example |code_examples/synth_flow|_. .. |code_examples/synth_flow| replace:: :file:`docs/source/code_examples/synth_flow` -.. _code_examples/synth_flow: https://github.com/YosysHQ/yosys/tree/master/docs/source/code_examples/synth_flow +.. _code_examples/synth_flow: https://github.com/YosysHQ/yosys/tree/main/docs/source/code_examples/synth_flow .. literalinclude:: /code_examples/synth_flow/proc_01.v :language: verilog diff --git a/docs/source/yosys_internals/extending_yosys/extensions.rst b/docs/source/yosys_internals/extending_yosys/extensions.rst index 2c159e3f089..68e1740be4a 100644 --- a/docs/source/yosys_internals/extending_yosys/extensions.rst +++ b/docs/source/yosys_internals/extending_yosys/extensions.rst @@ -24,7 +24,7 @@ Code examples from this section are included in the |code_examples/extensions|_ directory of the Yosys source code. .. |code_examples/extensions| replace:: :file:`docs/source/code_examples/extensions` -.. _code_examples/extensions: https://github.com/YosysHQ/yosys/tree/master/docs/source/code_examples/extensions +.. _code_examples/extensions: https://github.com/YosysHQ/yosys/tree/main/docs/source/code_examples/extensions Program components and data formats @@ -254,7 +254,7 @@ The following is the complete code of the "stubnets" example module. It is included in the Yosys source distribution under |code_examples/stubnets|_. .. |code_examples/stubnets| replace:: :file:`docs/source/code_examples/stubnets` -.. _code_examples/stubnets: https://github.com/YosysHQ/yosys/tree/master/docs/source/code_examples/stubnets +.. _code_examples/stubnets: https://github.com/YosysHQ/yosys/tree/main/docs/source/code_examples/stubnets .. literalinclude:: /code_examples/stubnets/stubnets.cc :language: c++ diff --git a/docs/source/yosys_internals/formats/cell_library.rst b/docs/source/yosys_internals/formats/cell_library.rst index c80b0740255..2b8dc300154 100644 --- a/docs/source/yosys_internals/formats/cell_library.rst +++ b/docs/source/yosys_internals/formats/cell_library.rst @@ -619,6 +619,48 @@ Finite state machines Add a brief description of the ``$fsm`` cell type. +Coarse arithmetics +~~~~~~~~~~~~~~~~~~~~~ + +The ``$macc`` cell type represents a generalized multiply and accumulate operation. The cell is purely combinational. It outputs the result of summing up a sequence of products and other injected summands. + +.. code-block:: + + Y = 0 +- a0factor1 * a0factor2 +- a1factor1 * a1factor2 +- ... + + B[0] + B[1] + ... + +The A port consists of concatenated pairs of multiplier inputs ("factors"). +A zero length factor2 acts as a constant 1, turning factor1 into a simple summand. + +In this pseudocode, ``u(foo)`` means an unsigned int that's foo bits long. + +.. code-block:: + + struct A { + u(CONFIG.mul_info[0].factor1_len) a0factor1; + u(CONFIG.mul_info[0].factor2_len) a0factor2; + u(CONFIG.mul_info[1].factor1_len) a1factor1; + u(CONFIG.mul_info[1].factor2_len) a1factor2; + ... + }; + +The cell's ``CONFIG`` parameter determines the layout of cell port ``A``. +The CONFIG parameter carries the following information: + +.. code-block:: + + struct CONFIG { + u4 num_bits; + struct mul_info { + bool is_signed; + bool is_subtract; + u(num_bits) factor1_len; + u(num_bits) factor2_len; + }[num_ports]; + }; + +B is an array of concatenated 1-bit-wide unsigned integers to also be summed up. + Specify rules ~~~~~~~~~~~~~ @@ -1152,4 +1194,4 @@ file via ABC using the abc pass. .. todo:: Add information about ``$lut`` and ``$sop`` cells. -.. todo:: Add information about ``$alu``, ``$macc``, ``$fa``, and ``$lcu`` cells. +.. todo:: Add information about ``$alu``, ``$fa``, and ``$lcu`` cells. diff --git a/docs/source/yosys_internals/techmap.rst b/docs/source/yosys_internals/techmap.rst index ef2bbd87a28..ab161ed92d3 100644 --- a/docs/source/yosys_internals/techmap.rst +++ b/docs/source/yosys_internals/techmap.rst @@ -16,7 +16,7 @@ Code examples used in this document are included in the Yosys code base under |code_examples/techmap|_. .. |code_examples/techmap| replace:: :file:`docs/source/code_examples/techmap` -.. _code_examples/techmap: https://github.com/YosysHQ/yosys/tree/master/docs/source/code_examples/techmap +.. _code_examples/techmap: https://github.com/YosysHQ/yosys/tree/main/docs/source/code_examples/techmap Mapping OR3X1 diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index fe67f00c692..bc7f1ddd1d6 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -790,7 +790,7 @@ struct AST_INTERNAL::ProcessGenerator Fmt fmt; fmt.parse_verilog(args, /*sformat_like=*/false, default_base, /*task_name=*/ast->str, current_module->name); if (ast->str.substr(0, 8) == "$display") - fmt.append_string("\n"); + fmt.append_literal("\n"); fmt.emit_rtlil(cell); } else if (!ast->str.empty()) { log_file_error(ast->filename, ast->location.first_line, "Found unsupported invocation of system task `%s'!\n", ast->str.c_str()); diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 43a4e03a29d..3d8478ef160 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -1079,7 +1079,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin // when $display()/$write() functions are used in an initial block, print them during synthesis Fmt fmt = processFormat(stage, /*sformat_like=*/false, default_base, /*first_arg_at=*/0, /*may_fail=*/true); if (str.substr(0, 8) == "$display") - fmt.append_string("\n"); + fmt.append_literal("\n"); log("%s", fmt.render().c_str()); } diff --git a/frontends/verific/Makefile.inc b/frontends/verific/Makefile.inc index c82428613af..df3ac8d2d43 100644 --- a/frontends/verific/Makefile.inc +++ b/frontends/verific/Makefile.inc @@ -14,6 +14,7 @@ ifneq ($(DISABLE_VERIFIC_VHDL),1) $(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_1987/. share/verific.new/vhdl_vdbs_1987 $(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_1993/. share/verific.new/vhdl_vdbs_1993 $(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_2008/. share/verific.new/vhdl_vdbs_2008 + $(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_2019/. share/verific.new/vhdl_vdbs_2019 endif $(Q) chmod -R a+rX share/verific.new $(Q) mv share/verific.new share/verific diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index faa0e1bcdd3..81e79f7497f 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -214,23 +214,120 @@ RTLIL::IdString VerificImporter::new_verific_id(Verific::DesignObj *obj) return s; } -// When used as attributes or parameter values Verific constants come already processed. -// - Real string values are already under quotes -// - Numeric values with specified width are always converted to binary -// - Rest of user defined values are handled as 32bit integers -// - There could be some internal values that are strings without quotes -// so we check if value is all digits or not -// -// Note: For signed values, verific uses 'sb and decimal values can -// also be negative. -static const RTLIL::Const verific_const(const char *value, bool allow_string = true, bool output_signed = false) +RTLIL::Const mkconst_str(const std::string &str) { - size_t found; + RTLIL::Const val; + std::vector data; + data.reserve(str.size() * 8); + for (size_t i = 0; i < str.size(); i++) { + unsigned char ch = str[str.size() - i - 1]; + for (int j = 0; j < 8; j++) { + data.push_back((ch & 1) ? State::S1 : State::S0); + ch = ch >> 1; + } + } + val.bits = data; + val.flags |= RTLIL::CONST_FLAG_STRING; + return val; +} + +static const RTLIL::Const extract_vhdl_boolean(std::string &val) +{ + if (val == "false") + return RTLIL::Const::from_string("0"); + if (val == "true") + return RTLIL::Const::from_string("1"); + log_error("Expecting VHDL boolean value.\n"); +} + +static const RTLIL::Const extract_vhdl_bit(std::string &val, std::string &typ) +{ + if (val.size()==3 && val[0]=='\'' && val.back()=='\'') + return RTLIL::Const::from_string(val.substr(1,val.size()-2)); + log_error("Error parsing VHDL %s.\n", typ.c_str()); +} + +static const RTLIL::Const extract_vhdl_bit_vector(std::string &val, std::string &typ) +{ + if (val.size()>1 && val[0]=='\"' && val.back()=='\"') { + RTLIL::Const c = RTLIL::Const::from_string(val.substr(1,val.size()-2)); + if (typ == "signed") + c.flags |= RTLIL::CONST_FLAG_SIGNED; + return c; + } + log_error("Error parsing VHDL %s.\n", typ.c_str()); +} + +static const RTLIL::Const extract_vhdl_integer(std::string &val) +{ + char *end; + return RTLIL::Const((int)std::strtol(val.c_str(), &end, 10), 32); +} + +static const RTLIL::Const extract_vhdl_char(std::string &val) +{ + if (val.size()==3 && val[0]=='\"' && val.back()=='\"') + return RTLIL::Const((int)val[1], 32); + log_error("Error parsing VHDL character.\n"); +} + +static const RTLIL::Const extract_real_value(std::string &val) +{ + RTLIL::Const c = mkconst_str(val); + c.flags |= RTLIL::CONST_FLAG_REAL; + return c; +} + +static const RTLIL::Const extract_vhdl_string(std::string &val) +{ + if (!(val.size()>1 && val[0]=='\"' && val.back()=='\"')) + log_error("Error parsing VHDL string.\n"); + return RTLIL::Const(val.substr(1,val.size()-2)); +} + +static const RTLIL::Const extract_vhdl_const(const char *value, bool output_signed) +{ + RTLIL::Const c; char *end; int decimal; bool is_signed = false; + std::string val = std::string(value); + + if (val.size()>1 && val[0]=='\"' && val.back()=='\"') { + std::string data = val.substr(1,val.size()-2); + bool isBinary = std::all_of(data.begin(), data.end(), [](char c) {return c=='1' || c=='0'; }); + if (isBinary) + c = RTLIL::Const::from_string(data); + else + c = RTLIL::Const(data); + } else if (val.size()==3 && val[0]=='\'' && val.back()=='\'') { + c = RTLIL::Const::from_string(val.substr(1,val.size()-2)); + } else if ((value[0] == '-' || (value[0] >= '0' && value[0] <= '9')) && + ((decimal = std::strtol(value, &end, 10)), !end[0])) { + is_signed = output_signed; + c = RTLIL::Const((int)decimal, 32); + } else if (val == "false") { + c = RTLIL::Const::from_string("0"); + } else if (val == "true") { + c = RTLIL::Const::from_string("1"); + } else { + c = mkconst_str(val); + log_warning("encoding value '%s' as string.\n", value); + } + if (is_signed) + c.flags |= RTLIL::CONST_FLAG_SIGNED; + return c; +} + +static const RTLIL::Const extract_verilog_const(const char *value, bool allow_string, bool output_signed) +{ RTLIL::Const c; + char *end; + int decimal; + bool is_signed = false; + size_t found; std::string val = std::string(value); + if (allow_string && val.size()>1 && val[0]=='\"' && val.back()=='\"') { c = RTLIL::Const(val.substr(1,val.size()-2)); } else if ((found = val.find("'sb")) != std::string::npos) { @@ -245,15 +342,56 @@ static const RTLIL::Const verific_const(const char *value, bool allow_string = t } else if (allow_string) { c = RTLIL::Const(val); } else { - log_error("expected numeric constant but found '%s'", value); + c = mkconst_str(val); + log_warning("encoding value '%s' as string.\n", value); } - if (is_signed) c.flags |= RTLIL::CONST_FLAG_SIGNED; - return c; } +// When used as attributes or parameter values Verific constants come already processed. +// - Real string values are already under quotes +// - Numeric values with specified width are always converted to binary +// - Rest of user defined values are handled as 32bit integers +// - There could be some internal values that are strings without quotes +// so we check if value is all digits or not +// +// Note: For signed values, verific uses 'sb and decimal values can +// also be negative. +static const RTLIL::Const verific_const(const char* type_name, const char *value, DesignObj *obj, bool allow_string = true, bool output_signed = false) +{ + std::string val = std::string(value); + // VHDL + if (obj->IsFromVhdl()) { + if (type_name) { + std::string typ = std::string(type_name); + transform(typ.begin(), typ.end(), typ.begin(), ::tolower); + if (typ == "integer" || typ == "natural" || typ=="positive") return extract_vhdl_integer(val); + else if (typ =="boolean") return extract_vhdl_boolean(val); + else if (typ == "bit" || typ =="std_logic" || typ == "std_ulogic") return extract_vhdl_bit(val,typ); + else if (typ == "character") return extract_vhdl_char(val); + else if (typ == "bit_vector" || typ == "std_logic_vector" || typ == "std_ulogic_vector" || + typ == "unsigned" || typ == "signed") return extract_vhdl_bit_vector(val,typ); + else if (typ == "real") return extract_real_value(val); + else if (typ == "string") return extract_vhdl_string(val); + else { + if (val.size()>1 && val[0]=='\"' && val.back()=='\"') + return RTLIL::Const(val.substr(1,val.size()-2)); + else if (val.size()==3 && val[0]=='\'' && val.back()=='\'') + return RTLIL::Const(val.substr(1,val.size()-2)); + else + return RTLIL::Const(val); + } + } else extract_vhdl_const(value, output_signed); + } + // SystemVerilog + if (type_name && strcmp(type_name, "real")==0) { + return extract_real_value(val); + } else + return extract_verilog_const(value, allow_string, output_signed); +} + static const std::string verific_unescape(const char *value) { std::string val = std::string(value); @@ -276,7 +414,7 @@ void VerificImporter::import_attributes(dict &att FOREACH_ATTRIBUTE(obj, mi, attr) { if (attr->Key()[0] == ' ' || attr->Value() == nullptr) continue; - attributes[RTLIL::escape_id(attr->Key())] = verific_const(attr->Value()); + attributes[RTLIL::escape_id(attr->Key())] = verific_const(nullptr, attr->Value(), obj); } if (nl) { @@ -298,7 +436,7 @@ void VerificImporter::import_attributes(dict &att const char *k, *v; FOREACH_MAP_ITEM(type_range->GetEnumIdMap(), mi, &k, &v) { if (nl->IsFromVerilog()) { - auto const value = verific_const(v, false); + auto const value = verific_const(type_name, v, nl, false); attributes.emplace(stringf("\\enum_value_%s", value.as_string().c_str()), RTLIL::escape_id(k)); } @@ -1292,6 +1430,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma } import_attributes(module->attributes, nl, nl); module->set_string_attribute(ID::hdlname, nl->CellBaseName()); + module->set_string_attribute(ID(library), nl->Owner()->Owner()->Name()); #ifdef VERIFIC_VHDL_SUPPORT if (nl->IsFromVhdl()) { NameSpace name_space(0); @@ -1304,7 +1443,8 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma MapIter mi; FOREACH_PARAMETER_OF_NETLIST(nl, mi, param_name, param_value) { module->avail_parameters(RTLIL::escape_id(param_name)); - module->parameter_default_values[RTLIL::escape_id(param_name)] = verific_const(param_value); + const TypeRange *tr = nl->GetTypeRange(param_name) ; + module->parameter_default_values[RTLIL::escape_id(param_name)] = verific_const(tr->GetTypeName(), param_value, nl); } SetIter si; @@ -2004,7 +2144,8 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma const char *param_value ; if (is_blackbox(inst->View())) { FOREACH_PARAMETER_OF_INST(inst, mi2, param_name, param_value) { - cell->setParam(RTLIL::escape_id(param_name), verific_const(param_value)); + const TypeRange *tr = inst->View()->GetTypeRange(param_name) ; + cell->setParam(RTLIL::escape_id(param_name), verific_const(tr->GetTypeName(), param_value, inst->View())); } } @@ -2699,7 +2840,7 @@ struct VerificPass : public Pass { log("\n"); log("\n"); #ifdef VERIFIC_VHDL_SUPPORT - log(" verific {-vhdl87|-vhdl93|-vhdl2k|-vhdl2008|-vhdl} ..\n"); + log(" verific {-vhdl87|-vhdl93|-vhdl2k|-vhdl2008|-vhdl2019|-vhdl} ..\n"); log("\n"); log("Load the specified VHDL files into Verific.\n"); log("\n"); @@ -3436,6 +3577,29 @@ struct VerificPass : public Pass { goto check_error; } + if (GetSize(args) > argidx && (args[argidx] == "-vhdl2019")) { + vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_2019").c_str()); + 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; + } + Map map(POINTER_HASH); + add_units_to_map(map, work, flag_lib); + std::string filename = frontent_rewrite(args, argidx, tmp_files); + if (!vhdl_file::Analyze(filename.c_str(), work.c_str(), vhdl_file::VHDL_2019)) + log_cmd_error("Reading `%s' in VHDL_2019 mode failed.\n", filename.c_str()); + set_units_to_blackbox(map, work, flag_lib); + } + verific_import_pending = true; + goto check_error; + } + if (GetSize(args) > argidx && (args[argidx] == "-vhdl2008" || args[argidx] == "-vhdl")) { vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_2008").c_str()); bool flag_lib = false; @@ -3979,7 +4143,7 @@ struct ReadPass : public Pass { log("\n"); log("\n"); #ifdef VERIFIC_VHDL_SUPPORT - log(" read {-vhdl87|-vhdl93|-vhdl2k|-vhdl2008|-vhdl} ..\n"); + log(" read {-vhdl87|-vhdl93|-vhdl2k|-vhdl2008|-vhdl2019|-vhdl} ..\n"); log("\n"); log("Load the specified VHDL files. (Requires Verific.)\n"); log("\n"); @@ -4083,7 +4247,7 @@ struct ReadPass : public Pass { } #ifdef VERIFIC_VHDL_SUPPORT - if (args[1] == "-vhdl87" || args[1] == "-vhdl93" || args[1] == "-vhdl2k" || args[1] == "-vhdl2008" || args[1] == "-vhdl") { + if (args[1] == "-vhdl87" || args[1] == "-vhdl93" || args[1] == "-vhdl2k" || args[1] == "-vhdl2008" || args[1] == "-vhdl2019" || args[1] == "-vhdl") { if (use_verific) { args[0] = "verific"; Pass::call(design, args); diff --git a/guidelines/CodingStyle b/guidelines/CodingStyle index ee1e1a2b6a1..8a3df2d62e8 100644 --- a/guidelines/CodingStyle +++ b/guidelines/CodingStyle @@ -19,7 +19,7 @@ Formatting of code blank lines. - Otherwise stick to the Linux Kernel Coding Style: - https://www.kernel.org/doc/Documentation/CodingStyle + https://www.kernel.org/doc/Documentation/process/coding-style.rst C++ Language diff --git a/guidelines/Windows b/guidelines/Windows index 2af0620fae9..c4548c37cc7 100644 --- a/guidelines/Windows +++ b/guidelines/Windows @@ -44,7 +44,7 @@ Visual Studio builds are not directly supported by build scripts, but they are s 1. Easy way - - Go to https://github.com/YosysHQ/yosys/actions/workflows/vs.yml?query=branch%3Amaster + - Go to https://github.com/YosysHQ/yosys/actions/workflows/vs.yml?query=branch%3Amain - Click on the most recent completed run - In Artifacts region find vcxsrc and click on it to download - Unpack downloaded ZIP file diff --git a/kernel/celledges.cc b/kernel/celledges.cc index 2ed0d503605..09cdfd55b96 100644 --- a/kernel/celledges.cc +++ b/kernel/celledges.cc @@ -307,6 +307,87 @@ void shift_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell) } } +void packed_mem_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell) +{ + log_assert(cell->type == ID($mem_v2)); + Const rd_clk_enable = cell->getParam(ID::RD_CLK_ENABLE); + int n_rd_ports = cell->getParam(ID::RD_PORTS).as_int(); + int abits = cell->getParam(ID::ABITS).as_int(); + int width = cell->getParam(ID::WIDTH).as_int(); + + for (int i = 0; i < n_rd_ports; i++) { + if (rd_clk_enable[i] != State::S0) { + for (int k = 0; k < width; k++) + db->add_edge(cell, ID::RD_ARST, i, ID::RD_DATA, i * width + k, -1); + continue; + } + + for (int j = 0; j < abits; j++) + for (int k = 0; k < width; k++) + db->add_edge(cell, ID::RD_ADDR, i * abits + j, + ID::RD_DATA, i * width + k, -1); + } +} + +void memrd_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell) +{ + log_assert(cell->type.in(ID($memrd), ID($memrd_v2))); + int abits = cell->getParam(ID::ABITS).as_int(); + int width = cell->getParam(ID::WIDTH).as_int(); + + if (cell->getParam(ID::CLK_ENABLE).as_bool()) { + if (cell->type == ID($memrd_v2)) { + for (int k = 0; k < width; k++) + db->add_edge(cell, ID::ARST, 0, ID::DATA, k, -1); + } + return; + } + + for (int j = 0; j < abits; j++) + for (int k = 0; k < width; k++) + db->add_edge(cell, ID::ADDR, j, ID::DATA, k, -1); +} + +void mem_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell) +{ + if (cell->type == ID($mem_v2)) + packed_mem_op(db, cell); + else if (cell->type.in(ID($memrd), ID($memrd_v2))) + memrd_op(db, cell); + else if (cell->type.in(ID($memwr), ID($memwr_v2), ID($meminit))) + return; /* no edges here */ + else + log_abort(); +} + +void ff_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell) +{ + int width = cell->getPort(ID::Q).size(); + + if (cell->type.in(ID($dlatch), ID($adlatch), ID($dlatchsr))) { + for (int k = 0; k < width; k++) { + db->add_edge(cell, ID::D, k, ID::Q, k, -1); + db->add_edge(cell, ID::EN, 0, ID::Q, k, -1); + } + } + + if (cell->hasPort(ID::CLR)) + for (int k = 0; k < width; k++) + db->add_edge(cell, ID::CLR, 0, ID::Q, k, -1); + if (cell->hasPort(ID::SET)) + for (int k = 0; k < width; k++) + db->add_edge(cell, ID::SET, 0, ID::Q, k, -1); + if (cell->hasPort(ID::ALOAD)) + for (int k = 0; k < width; k++) + db->add_edge(cell, ID::ALOAD, 0, ID::Q, k, -1); + if (cell->hasPort(ID::AD)) + for (int k = 0; k < width; k++) + db->add_edge(cell, ID::AD, k, ID::Q, k, -1); + if (cell->hasPort(ID::ARST)) + for (int k = 0; k < width; k++) + db->add_edge(cell, ID::ARST, 0, ID::Q, k, -1); +} + PRIVATE_NAMESPACE_END bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL::Cell *cell) @@ -361,6 +442,18 @@ bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL return true; } + if (cell->type.in(ID($mem_v2), ID($memrd), ID($memrd_v2), ID($memwr), ID($memwr_v2), ID($meminit))) { + mem_op(this, cell); + return true; + } + + if (RTLIL::builtin_ff_cell_types().count(cell->type)) { + ff_op(this, cell); + return true; + } + + // FIXME: $mul $div $mod $divfloor $modfloor $slice $concat + // FIXME: $lut $sop $alu $lcu $macc $fa // FIXME: $mul $div $mod $divfloor $modfloor $pow $slice $concat $bweqx // FIXME: $lut $sop $alu $lcu $macc $fa $logic_and $logic_or $bwmux diff --git a/kernel/fmt.cc b/kernel/fmt.cc index 18eb7cb7193..cbf2d12e9ac 100644 --- a/kernel/fmt.cc +++ b/kernel/fmt.cc @@ -22,9 +22,9 @@ USING_YOSYS_NAMESPACE -void Fmt::append_string(const std::string &str) { +void Fmt::append_literal(const std::string &str) { FmtPart part = {}; - part.type = FmtPart::STRING; + part.type = FmtPart::LITERAL; part.str = str; parts.push_back(part); } @@ -42,11 +42,11 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) { } else if (fmt.substr(i, 2) == "{{") { part.str += '{'; ++i; - } else if (fmt[i] == '}') + } else if (fmt[i] == '}') { log_assert(false && "Unexpected '}' in format string"); - else if (fmt[i] == '{') { + } else if (fmt[i] == '{') { if (!part.str.empty()) { - part.type = FmtPart::STRING; + part.type = FmtPart::LITERAL; parts.push_back(part); part = {}; } @@ -74,19 +74,24 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) { part.sig = args.extract(0, arg_size); args.remove(0, arg_size); + if (fmt[i] == 'U') { + part.type = FmtPart::UNICHAR; + ++i; + goto success; + } + if (fmt[i] == '>') part.justify = FmtPart::RIGHT; else if (fmt[i] == '<') part.justify = FmtPart::LEFT; + else if (fmt[i] == '=') + part.justify = FmtPart::NUMERIC; else log_assert(false && "Unexpected justification in format substitution"); if (++i == fmt.size()) log_assert(false && "Unexpected end in format substitution"); - if (fmt[i] == '0' || fmt[i] == ' ') - part.padding = fmt[i]; - else - log_assert(false && "Unexpected padding in format substitution"); + part.padding = fmt[i]; if (++i == fmt.size()) log_assert(false && "Unexpected end in format substitution"); @@ -107,8 +112,12 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) { } else if (fmt[i] == 'h') { part.type = FmtPart::INTEGER; part.base = 16; + } else if (fmt[i] == 'H') { + part.type = FmtPart::INTEGER; + part.base = 16; + part.hex_upper = true; } else if (fmt[i] == 'c') { - part.type = FmtPart::CHARACTER; + part.type = FmtPart::STRING; } else if (fmt[i] == 't') { part.type = FmtPart::VLOG_TIME; } else if (fmt[i] == 'r') { @@ -124,10 +133,29 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) { log_assert(false && "Unexpected end in format substitution"); if (part.type == FmtPart::INTEGER) { - if (fmt[i] == '+') { - part.plus = true; + if (fmt[i] == '-') { + part.sign = FmtPart::MINUS; + if (++i == fmt.size()) + log_assert(false && "Unexpected end in format substitution"); + } else if (fmt[i] == '+') { + part.sign = FmtPart::PLUS_MINUS; + if (++i == fmt.size()) + log_assert(false && "Unexpected end in format substitution"); + } else if (fmt[i] == ' ') { + part.sign = FmtPart::SPACE_MINUS; if (++i == fmt.size()) log_assert(false && "Unexpected end in format substitution"); + } else { + // also accept no sign character and treat like MINUS for compatibility + } + + if (fmt[i] == '#') { + part.show_base = true; + ++i; + } + if (fmt[i] == '_') { + part.group = true; + ++i; } if (fmt[i] == 'u') @@ -140,6 +168,7 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) { log_assert(false && "Unexpected end in format substitution"); } + success: if (fmt[i] != '}') log_assert(false && "Expected '}' after format substitution"); @@ -150,7 +179,7 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) { } } if (!part.str.empty()) { - part.type = FmtPart::STRING; + part.type = FmtPart::LITERAL; parts.push_back(part); } } @@ -161,7 +190,7 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const { for (auto &part : parts) { switch (part.type) { - case FmtPart::STRING: + case FmtPart::LITERAL: for (char c : part.str) { if (c == '{') fmt += "{{"; @@ -172,10 +201,15 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const { } break; + case FmtPart::UNICHAR: + log_assert(part.sig.size() <= 32); + fmt += "{U}"; + break; + case FmtPart::VLOG_TIME: log_assert(part.sig.size() == 0); YS_FALLTHROUGH - case FmtPart::CHARACTER: + case FmtPart::STRING: log_assert(part.sig.size() % 8 == 0); YS_FALLTHROUGH case FmtPart::INTEGER: @@ -187,6 +221,8 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const { fmt += '>'; else if (part.justify == FmtPart::LEFT) fmt += '<'; + else if (part.justify == FmtPart::NUMERIC) + fmt += '='; else log_abort(); log_assert(part.width == 0 || part.padding != '\0'); fmt += part.padding != '\0' ? part.padding : ' '; @@ -197,13 +233,18 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const { case 2: fmt += 'b'; break; case 8: fmt += 'o'; break; case 10: fmt += 'd'; break; - case 16: fmt += 'h'; break; + case 16: fmt += part.hex_upper ? 'H' : 'h'; break; default: log_abort(); } - if (part.plus) - fmt += '+'; + switch (part.sign) { + case FmtPart::MINUS: fmt += '-'; break; + case FmtPart::PLUS_MINUS: fmt += '+'; break; + case FmtPart::SPACE_MINUS: fmt += ' '; break; + } + fmt += part.show_base ? "#" : ""; + fmt += part.group ? "_" : ""; fmt += part.signed_ ? 's' : 'u'; - } else if (part.type == FmtPart::CHARACTER) { + } else if (part.type == FmtPart::STRING) { fmt += 'c'; } else if (part.type == FmtPart::VLOG_TIME) { if (part.realtime) @@ -299,12 +340,12 @@ void Fmt::apply_verilog_automatic_sizing_and_add(FmtPart &part) part.width = places; if (part.justify == FmtPart::RIGHT) { - append_string(gap); + append_literal(gap); parts.push_back(part); } else { part.justify = FmtPart::RIGHT; parts.push_back(part); - append_string(gap); + append_literal(gap); } } } @@ -355,7 +396,7 @@ void Fmt::parse_verilog(const std::vector &args, bool sformat_lik part.str += module_name.str(); } else { if (!part.str.empty()) { - part.type = FmtPart::STRING; + part.type = FmtPart::LITERAL; parts.push_back(part); part = {}; } @@ -375,7 +416,7 @@ void Fmt::parse_verilog(const std::vector &args, bool sformat_lik part.justify = FmtPart::LEFT; } else if (fmt[i] == '+') { // always show sign; not in IEEE 1800-2017 or verilator but iverilog has it - part.plus = true; + part.sign = FmtPart::PLUS_MINUS; } else break; } if (i == fmt.size()) { @@ -408,11 +449,11 @@ void Fmt::parse_verilog(const std::vector &args, bool sformat_lik part.type = FmtPart::INTEGER; part.base = 16; } else if (fmt[i] == 'c' || fmt[i] == 'C') { - part.type = FmtPart::CHARACTER; + part.type = FmtPart::STRING; part.sig.extend_u0(8); // %10c and %010c not fully defined in IEEE 1800-2017 and do different things in iverilog } else if (fmt[i] == 's' || fmt[i] == 'S') { - part.type = FmtPart::CHARACTER; + part.type = FmtPart::STRING; if ((part.sig.size() % 8) != 0) part.sig.extend_u0((part.sig.size() + 7) / 8 * 8); // %10s and %010s not fully defined in IEEE 1800-2017 and do the same thing in iverilog @@ -435,12 +476,20 @@ void Fmt::parse_verilog(const std::vector &args, bool sformat_lik log_file_error(fmtarg->filename, fmtarg->first_line, "System task `%s' called with incomplete format specifier in argument %zu.\n", task_name.c_str(), fmtarg - args.begin() + 1); } - if (part.padding == '\0') - part.padding = (has_leading_zero && part.justify == FmtPart::RIGHT) ? '0' : ' '; + if (part.padding == '\0') { + if (has_leading_zero && part.justify == FmtPart::RIGHT) { + part.padding = '0'; + part.justify = FmtPart::NUMERIC; + } else { + part.padding = ' '; + } + } - if (part.type == FmtPart::INTEGER && part.base != 10 && part.plus) + if (part.type == FmtPart::INTEGER && part.base != 10 && part.sign != FmtPart::MINUS) log_file_error(fmtarg->filename, fmtarg->first_line, "System task `%s' called with invalid format specifier in argument %zu.\n", task_name.c_str(), fmtarg - args.begin() + 1); + if (part.base != 10) + part.signed_ = false; if (part.type == FmtPart::INTEGER && !has_leading_zero) apply_verilog_automatic_sizing_and_add(part); else @@ -449,12 +498,12 @@ void Fmt::parse_verilog(const std::vector &args, bool sformat_lik } } if (!part.str.empty()) { - part.type = FmtPart::STRING; + part.type = FmtPart::LITERAL; parts.push_back(part); } } else { FmtPart part = {}; - part.type = FmtPart::STRING; + part.type = FmtPart::LITERAL; part.str = arg->str; parts.push_back(part); } @@ -474,7 +523,7 @@ std::vector Fmt::emit_verilog() const for (auto &part : parts) { switch (part.type) { - case FmtPart::STRING: + case FmtPart::LITERAL: for (char c : part.str) { if (c == '%') fmt.str += "%%"; @@ -491,14 +540,13 @@ std::vector Fmt::emit_verilog() const args.push_back(arg); fmt.str += '%'; - if (part.plus) - fmt.str += '+'; + if (part.sign == FmtPart::PLUS_MINUS || part.sign == FmtPart::SPACE_MINUS) + fmt.str += '+'; // treat space/minus as plus/minus if (part.justify == FmtPart::LEFT) fmt.str += '-'; if (part.width == 0) { fmt.str += '0'; } else if (part.width > 0) { - log_assert(part.padding == ' ' || part.padding == '0'); if (part.base != 10 || part.padding == '0') fmt.str += '0'; fmt.str += std::to_string(part.width); @@ -507,13 +555,13 @@ std::vector Fmt::emit_verilog() const case 2: fmt.str += 'b'; break; case 8: fmt.str += 'o'; break; case 10: fmt.str += 'd'; break; - case 16: fmt.str += 'h'; break; + case 16: fmt.str += 'h'; break; // treat uppercase hex as lowercase default: log_abort(); } break; } - case FmtPart::CHARACTER: { + case FmtPart::STRING: { VerilogFmtArg arg; arg.type = VerilogFmtArg::INTEGER; arg.sig = part.sig; @@ -524,7 +572,6 @@ std::vector Fmt::emit_verilog() const fmt.str += '-'; if (part.sig.size() == 8) { if (part.width > 0) { - log_assert(part.padding == '0' || part.padding == ' '); if (part.padding == '0') fmt.str += part.padding; fmt.str += std::to_string(part.width); @@ -533,7 +580,6 @@ std::vector Fmt::emit_verilog() const } else { log_assert(part.sig.size() % 8 == 0); if (part.width > 0) { - log_assert(part.padding == ' '); // no zero padding fmt.str += std::to_string(part.width); } fmt.str += 's'; @@ -541,6 +587,16 @@ std::vector Fmt::emit_verilog() const break; } + case FmtPart::UNICHAR: { + VerilogFmtArg arg; + arg.type = VerilogFmtArg::INTEGER; + arg.sig = part.sig.extract(0, 7); // only ASCII + args.push_back(arg); + + fmt.str += "%c"; + break; + } + case FmtPart::VLOG_TIME: { VerilogFmtArg arg; arg.type = VerilogFmtArg::TIME; @@ -549,11 +605,11 @@ std::vector Fmt::emit_verilog() const args.push_back(arg); fmt.str += '%'; - if (part.plus) + log_assert(part.sign == FmtPart::MINUS || part.sign == FmtPart::PLUS_MINUS); + if (part.sign == FmtPart::PLUS_MINUS) fmt.str += '+'; if (part.justify == FmtPart::LEFT) fmt.str += '-'; - log_assert(part.padding == ' ' || part.padding == '0'); if (part.padding == '0' && part.width > 0) fmt.str += '0'; fmt.str += std::to_string(part.width); @@ -599,24 +655,35 @@ void Fmt::emit_cxxrtl(std::ostream &os, std::string indent, std::function= 0x10000) + str += (char)(0xf0 | (codepoint >> 18)); + else if (codepoint >= 0x800) + str += (char)(0xe0 | (codepoint >> 12)); + else if (codepoint >= 0x80) + str += (char)(0xc0 | (codepoint >> 6)); + else + str += (char)codepoint; + if (codepoint >= 0x10000) + str += (char)(0x80 | ((codepoint >> 12) & 0x3f)); + if (codepoint >= 0x800) + str += (char)(0x80 | ((codepoint >> 6) & 0x3f)); + if (codepoint >= 0x80) + str += (char)(0x80 | ((codepoint >> 0) & 0x3f)); + break; + } + case FmtPart::INTEGER: - case FmtPart::CHARACTER: + case FmtPart::STRING: case FmtPart::VLOG_TIME: { std::string buf; + std::string prefix; if (part.type == FmtPart::INTEGER) { RTLIL::Const value = part.sig.as_const(); + bool has_x = false, all_x = true, has_z = false, all_z = true; + for (State bit : value) { + if (bit == State::Sx) + has_x = true; + else + all_x = false; + if (bit == State::Sz) + has_z = true; + else + all_z = false; + } + + if (!has_z && !has_x && part.signed_ && value[value.size() - 1]) { + prefix = "-"; + value = RTLIL::const_neg(value, {}, part.signed_, {}, value.size() + 1); + } else { + switch (part.sign) { + case FmtPart::MINUS: break; + case FmtPart::PLUS_MINUS: prefix = "+"; break; + case FmtPart::SPACE_MINUS: prefix = " "; break; + } + } if (part.base != 10) { - size_t minimum_size = 0; + size_t minimum_size = 1; for (size_t index = 0; index < (size_t)value.size(); index++) if (value[index] != State::S0) minimum_size = index + 1; @@ -651,10 +761,28 @@ std::string Fmt::render() const } if (part.base == 2) { - buf = value.as_string(); + if (part.show_base) + prefix += "0b"; + for (size_t index = 0; index < (size_t)value.size(); index++) { + if (part.group && index > 0 && index % 4 == 0) + buf += '_'; + RTLIL::State bit = value[index]; + if (bit == State::Sx) + buf += 'x'; + else if (bit == State::Sz) + buf += 'z'; + else if (bit == State::S1) + buf += '1'; + else /* if (bit == State::S0) */ + buf += '0'; + } } else if (part.base == 8 || part.base == 16) { + if (part.show_base) + prefix += (part.base == 16) ? (part.hex_upper ? "0X" : "0x") : "0o"; size_t step = (part.base == 16) ? 4 : 3; for (size_t index = 0; index < (size_t)value.size(); index += step) { + if (part.group && index > 0 && index % (4 * step) == 0) + buf += '_'; RTLIL::Const subvalue = value.extract(index, min(step, value.size() - index)); bool has_x = false, all_x = true, has_z = false, all_z = true; for (State bit : subvalue) { @@ -676,21 +804,11 @@ std::string Fmt::render() const else if (has_z) buf += 'Z'; else - buf += "0123456789abcdef"[subvalue.as_int()]; + buf += (part.hex_upper ? "0123456789ABCDEF" : "0123456789abcdef")[subvalue.as_int()]; } - std::reverse(buf.begin(), buf.end()); } else if (part.base == 10) { - bool has_x = false, all_x = true, has_z = false, all_z = true; - for (State bit : value) { - if (bit == State::Sx) - has_x = true; - else - all_x = false; - if (bit == State::Sz) - has_z = true; - else - all_z = false; - } + if (part.show_base) + prefix += "0d"; if (all_x) buf += 'x'; else if (all_z) @@ -700,25 +818,29 @@ std::string Fmt::render() const else if (has_z) buf += 'Z'; else { - bool negative = part.signed_ && value[value.size() - 1]; - RTLIL::Const absvalue; - if (negative) - absvalue = RTLIL::const_neg(value, {}, part.signed_, {}, value.size() + 1); - else - absvalue = value; - log_assert(absvalue.is_fully_def()); - if (absvalue.is_fully_zero()) + log_assert(value.is_fully_def()); + if (value.is_fully_zero()) buf += '0'; - while (!absvalue.is_fully_zero()) { - buf += '0' + RTLIL::const_mod(absvalue, 10, false, false, 4).as_int(); - absvalue = RTLIL::const_div(absvalue, 10, false, false, absvalue.size()); + size_t index = 0; + while (!value.is_fully_zero()) { + if (part.group && index > 0 && index % 3 == 0) + buf += '_'; + buf += '0' + RTLIL::const_mod(value, 10, false, false, 4).as_int(); + value = RTLIL::const_div(value, 10, false, false, value.size()); + index++; } - if (negative || part.plus) - buf += negative ? '-' : '+'; - std::reverse(buf.begin(), buf.end()); } } else log_abort(); - } else if (part.type == FmtPart::CHARACTER) { + if (part.justify == FmtPart::NUMERIC && part.group && part.padding == '0') { + int group_size = part.base == 10 ? 3 : 4; + while (prefix.size() + buf.size() < part.width) { + if (buf.size() % (group_size + 1) == group_size) + buf += '_'; + buf += '0'; + } + } + std::reverse(buf.begin(), buf.end()); + } else if (part.type == FmtPart::STRING) { buf = part.sig.as_const().decode_string(); } else if (part.type == FmtPart::VLOG_TIME) { // We only render() during initial, so time is always zero. @@ -726,17 +848,29 @@ std::string Fmt::render() const } log_assert(part.width == 0 || part.padding != '\0'); - if (part.justify == FmtPart::RIGHT && buf.size() < part.width) { - size_t pad_width = part.width - buf.size(); - if (part.padding == '0' && (!buf.empty() && (buf.front() == '+' || buf.front() == '-'))) { - str += buf.front(); - buf.erase(0, 1); + if (prefix.size() + buf.size() < part.width) { + size_t pad_width = part.width - prefix.size() - buf.size(); + switch (part.justify) { + case FmtPart::LEFT: + str += prefix; + str += buf; + str += std::string(pad_width, part.padding); + break; + case FmtPart::RIGHT: + str += std::string(pad_width, part.padding); + str += prefix; + str += buf; + break; + case FmtPart::NUMERIC: + str += prefix; + str += std::string(pad_width, part.padding); + str += buf; + break; } - str += std::string(pad_width, part.padding); + } else { + str += prefix; + str += buf; } - str += buf; - if (part.justify == FmtPart::LEFT && buf.size() < part.width) - str += std::string(part.width - buf.size(), part.padding); break; } } diff --git a/kernel/fmt.h b/kernel/fmt.h index 3bedb786ec4..2d4b24979b2 100644 --- a/kernel/fmt.h +++ b/kernel/fmt.h @@ -53,22 +53,24 @@ struct VerilogFmtArg { // Must be kept in sync with `struct fmt_part` in backends/cxxrtl/runtime/cxxrtl/cxxrtl.h! struct FmtPart { enum { - STRING = 0, + LITERAL = 0, INTEGER = 1, - CHARACTER = 2, - VLOG_TIME = 3, + STRING = 2, + UNICHAR = 3, + VLOG_TIME = 4, } type; - // STRING type + // LITERAL type std::string str; - // INTEGER/CHARACTER types + // INTEGER/STRING/UNICHAR types RTLIL::SigSpec sig; - // INTEGER/CHARACTER/VLOG_TIME types + // INTEGER/STRING/VLOG_TIME types enum { RIGHT = 0, LEFT = 1, + NUMERIC = 2, } justify = RIGHT; char padding = '\0'; size_t width = 0; @@ -76,7 +78,14 @@ struct FmtPart { // INTEGER type unsigned base = 10; bool signed_ = false; - bool plus = false; + enum { + MINUS = 0, + PLUS_MINUS = 1, + SPACE_MINUS = 2, + } sign = MINUS; + bool hex_upper = false; + bool show_base = false; + bool group = false; // VLOG_TIME type bool realtime = false; @@ -86,7 +95,7 @@ struct Fmt { public: std::vector parts; - void append_string(const std::string &str); + void append_literal(const std::string &str); void parse_rtlil(const RTLIL::Cell *cell); void emit_rtlil(RTLIL::Cell *cell) const; diff --git a/kernel/log.h b/kernel/log.h index e4f06c69d19..53aae58c6bc 100644 --- a/kernel/log.h +++ b/kernel/log.h @@ -17,11 +17,11 @@ * */ -#include "kernel/yosys.h" - #ifndef LOG_H #define LOG_H +#include "kernel/yosys_common.h" + #include #include @@ -449,4 +449,6 @@ void log_dump_args_worker(const char *p, T first, Args ... args) YOSYS_NAMESPACE_END +#include "kernel/yosys.h" + #endif diff --git a/kernel/register.h b/kernel/register.h index 08ce4b28787..25ea9f2321c 100644 --- a/kernel/register.h +++ b/kernel/register.h @@ -17,11 +17,12 @@ * */ -#include "kernel/yosys.h" - #ifndef REGISTER_H #define REGISTER_H +#include "kernel/yosys_common.h" +#include "kernel/yosys.h" + YOSYS_NAMESPACE_BEGIN struct Pass diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 40422dce56d..f9da2949508 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -17,11 +17,12 @@ * */ -#include "kernel/yosys.h" - #ifndef RTLIL_H #define RTLIL_H +#include "kernel/yosys_common.h" +#include "kernel/yosys.h" + YOSYS_NAMESPACE_BEGIN namespace RTLIL diff --git a/kernel/utils.h b/kernel/utils.h index 8fa223824da..3216c5eb5f0 100644 --- a/kernel/utils.h +++ b/kernel/utils.h @@ -149,7 +149,7 @@ template , typename OPS = hash_ops> cla std::map node_to_index; std::vector> edges; std::vector sorted; - std::set> loops; + std::set> loops; TopoSort() : indirect_cmp(nodes) { @@ -220,10 +220,10 @@ template , typename OPS = hash_ops> cla if (active_cells[root_index]) { found_loops = true; if (analyze_loops) { - std::set loop; + std::vector loop; for (int i = GetSize(active_stack) - 1; i >= 0; i--) { const int index = active_stack[i]; - loop.insert(nodes[index]); + loop.push_back(nodes[index]); if (index == root_index) break; } diff --git a/kernel/yosys.h b/kernel/yosys.h index 0a4641d1819..5922d9ab752 100644 --- a/kernel/yosys.h +++ b/kernel/yosys.h @@ -39,323 +39,7 @@ #ifndef YOSYS_H #define YOSYS_H -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef WITH_PYTHON -#include -#endif - -#ifndef _YOSYS_ -# error It looks like you are trying to build Yosys without the config defines set. \ - When building Yosys with a custom make system, make sure you set all the \ - defines the Yosys Makefile would set for your build configuration. -#endif - -#ifdef YOSYS_ENABLE_TCL -# include -# ifdef YOSYS_MXE_HACKS -extern Tcl_Command Tcl_CreateCommand(Tcl_Interp *interp, const char *cmdName, Tcl_CmdProc *proc, ClientData clientData, Tcl_CmdDeleteProc *deleteProc); -extern Tcl_Interp *Tcl_CreateInterp(void); -extern void Tcl_Preserve(ClientData data); -extern void Tcl_Release(ClientData clientData); -extern int Tcl_InterpDeleted(Tcl_Interp *interp); -extern void Tcl_DeleteInterp(Tcl_Interp *interp); -extern int Tcl_Eval(Tcl_Interp *interp, const char *script); -extern int Tcl_EvalFile(Tcl_Interp *interp, const char *fileName); -extern void Tcl_Finalize(void); -extern int Tcl_GetCommandInfo(Tcl_Interp *interp, const char *cmdName, Tcl_CmdInfo *infoPtr); -extern const char *Tcl_GetStringResult(Tcl_Interp *interp); -extern Tcl_Obj *Tcl_NewStringObj(const char *bytes, int length); -extern Tcl_Obj *Tcl_NewIntObj(int intValue); -extern Tcl_Obj *Tcl_NewListObj(int objc, Tcl_Obj *const objv[]); -extern Tcl_Obj *Tcl_ObjSetVar2(Tcl_Interp *interp, Tcl_Obj *part1Ptr, Tcl_Obj *part2Ptr, Tcl_Obj *newValuePtr, int flags); -# endif -# undef CONST -# undef INLINE -#endif - -#ifdef _WIN32 -# undef NOMINMAX -# define NOMINMAX 1 -# undef YY_NO_UNISTD_H -# define YY_NO_UNISTD_H 1 - -# include -# include -# include - -# define strtok_r strtok_s -# define strdup _strdup -# define snprintf _snprintf -# define getcwd _getcwd -# define mkdir _mkdir -# define popen _popen -# define pclose _pclose - -# ifndef __MINGW32__ -# define PATH_MAX MAX_PATH -# define isatty _isatty -# define fileno _fileno -# endif - -// The following defines conflict with our identifiers: -# undef CONST -// `wingdi.h` defines a TRANSPARENT macro that conflicts with X(TRANSPARENT) entry in kernel/constids.inc -# undef TRANSPARENT -#endif - -#ifndef PATH_MAX -# define PATH_MAX 4096 -#endif - -#define YOSYS_NAMESPACE Yosys -#define PRIVATE_NAMESPACE_BEGIN namespace { -#define PRIVATE_NAMESPACE_END } -#define YOSYS_NAMESPACE_BEGIN namespace Yosys { -#define YOSYS_NAMESPACE_END } -#define YOSYS_NAMESPACE_PREFIX Yosys:: -#define USING_YOSYS_NAMESPACE using namespace Yosys; - -#if defined(__GNUC__) || defined(__clang__) -# define YS_ATTRIBUTE(...) __attribute__((__VA_ARGS__)) -#elif defined(_MSC_VER) -# define YS_ATTRIBUTE(...) -#else -# define YS_ATTRIBUTE(...) -#endif - -#if defined(__GNUC__) || defined(__clang__) -# define YS_MAYBE_UNUSED __attribute__((__unused__)) -#else -# define YS_MAYBE_UNUSED -#endif - -#if __cplusplus >= 201703L -# define YS_FALLTHROUGH [[fallthrough]]; -#elif defined(__clang__) -# define YS_FALLTHROUGH [[clang::fallthrough]]; -#elif defined(__GNUC__) -# define YS_FALLTHROUGH [[gnu::fallthrough]]; -#else -# define YS_FALLTHROUGH -#endif - -YOSYS_NAMESPACE_BEGIN - -// Note: All headers included in hashlib.h must be included -// outside of YOSYS_NAMESPACE before this or bad things will happen. -#ifdef HASHLIB_H -# undef HASHLIB_H -# include "kernel/hashlib.h" -#else -# include "kernel/hashlib.h" -# undef HASHLIB_H -#endif - -using std::vector; -using std::string; -using std::tuple; -using std::pair; - -using std::make_tuple; -using std::make_pair; -using std::get; -using std::min; -using std::max; - -// A primitive shared string implementation that does not -// move its .c_str() when the object is copied or moved. -struct shared_str { - std::shared_ptr content; - shared_str() { } - shared_str(string s) { content = std::shared_ptr(new string(s)); } - shared_str(const char *s) { content = std::shared_ptr(new string(s)); } - const char *c_str() const { return content->c_str(); } - const string &str() const { return *content; } - bool operator==(const shared_str &other) const { return *content == *other.content; } - unsigned int hash() const { return hashlib::hash_ops::hash(*content); } -}; - -using hashlib::mkhash; -using hashlib::mkhash_init; -using hashlib::mkhash_add; -using hashlib::mkhash_xorshift; -using hashlib::hash_ops; -using hashlib::hash_cstr_ops; -using hashlib::hash_ptr_ops; -using hashlib::hash_obj_ops; -using hashlib::dict; -using hashlib::idict; -using hashlib::pool; -using hashlib::mfp; - -namespace RTLIL { - struct IdString; - struct Const; - struct SigBit; - struct SigSpec; - struct Wire; - struct Cell; - struct Memory; - struct Process; - struct Module; - struct Design; - struct Monitor; - enum State : unsigned char; -} - -namespace AST { - struct AstNode; -} - -using RTLIL::IdString; -using RTLIL::Const; -using RTLIL::SigBit; -using RTLIL::SigSpec; -using RTLIL::Wire; -using RTLIL::Cell; -using RTLIL::Module; -using RTLIL::Design; - -namespace hashlib { - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; -} - -void memhasher_on(); -void memhasher_off(); -void memhasher_do(); - -extern bool memhasher_active; -inline void memhasher() { if (memhasher_active) memhasher_do(); } - -void yosys_banner(); -int ceil_log2(int x) YS_ATTRIBUTE(const); - -inline std::string vstringf(const char *fmt, va_list ap) -{ - // For the common case of strings shorter than 128, save a heap - // allocation by using a stack allocated buffer. - const int kBufSize = 128; - char buf[kBufSize]; - buf[0] = '\0'; - va_list apc; - va_copy(apc, ap); - int n = vsnprintf(buf, kBufSize, fmt, apc); - va_end(apc); - if (n < kBufSize) - return std::string(buf); - - std::string string; - char *str = NULL; -#if defined(_WIN32 )|| defined(__CYGWIN__) - int sz = 2 * kBufSize, rc; - while (1) { - va_copy(apc, ap); - str = (char*)realloc(str, sz); - rc = vsnprintf(str, sz, fmt, apc); - va_end(apc); - if (rc >= 0 && rc < sz) - break; - sz *= 2; - } - if (str != NULL) { - string = str; - free(str); - } - return string; -#else - if (vasprintf(&str, fmt, ap) < 0) - str = NULL; - if (str != NULL) { - string = str; - free(str); - } - return string; -#endif -} - -std::string stringf(const char *fmt, ...) YS_ATTRIBUTE(format(printf, 1, 2)); - -inline std::string stringf(const char *fmt, ...) -{ - std::string string; - va_list ap; - - va_start(ap, fmt); - string = vstringf(fmt, ap); - va_end(ap); - - return string; -} - -int readsome(std::istream &f, char *s, int n); -std::string next_token(std::string &text, const char *sep = " \t\r\n", bool long_strings = false); -std::vector split_tokens(const std::string &text, const char *sep = " \t\r\n"); -bool patmatch(const char *pattern, const char *string); -#if !defined(YOSYS_DISABLE_SPAWN) -int run_command(const std::string &command, std::function process_line = std::function()); -#endif -std::string get_base_tmpdir(); -std::string make_temp_file(std::string template_str = get_base_tmpdir() + "/yosys_XXXXXX"); -std::string make_temp_dir(std::string template_str = get_base_tmpdir() + "/yosys_XXXXXX"); -bool check_file_exists(std::string filename, bool is_exec = false); -bool check_directory_exists(const std::string& dirname); -bool is_absolute_path(std::string filename); -void remove_directory(std::string dirname); -bool create_directory(const std::string& dirname); -std::string escape_filename_spaces(const std::string& filename); - -template int GetSize(const T &obj) { return obj.size(); } -inline int GetSize(RTLIL::Wire *wire); - -extern int autoidx; -extern int yosys_xtrace; - -YOSYS_NAMESPACE_END +#include "kernel/yosys_common.h" #include "kernel/log.h" #include "kernel/rtlil.h" @@ -363,14 +47,6 @@ YOSYS_NAMESPACE_END YOSYS_NAMESPACE_BEGIN -using RTLIL::State; -using RTLIL::SigChunk; -using RTLIL::SigSig; - -namespace hashlib { - template<> struct hash_ops : hash_ops {}; -} - void yosys_setup(); #ifdef WITH_PYTHON @@ -385,26 +61,6 @@ Tcl_Interp *yosys_get_tcl_interp(); extern RTLIL::Design *yosys_design; -RTLIL::IdString new_id(std::string file, int line, std::string func); -RTLIL::IdString new_id_suffix(std::string file, int line, std::string func, std::string suffix); - -#define NEW_ID \ - YOSYS_NAMESPACE_PREFIX new_id(__FILE__, __LINE__, __FUNCTION__) -#define NEW_ID_SUFFIX(suffix) \ - YOSYS_NAMESPACE_PREFIX new_id_suffix(__FILE__, __LINE__, __FUNCTION__, suffix) - -// Create a statically allocated IdString object, using for example ID::A or ID($add). -// -// Recipe for Converting old code that is using conversion of strings like ID::A and -// "$add" for creating IdStrings: Run below SED command on the .cc file and then use for -// example "meld foo.cc foo.cc.orig" to manually compile errors, if necessary. -// -// sed -i.orig -r 's/"\\\\([a-zA-Z0-9_]+)"/ID(\1)/g; s/"(\$[a-zA-Z0-9_]+)"/ID(\1)/g;' -// -#define ID(_id) ([]() { const char *p = "\\" #_id, *q = p[1] == '$' ? p+1 : p; \ - static const YOSYS_NAMESPACE_PREFIX RTLIL::IdString id(q); return id; })() -namespace ID = RTLIL::ID; - RTLIL::Design *yosys_get_design(); std::string proc_self_dirname(); std::string proc_share_dirname(); diff --git a/kernel/yosys_common.h b/kernel/yosys_common.h new file mode 100644 index 00000000000..9f1bc2f58d7 --- /dev/null +++ b/kernel/yosys_common.h @@ -0,0 +1,379 @@ +/* -*- c++ -*- + * 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. + * + */ + +#ifndef YOSYS_COMMON_H +#define YOSYS_COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef WITH_PYTHON +#include +#endif + +#ifndef _YOSYS_ +# error It looks like you are trying to build Yosys without the config defines set. \ + When building Yosys with a custom make system, make sure you set all the \ + defines the Yosys Makefile would set for your build configuration. +#endif + +#ifdef YOSYS_ENABLE_TCL +# include +# ifdef YOSYS_MXE_HACKS +extern Tcl_Command Tcl_CreateCommand(Tcl_Interp *interp, const char *cmdName, Tcl_CmdProc *proc, ClientData clientData, Tcl_CmdDeleteProc *deleteProc); +extern Tcl_Interp *Tcl_CreateInterp(void); +extern void Tcl_Preserve(ClientData data); +extern void Tcl_Release(ClientData clientData); +extern int Tcl_InterpDeleted(Tcl_Interp *interp); +extern void Tcl_DeleteInterp(Tcl_Interp *interp); +extern int Tcl_Eval(Tcl_Interp *interp, const char *script); +extern int Tcl_EvalFile(Tcl_Interp *interp, const char *fileName); +extern void Tcl_Finalize(void); +extern int Tcl_GetCommandInfo(Tcl_Interp *interp, const char *cmdName, Tcl_CmdInfo *infoPtr); +extern const char *Tcl_GetStringResult(Tcl_Interp *interp); +extern Tcl_Obj *Tcl_NewStringObj(const char *bytes, int length); +extern Tcl_Obj *Tcl_NewIntObj(int intValue); +extern Tcl_Obj *Tcl_NewListObj(int objc, Tcl_Obj *const objv[]); +extern Tcl_Obj *Tcl_ObjSetVar2(Tcl_Interp *interp, Tcl_Obj *part1Ptr, Tcl_Obj *part2Ptr, Tcl_Obj *newValuePtr, int flags); +# endif +# undef CONST +# undef INLINE +#endif + +#ifdef _WIN32 +# undef NOMINMAX +# define NOMINMAX 1 +# undef YY_NO_UNISTD_H +# define YY_NO_UNISTD_H 1 + +# include +# include +# include + +# define strtok_r strtok_s +# define strdup _strdup +# define snprintf _snprintf +# define getcwd _getcwd +# define mkdir _mkdir +# define popen _popen +# define pclose _pclose + +# ifndef __MINGW32__ +# define PATH_MAX MAX_PATH +# define isatty _isatty +# define fileno _fileno +# endif + +// The following defines conflict with our identifiers: +# undef CONST +// `wingdi.h` defines a TRANSPARENT macro that conflicts with X(TRANSPARENT) entry in kernel/constids.inc +# undef TRANSPARENT +#endif + +#ifndef PATH_MAX +# define PATH_MAX 4096 +#endif + + +#define YOSYS_NAMESPACE Yosys +#define PRIVATE_NAMESPACE_BEGIN namespace { +#define PRIVATE_NAMESPACE_END } +#define YOSYS_NAMESPACE_BEGIN namespace Yosys { +#define YOSYS_NAMESPACE_END } +#define YOSYS_NAMESPACE_PREFIX Yosys:: +#define USING_YOSYS_NAMESPACE using namespace Yosys; + +#if defined(__GNUC__) || defined(__clang__) +# define YS_ATTRIBUTE(...) __attribute__((__VA_ARGS__)) +#elif defined(_MSC_VER) +# define YS_ATTRIBUTE(...) +#else +# define YS_ATTRIBUTE(...) +#endif + +#if defined(__GNUC__) || defined(__clang__) +# define YS_MAYBE_UNUSED __attribute__((__unused__)) +#else +# define YS_MAYBE_UNUSED +#endif + +#if __cplusplus >= 201703L +# define YS_FALLTHROUGH [[fallthrough]]; +#elif defined(__clang__) +# define YS_FALLTHROUGH [[clang::fallthrough]]; +#elif defined(__GNUC__) +# define YS_FALLTHROUGH [[gnu::fallthrough]]; +#else +# define YS_FALLTHROUGH +#endif + + +YOSYS_NAMESPACE_BEGIN + +// Note: All headers included in hashlib.h must be included +// outside of YOSYS_NAMESPACE before this or bad things will happen. +#ifdef HASHLIB_H +# undef HASHLIB_H +# include "kernel/hashlib.h" +#else +# include "kernel/hashlib.h" +# undef HASHLIB_H +#endif + + +using std::vector; +using std::string; +using std::tuple; +using std::pair; + +using std::make_tuple; +using std::make_pair; +using std::get; +using std::min; +using std::max; + +// A primitive shared string implementation that does not +// move its .c_str() when the object is copied or moved. +struct shared_str { + std::shared_ptr content; + shared_str() { } + shared_str(string s) { content = std::shared_ptr(new string(s)); } + shared_str(const char *s) { content = std::shared_ptr(new string(s)); } + const char *c_str() const { return content->c_str(); } + const string &str() const { return *content; } + bool operator==(const shared_str &other) const { return *content == *other.content; } + unsigned int hash() const { return hashlib::hash_ops::hash(*content); } +}; + +using hashlib::mkhash; +using hashlib::mkhash_init; +using hashlib::mkhash_add; +using hashlib::mkhash_xorshift; +using hashlib::hash_ops; +using hashlib::hash_cstr_ops; +using hashlib::hash_ptr_ops; +using hashlib::hash_obj_ops; +using hashlib::dict; +using hashlib::idict; +using hashlib::pool; +using hashlib::mfp; + +namespace RTLIL { + struct IdString; + struct Const; + struct SigBit; + struct SigSpec; + struct Wire; + struct Cell; + struct Memory; + struct Process; + struct Module; + struct Design; + struct Monitor; + struct Selection; + struct SigChunk; + enum State : unsigned char; + + typedef std::pair SigSig; + + namespace ID {} +} + +namespace AST { + struct AstNode; +} + +using RTLIL::IdString; +using RTLIL::Const; +using RTLIL::SigBit; +using RTLIL::SigSpec; +using RTLIL::Wire; +using RTLIL::Cell; +using RTLIL::Module; +using RTLIL::Design; + +using RTLIL::State; +using RTLIL::SigChunk; +using RTLIL::SigSig; + +namespace hashlib { + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; + + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; +} + +void memhasher_on(); +void memhasher_off(); +void memhasher_do(); + +extern bool memhasher_active; +inline void memhasher() { if (memhasher_active) memhasher_do(); } + +void yosys_banner(); +int ceil_log2(int x) YS_ATTRIBUTE(const); + +inline std::string vstringf(const char *fmt, va_list ap) +{ + // For the common case of strings shorter than 128, save a heap + // allocation by using a stack allocated buffer. + const int kBufSize = 128; + char buf[kBufSize]; + buf[0] = '\0'; + va_list apc; + va_copy(apc, ap); + int n = vsnprintf(buf, kBufSize, fmt, apc); + va_end(apc); + if (n < kBufSize) + return std::string(buf); + + std::string string; + char *str = NULL; +#if defined(_WIN32 )|| defined(__CYGWIN__) + int sz = 2 * kBufSize, rc; + while (1) { + va_copy(apc, ap); + str = (char*)realloc(str, sz); + rc = vsnprintf(str, sz, fmt, apc); + va_end(apc); + if (rc >= 0 && rc < sz) + break; + sz *= 2; + } + if (str != NULL) { + string = str; + free(str); + } + return string; +#else + if (vasprintf(&str, fmt, ap) < 0) + str = NULL; + if (str != NULL) { + string = str; + free(str); + } + return string; +#endif +} + +std::string stringf(const char *fmt, ...) YS_ATTRIBUTE(format(printf, 1, 2)); + +inline std::string stringf(const char *fmt, ...) +{ + std::string string; + va_list ap; + + va_start(ap, fmt); + string = vstringf(fmt, ap); + va_end(ap); + + return string; +} + +int readsome(std::istream &f, char *s, int n); +std::string next_token(std::string &text, const char *sep = " \t\r\n", bool long_strings = false); +std::vector split_tokens(const std::string &text, const char *sep = " \t\r\n"); +bool patmatch(const char *pattern, const char *string); +#if !defined(YOSYS_DISABLE_SPAWN) +int run_command(const std::string &command, std::function process_line = std::function()); +#endif +std::string get_base_tmpdir(); +std::string make_temp_file(std::string template_str = get_base_tmpdir() + "/yosys_XXXXXX"); +std::string make_temp_dir(std::string template_str = get_base_tmpdir() + "/yosys_XXXXXX"); +bool check_file_exists(std::string filename, bool is_exec = false); +bool check_directory_exists(const std::string& dirname); +bool is_absolute_path(std::string filename); +void remove_directory(std::string dirname); +bool create_directory(const std::string& dirname); +std::string escape_filename_spaces(const std::string& filename); + +template int GetSize(const T &obj) { return obj.size(); } +inline int GetSize(RTLIL::Wire *wire); + +extern int autoidx; +extern int yosys_xtrace; + +RTLIL::IdString new_id(std::string file, int line, std::string func); +RTLIL::IdString new_id_suffix(std::string file, int line, std::string func, std::string suffix); + +#define NEW_ID \ + YOSYS_NAMESPACE_PREFIX new_id(__FILE__, __LINE__, __FUNCTION__) +#define NEW_ID_SUFFIX(suffix) \ + YOSYS_NAMESPACE_PREFIX new_id_suffix(__FILE__, __LINE__, __FUNCTION__, suffix) + +// Create a statically allocated IdString object, using for example ID::A or ID($add). +// +// Recipe for Converting old code that is using conversion of strings like ID::A and +// "$add" for creating IdStrings: Run below SED command on the .cc file and then use for +// example "meld foo.cc foo.cc.orig" to manually compile errors, if necessary. +// +// sed -i.orig -r 's/"\\\\([a-zA-Z0-9_]+)"/ID(\1)/g; s/"(\$[a-zA-Z0-9_]+)"/ID(\1)/g;' +// +#define ID(_id) ([]() { const char *p = "\\" #_id, *q = p[1] == '$' ? p+1 : p; \ + static const YOSYS_NAMESPACE_PREFIX RTLIL::IdString id(q); return id; })() +namespace ID = RTLIL::ID; + +namespace hashlib { + template<> struct hash_ops : hash_ops {}; +} + + +YOSYS_NAMESPACE_END + +#endif diff --git a/misc/create_vcxsrc.sh b/misc/create_vcxsrc.sh index 8b39d59e30e..eee215015b5 100644 --- a/misc/create_vcxsrc.sh +++ b/misc/create_vcxsrc.sh @@ -46,7 +46,7 @@ Open "Git Bash" in this directory and run: mv yosys yosys.bak git clone https://github.com/YosysHQ/yosys.git yosys cd yosys - git checkout -B master $(git rev-parse HEAD | cut -c1-10) + git checkout -B main $(git rev-parse HEAD | cut -c1-10) unzip ../genfiles.zip EOT diff --git a/misc/jny.schema.json b/misc/jny.schema.json index 0fff8ee57d8..278b1a55f63 100644 --- a/misc/jny.schema.json +++ b/misc/jny.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://raw.githubusercontent.com/YosysHQ/yosys/master/misc/jny.schema.json", + "$id": "https://raw.githubusercontent.com/YosysHQ/yosys/main/misc/jny.schema.json", "title": "Yosys JSON Netlist metadata", "description": "Yosys JSON Netlist", "type": "object", diff --git a/passes/cmds/check.cc b/passes/cmds/check.cc index d1c83f04d88..255c32fbad9 100644 --- a/passes/cmds/check.cc +++ b/passes/cmds/check.cc @@ -19,6 +19,7 @@ #include "kernel/yosys.h" #include "kernel/sigtools.h" +#include "kernel/celledges.h" #include "kernel/celltypes.h" #include "kernel/utils.h" @@ -102,10 +103,10 @@ struct CheckPass : public Pass { SigMap sigmap(module); dict> wire_drivers; + dict driver_cells; dict wire_drivers_count; pool used_wires; - TopoSort topo; - + TopoSort> topo; for (auto &proc_it : module->processes) { std::vector all_cases = {&proc_it.second->root_case}; @@ -150,6 +151,50 @@ struct CheckPass : public Pass { } } + struct CircuitEdgesDatabase : AbstractCellEdgesDatabase { + TopoSort> &topo; + SigMap sigmap; + + CircuitEdgesDatabase(TopoSort> &topo, SigMap &sigmap) + : topo(topo), sigmap(sigmap) {} + + void add_edge(RTLIL::Cell *cell, RTLIL::IdString from_port, int from_bit, + RTLIL::IdString to_port, int to_bit, int) override { + SigSpec from_portsig = cell->getPort(from_port); + SigSpec to_portsig = cell->getPort(to_port); + log_assert(from_bit >= 0 && from_bit < from_portsig.size()); + log_assert(to_bit >= 0 && to_bit < to_portsig.size()); + SigBit from = sigmap(from_portsig[from_bit]); + SigBit to = sigmap(to_portsig[to_bit]); + + if (from.wire && to.wire) + topo.edge(std::make_pair(from.wire->name, from.offset), std::make_pair(to.wire->name, to.offset)); + } + + bool add_edges_from_cell(Cell *cell) { + if (AbstractCellEdgesDatabase::add_edges_from_cell(cell)) + return true; + + // We don't have accurate cell edges, do the fallback of all input-output pairs + for (auto &conn : cell->connections()) { + if (cell->input(conn.first)) + for (auto bit : sigmap(conn.second)) + if (bit.wire) + topo.edge(std::make_pair(bit.wire->name, bit.offset), + std::make_pair(cell->name, -1)); + + if (cell->output(conn.first)) + for (auto bit : sigmap(conn.second)) + if (bit.wire) + topo.edge(std::make_pair(cell->name, -1), + std::make_pair(bit.wire->name, bit.offset)); + } + return true; + } + }; + + CircuitEdgesDatabase edges_db(topo, sigmap); + for (auto cell : module->cells()) { if (mapped && cell->type.begins_with("$") && design->module(cell->type) == nullptr) { @@ -158,31 +203,30 @@ struct CheckPass : public Pass { counter++; cell_allowed:; } + for (auto &conn : cell->connections()) { + bool input = cell->input(conn.first); + bool output = cell->output(conn.first); + SigSpec sig = sigmap(conn.second); - bool logic_cell = yosys_celltypes.cell_evaluable(cell->type); - if (cell->input(conn.first)) - for (auto bit : sig) - if (bit.wire) { - if (logic_cell) - topo.edge(stringf("wire %s", log_signal(bit)), - stringf("cell %s (%s)", log_id(cell), log_id(cell->type))); - used_wires.insert(bit); - } - if (cell->output(conn.first)) - for (int i = 0; i < GetSize(sig); i++) { - if (logic_cell) - topo.edge(stringf("cell %s (%s)", log_id(cell), log_id(cell->type)), - stringf("wire %s", log_signal(sig[i]))); - - if (sig[i].wire || !cell->input(conn.first)) - wire_drivers[sig[i]].push_back(stringf("port %s[%d] of cell %s (%s)", - log_id(conn.first), i, log_id(cell), log_id(cell->type))); - } - if (!cell->input(conn.first) && cell->output(conn.first)) - for (auto bit : sig) - if (bit.wire) wire_drivers_count[bit]++; + for (int i = 0; i < sig.size(); i++) { + SigBit bit = sig[i]; + + if (input && bit.wire) + used_wires.insert(bit); + if (output && !input && bit.wire) + wire_drivers_count[bit]++; + if (output && (bit.wire || !input)) + wire_drivers[bit].push_back(stringf("port %s[%d] of cell %s (%s)", log_id(conn.first), i, + log_id(cell), log_id(cell->type))); + if (output) + driver_cells[bit] = cell; + } } + + if (yosys_celltypes.cell_evaluable(cell->type) || cell->type.in(ID($mem_v2), ID($memrd), ID($memrd_v2)) \ + || RTLIL::builtin_ff_cell_types().count(cell->type)) + edges_db.add_edges_from_cell(cell); } pool init_bits; @@ -239,8 +283,72 @@ struct CheckPass : public Pass { topo.sort(); for (auto &loop : topo.loops) { string message = stringf("found logic loop in module %s:\n", log_id(module)); - for (auto &str : loop) - message += stringf(" %s\n", str.c_str()); + + // `loop` only contains wire bits, or an occassional special helper node for cells for + // which we have done the edges fallback. The cell and its ports that led to an edge is + // an information we need to recover now. For that we need to have the previous wire bit + // of the loop at hand. + SigBit prev; + for (auto it = loop.rbegin(); it != loop.rend(); it++) + if (it->second != -1) { // skip the fallback helper nodes + prev = SigBit(module->wire(it->first), it->second); + break; + } + log_assert(prev != SigBit()); + + for (auto &pair : loop) { + if (pair.second == -1) + continue; // helper node for edges fallback, we can ignore it + + struct MatchingEdgePrinter : AbstractCellEdgesDatabase { + std::string &message; + SigMap &sigmap; + SigBit from, to; + int nhits; + const int HITS_LIMIT = 3; + + MatchingEdgePrinter(std::string &message, SigMap &sigmap, SigBit from, SigBit to) + : message(message), sigmap(sigmap), from(from), to(to), nhits(0) {} + + void add_edge(RTLIL::Cell *cell, RTLIL::IdString from_port, int from_bit, + RTLIL::IdString to_port, int to_bit, int) override { + SigBit edge_from = sigmap(cell->getPort(from_port))[from_bit]; + SigBit edge_to = sigmap(cell->getPort(to_port))[to_bit]; + + if (edge_from == from && edge_to == to && nhits++ < HITS_LIMIT) + message += stringf(" %s[%d] --> %s[%d]\n", log_id(from_port), from_bit, + log_id(to_port), to_bit); + if (nhits == HITS_LIMIT) + message += " ...\n"; + } + }; + + Wire *wire = module->wire(pair.first); + log_assert(wire); + SigBit bit(module->wire(pair.first), pair.second); + log_assert(driver_cells.count(bit)); + Cell *driver = driver_cells.at(bit); + + std::string driver_src; + if (driver->has_attribute(ID::src)) { + std::string src_attr = driver->get_src_attribute(); + driver_src = stringf(" source: %s", src_attr.c_str()); + } + message += stringf(" cell %s (%s)%s\n", log_id(driver), log_id(driver->type), driver_src.c_str()); + MatchingEdgePrinter printer(message, sigmap, prev, bit); + printer.add_edges_from_cell(driver); + + if (wire->name.isPublic()) { + std::string wire_src; + if (wire->has_attribute(ID::src)) { + std::string src_attr = wire->get_src_attribute(); + wire_src = stringf(" source: %s", src_attr.c_str()); + } + message += stringf(" wire %s%s\n", log_signal(SigBit(wire, pair.second)), wire_src.c_str()); + } + + prev = bit; + } log_warning("%s", message.c_str()); counter++; } diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index 85107b68f33..c2bb72f3bb3 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -36,7 +36,8 @@ struct cell_area_t { struct statdata_t { #define STAT_INT_MEMBERS X(num_wires) X(num_wire_bits) X(num_pub_wires) X(num_pub_wire_bits) \ - X(num_memories) X(num_memory_bits) X(num_cells) X(num_processes) + X(num_ports) X(num_port_bits) X(num_memories) X(num_memory_bits) X(num_cells) \ + X(num_processes) #define STAT_NUMERIC_MEMBERS STAT_INT_MEMBERS X(area) @@ -90,6 +91,11 @@ struct statdata_t for (auto wire : mod->selected_wires()) { + if (wire->port_input || wire->port_output) { + num_ports++; + num_port_bits += wire->width; + } + if (wire->name.isPublic()) { num_pub_wires++; num_pub_wire_bits += wire->width; @@ -239,6 +245,8 @@ struct statdata_t log(" Number of wire bits: %6u\n", num_wire_bits); log(" Number of public wires: %6u\n", num_pub_wires); log(" Number of public wire bits: %6u\n", num_pub_wire_bits); + log(" Number of ports: %6u\n", num_ports); + log(" Number of port bits: %6u\n", num_port_bits); log(" Number of memories: %6u\n", num_memories); log(" Number of memory bits: %6u\n", num_memory_bits); log(" Number of processes: %6u\n", num_processes); @@ -284,6 +292,8 @@ struct statdata_t log(" \"num_wire_bits\": %u,\n", num_wire_bits); log(" \"num_pub_wires\": %u,\n", num_pub_wires); log(" \"num_pub_wire_bits\": %u,\n", num_pub_wire_bits); + log(" \"num_ports\": %u,\n", num_ports); + log(" \"num_port_bits\": %u,\n", num_port_bits); log(" \"num_memories\": %u,\n", num_memories); log(" \"num_memory_bits\": %u,\n", num_memory_bits); log(" \"num_processes\": %u,\n", num_processes); @@ -494,6 +504,8 @@ struct StatPass : public Pass { design->scratchpad_set_int("stat.num_wire_bits", data.num_wire_bits); design->scratchpad_set_int("stat.num_pub_wires", data.num_pub_wires); design->scratchpad_set_int("stat.num_pub_wire_bits", data.num_pub_wire_bits); + design->scratchpad_set_int("stat.num_ports", data.num_ports); + design->scratchpad_set_int("stat.num_port_bits", data.num_port_bits); design->scratchpad_set_int("stat.num_memories", data.num_memories); design->scratchpad_set_int("stat.num_memory_bits", data.num_memory_bits); design->scratchpad_set_int("stat.num_processes", data.num_processes); diff --git a/passes/memory/memory_map.cc b/passes/memory/memory_map.cc index cafc0aaf3e0..d1dd0fd882d 100644 --- a/passes/memory/memory_map.cc +++ b/passes/memory/memory_map.cc @@ -415,7 +415,7 @@ struct MemoryMapPass : public Pass { log(" to any of the values.\n"); log("\n"); log(" -iattr\n"); - log(" for -attr, ignore case of .\n"); + log(" for -attr, suppress case sensitivity in matching of .\n"); log("\n"); log(" -rom-only\n"); log(" only perform conversion for ROMs (memories with no write ports).\n"); diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc index b3e43c18ca7..ba39b9c8086 100644 --- a/passes/opt/opt_clean.cc +++ b/passes/opt/opt_clean.cc @@ -443,13 +443,6 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos if (!raw_used_signals.check_any(s1)) { // delete wires that aren't used by anything directly goto delete_this_wire; - } else - if (!used_signals.check_any(s2)) { - // this path shouldn't be possible: this wire is used directly (otherwise it would get cleaned up above), and indirectly - // used wires are a superset of those used directly - log_assert(false); - // delete wires that aren't used by anything indirectly, even though other wires may alias it - goto delete_this_wire; } if (0) diff --git a/passes/opt/opt_demorgan.cc b/passes/opt/opt_demorgan.cc index 1464c4177cd..4db3a810105 100644 --- a/passes/opt/opt_demorgan.cc +++ b/passes/opt/opt_demorgan.cc @@ -183,7 +183,7 @@ struct OptDemorganPass : public Pass { { log_header(design, "Executing OPT_DEMORGAN pass (push inverters through $reduce_* cells).\n"); - int argidx = 0; + int argidx = 1; extra_args(args, argidx, design); unsigned int cells_changed = 0; diff --git a/passes/sat/formalff.cc b/passes/sat/formalff.cc index 264a9fb3be0..0eadb69e0eb 100644 --- a/passes/sat/formalff.cc +++ b/passes/sat/formalff.cc @@ -237,7 +237,7 @@ struct InitValWorker return true; if (ff.has_ce && initconst(ff.sig_ce.as_bit()) == (ff.pol_ce ? State::S0 : State::S1)) continue; - if (ff.has_srst && initconst(ff.sig_ce.as_bit()) == (ff.pol_srst ? State::S1 : State::S0)) + if (ff.has_srst && initconst(ff.sig_srst.as_bit()) == (ff.pol_srst ? State::S1 : State::S0)) continue; return true; diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc index 42287966288..b33e1a17df4 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -804,8 +804,10 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin for (std::string dont_use_cell : dont_use_cells) { dont_use_args += stringf("-X \"%s\" ", dont_use_cell.c_str()); } + bool first_lib = true; for (std::string liberty_file : liberty_files) { - abc_script += stringf("read_lib %s -w \"%s\" ; ", dont_use_args.c_str(), liberty_file.c_str()); + abc_script += stringf("read_lib %s %s -w \"%s\" ; ", dont_use_args.c_str(), first_lib ? "" : "-m", liberty_file.c_str()); + first_lib = false; } for (std::string liberty_file : genlib_files) abc_script += stringf("read_library \"%s\"; ", liberty_file.c_str()); diff --git a/techlibs/common/Makefile.inc b/techlibs/common/Makefile.inc index 6b377855e3e..fa5c2a825de 100644 --- a/techlibs/common/Makefile.inc +++ b/techlibs/common/Makefile.inc @@ -35,3 +35,4 @@ $(eval $(call add_share_file,share,techlibs/common/abc9_map.v)) $(eval $(call add_share_file,share,techlibs/common/abc9_unmap.v)) $(eval $(call add_share_file,share,techlibs/common/cmp2lcu.v)) $(eval $(call add_share_file,share,techlibs/common/cmp2softlogic.v)) +$(eval $(call add_share_file,share/choices,techlibs/common/choices/kogge-stone.v)) diff --git a/techlibs/common/choices/kogge-stone.v b/techlibs/common/choices/kogge-stone.v new file mode 100644 index 00000000000..ad2e2b16954 --- /dev/null +++ b/techlibs/common/choices/kogge-stone.v @@ -0,0 +1,54 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2024 Martin PoviĊĦer + * + * 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. + * + */ + +(* techmap_celltype = "$lcu" *) +module _80_lcu_kogge_stone (P, G, CI, CO); + parameter WIDTH = 2; + + (* force_downto *) + input [WIDTH-1:0] P, G; + input CI; + + (* force_downto *) + output [WIDTH-1:0] CO; + + integer i, j; + (* force_downto *) + reg [WIDTH-1:0] p, g; + + wire [1023:0] _TECHMAP_DO_ = "proc; opt -fast"; + + always @* begin + p = P; + g = G; + + // in almost all cases CI will be constant zero + g[0] = g[0] | (p[0] & CI); + + for (i = 0; i < $clog2(WIDTH); i = i + 1) begin + // iterate in reverse so we don't confuse a result from this stage and the previous + for (j = WIDTH - 1; j >= 2**i; j = j - 1) begin + g[j] = g[j] | p[j] & g[j - 2**i]; + p[j] = p[j] & p[j - 2**i]; + end + end + end + + assign CO = g; +endmodule diff --git a/techlibs/common/simlib.v b/techlibs/common/simlib.v index 489281f26a0..7dc03da6d2d 100644 --- a/techlibs/common/simlib.v +++ b/techlibs/common/simlib.v @@ -902,18 +902,34 @@ endgenerate endmodule // -------------------------------------------------------- - +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $macc (A, B, Y) +//- +//- Multiply and accumulate. +//- A building block for summing any number of negated and unnegated signals +//- and arithmetic products of pairs of signals. Cell port A concatenates pairs +//- of signals to be multiplied together. When the second signal in a pair is zero +//- length, a constant 1 is used instead as the second factor. Cell port B +//- concatenates 1-bit-wide signals to also be summed, such as "carry in" in adders. +//- Typically created by the `alumacc` pass, which transforms $add and $mul +//- into $macc cells. module \$macc (A, B, Y); parameter A_WIDTH = 0; parameter B_WIDTH = 0; parameter Y_WIDTH = 0; +// CONFIG determines the layout of A, as explained below parameter CONFIG = 4'b0000; parameter CONFIG_WIDTH = 4; -input [A_WIDTH-1:0] A; -input [B_WIDTH-1:0] B; -output reg [Y_WIDTH-1:0] Y; +// In the terms used for this cell, there's mixed meanings for the term "port". To disambiguate: +// A cell port is for example the A input (it is constructed in C++ as cell->setPort(ID::A, ...)) +// Multiplier ports are pairs of multiplier inputs ("factors"). +// If the second signal in such a pair is zero length, no multiplication is necessary, and the first signal is just added to the sum. +input [A_WIDTH-1:0] A; // Cell port A is the concatenation of all arithmetic ports +input [B_WIDTH-1:0] B; // Cell port B is the concatenation of single-bit unsigned signals to be also added to the sum +output reg [Y_WIDTH-1:0] Y; // Output sum // Xilinx XSIM does not like $clog2() below.. function integer my_clog2; @@ -929,10 +945,42 @@ function integer my_clog2; end endfunction +// Bits that a factor's length field in CONFIG per factor in cell port A localparam integer num_bits = CONFIG[3:0] > 0 ? CONFIG[3:0] : 1; +// Number of multiplier ports localparam integer num_ports = (CONFIG_WIDTH-4) / (2 + 2*num_bits); +// Minium bit width of an induction variable to iterate over all bits of cell port A localparam integer num_abits = my_clog2(A_WIDTH) > 0 ? my_clog2(A_WIDTH) : 1; +// In this pseudocode, u(foo) means an unsigned int that's foo bits long. +// The CONFIG parameter carries the following information: +// struct CONFIG { +// u4 num_bits; +// struct port_field { +// bool is_signed; +// bool is_subtract; +// u(num_bits) factor1_len; +// u(num_bits) factor2_len; +// }[num_ports]; +// }; + +// The A cell port carries the following information: +// struct A { +// u(CONFIG.port_field[0].factor1_len) port0factor1; +// u(CONFIG.port_field[0].factor2_len) port0factor2; +// u(CONFIG.port_field[1].factor1_len) port1factor1; +// u(CONFIG.port_field[1].factor2_len) port1factor2; +// ... +// }; +// and log(sizeof(A)) is num_abits. +// No factor1 may have a zero length. +// A factor2 having a zero length implies factor2 is replaced with a constant 1. + +// Additionally, B is an array of 1-bit-wide unsigned integers to also be summed up. +// Finally, we have: +// Y = port0factor1 * port0factor2 + port1factor1 * port1factor2 + ... +// * B[0] + B[1] + ... + function [2*num_ports*num_abits-1:0] get_port_offsets; input [CONFIG_WIDTH-1:0] cfg; integer i, cursor; diff --git a/techlibs/common/techmap.v b/techlibs/common/techmap.v index 7fb8173b069..68b276588b5 100644 --- a/techlibs/common/techmap.v +++ b/techlibs/common/techmap.v @@ -207,7 +207,7 @@ module _90_fa (A, B, C, X, Y); endmodule (* techmap_celltype = "$lcu" *) -module _90_lcu (P, G, CI, CO); +module _90_lcu_brent_kung (P, G, CI, CO); parameter WIDTH = 2; (* force_downto *) diff --git a/techlibs/efinix/cells_sim.v b/techlibs/efinix/cells_sim.v index 22c7bc776da..26e45464613 100644 --- a/techlibs/efinix/cells_sim.v +++ b/techlibs/efinix/cells_sim.v @@ -113,7 +113,31 @@ module EFX_GBUFCE( endmodule -module EFX_RAM_5K( +module EFX_RAM_5K +# ( + parameter READ_WIDTH = 20, + parameter WRITE_WIDTH = 20, + localparam READ_ADDR_WIDTH = + (READ_WIDTH == 16) ? 8 : // 256x16 + (READ_WIDTH == 8) ? 9 : // 512x8 + (READ_WIDTH == 4) ? 10 : // 1024x4 + (READ_WIDTH == 2) ? 11 : // 2048x2 + (READ_WIDTH == 1) ? 12 : // 4096x1 + (READ_WIDTH == 20) ? 8 : // 256x20 + (READ_WIDTH == 10) ? 9 : // 512x10 + (READ_WIDTH == 5) ? 10 : -1, // 1024x5 + + localparam WRITE_ADDR_WIDTH = + (WRITE_WIDTH == 16) ? 8 : // 256x16 + (WRITE_WIDTH == 8) ? 9 : // 512x8 + (WRITE_WIDTH == 4) ? 10 : // 1024x4 + (WRITE_WIDTH == 2) ? 11 : // 2048x2 + (WRITE_WIDTH == 1) ? 12 : // 4096x1 + (WRITE_WIDTH == 20) ? 8 : // 256x20 + (WRITE_WIDTH == 10) ? 9 : // 512x10 + (WRITE_WIDTH == 5) ? 10 : -1 // 1024x5 +) +( input [WRITE_WIDTH-1:0] WDATA, input [WRITE_ADDR_WIDTH-1:0] WADDR, input WE, @@ -126,8 +150,6 @@ module EFX_RAM_5K( (* clkbuf_sink *) input RCLK ); - parameter READ_WIDTH = 20; - parameter WRITE_WIDTH = 20; parameter OUTPUT_REG = 1'b0; parameter RCLK_POLARITY = 1'b1; parameter RE_POLARITY = 1'b1; @@ -155,25 +177,4 @@ module EFX_RAM_5K( parameter INIT_11 = 256'h0000000000000000000000000000000000000000000000000000000000000000; parameter INIT_12 = 256'h0000000000000000000000000000000000000000000000000000000000000000; parameter INIT_13 = 256'h0000000000000000000000000000000000000000000000000000000000000000; - - localparam READ_ADDR_WIDTH = - (READ_WIDTH == 16) ? 8 : // 256x16 - (READ_WIDTH == 8) ? 9 : // 512x8 - (READ_WIDTH == 4) ? 10 : // 1024x4 - (READ_WIDTH == 2) ? 11 : // 2048x2 - (READ_WIDTH == 1) ? 12 : // 4096x1 - (READ_WIDTH == 20) ? 8 : // 256x20 - (READ_WIDTH == 10) ? 9 : // 512x10 - (READ_WIDTH == 5) ? 10 : -1; // 1024x5 - - localparam WRITE_ADDR_WIDTH = - (WRITE_WIDTH == 16) ? 8 : // 256x16 - (WRITE_WIDTH == 8) ? 9 : // 512x8 - (WRITE_WIDTH == 4) ? 10 : // 1024x4 - (WRITE_WIDTH == 2) ? 11 : // 2048x2 - (WRITE_WIDTH == 1) ? 12 : // 4096x1 - (WRITE_WIDTH == 20) ? 8 : // 256x20 - (WRITE_WIDTH == 10) ? 9 : // 512x10 - (WRITE_WIDTH == 5) ? 10 : -1; // 1024x5 - endmodule diff --git a/tests/arch/quicklogic/.gitignore b/tests/arch/quicklogic/.gitignore index 9a71dca69b0..ae20ed34262 100644 --- a/tests/arch/quicklogic/.gitignore +++ b/tests/arch/quicklogic/.gitignore @@ -1,4 +1,4 @@ *.log -/run-test.mk +run-test.mk +*_synth.v +*_testbench diff --git a/tests/arch/run-test.sh b/tests/arch/run-test.sh index a14b795090d..68f925b34d9 100755 --- a/tests/arch/run-test.sh +++ b/tests/arch/run-test.sh @@ -16,7 +16,7 @@ for arch in ../../techlibs/*; do done else echo -n "Test $path ->" - iverilog -t null -I$arch $path + iverilog -t null -I$arch -g2005-sv $path echo " ok" fi done diff --git a/tests/cxxrtl/test_value_fuzz.cc b/tests/cxxrtl/test_value_fuzz.cc index 4428e9f6e7f..a7bbb293afc 100644 --- a/tests/cxxrtl/test_value_fuzz.cc +++ b/tests/cxxrtl/test_value_fuzz.cc @@ -25,6 +25,11 @@ T rand_int(T min = std::numeric_limits::min(), T max = std::numeric_limits return dist(generator); } +int64_t sext(size_t bits, uint64_t value) +{ + return (int64_t)(value << (64 - bits)) >> (64 - bits); +} + struct BinaryOperationBase { void tweak_input(uint64_t &a, uint64_t &b) {} @@ -246,6 +251,106 @@ struct CtlzTest } } ctlz; +struct UdivTest : BinaryOperationBase +{ + UdivTest() + { + std::printf("Randomized tests for value::udivmod (div):\n"); + test_binary_operation(*this); + } + + uint64_t reference_impl(size_t bits, uint64_t a, uint64_t b) + { + return a / b; + } + + template + cxxrtl::value testing_impl(cxxrtl::value a, cxxrtl::value b) + { + return std::get<0>(a.udivmod(b)); + } + + void tweak_input(uint64_t &, uint64_t &b) + { + if (b == 0) b = 1; // Avoid divide by zero + } +} udiv; + +struct UmodTest : BinaryOperationBase +{ + UmodTest() + { + std::printf("Randomized tests for value::udivmod (mod):\n"); + test_binary_operation(*this); + } + + uint64_t reference_impl(size_t bits, uint64_t a, uint64_t b) + { + return a % b; + } + + template + cxxrtl::value testing_impl(cxxrtl::value a, cxxrtl::value b) + { + return std::get<1>(a.udivmod(b)); + } + + void tweak_input(uint64_t &, uint64_t &b) + { + if (b == 0) b = 1; // Avoid divide by zero + } +} umod; + +struct SdivTest : BinaryOperationBase +{ + SdivTest() + { + std::printf("Randomized tests for value::sdivmod (div):\n"); + test_binary_operation(*this); + } + + uint64_t reference_impl(size_t bits, uint64_t a, uint64_t b) + { + return (uint64_t)(sext(bits, a) / sext(bits, b)); + } + + template + cxxrtl::value testing_impl(cxxrtl::value a, cxxrtl::value b) + { + return std::get<0>(a.sdivmod(b)); + } + + void tweak_input(uint64_t &, uint64_t &b) + { + if (b == 0) b = 1; // Avoid divide by zero + } +} sdiv; + +struct SmodTest : BinaryOperationBase +{ + SmodTest() + { + std::printf("Randomized tests for value::sdivmod (mod):\n"); + test_binary_operation(*this); + } + + uint64_t reference_impl(size_t bits, uint64_t a, uint64_t b) + { + return (uint64_t)(sext(bits, a) % sext(bits, b)); + } + + template + cxxrtl::value testing_impl(cxxrtl::value a, cxxrtl::value b) + { + return std::get<1>(a.sdivmod(b)); + } + + void tweak_input(uint64_t &, uint64_t &b) + { + if (b == 0) b = 1; // Avoid divide by zero + } +} smod; + int main() { } diff --git a/tests/memlib/memlib_9b1B.v b/tests/memlib/memlib_9b1B.v index 55545ebd4db..9a8246c9f19 100644 --- a/tests/memlib/memlib_9b1B.v +++ b/tests/memlib/memlib_9b1B.v @@ -1,4 +1,14 @@ -module RAM_9b1B ( +module RAM_9b1B +#( + parameter INIT = 0, + parameter OPTION_INIT = "UNDEFINED", + parameter PORT_R_WIDTH = 9, + parameter PORT_W_WIDTH = 9, + parameter PORT_R_CLK_POL = 0, + parameter PORT_W_CLK_POL = 0, + parameter PORT_W_WR_EN_WIDTH = 1 +) +( input PORT_R_CLK, input [6:0] PORT_R_ADDR, output reg [PORT_R_WIDTH-1:0] PORT_R_RD_DATA, @@ -8,14 +18,6 @@ module RAM_9b1B ( input [PORT_W_WIDTH-1:0] PORT_W_WR_DATA ); -parameter INIT = 0; -parameter OPTION_INIT = "UNDEFINED"; -parameter PORT_R_WIDTH = 9; -parameter PORT_W_WIDTH = 9; -parameter PORT_R_CLK_POL = 0; -parameter PORT_W_CLK_POL = 0; -parameter PORT_W_WR_EN_WIDTH = 1; - reg [8:0] mem [0:15]; integer i; diff --git a/tests/memlib/memlib_wren.v b/tests/memlib/memlib_wren.v index b9433d42e66..c687184bdca 100644 --- a/tests/memlib/memlib_wren.v +++ b/tests/memlib/memlib_wren.v @@ -1,4 +1,11 @@ -module RAM_WREN ( +module RAM_WREN #( + parameter ABITS=4, + parameter WIDTH=8, + parameter PORT_A_WR_EN_WIDTH=1, + parameter PORT_A_WR_BE_WIDTH=0, + parameter OPTION_BYTESIZE=WIDTH, + parameter WB=OPTION_BYTESIZE +)( input PORT_A_CLK, input [ABITS-1:0] PORT_A_ADDR, input [WIDTH-1:0] PORT_A_WR_DATA, @@ -7,13 +14,6 @@ module RAM_WREN ( input [PORT_A_WR_BE_WIDTH-1:0] PORT_A_WR_BE ); -parameter ABITS=4; -parameter WIDTH=8; -parameter PORT_A_WR_EN_WIDTH=1; -parameter PORT_A_WR_BE_WIDTH=0; -parameter OPTION_BYTESIZE=WIDTH; -parameter WB=OPTION_BYTESIZE; - reg [WIDTH-1:0] mem [0:2**ABITS-1]; integer i; diff --git a/tests/memories/issue00335.v b/tests/memories/issue00335.v index f3b6e5dfe28..305339dd959 100644 --- a/tests/memories/issue00335.v +++ b/tests/memories/issue00335.v @@ -2,13 +2,15 @@ // expect-rd-ports 1 // expect-rd-clk \clk -module ram2 (input clk, +module ram2 #( + parameter SIZE = 5 // Address size + ) (input clk, input sel, input we, input [SIZE-1:0] adr, input [63:0] dat_i, output reg [63:0] dat_o); - parameter SIZE = 5; // Address size + reg [63:0] mem [0:(1 << SIZE)-1]; integer i; diff --git a/tests/opt/opt_dff_qd.ys b/tests/opt/opt_dff_qd.ys index 7b0b4c2242a..c6232643f1f 100644 --- a/tests/opt/opt_dff_qd.ys +++ b/tests/opt/opt_dff_qd.ys @@ -7,7 +7,7 @@ module top(...); input CLK; input EN; (* init = 24'h555555 *) -output [19:0] Q; +output [17:0] Q; input SRST; input ARST; input [1:0] CLR; @@ -23,26 +23,20 @@ $sdffce #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b1), .SRST_POLARITY(1'b1), .SRST_V $dffsr #(.CLK_POLARITY(1'b1), .CLR_POLARITY(1'b1), .SET_POLARITY(1'b0), .WIDTH(2)) ff7 (.CLK(CLK), .SET(SET), .CLR(CLR), .D(Q[15:14]), .Q(Q[15:14])); $dffsre #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b0), .CLR_POLARITY(1'b1), .SET_POLARITY(1'b0), .WIDTH(2)) ff8 (.CLK(CLK), .EN(EN), .SET(SET), .CLR(CLR), .D(Q[17:16]), .Q(Q[17:16])); -$dlatch #(.EN_POLARITY(1'b1), .WIDTH(2)) ff9 (.EN(EN), .D(Q[19:18]), .Q(Q[19:18])); -$adlatch #(.EN_POLARITY(1'b0), .ARST_POLARITY(1'b1), .ARST_VALUE(2'h2), .WIDTH(2)) ff10 (.EN(EN), .ARST(ARST), .D(Q[21:20]), .Q(Q[21:20])); -$dlatchsr #(.EN_POLARITY(1'b0), .CLR_POLARITY(1'b1), .SET_POLARITY(1'b0), .WIDTH(2)) ff11 (.EN(EN), .SET(SET), .CLR(CLR), .D(Q[23:22]), .Q(Q[23:22])); - endmodule EOT design -save orig -# Equivalence check will fail for unmapped adlatch and dlatchsr due to negative hold hack. -delete top/ff10 top/ff11 equiv_opt -undef -assert -multiclock opt_dff -keepdc design -load orig opt_dff -keepdc select -assert-count 1 t:$and select -assert-count 3 t:$dffe -select -assert-count 3 t:$dlatch -select -assert-count 3 t:$sr +select -assert-count 2 t:$dlatch +select -assert-count 2 t:$sr select -assert-none t:$and t:$dffe t:$dlatch t:$sr %% %n t:* %i design -load orig @@ -50,7 +44,7 @@ simplemap opt_dff -keepdc select -assert-count 2 t:$_AND_ select -assert-count 6 t:$_DFFE_??_ -select -assert-count 6 t:$_DLATCH_?_ -select -assert-count 6 t:$_SR_??_ +select -assert-count 4 t:$_DLATCH_?_ +select -assert-count 4 t:$_SR_??_ select -assert-none t:$_AND_ t:$_DFFE_??_ t:$_DLATCH_?_ t:$_SR_??_ %% %n t:* %i diff --git a/tests/simple/.gitignore b/tests/simple/.gitignore index 073f46157e1..5daaadbd734 100644 --- a/tests/simple/.gitignore +++ b/tests/simple/.gitignore @@ -1,2 +1,3 @@ *.log *.out +*.err diff --git a/tests/techmap/kogge-stone.ys b/tests/techmap/kogge-stone.ys new file mode 100644 index 00000000000..fc3637f10b0 --- /dev/null +++ b/tests/techmap/kogge-stone.ys @@ -0,0 +1 @@ +test_cell -s 1711533949 -n 10 -map +/techmap.v -map +/choices/kogge-stone.v $lcu diff --git a/tests/various/check.ys b/tests/various/check.ys new file mode 100644 index 00000000000..41b9960911f --- /dev/null +++ b/tests/various/check.ys @@ -0,0 +1,43 @@ +design -reset +read -vlog2k <