diff --git a/Makefile b/Makefile index d3a7cf09f..ec01e3394 100644 --- a/Makefile +++ b/Makefile @@ -129,6 +129,12 @@ LIBS += -ldw # SILIMATE: support for backward-cpp CXXFLAGS += -I/usr/include/libdwarf/ -DBACKWARD_HAS_DW # SILIMATE: support for backward-cpp endif +ifneq ($(shell :; command -v rsync),) +RSYNC_CP ?= rsync -rc +else +RSYNC_CP ?= cp -ru +endif + ifeq ($(OS), Darwin) PLUGIN_LINKFLAGS += -undefined dynamic_lookup LINKFLAGS += -rdynamic @@ -169,7 +175,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.51+46 +YOSYS_VER := 0.51+85 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'.' -f3) @@ -1074,13 +1080,13 @@ docs/source/cmd/abc.rst: $(TARGETS) $(EXTRA_TARGETS) $(Q) mkdir -p docs/source/cmd $(Q) mkdir -p temp/docs/source/cmd $(Q) cd temp && ./../$(PROGRAM_PREFIX)yosys -p 'help -write-rst-command-reference-manual' - $(Q) rsync -rc temp/docs/source/cmd docs/source + $(Q) $(RSYNC_CP) temp/docs/source/cmd docs/source $(Q) rm -rf temp docs/source/cell/word_add.rst: $(TARGETS) $(EXTRA_TARGETS) $(Q) mkdir -p docs/source/cell $(Q) mkdir -p temp/docs/source/cell $(Q) cd temp && ./../$(PROGRAM_PREFIX)yosys -p 'help -write-rst-cells-manual' - $(Q) rsync -rc temp/docs/source/cell docs/source + $(Q) $(RSYNC_CP) temp/docs/source/cell docs/source $(Q) rm -rf temp docs/source/generated/cells.json: docs/source/generated $(TARGETS) $(EXTRA_TARGETS) diff --git a/docs/source/_images/Makefile b/docs/source/_images/Makefile index cc00050f1..71f52c578 100644 --- a/docs/source/_images/Makefile +++ b/docs/source/_images/Makefile @@ -3,6 +3,12 @@ all: examples all_tex # set a fake time in pdf generation to prevent unnecessary differences in output FAKETIME := TZ='Z' faketime -f '2022-01-01 00:00:00 x0,001' +ifneq ($(shell :; command -v rsync),) +RSYNC_CP ?= rsync -t +else +RSYNC_CP ?= cp -a +endif + # find all code example makefiles .PHONY: examples CODE_EXAMPLES := ../code_examples/*/Makefile @@ -19,7 +25,7 @@ FORCE: ../%/Makefile: FORCE @make -C $(@D) dots @mkdir -p $* - @find $(@D) -name *.dot -exec rsync -t {} $* \; + @find $(@D) -name *.dot -exec $(RSYNC_CP) {} $* \; # find and build all tex files .PHONY: all_tex diff --git a/docs/source/conf.py b/docs/source/conf.py index 6a0e92167..dc7cbfe60 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -16,15 +16,17 @@ html_theme_options: dict[str] = { "source_branch": "main", "source_directory": "docs/source/", } +html_context: dict[str] = {} # try to fix the readthedocs detection -html_context: dict[str] = { - "READTHEDOCS": True, - "display_github": True, - "github_user": "YosysHQ", - "github_repo": "yosys", - "slug": "yosys", -} +if os.getenv("READTHEDOCS"): + html_context.update({ + "READTHEDOCS": True, + "display_github": True, + "github_user": "YosysHQ", + "github_repo": "yosys", + "slug": "yosys", + }) # override source_branch if not main git_slug = os.getenv("READTHEDOCS_VERSION_NAME") diff --git a/docs/source/getting_started/installation.rst b/docs/source/getting_started/installation.rst index 4f31af966..fe96b2314 100644 --- a/docs/source/getting_started/installation.rst +++ b/docs/source/getting_started/installation.rst @@ -34,31 +34,18 @@ Targeted architectures The `OSS CAD Suite`_ releases `nightly builds`_ for the following architectures: -.. only:: html +- **linux-x64** - Most personal Linux based computers +- **darwin-x64** - macOS 12 or later with Intel CPU +- **darwin-arm64** - macOS 12 or later with M1/M2 CPU +- **windows-x64** - Targeted for Windows 10 and 11 +- **linux-arm64** - Devices such as Raspberry Pi with 64bit OS - - linux-x64 |linux-x64| - - Most personal Linux based computers - - - darwin-x64 |darwin-x64| - - macOS 12 or later with Intel CPU - - - darwin-arm64 |darwin-arm64| - - macOS 12 or later with M1/M2 CPU - - - windows-x64 |windows-x64| - - Targeted for Windows 10 and 11 - - - linux-arm64 |linux-arm64| +For more information about the targeted architectures, and the current build +status, check the `OSS CAD Suite`_ git repository. .. _OSS CAD Suite: https://github.com/YosysHQ/oss-cad-suite-build .. _nightly builds: https://github.com/YosysHQ/oss-cad-suite-build/releases/latest -.. |linux-x64| image:: https://github.com/YosysHQ/oss-cad-suite-build/actions/workflows/linux-x64.yml/badge.svg -.. |darwin-x64| image:: https://github.com/YosysHQ/oss-cad-suite-build/actions/workflows/darwin-x64.yml/badge.svg -.. |darwin-arm64| image:: https://github.com/YosysHQ/oss-cad-suite-build/actions/workflows/darwin-arm64.yml/badge.svg -.. |windows-x64| image:: https://github.com/YosysHQ/oss-cad-suite-build/actions/workflows/windows-x64.yml/badge.svg -.. |linux-arm64| image:: https://github.com/YosysHQ/oss-cad-suite-build/actions/workflows/linux-arm64.yml/badge.svg - Building from source ~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/source/yosys_internals/extending_yosys/test_suites.rst b/docs/source/yosys_internals/extending_yosys/test_suites.rst index 264b97ce1..3e5f45b94 100644 --- a/docs/source/yosys_internals/extending_yosys/test_suites.rst +++ b/docs/source/yosys_internals/extending_yosys/test_suites.rst @@ -6,22 +6,12 @@ Testing Yosys Automatic testing ----------------- -.. only:: html - - The `Yosys Git repo`_ has automatic testing of builds and running of the - included test suite on the following platforms: - - - Ubuntu |test-linux| - - macOS |test-macos| +The `Yosys Git repo`_ has automatic testing of builds and running of the +included test suite on both Ubuntu and macOS, as well as across range of +compiler versions. For up to date information, including OS versions, refer to +`the git actions page`_. .. _Yosys Git repo: https://github.com/YosysHQ/yosys - -.. |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`_. - .. _the git actions page: https://github.com/YosysHQ/yosys/actions .. todo:: are unit tests currently working diff --git a/libs/fst/00_PATCH_i386_endian.patch b/libs/fst/00_PATCH_i386_endian.patch new file mode 100644 index 000000000..3857f1fdf --- /dev/null +++ b/libs/fst/00_PATCH_i386_endian.patch @@ -0,0 +1,14 @@ +--- fstapi.cc ++++ fstapi.cc +@@ -4723,7 +4723,10 @@ if(gzread_pass_status) + hdr_incomplete = (xc->start_time == 0) && (xc->end_time == 0); + + fstFread(&dcheck, 8, 1, xc->f); +- xc->double_endian_match = (dcheck == FST_DOUBLE_ENDTEST); ++ /* ++ * Yosys patch: Fix double endian check for i386 targets built in modern gcc ++ */ ++ xc->double_endian_match = (dcheck == (double)FST_DOUBLE_ENDTEST); + if(!xc->double_endian_match) + { + union { diff --git a/libs/fst/00_UPDATE.sh b/libs/fst/00_UPDATE.sh index 66a0fd8df..302e187f2 100755 --- a/libs/fst/00_UPDATE.sh +++ b/libs/fst/00_UPDATE.sh @@ -19,3 +19,4 @@ patch -p0 < 00_PATCH_win_zlib.patch patch -p0 < 00_PATCH_win_io.patch patch -p1 < 00_PATCH_strict_alignment.patch patch -p0 < 00_PATCH_wx_len_overread.patch +patch -p0 < 00_PATCH_i386_endian.patch diff --git a/libs/fst/fstapi.cc b/libs/fst/fstapi.cc index ab3c54469..2463e6f15 100644 --- a/libs/fst/fstapi.cc +++ b/libs/fst/fstapi.cc @@ -4723,7 +4723,10 @@ if(gzread_pass_status) hdr_incomplete = (xc->start_time == 0) && (xc->end_time == 0); fstFread(&dcheck, 8, 1, xc->f); - xc->double_endian_match = (dcheck == FST_DOUBLE_ENDTEST); + /* + * Yosys patch: Fix double endian check for i386 targets built in modern gcc + */ + xc->double_endian_match = (dcheck == (double)FST_DOUBLE_ENDTEST); if(!xc->double_endian_match) { union { diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index 1e67dcd88..d4eb22ca5 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -45,9 +45,9 @@ struct Slice { } static int parse_index(const char *begin, const char *end, const std::string &slice) { - int value; + int value = 0; auto result = std::from_chars(begin, end, value, 10); - if (result.ptr != end || result.ptr == begin) + if (result.ptr != end || result.ptr == begin) syntax_error(slice); return value; } diff --git a/passes/opt/opt_merge.cc b/passes/opt/opt_merge.cc index e23752471..b519d33d6 100644 --- a/passes/opt/opt_merge.cc +++ b/passes/opt/opt_merge.cc @@ -26,6 +26,8 @@ #include #include #include +#include +#include USING_YOSYS_NAMESPACE @@ -42,6 +44,22 @@ struct OptMergeWorker CellTypes ct; int total_count; + static vector> sorted_pmux_in(const dict &conn) + { + SigSpec sig_s = conn.at(ID::S); + SigSpec sig_b = conn.at(ID::B); + + int s_width = GetSize(sig_s); + int width = GetSize(sig_b) / s_width; + + vector> sb_pairs; + for (int i = 0; i < s_width; i++) + sb_pairs.push_back(pair(sig_s[i], sig_b.extract(i*width, width))); + + std::sort(sb_pairs.begin(), sb_pairs.end()); + return sb_pairs; + } + static void sort_pmux_conn(dict &conn) { SigSpec sig_s = conn.at(ID::S); @@ -65,95 +83,78 @@ struct OptMergeWorker } } - std::string int_to_hash_string(unsigned int v) + Hasher hash_cell_inputs(const RTLIL::Cell *cell, Hasher h) const { - if (v == 0) - return "0"; - std::string str = ""; - while (v > 0) { - str += 'a' + (v & 15); - v = v >> 4; - } - return str; - } - - uint64_t hash_cell_parameters_and_connections(const RTLIL::Cell *cell) - { - vector hash_conn_strings; - std::string hash_string = cell->type.str() + "\n"; - - const dict *conn = &cell->connections(); - dict alt_conn; - + // TODO: when implemented, use celltypes to match: + // (builtin || stdcell) && (unary || binary) && symmetrical if (cell->type.in(ID($and), ID($or), ID($xor), ID($xnor), ID($add), ID($mul), ID($logic_and), ID($logic_or), ID($_AND_), ID($_OR_), ID($_XOR_))) { - alt_conn = *conn; - if (assign_map(alt_conn.at(ID::A)) < assign_map(alt_conn.at(ID::B))) { - alt_conn[ID::A] = conn->at(ID::B); - alt_conn[ID::B] = conn->at(ID::A); + std::array inputs = { + assign_map(cell->getPort(ID::A)), + assign_map(cell->getPort(ID::B)) + }; + std::sort(inputs.begin(), inputs.end()); + h = hash_ops>::hash_into(inputs, h); + } else if (cell->type.in(ID($reduce_xor), ID($reduce_xnor))) { + SigSpec a = assign_map(cell->getPort(ID::A)); + a.sort(); + h = a.hash_into(h); + } else if (cell->type.in(ID($reduce_and), ID($reduce_or), ID($reduce_bool))) { + SigSpec a = assign_map(cell->getPort(ID::A)); + a.sort_and_unify(); + h = a.hash_into(h); + } else if (cell->type == ID($pmux)) { + dict conn = cell->connections(); + assign_map.apply(conn.at(ID::A)); + assign_map.apply(conn.at(ID::B)); + assign_map.apply(conn.at(ID::S)); + for (const auto& [s_bit, b_chunk] : sorted_pmux_in(conn)) { + h = s_bit.hash_into(h); + h = b_chunk.hash_into(h); } - conn = &alt_conn; - } else - if (cell->type.in(ID($reduce_xor), ID($reduce_xnor))) { - alt_conn = *conn; - assign_map.apply(alt_conn.at(ID::A)); - alt_conn.at(ID::A).sort(); - conn = &alt_conn; - } else - if (cell->type.in(ID($reduce_and), ID($reduce_or), ID($reduce_bool))) { - alt_conn = *conn; - assign_map.apply(alt_conn.at(ID::A)); - alt_conn.at(ID::A).sort_and_unify(); - conn = &alt_conn; - } else - if (cell->type == ID($pmux)) { - alt_conn = *conn; - assign_map.apply(alt_conn.at(ID::A)); - assign_map.apply(alt_conn.at(ID::B)); - assign_map.apply(alt_conn.at(ID::S)); - sort_pmux_conn(alt_conn); - conn = &alt_conn; - } - - for (auto &it : *conn) { - RTLIL::SigSpec sig; - if (cell->output(it.first)) { - if (it.first == ID::Q && RTLIL::builtin_ff_cell_types().count(cell->type)) { - // For the 'Q' output of state elements, - // use its (* init *) attribute value - sig = initvals(it.second); + h = assign_map(cell->getPort(ID::A)).hash_into(h); + } else { + std::vector> conns; + for (const auto& conn : cell->connections()) { + conns.push_back(conn); + } + std::sort(conns.begin(), conns.end()); + for (const auto& [port, sig] : conns) { + if (!cell->output(port)) { + h = port.hash_into(h); + h = assign_map(sig).hash_into(h); } - else - continue; } - else - sig = assign_map(it.second); - string s = "C " + it.first.str() + "="; - for (auto &chunk : sig.chunks()) { - if (chunk.wire) - s += "{" + chunk.wire->name.str() + " " + - int_to_hash_string(chunk.offset) + " " + - int_to_hash_string(chunk.width) + "}"; - else - s += RTLIL::Const(chunk.data).as_string(); - } - hash_conn_strings.push_back(s + "\n"); + + if (RTLIL::builtin_ff_cell_types().count(cell->type)) + h = initvals(cell->getPort(ID::Q)).hash_into(h); + } - - for (auto &it : cell->parameters) - hash_conn_strings.push_back("P " + it.first.str() + "=" + it.second.as_string() + "\n"); - - std::sort(hash_conn_strings.begin(), hash_conn_strings.end()); - - for (auto it : hash_conn_strings) - hash_string += it; - - return std::hash{}(hash_string); + return h; } - bool compare_cell_parameters_and_connections(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2) + static Hasher hash_cell_parameters(const RTLIL::Cell *cell, Hasher h) { - log_assert(cell1 != cell2); + using Paramvec = std::vector>; + Paramvec params; + for (const auto& param : cell->parameters) { + params.push_back(param); + } + std::sort(params.begin(), params.end()); + return hash_ops::hash_into(params, h); + } + + Hasher hash_cell_function(const RTLIL::Cell *cell, Hasher h) const + { + h.eat(cell->type); + h = hash_cell_inputs(cell, h); + h = hash_cell_parameters(cell, h); + return h; + } + + bool compare_cell_parameters_and_connections(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2) const + { + if (cell1 == cell2) return true; if (cell1->type != cell2->type) return false; if (cell1->parameters != cell2->parameters) @@ -252,21 +253,51 @@ struct OptMergeWorker initvals.set(&assign_map, module); bool did_something = true; + // A cell may have to go through a lot of collisions if the hash + // function is performing poorly, but it's a symptom of something bad + // beyond the user's control. while (did_something) { std::vector cells; - cells.reserve(module->cells_.size()); - for (auto &it : module->cells_) { - if (!design->selected(module, it.second)) + cells.reserve(module->cells().size()); + for (auto cell : module->cells()) { + if (!design->selected(module, cell)) continue; - if (mode_keepdc && has_dont_care_initval(it.second)) + if (cell->type.in(ID($meminit), ID($meminit_v2), ID($mem), ID($mem_v2))) { + // Ignore those for performance: meminit can have an excessively large port, + // mem can have an excessively large parameter holding the init data continue; - if (ct.cell_known(it.second->type) || (mode_share_all && it.second->known())) - cells.push_back(it.second); + } + if (mode_keepdc && has_dont_care_initval(cell)) + continue; + if (ct.cell_known(cell->type) || (mode_share_all && cell->known())) + cells.push_back(cell); } did_something = false; - dict sharemap; + + // We keep a set of known cells. They're hashed with our hash_cell_function + // and compared with our compare_cell_parameters_and_connections. + // Both need to capture OptMergeWorker to access initvals + struct CellPtrHash { + const OptMergeWorker& worker; + CellPtrHash(const OptMergeWorker& w) : worker(w) {} + std::size_t operator()(const Cell* c) const { + return (std::size_t)worker.hash_cell_function(c, Hasher()).yield(); + } + }; + struct CellPtrEqual { + const OptMergeWorker& worker; + CellPtrEqual(const OptMergeWorker& w) : worker(w) {} + bool operator()(const Cell* lhs, const Cell* rhs) const { + return worker.compare_cell_parameters_and_connections(lhs, rhs); + } + }; + std::unordered_set< + RTLIL::Cell*, + CellPtrHash, + CellPtrEqual> known_cells (0, CellPtrHash(*this), CellPtrEqual(*this)); + for (auto cell : cells) { if ((!mode_share_all && !ct.cell_known(cell->type)) || !cell->known()) @@ -275,40 +306,36 @@ struct OptMergeWorker if (cell->type == ID($scopeinfo)) continue; - uint64_t hash = hash_cell_parameters_and_connections(cell); - auto r = sharemap.insert(std::make_pair(hash, cell)); - if (!r.second) { - if (compare_cell_parameters_and_connections(cell, r.first->second)) { - // SILIMATE: Keep the shorter name always - if (cell->name.str() < r.first->second->name.str()) - std::swap(r.first->second, cell); - - if (cell->has_keep_attr()) { - if (r.first->second->has_keep_attr()) - continue; - std::swap(r.first->second, cell); - } - - - did_something = true; - log_debug(" Cell `%s' is identical to cell `%s'.\n", cell->name.c_str(), r.first->second->name.c_str()); - for (auto &it : cell->connections()) { - if (cell->output(it.first)) { - RTLIL::SigSpec other_sig = r.first->second->getPort(it.first); - log_debug(" Redirecting output %s: %s = %s\n", it.first.c_str(), - log_signal(it.second), log_signal(other_sig)); - Const init = initvals(other_sig); - initvals.remove_init(it.second); - initvals.remove_init(other_sig); - module->connect(RTLIL::SigSig(it.second, other_sig)); - assign_map.add(it.second, other_sig); - initvals.set_init(other_sig, init); - } - } - log_debug(" Removing %s cell `%s' from module `%s'.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str()); - module->remove(cell); - total_count++; + auto [cell_in_map, inserted] = known_cells.insert(cell); + if (!inserted) { + // We've failed to insert since we already have an equivalent cell + Cell* other_cell = *cell_in_map; + if (cell->has_keep_attr()) { + if (other_cell->has_keep_attr()) + continue; + known_cells.erase(other_cell); + known_cells.insert(cell); + std::swap(other_cell, cell); } + + did_something = true; + log_debug(" Cell `%s' is identical to cell `%s'.\n", cell->name.c_str(), other_cell->name.c_str()); + for (auto &it : cell->connections()) { + if (cell->output(it.first)) { + RTLIL::SigSpec other_sig = other_cell->getPort(it.first); + log_debug(" Redirecting output %s: %s = %s\n", it.first.c_str(), + log_signal(it.second), log_signal(other_sig)); + Const init = initvals(other_sig); + initvals.remove_init(it.second); + initvals.remove_init(other_sig); + module->connect(RTLIL::SigSig(it.second, other_sig)); + assign_map.add(it.second, other_sig); + initvals.set_init(other_sig, init); + } + } + log_debug(" Removing %s cell `%s' from module `%s'.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str()); + module->remove(cell); + total_count++; } } } diff --git a/passes/sat/cutpoint.cc b/passes/sat/cutpoint.cc index bca6a5ec6..88f995dda 100644 --- a/passes/sat/cutpoint.cc +++ b/passes/sat/cutpoint.cc @@ -34,8 +34,8 @@ struct CutpointPass : public Pass { log("This command adds formal cut points to the design.\n"); log("\n"); log(" -undef\n"); - log(" set cupoint nets to undef (x). the default behavior is to create a\n"); - log(" $anyseq cell and drive the cutpoint net from that\n"); + log(" set cutpoint nets to undef (x). the default behavior is to create\n"); + log(" an $anyseq cell and drive the cutpoint net from that\n"); log("\n"); } void execute(std::vector args, RTLIL::Design *design) override diff --git a/techlibs/common/simlib.v b/techlibs/common/simlib.v index 028e4e5a9..6e39aa60a 100644 --- a/techlibs/common/simlib.v +++ b/techlibs/common/simlib.v @@ -2224,10 +2224,10 @@ module \$print (EN, TRG, ARGS); parameter PRIORITY = 0; parameter FORMAT = ""; -parameter ARGS_WIDTH = 0; +parameter signed ARGS_WIDTH = 0; parameter TRG_ENABLE = 1; -parameter TRG_WIDTH = 0; +parameter signed TRG_WIDTH = 0; parameter TRG_POLARITY = 0; input EN; diff --git a/tests/cxxrtl/test_value_fuzz.cc b/tests/cxxrtl/test_value_fuzz.cc index 7e8fbb0a9..a77120136 100644 --- a/tests/cxxrtl/test_value_fuzz.cc +++ b/tests/cxxrtl/test_value_fuzz.cc @@ -241,7 +241,10 @@ struct CtlzTest { if (a == 0) return bits; - return __builtin_clzl(a) - (64 - bits); + if (sizeof(long) == 4) + return __builtin_clzll(a) - (64 - bits); + else + return __builtin_clzl(a) - (64 - bits); } template diff --git a/tests/opt/opt_merge_basic.ys b/tests/opt/opt_merge_basic.ys new file mode 100644 index 000000000..9de320aaa --- /dev/null +++ b/tests/opt/opt_merge_basic.ys @@ -0,0 +1,166 @@ +read_verilog < B; +assign Y = A > B; +endmodule +EOT +# Exercise the general case in hash_cell_inputs - accept +opt_expr +select -assert-count 2 t:$gt +equiv_opt -assert opt_merge +design -load postopt +select -assert-count 1 t:$gt + +design -reset +read_verilog < B; +assign Y = A > C; // <- look here +endmodule +EOT +# Exercise the general case in hash_cell_inputs - reject +opt_expr +select -assert-count 2 t:$gt +opt_merge +select -assert-count 2 t:$gt