diff --git a/Makefile b/Makefile index 148c40a67..1ee7bc850 100644 --- a/Makefile +++ b/Makefile @@ -177,7 +177,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.60+88 +YOSYS_VER := 0.60+102 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) diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index d9f14469b..054f9b31a 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -2161,6 +2161,9 @@ void dump_case_actions(std::ostream &f, std::string indent, RTLIL::CaseRule *cs) bool dump_proc_switch_ifelse(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw) { + if (sw->cases.empty()) + return true; + for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) { if ((*it)->compare.size() == 0) { break; diff --git a/kernel/hashlib.h b/kernel/hashlib.h index 1456a945b..142506083 100644 --- a/kernel/hashlib.h +++ b/kernel/hashlib.h @@ -1327,6 +1327,12 @@ public: return i < 0 ? 0 : 1; } + int lookup(const K &key) const + { + Hasher::hash_t hash = database.do_hash(key); + return database.do_lookup_no_rehash(key, hash); + } + void expect(const K &key, int i) { int j = (*this)(key); diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 127bda2a4..2bf91030e 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -2151,6 +2151,8 @@ public: int wires_size() const { return wires_.size(); } RTLIL::Wire* wire_at(int index) const { return wires_.element(index)->second; } RTLIL::ObjRange cells() { return RTLIL::ObjRange(&cells_, &refcount_cells_); } + int cells_size() const { return cells_.size(); } + RTLIL::Cell* cell_at(int index) const { return cells_.element(index)->second; } void add(RTLIL::Binding *binding); diff --git a/passes/cmds/sdc/sdc.cc b/passes/cmds/sdc/sdc.cc index fad001e50..635aad016 100644 --- a/passes/cmds/sdc/sdc.cc +++ b/passes/cmds/sdc/sdc.cc @@ -165,7 +165,12 @@ struct SdcObjects { if (!top) log_error("Top module couldn't be determined. Check 'top' attribute usage"); for (auto port : top->ports) { - design_ports.push_back(std::make_pair(port.str().substr(1), top->wire(port))); + RTLIL::Wire *wire = top->wire(port); + if (!wire) { + // This should not be possible. See https://github.com/YosysHQ/yosys/pull/5594#issue-3791198573 + log_error("Port %s doesn't exist", log_id(port)); + } + design_ports.push_back(std::make_pair(port.str().substr(1), wire)); } std::list hierarchy{}; sniff_module(hierarchy, top); diff --git a/passes/opt/opt_merge.cc b/passes/opt/opt_merge.cc index 69474b5f9..a6121b268 100644 --- a/passes/opt/opt_merge.cc +++ b/passes/opt/opt_merge.cc @@ -22,6 +22,7 @@ #include "kernel/sigtools.h" #include "kernel/log.h" #include "kernel/celltypes.h" +#include "kernel/threading.h" #include "libs/sha1/sha1.h" #include #include @@ -37,16 +38,73 @@ PRIVATE_NAMESPACE_BEGIN template inline Hasher hash_pair(const T &t, const U &u) { return hash_ops>::hash(t, u); } -struct OptMergeWorker +// Some cell and its hash value. +struct CellHash { - RTLIL::Design *design; - RTLIL::Module *module; - SigMap assign_map; - FfInitVals initvals; - bool mode_share_all; + // Index of a cell in the module + int cell_index; + Hasher::hash_t hash_value; +}; - CellTypes ct; - int total_count; +// The algorithm: +// 1) Compute and store the hashes of all relevant cells, in parallel. +// 2) Given N = the number of threads, partition the cells into N buckets by hash value: +// bucket k contains the cells whose hash value mod N = k. +// 3) For each bucket in parallel, build a hashtable of that bucket’s cells (using the +// precomputed hashes) and record the duplicates found. +// 4) On the main thread, process the list of duplicates to remove cells. +// For efficiency we fuse the second step into the first step by having the parallel +// threads write the cells into buckets directly. +// To avoid synchronization overhead, we divide each bucket into N shards. Each +// thread j adds a cell to bucket k by writing to shard j of bucket k — +// no synchronization required. In the next phase, thread k builds the hashtable for +// bucket k by iterating over all shards of the bucket. + +// The input to each thread in the "compute cell hashes" phase. +struct CellRange +{ + int begin; + int end; +}; + +// The output from each thread in the "compute cell hashes" phase. +struct CellHashes +{ + // Entry i contains the hashes where hash_value % bucketed_cell_hashes.size() == i + std::vector> bucketed_cell_hashes; +}; + +// A duplicate cell that has been found. +struct DuplicateCell +{ + // Remove this cell from the design + int remove_cell; + // ... and use this cell instead. + int keep_cell; +}; + +// The input to each thread in the "find duplicate cells" phase. +// Shards of buckets of cell hashes +struct Shards +{ + std::vector>> &bucketed_cell_hashes; +}; + +// The output from each thread in the "find duplicate cells" phase. +struct FoundDuplicates +{ + std::vector duplicates; +}; + +struct OptMergeThreadWorker +{ + const RTLIL::Module *module; + const SigMap &assign_map; + const FfInitVals &initvals; + const CellTypes &ct; + int workers; + bool mode_share_all; + bool mode_keepdc; static Hasher hash_pmux_in(const SigSpec& sig_s, const SigSpec& sig_b, Hasher h) { @@ -62,8 +120,8 @@ struct OptMergeWorker static void sort_pmux_conn(dict &conn) { - SigSpec sig_s = conn.at(ID::S); - SigSpec sig_b = conn.at(ID::B); + const SigSpec &sig_s = conn.at(ID::S); + const SigSpec &sig_b = conn.at(ID::B); int s_width = GetSize(sig_s); int width = GetSize(sig_b) / s_width; @@ -144,7 +202,6 @@ struct OptMergeWorker if (cell1->parameters != cell2->parameters) return false; - if (cell1->connections_.size() != cell2->connections_.size()) return false; for (const auto &it : cell1->connections_) @@ -199,7 +256,7 @@ struct OptMergeWorker return conn1 == conn2; } - bool has_dont_care_initval(const RTLIL::Cell *cell) + bool has_dont_care_initval(const RTLIL::Cell *cell) const { if (!cell->is_builtin_ff()) return false; @@ -207,36 +264,134 @@ struct OptMergeWorker return !initvals(cell->getPort(ID::Q)).is_fully_def(); } - OptMergeWorker(RTLIL::Design *design, RTLIL::Module *module, bool mode_nomux, bool mode_share_all, bool mode_keepdc) : - design(design), module(module), mode_share_all(mode_share_all) + OptMergeThreadWorker(const RTLIL::Module *module, const FfInitVals &initvals, + const SigMap &assign_map, const CellTypes &ct, int workers, + bool mode_share_all, bool mode_keepdc) : + module(module), assign_map(assign_map), initvals(initvals), ct(ct), + workers(workers), mode_share_all(mode_share_all), mode_keepdc(mode_keepdc) { - total_count = 0; - ct.setup_internals(); - ct.setup_internals_mem(); - ct.setup_stdcells(); - ct.setup_stdcells_mem(); + } - if (mode_nomux) { - ct.cell_types.erase(ID($mux)); - ct.cell_types.erase(ID($pmux)); + CellHashes compute_cell_hashes(const CellRange &cell_range) const + { + std::vector> bucketed_cell_hashes(workers); + for (int cell_index = cell_range.begin; cell_index < cell_range.end; ++cell_index) { + const RTLIL::Cell *cell = module->cell_at(cell_index); + if (!module->selected(cell)) + continue; + 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 (cell->type == ID($scopeinfo)) + continue; + if (mode_keepdc && has_dont_care_initval(cell)) + continue; + if (!cell->known()) + continue; + if (!mode_share_all && !ct.cell_known(cell->type)) + continue; + + Hasher::hash_t h = hash_cell_function(cell, Hasher()).yield(); + int bucket_index = h % workers; + bucketed_cell_hashes[bucket_index].push_back({cell_index, h}); } + return {std::move(bucketed_cell_hashes)}; + } - ct.cell_types.erase(ID($tribuf)); - ct.cell_types.erase(ID($_TBUF_)); - ct.cell_types.erase(ID($anyseq)); - ct.cell_types.erase(ID($anyconst)); - ct.cell_types.erase(ID($allseq)); - ct.cell_types.erase(ID($allconst)); - ct.cell_types.erase(ID($check)); - ct.cell_types.erase(ID($assert)); - ct.cell_types.erase(ID($assume)); - ct.cell_types.erase(ID($live)); - ct.cell_types.erase(ID($cover)); + FoundDuplicates find_duplicate_cells(int index, const Shards &in) const + { + // We keep a set of known cells. They're hashed with our hash_cell_function + // and compared with our compare_cell_parameters_and_connections. + struct CellHashOp { + std::size_t operator()(const CellHash &c) const { + return (std::size_t)c.hash_value; + } + }; + struct CellEqualOp { + const OptMergeThreadWorker& worker; + CellEqualOp(const OptMergeThreadWorker& w) : worker(w) {} + bool operator()(const CellHash &lhs, const CellHash &rhs) const { + return worker.compare_cell_parameters_and_connections( + worker.module->cell_at(lhs.cell_index), + worker.module->cell_at(rhs.cell_index)); + } + }; + std::unordered_set< + CellHash, + CellHashOp, + CellEqualOp> known_cells(0, CellHashOp(), CellEqualOp(*this)); + + std::vector duplicates; + for (const std::vector> &buckets : in.bucketed_cell_hashes) { + // Clear out our buckets as we go. This keeps the work of deallocation + // off the main thread. + std::vector bucket = std::move(buckets[index]); + for (CellHash c : bucket) { + auto [cell_in_map, inserted] = known_cells.insert(c); + if (inserted) + continue; + CellHash map_c = *cell_in_map; + if (module->cell_at(c.cell_index)->has_keep_attr()) { + if (module->cell_at(map_c.cell_index)->has_keep_attr()) + continue; + known_cells.erase(map_c); + known_cells.insert(c); + std::swap(c, map_c); + } + duplicates.push_back({c.cell_index, map_c.cell_index}); + } + } + return {duplicates}; + } +}; + +template +void initialize_queues(std::vector> &queues, int size) { + queues.reserve(size); + for (int i = 0; i < size; ++i) + queues.emplace_back(1); +} + +struct OptMergeWorker +{ + int total_count; + + OptMergeWorker(RTLIL::Module *module, const CellTypes &ct, bool mode_share_all, bool mode_keepdc) : + total_count(0) + { + SigMap assign_map(module); + FfInitVals initvals; + initvals.set(&assign_map, module); log("Finding identical cells in module `%s'.\n", module->name); - assign_map.set(module); - initvals.set(&assign_map, module); + // Use no more than one worker per thousand cells, rounded down, so + // we only start multithreading with at least 2000 cells. + int num_worker_threads = ThreadPool::pool_size(0, module->cells_size()/1000); + int workers = std::max(1, num_worker_threads); + + // The main thread doesn't do any work, so if there is only one worker thread, + // just run everything on the main thread instead. + // This avoids creating and waiting on a thread, which is pretty high overhead + // for very small modules. + if (num_worker_threads == 1) + num_worker_threads = 0; + OptMergeThreadWorker thread_worker(module, initvals, assign_map, ct, workers, mode_share_all, mode_keepdc); + + std::vector> cell_ranges_queues(num_worker_threads); + std::vector> cell_hashes_queues(num_worker_threads); + std::vector> shards_queues(num_worker_threads); + std::vector> duplicates_queues(num_worker_threads); + + ThreadPool thread_pool(num_worker_threads, [&](int i) { + while (std::optional c = cell_ranges_queues[i].pop_front()) { + cell_hashes_queues[i].push_back(thread_worker.compute_cell_hashes(*c)); + std::optional shards = shards_queues[i].pop_front(); + duplicates_queues[i].push_back(thread_worker.find_duplicate_cells(i, *shards)); + } + }); bool did_something = true; // A cell may have to go through a lot of collisions if the hash @@ -244,91 +399,99 @@ struct OptMergeWorker // beyond the user's control. while (did_something) { - std::vector cells; - cells.reserve(module->cells().size()); - for (auto cell : module->cells()) { - if (!design->selected(module, cell)) - continue; - 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 (cell->type == ID($scopeinfo)) - continue; - if (mode_keepdc && has_dont_care_initval(cell)) - continue; - if (!cell->known()) - continue; - if (!mode_share_all && !ct.cell_known(cell->type)) - continue; - cells.push_back(cell); - } + int cells_size = module->cells_size(); + log("Computing hashes of %d cells of `%s'.\n", cells_size, module->name); + std::vector>> sharded_bucketed_cell_hashes(workers); - did_something = false; - - // 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)); - - std::vector redirects; - for (auto cell : cells) + int cell_index = 0; + int cells_size_mod_workers = cells_size % workers; { - 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, other_cell->name); - 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, - log_signal(it.second), log_signal(other_sig)); - redirects.push_back(RTLIL::SigSig(it.second, std::move(other_sig))); - } - } - log_debug(" Removing %s cell `%s' from module `%s'.\n", cell->type, cell->name, module->name); - module->remove(cell); - total_count++; + Multithreading multithreading; + for (int i = 0; i < workers; ++i) { + int num_cells = cells_size/workers + ((i < cells_size_mod_workers) ? 1 : 0); + CellRange c = { cell_index, cell_index + num_cells }; + cell_index += num_cells; + if (num_worker_threads > 0) + cell_ranges_queues[i].push_back(c); + else + sharded_bucketed_cell_hashes[i] = std::move(thread_worker.compute_cell_hashes(c).bucketed_cell_hashes); } + log_assert(cell_index == cells_size); + if (num_worker_threads > 0) + for (int i = 0; i < workers; ++i) + sharded_bucketed_cell_hashes[i] = std::move(cell_hashes_queues[i].pop_front()->bucketed_cell_hashes); } - for (const RTLIL::SigSig &redirect : redirects) { - module->connect(redirect); - Const init = initvals(redirect.second); - initvals.remove_init(redirect.first); - initvals.remove_init(redirect.second); - assign_map.add(redirect.first, redirect.second); - initvals.set_init(redirect.second, init); + + log("Finding duplicate cells in `%s'.\n", module->name); + std::vector merged_duplicates; + { + Multithreading multithreading; + for (int i = 0; i < workers; ++i) { + Shards thread_shards = { sharded_bucketed_cell_hashes }; + if (num_worker_threads > 0) + shards_queues[i].push_back(thread_shards); + else { + std::vector d = std::move(thread_worker.find_duplicate_cells(i, thread_shards).duplicates); + merged_duplicates.insert(merged_duplicates.end(), d.begin(), d.end()); + } + } + if (num_worker_threads > 0) + for (int i = 0; i < workers; ++i) { + std::vector d = std::move(duplicates_queues[i].pop_front()->duplicates); + merged_duplicates.insert(merged_duplicates.end(), d.begin(), d.end()); + } } + std::sort(merged_duplicates.begin(), merged_duplicates.end(), [](const DuplicateCell &lhs, const DuplicateCell &rhs) { + // Sort them by the order in which duplicates would have been detected in a single-threaded + // run. The cell at which the duplicate would have been detected is the latter of the two + // cells involved. + return std::max(lhs.remove_cell, lhs.keep_cell) < std::max(rhs.remove_cell, rhs.keep_cell); + }); + + // Convert to cell pointers because removing cells will invalidate the indices. + std::vector> cell_ptrs; + for (DuplicateCell dup : merged_duplicates) + cell_ptrs.push_back({module->cell_at(dup.remove_cell), module->cell_at(dup.keep_cell)}); + + for (auto [remove_cell, keep_cell] : cell_ptrs) + { + log_debug(" Cell `%s' is identical to cell `%s'.\n", remove_cell->name, keep_cell->name); + for (auto &it : remove_cell->connections()) { + if (remove_cell->output(it.first)) { + RTLIL::SigSpec keep_sig = keep_cell->getPort(it.first); + log_debug(" Redirecting output %s: %s = %s\n", it.first, + log_signal(it.second), log_signal(keep_sig)); + Const init = initvals(keep_sig); + initvals.remove_init(it.second); + initvals.remove_init(keep_sig); + module->connect(RTLIL::SigSig(it.second, keep_sig)); + auto keep_sig_it = keep_sig.begin(); + for (SigBit remove_sig_bit : it.second) { + assign_map.add(remove_sig_bit, *keep_sig_it); + ++keep_sig_it; + } + initvals.set_init(keep_sig, init); + } + } + log_debug(" Removing %s cell `%s' from module `%s'.\n", remove_cell->type, remove_cell->name, module->name); + module->remove(remove_cell); + total_count++; + } + did_something = !merged_duplicates.empty(); } + for (ConcurrentQueue &q : cell_ranges_queues) + q.close(); + + for (ConcurrentQueue &q : shards_queues) + q.close(); + + for (ConcurrentQueue &q : cell_ranges_queues) + q.close(); + + for (ConcurrentQueue &q : shards_queues) + q.close(); + log_suppressed(); } }; @@ -381,9 +544,25 @@ struct OptMergePass : public Pass { } extra_args(args, argidx, design); + CellTypes ct; + ct.setup_internals(); + ct.setup_internals_mem(); + ct.setup_stdcells(); + ct.setup_stdcells_mem(); + if (mode_nomux) { + ct.cell_types.erase(ID($mux)); + ct.cell_types.erase(ID($pmux)); + } + ct.cell_types.erase(ID($tribuf)); + ct.cell_types.erase(ID($_TBUF_)); + ct.cell_types.erase(ID($anyseq)); + ct.cell_types.erase(ID($anyconst)); + ct.cell_types.erase(ID($allseq)); + ct.cell_types.erase(ID($allconst)); + int total_count = 0; for (auto module : design->selected_modules()) { - OptMergeWorker worker(design, module, mode_nomux, mode_share_all, mode_keepdc); + OptMergeWorker worker(module, ct, mode_share_all, mode_keepdc); total_count += worker.total_count; } diff --git a/passes/proc/proc_clean.cc b/passes/proc/proc_clean.cc index 19c2be4ca..8cccb96c4 100644 --- a/passes/proc/proc_clean.cc +++ b/passes/proc/proc_clean.cc @@ -97,6 +97,7 @@ void proc_clean_switch(RTLIL::SwitchRule *sw, RTLIL::CaseRule *parent, bool &did all_empty = false; if (all_empty) { + did_something = true; for (auto cs : sw->cases) delete cs; sw->cases.clear(); diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index 91b3b563a..083778d3c 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -39,6 +39,7 @@ OBJS += passes/techmap/muxcover.o OBJS += passes/techmap/aigmap.o OBJS += passes/techmap/tribuf.o OBJS += passes/techmap/lut2mux.o +OBJS += passes/techmap/lut2bmux.o OBJS += passes/techmap/nlutmap.o OBJS += passes/techmap/shregmap.o OBJS += passes/techmap/deminout.o diff --git a/passes/techmap/lut2bmux.cc b/passes/techmap/lut2bmux.cc new file mode 100644 index 000000000..42042c942 --- /dev/null +++ b/passes/techmap/lut2bmux.cc @@ -0,0 +1,58 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct Lut2BmuxPass : public Pass { + Lut2BmuxPass() : Pass("lut2bmux", "convert $lut to $bmux") { } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" lut2bmux [options] [selection]\n"); + log("\n"); + log("This pass converts $lut cells to $bmux cells.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) override + { + log_header(design, "Executing LUT2BMUX pass (convert $lut to $bmux).\n"); + + size_t argidx = 1; + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + for (auto cell : module->selected_cells()) { + if (cell->type == ID($lut)) { + cell->type = ID($bmux); + cell->setPort(ID::S, cell->getPort(ID::A)); + cell->setPort(ID::A, cell->getParam(ID::LUT)); + cell->unsetParam(ID::LUT); + cell->fixup_parameters(); + log("Converted %s.%s to BMUX cell.\n", log_id(module), log_id(cell)); + } + } + } +} Lut2BmuxPass; + +PRIVATE_NAMESPACE_END diff --git a/tests/proc/bug5572.ys b/tests/proc/bug5572.ys new file mode 100644 index 000000000..1d8f4e514 --- /dev/null +++ b/tests/proc/bug5572.ys @@ -0,0 +1,19 @@ +read_rtlil << EOT +attribute \top 1 +module \top + wire width 1 \sig + wire width 1 \val + + process $2 + switch \sig [0] + case 1'0 + case 1'1 + case + assign \val [0] 1'1 + end + end +end +EOT +proc_rmdead +proc_clean +select -assert-none p:* diff --git a/tests/techmap/lut2bmux.ys b/tests/techmap/lut2bmux.ys new file mode 100644 index 000000000..2d7387fc1 --- /dev/null +++ b/tests/techmap/lut2bmux.ys @@ -0,0 +1,24 @@ +read_rtlil << EOT +module \top + wire width 4 input 0 \A + wire output 1 \Y + + cell $lut $0 + parameter \WIDTH 4 + parameter \LUT 16'0110100110010110 + connect \A \A + connect \Y \Y + end +end +EOT + +hierarchy -auto-top + + +equiv_opt -assert lut2bmux + + +lut2bmux + +select -assert-count 0 t:$lut +select -assert-count 1 t:$bmux r:WIDTH=1 r:S_WIDTH=4 %i diff --git a/tests/verilog/.gitignore b/tests/verilog/.gitignore index b16ed0890..6a226989c 100644 --- a/tests/verilog/.gitignore +++ b/tests/verilog/.gitignore @@ -1,3 +1,4 @@ +/bug5572.v /const_arst.v /const_sr.v /doubleslash.v diff --git a/tests/verilog/bug5572.ys b/tests/verilog/bug5572.ys new file mode 100644 index 000000000..3044e3572 --- /dev/null +++ b/tests/verilog/bug5572.ys @@ -0,0 +1,15 @@ +read_rtlil << EOT +module \top + wire \sig + wire \val + process $2 + attribute \full_case 1 + switch \sig + end + end +end +EOT + +write_verilog bug5572.v +design -reset +read_verilog bug5572.v