diff options
Diffstat (limited to 'passes/proc')
-rw-r--r-- | passes/proc/Makefile.inc | 1 | ||||
-rw-r--r-- | passes/proc/proc.cc | 27 | ||||
-rw-r--r-- | passes/proc/proc_arst.cc | 13 | ||||
-rw-r--r-- | passes/proc/proc_clean.cc | 8 | ||||
-rw-r--r-- | passes/proc/proc_dff.cc | 8 | ||||
-rw-r--r-- | passes/proc/proc_dlatch.cc | 449 | ||||
-rw-r--r-- | passes/proc/proc_init.cc | 39 | ||||
-rw-r--r-- | passes/proc/proc_mux.cc | 242 | ||||
-rw-r--r-- | passes/proc/proc_rmdead.cc | 12 |
9 files changed, 708 insertions, 91 deletions
diff --git a/passes/proc/Makefile.inc b/passes/proc/Makefile.inc index dfbc78eae..397fe46a1 100644 --- a/passes/proc/Makefile.inc +++ b/passes/proc/Makefile.inc @@ -5,5 +5,6 @@ OBJS += passes/proc/proc_rmdead.o OBJS += passes/proc/proc_init.o OBJS += passes/proc/proc_arst.o OBJS += passes/proc/proc_mux.o +OBJS += passes/proc/proc_dlatch.o OBJS += passes/proc/proc_dff.o diff --git a/passes/proc/proc.cc b/passes/proc/proc.cc index 30efbab4b..d5366f266 100644 --- a/passes/proc/proc.cc +++ b/passes/proc/proc.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * 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 @@ -40,22 +40,29 @@ struct ProcPass : public Pass { log(" proc_init\n"); log(" proc_arst\n"); log(" proc_mux\n"); + log(" proc_dlatch\n"); log(" proc_dff\n"); log(" proc_clean\n"); log("\n"); - log("This replaces the processes in the design with multiplexers and flip-flops.\n"); + log("This replaces the processes in the design with multiplexers,\n"); + log("flip-flops and latches.\n"); log("\n"); log("The following options are supported:\n"); log("\n"); log(" -global_arst [!]<netname>\n"); log(" This option is passed through to proc_arst.\n"); log("\n"); + log(" -ifx\n"); + log(" This option is passed through to proc_mux. proc_rmdead is not\n"); + log(" executed in -ifx mode.\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { std::string global_arst; + bool ifxmode = false; - log_header("Executing PROC pass (convert processes to netlists).\n"); + log_header(design, "Executing PROC pass (convert processes to netlists).\n"); log_push(); size_t argidx; @@ -65,23 +72,29 @@ struct ProcPass : public Pass { global_arst = args[++argidx]; continue; } + if (args[argidx] == "-ifx") { + ifxmode = true; + continue; + } break; } extra_args(args, argidx, design); Pass::call(design, "proc_clean"); - Pass::call(design, "proc_rmdead"); + if (!ifxmode) + Pass::call(design, "proc_rmdead"); Pass::call(design, "proc_init"); if (global_arst.empty()) Pass::call(design, "proc_arst"); else Pass::call(design, "proc_arst -global_arst " + global_arst); - Pass::call(design, "proc_mux"); + Pass::call(design, ifxmode ? "proc_mux -ifx" : "proc_mux"); + Pass::call(design, "proc_dlatch"); Pass::call(design, "proc_dff"); Pass::call(design, "proc_clean"); log_pop(); } } ProcPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/proc/proc_arst.cc b/passes/proc/proc_arst.cc index 27c6b3bcf..216b00ddd 100644 --- a/passes/proc/proc_arst.cc +++ b/passes/proc/proc_arst.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * 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 @@ -226,7 +226,7 @@ struct ProcArstPass : public Pass { std::string global_arst; bool global_arst_neg = false; - log_header("Executing PROC_ARST pass (detect async resets in processes).\n"); + log_header(design, "Executing PROC_ARST pass (detect async resets in processes).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -244,6 +244,7 @@ struct ProcArstPass : public Pass { } extra_args(args, argidx, design); + pool<Wire*> delete_initattr_wires; for (auto mod : design->modules()) if (design->selected(mod)) { @@ -265,6 +266,7 @@ struct ProcArstPass : public Pass { value.extend_u0(chunk.wire->width, false); arst_sig.append(chunk); arst_val.append(value.extract(chunk.offset, chunk.width)); + delete_initattr_wires.insert(chunk.wire); } if (arst_sig.size()) { log("Added global reset to process %s: %s <- %s\n", @@ -281,7 +283,10 @@ struct ProcArstPass : public Pass { } } } + + for (auto wire : delete_initattr_wires) + wire->attributes.erase("\\init"); } } ProcArstPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/proc/proc_clean.cc b/passes/proc/proc_clean.cc index 82716cd06..7dbabc211 100644 --- a/passes/proc/proc_clean.cc +++ b/passes/proc/proc_clean.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * 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 @@ -156,7 +156,7 @@ struct ProcCleanPass : public Pass { virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { int total_count = 0; - log_header("Executing PROC_CLEAN pass (remove empty switches from decision trees).\n"); + log_header(design, "Executing PROC_CLEAN pass (remove empty switches from decision trees).\n"); extra_args(args, 1, design); @@ -183,5 +183,5 @@ struct ProcCleanPass : public Pass { log("Cleaned up %d empty switch%s.\n", total_count, total_count == 1 ? "" : "es"); } } ProcCleanPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/proc/proc_dff.cc b/passes/proc/proc_dff.cc index 76842da6b..f532990c2 100644 --- a/passes/proc/proc_dff.cc +++ b/passes/proc/proc_dff.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * 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 @@ -369,7 +369,7 @@ struct ProcDffPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing PROC_DFF pass (convert process syncs to FFs).\n"); + log_header(design, "Executing PROC_DFF pass (convert process syncs to FFs).\n"); extra_args(args, 1, design); @@ -382,5 +382,5 @@ struct ProcDffPass : public Pass { } } } ProcDffPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/proc/proc_dlatch.cc b/passes/proc/proc_dlatch.cc new file mode 100644 index 000000000..6621afd33 --- /dev/null +++ b/passes/proc/proc_dlatch.cc @@ -0,0 +1,449 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * 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/register.h" +#include "kernel/sigtools.h" +#include "kernel/consteval.h" +#include "kernel/log.h" +#include <sstream> +#include <stdlib.h> +#include <stdio.h> + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct proc_dlatch_db_t +{ + Module *module; + SigMap sigmap; + + pool<Cell*> generated_dlatches; + dict<Cell*, vector<SigBit>> mux_srcbits; + dict<SigBit, pair<Cell*, int>> mux_drivers; + dict<SigBit, int> sigusers; + + proc_dlatch_db_t(Module *module) : module(module), sigmap(module) + { + for (auto cell : module->cells()) + { + if (cell->type.in("$mux", "$pmux")) + { + auto sig_y = sigmap(cell->getPort("\\Y")); + for (int i = 0; i < GetSize(sig_y); i++) + mux_drivers[sig_y[i]] = pair<Cell*, int>(cell, i); + + pool<SigBit> mux_srcbits_pool; + for (auto bit : sigmap(cell->getPort("\\A"))) + mux_srcbits_pool.insert(bit); + for (auto bit : sigmap(cell->getPort("\\B"))) + mux_srcbits_pool.insert(bit); + + vector<SigBit> mux_srcbits_vec; + for (auto bit : mux_srcbits_pool) + if (bit.wire != nullptr) + mux_srcbits_vec.push_back(bit); + + mux_srcbits[cell].swap(mux_srcbits_vec); + } + + for (auto &conn : cell->connections()) + if (!cell->known() || cell->input(conn.first)) + for (auto bit : sigmap(conn.second)) + sigusers[bit]++; + } + + for (auto wire : module->wires()) + if (wire->port_input) + for (auto bit : sigmap(wire)) + sigusers[bit]++; + } + + bool quickcheck(const SigSpec &haystack, const SigSpec &needle) + { + pool<SigBit> haystack_bits = sigmap(haystack).to_sigbit_pool(); + pool<SigBit> needle_bits = sigmap(needle).to_sigbit_pool(); + + pool<Cell*> cells_queue, cells_visited; + pool<SigBit> bits_queue, bits_visited; + + bits_queue = haystack_bits; + while (!bits_queue.empty()) + { + for (auto &bit : bits_queue) { + auto it = mux_drivers.find(bit); + if (it != mux_drivers.end()) + if (!cells_visited.count(it->second.first)) + cells_queue.insert(it->second.first); + bits_visited.insert(bit); + } + + bits_queue.clear(); + + for (auto c : cells_queue) { + for (auto bit : mux_srcbits[c]) { + if (needle_bits.count(bit)) + return true; + if (!bits_visited.count(bit)) + bits_queue.insert(bit); + } + } + + cells_queue.clear(); + } + + return false; + } + + struct rule_node_t + { + // a node is true if "signal" equals "match" and [any + // of the child nodes is true or "children" is empty] + SigBit signal, match; + vector<int> children; + + bool operator==(const rule_node_t &other) const { + return signal == other.signal && match == other.match && children == other.children; + } + + unsigned int hash() const { + unsigned int h = mkhash_init; + mkhash(h, signal.hash()); + mkhash(h, match.hash()); + for (auto i : children) mkhash(h, i); + return h; + } + }; + + enum tf_node_types_t : int { + true_node = 1, + false_node = 2 + }; + + idict<rule_node_t, 3> rules_db; + dict<int, SigBit> rules_sig; + + int make_leaf(SigBit signal, SigBit match) + { + rule_node_t node; + node.signal = signal; + node.match = match; + return rules_db(node); + } + + int make_inner(SigBit signal, SigBit match, int child) + { + rule_node_t node; + node.signal = signal; + node.match = match; + node.children.push_back(child); + return rules_db(node); + } + + int make_inner(const pool<int> &children) + { + rule_node_t node; + node.signal = State::S0; + node.match = State::S0; + node.children = vector<int>(children.begin(), children.end()); + std::sort(node.children.begin(), node.children.end()); + return rules_db(node); + } + + int find_mux_feedback(SigBit haystack, SigBit needle, bool set_undef) + { + if (sigusers[haystack] > 1) + set_undef = false; + + if (haystack == needle) + return true_node; + + auto it = mux_drivers.find(haystack); + if (it == mux_drivers.end()) + return false_node; + + Cell *cell = it->second.first; + int index = it->second.second; + + SigSpec sig_a = sigmap(cell->getPort("\\A")); + SigSpec sig_b = sigmap(cell->getPort("\\B")); + SigSpec sig_s = sigmap(cell->getPort("\\S")); + int width = GetSize(sig_a); + + pool<int> children; + + int n = find_mux_feedback(sig_a[index], needle, set_undef); + if (n != false_node) { + if (set_undef && sig_a[index] == needle) { + SigSpec sig = cell->getPort("\\A"); + sig[index] = State::Sx; + cell->setPort("\\A", sig); + } + for (int i = 0; i < GetSize(sig_s); i++) + n = make_inner(sig_s[i], State::S0, n); + children.insert(n); + } + + for (int i = 0; i < GetSize(sig_s); i++) { + n = find_mux_feedback(sig_b[i*width + index], needle, set_undef); + if (n != false_node) { + if (set_undef && sig_b[i*width + index] == needle) { + SigSpec sig = cell->getPort("\\B"); + sig[i*width + index] = State::Sx; + cell->setPort("\\B", sig); + } + children.insert(make_inner(sig_s[i], State::S1, n)); + } + } + + if (children.empty()) + return false_node; + + return make_inner(children); + } + + SigBit make_hold(int n) + { + if (n == true_node) + return State::S1; + + if (n == false_node) + return State::S0; + + if (rules_sig.count(n)) + return rules_sig.at(n); + + const rule_node_t &rule = rules_db[n]; + SigSpec and_bits; + + if (rule.signal != rule.match) { + if (rule.match == State::S1) + and_bits.append(rule.signal); + else if (rule.match == State::S0) + and_bits.append(module->Not(NEW_ID, rule.signal)); + else + and_bits.append(module->Eq(NEW_ID, rule.signal, rule.match)); + } + + if (!rule.children.empty()) { + SigSpec or_bits; + for (int k : rule.children) + or_bits.append(make_hold(k)); + and_bits.append(module->ReduceOr(NEW_ID, or_bits)); + } + + if (GetSize(and_bits) == 2) + and_bits = module->And(NEW_ID, and_bits[0], and_bits[1]); + log_assert(GetSize(and_bits) == 1); + + rules_sig[n] = and_bits[0]; + return and_bits[0]; + } + + void fixup_mux(Cell *cell) + { + SigSpec sig_a = cell->getPort("\\A"); + SigSpec sig_b = cell->getPort("\\B"); + SigSpec sig_s = cell->getPort("\\S"); + SigSpec sig_any_valid_b; + + SigSpec sig_new_b, sig_new_s; + for (int i = 0; i < GetSize(sig_s); i++) { + SigSpec b = sig_b.extract(i*GetSize(sig_a), GetSize(sig_a)); + if (!b.is_fully_undef()) { + sig_any_valid_b = b; + sig_new_b.append(b); + sig_new_s.append(sig_s[i]); + } + } + + if (sig_new_s.empty()) { + sig_new_b = sig_a; + sig_new_s = State::S0; + } + + if (sig_a.is_fully_undef() && !sig_any_valid_b.empty()) + cell->setPort("\\A", sig_any_valid_b); + + if (GetSize(sig_new_s) == 1) { + cell->type = "$mux"; + cell->unsetParam("\\S_WIDTH"); + } else { + cell->type = "$pmux"; + cell->setParam("\\S_WIDTH", GetSize(sig_new_s)); + } + + cell->setPort("\\B", sig_new_b); + cell->setPort("\\S", sig_new_s); + } + + void fixup_muxes() + { + pool<Cell*> visited, queue; + dict<Cell*, pool<SigBit>> upstream_cell2net; + dict<SigBit, pool<Cell*>> upstream_net2cell; + + CellTypes ct; + ct.setup_internals(); + + for (auto cell : module->cells()) + for (auto conn : cell->connections()) { + if (cell->input(conn.first)) + for (auto bit : sigmap(conn.second)) + upstream_cell2net[cell].insert(bit); + if (cell->output(conn.first)) + for (auto bit : sigmap(conn.second)) + upstream_net2cell[bit].insert(cell); + } + + queue = generated_dlatches; + while (!queue.empty()) + { + pool<Cell*> next_queue; + + for (auto cell : queue) { + if (cell->type.in("$mux", "$pmux")) + fixup_mux(cell); + for (auto bit : upstream_cell2net[cell]) + for (auto cell : upstream_net2cell[bit]) + next_queue.insert(cell); + visited.insert(cell); + } + + queue.clear(); + for (auto cell : next_queue) { + if (!visited.count(cell) && ct.cell_known(cell->type)) + queue.insert(cell); + } + } + } +}; + +void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc) +{ + std::vector<RTLIL::SyncRule*> new_syncs; + RTLIL::SigSig latches_bits, nolatches_bits; + dict<SigBit, SigBit> latches_out_in; + dict<SigBit, int> latches_hold; + + for (auto sr : proc->syncs) + { + if (sr->type != RTLIL::SyncType::STa) { + new_syncs.push_back(sr); + continue; + } + + for (auto ss : sr->actions) + { + db.sigmap.apply(ss.first); + db.sigmap.apply(ss.second); + + if (!db.quickcheck(ss.second, ss.first)) { + nolatches_bits.first.append(ss.first); + nolatches_bits.second.append(ss.second); + continue; + } + + for (int i = 0; i < GetSize(ss.first); i++) + latches_out_in[ss.first[i]] = ss.second[i]; + } + + delete sr; + } + + latches_out_in.sort(); + for (auto &it : latches_out_in) { + int n = db.find_mux_feedback(it.second, it.first, true); + if (n == db.false_node) { + nolatches_bits.first.append(it.first); + nolatches_bits.second.append(it.second); + } else { + latches_bits.first.append(it.first); + latches_bits.second.append(it.second); + latches_hold[it.first] = n; + } + } + + int offset = 0; + for (auto chunk : nolatches_bits.first.chunks()) { + SigSpec lhs = chunk, rhs = nolatches_bits.second.extract(offset, chunk.width); + log("No latch inferred for signal `%s.%s' from process `%s.%s'.\n", + db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str()); + db.module->connect(lhs, rhs); + offset += chunk.width; + } + + offset = 0; + while (offset < GetSize(latches_bits.first)) + { + int width = 1; + int n = latches_hold[latches_bits.first[offset]]; + Wire *w = latches_bits.first[offset].wire; + + if (w != nullptr) + { + while (offset+width < GetSize(latches_bits.first) && + n == latches_hold[latches_bits.first[offset+width]] && + w == latches_bits.first[offset+width].wire) + width++; + + SigSpec lhs = latches_bits.first.extract(offset, width); + SigSpec rhs = latches_bits.second.extract(offset, width); + + Cell *cell = db.module->addDlatch(NEW_ID, db.module->Not(NEW_ID, db.make_hold(n)), rhs, lhs); + db.generated_dlatches.insert(cell); + + log("Latch inferred for signal `%s.%s' from process `%s.%s': %s\n", + db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str(), log_id(cell)); + } + + offset += width; + } + + new_syncs.swap(proc->syncs); +} + +struct ProcDlatchPass : public Pass { + ProcDlatchPass() : Pass("proc_dlatch", "extract latches from processes") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" proc_dlatch [selection]\n"); + log("\n"); + log("This pass identifies latches in the processes and converts them to\n"); + log("d-type latches.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + log_header(design, "Executing PROC_DLATCH pass (convert process syncs to latches).\n"); + + extra_args(args, 1, design); + + for (auto module : design->selected_modules()) { + proc_dlatch_db_t db(module); + for (auto &proc_it : module->processes) + if (design->selected(module, proc_it.second)) + proc_dlatch(db, proc_it.second); + db.fixup_muxes(); + } + } +} ProcDlatchPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/proc/proc_init.cc b/passes/proc/proc_init.cc index dff68159f..0c8fb83dc 100644 --- a/passes/proc/proc_init.cc +++ b/passes/proc/proc_init.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * 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 @@ -61,13 +61,28 @@ void proc_init(RTLIL::Module *mod, RTLIL::Process *proc) log_cmd_error("Failed to get a constant init value for %s: %s\n", log_signal(lhs), log_signal(rhs)); int offset = 0; - for (auto &lhs_c : lhs.chunks()) { - if (lhs_c.wire != NULL) { - RTLIL::SigSpec value = rhs.extract(offset, lhs_c.width); - if (value.size() != lhs_c.wire->width) - log_cmd_error("Init value is not for the entire wire: %s = %s\n", log_signal(lhs_c), log_signal(value)); - log(" Setting init value: %s = %s\n", log_signal(lhs_c.wire), log_signal(value)); - lhs_c.wire->attributes["\\init"] = value.as_const(); + for (auto &lhs_c : lhs.chunks()) + { + if (lhs_c.wire != nullptr) + { + SigSpec valuesig = rhs.extract(offset, lhs_c.width); + if (!valuesig.is_fully_const()) + log_cmd_error("Non-const initialization value: %s = %s\n", log_signal(lhs_c), log_signal(valuesig)); + + Const value = valuesig.as_const(); + Const &wireinit = lhs_c.wire->attributes["\\init"]; + + while (GetSize(wireinit.bits) < lhs_c.wire->width) + wireinit.bits.push_back(State::Sx); + + for (int i = 0; i < lhs_c.width; i++) { + auto &initbit = wireinit.bits[i + lhs_c.offset]; + if (initbit != State::Sx && initbit != value[i]) + log_cmd_error("Conflicting initialization values for %s.\n", log_signal(lhs_c)); + initbit = value[i]; + } + + log(" Set init value: %s = %s\n", log_signal(lhs_c.wire), log_signal(wireinit)); } offset += lhs_c.width; } @@ -93,14 +108,14 @@ struct ProcInitPass : public Pass { log("\n"); log(" proc_init [selection]\n"); log("\n"); - log("This pass extracts the 'init' actions from processes (generated from verilog\n"); + log("This pass extracts the 'init' actions from processes (generated from Verilog\n"); log("'initial' blocks) and sets the initial value to the 'init' attribute on the\n"); log("respective wire.\n"); log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing PROC_INIT pass (extract init attributes).\n"); + log_header(design, "Executing PROC_INIT pass (extract init attributes).\n"); extra_args(args, 1, design); @@ -111,5 +126,5 @@ struct ProcInitPass : public Pass { proc_init(mod, proc_it.second); } } ProcInitPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/proc/proc_mux.cc b/passes/proc/proc_mux.cc index 4aa1aab54..57e131ca5 100644 --- a/passes/proc/proc_mux.cc +++ b/passes/proc/proc_mux.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * 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 @@ -27,37 +27,123 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -RTLIL::SigSpec find_any_lvalue(const RTLIL::CaseRule *cs) +struct SigSnippets { - for (auto &action : cs->actions) { - if (action.first.size()) - return action.first; - } + idict<SigSpec> sigidx; + dict<SigBit, int> bit2snippet; + pool<int> snippets; - for (auto sw : cs->switches) - for (auto cs2 : sw->cases) { - RTLIL::SigSpec sig = find_any_lvalue(cs2); - if (sig.size()) - return sig; + void insert(SigSpec sig) + { + if (sig.empty()) + return; + + int key = sigidx(sig); + if (snippets.count(key)) + return; + + SigSpec new_sig; + + for (int i = 0; i < GetSize(sig); i++) + { + int other_key = bit2snippet.at(sig[i], -1); + + if (other_key < 0) { + new_sig.append(sig[i]); + continue; + } + + if (!new_sig.empty()) { + int new_key = sigidx(new_sig); + snippets.insert(new_key); + for (auto bit : new_sig) + bit2snippet[bit] = new_key; + new_sig = SigSpec(); + } + + SigSpec other_sig = sigidx[other_key]; + int k = 0, n = 1; + + while (other_sig[k] != sig[i]) { + k++; + log_assert(k < GetSize(other_sig)); + } + + while (i+n < GetSize(sig) && k+n < GetSize(other_sig) && sig[i+n] == other_sig[k+n]) + n++; + + SigSpec sig1 = other_sig.extract(0, k); + SigSpec sig2 = other_sig.extract(k, n); + SigSpec sig3 = other_sig.extract(k+n, GetSize(other_sig)-k-n); + + for (auto bit : other_sig) + bit2snippet.erase(bit); + snippets.erase(other_key); + + insert(sig1); + insert(sig2); + insert(sig3); + + i += n-1; + } + + if (!new_sig.empty()) { + int new_key = sigidx(new_sig); + snippets.insert(new_key); + for (auto bit : new_sig) + bit2snippet[bit] = new_key; + } } - return RTLIL::SigSpec(); -} + void insert(const RTLIL::CaseRule *cs) + { + for (auto &action : cs->actions) + insert(action.first); -void extract_core_signal(const RTLIL::CaseRule *cs, RTLIL::SigSpec &sig) + for (auto sw : cs->switches) + for (auto cs2 : sw->cases) + insert(cs2); + } +}; + +struct SnippetSwCache { - for (auto &action : cs->actions) { - RTLIL::SigSpec lvalue = action.first.extract(sig); - if (lvalue.size()) - sig = lvalue; + dict<RTLIL::SwitchRule*, pool<int>, hash_ptr_ops> cache; + const SigSnippets *snippets; + int current_snippet; + + bool check(RTLIL::SwitchRule *sw) + { + return cache[sw].count(current_snippet) != 0; } - for (auto sw : cs->switches) - for (auto cs2 : sw->cases) - extract_core_signal(cs2, sig); -} + void insert(const RTLIL::CaseRule *cs, vector<RTLIL::SwitchRule*> &sw_stack) + { + for (auto &action : cs->actions) + for (auto bit : action.first) { + int sn = snippets->bit2snippet.at(bit, -1); + if (sn < 0) + continue; + for (auto sw : sw_stack) + cache[sw].insert(sn); + } + + for (auto sw : cs->switches) { + sw_stack.push_back(sw); + for (auto cs2 : sw->cases) + insert(cs2, sw_stack); + sw_stack.pop_back(); + } + } -RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SwitchRule *sw) + void insert(const RTLIL::CaseRule *cs) + { + vector<RTLIL::SwitchRule*> sw_stack; + insert(cs, sw_stack); + } +}; + +RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SwitchRule *sw, bool ifxmode) { std::stringstream sstr; sstr << "$procmux$" << (autoidx++); @@ -78,14 +164,14 @@ RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s if (comp.size() == 0) return RTLIL::SigSpec(); - if (sig.size() == 1 && comp == RTLIL::SigSpec(1,1)) + if (sig.size() == 1 && comp == RTLIL::SigSpec(1,1) && !ifxmode) { mod->connect(RTLIL::SigSig(RTLIL::SigSpec(cmp_wire, cmp_wire->width++), sig)); } else { // create compare cell - RTLIL::Cell *eq_cell = mod->addCell(stringf("%s_CMP%d", sstr.str().c_str(), cmp_wire->width), "$eq"); + RTLIL::Cell *eq_cell = mod->addCell(stringf("%s_CMP%d", sstr.str().c_str(), cmp_wire->width), ifxmode ? "$eqx" : "$eq"); eq_cell->attributes = sw->attributes; eq_cell->parameters["\\A_SIGNED"] = RTLIL::Const(0); @@ -125,7 +211,7 @@ RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s return RTLIL::SigSpec(ctrl_wire); } -RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::SigSpec else_signal, RTLIL::Cell *&last_mux_cell, RTLIL::SwitchRule *sw) +RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::SigSpec else_signal, RTLIL::Cell *&last_mux_cell, RTLIL::SwitchRule *sw, bool ifxmode) { log_assert(when_signal.size() == else_signal.size()); @@ -137,7 +223,7 @@ RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s return when_signal; // compare results - RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw); + RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw, ifxmode); if (ctrl_sig.size() == 0) return when_signal; log_assert(ctrl_sig.size() == 1); @@ -159,12 +245,15 @@ RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s return RTLIL::SigSpec(result_wire); } -void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::Cell *last_mux_cell, RTLIL::SwitchRule *sw) +void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::Cell *last_mux_cell, RTLIL::SwitchRule *sw, bool ifxmode) { log_assert(last_mux_cell != NULL); log_assert(when_signal.size() == last_mux_cell->getPort("\\A").size()); - RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw); + if (when_signal == last_mux_cell->getPort("\\A")) + return; + + RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw, ifxmode); log_assert(ctrl_sig.size() == 1); last_mux_cell->type = "$pmux"; @@ -179,7 +268,8 @@ void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::ve last_mux_cell->parameters["\\S_WIDTH"] = last_mux_cell->getPort("\\S").size(); } -RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, RTLIL::CaseRule *cs, const RTLIL::SigSpec &sig, const RTLIL::SigSpec &defval) +RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, dict<RTLIL::SwitchRule*, bool, hash_ptr_ops> &swpara, + RTLIL::CaseRule *cs, const RTLIL::SigSpec &sig, const RTLIL::SigSpec &defval, bool ifxmode) { RTLIL::SigSpec result = defval; @@ -190,9 +280,37 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, RTLIL::CaseRule *cs, const for (auto sw : cs->switches) { + if (!swcache.check(sw)) + continue; + // detect groups of parallel cases std::vector<int> pgroups(sw->cases.size()); + bool is_simple_parallel_case = true; + if (!sw->get_bool_attribute("\\parallel_case")) { + if (!swpara.count(sw)) { + pool<Const> case_values; + for (size_t i = 0; i < sw->cases.size(); i++) { + RTLIL::CaseRule *cs2 = sw->cases[i]; + for (auto pat : cs2->compare) { + if (!pat.is_fully_def()) + goto not_simple_parallel_case; + Const cpat = pat.as_const(); + if (case_values.count(cpat)) + goto not_simple_parallel_case; + case_values.insert(cpat); + } + } + if (0) + not_simple_parallel_case: + is_simple_parallel_case = false; + swpara[sw] = is_simple_parallel_case; + } else { + is_simple_parallel_case = swpara.at(sw); + } + } + + if (!is_simple_parallel_case) { BitPatternPool pool(sw->signal.size()); bool extra_group_for_next_case = false; for (size_t i = 0; i < sw->cases.size(); i++) { @@ -214,7 +332,7 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, RTLIL::CaseRule *cs, const for (auto pat : cs2->compare) if (!pat.is_fully_const()) extra_group_for_next_case = true; - else + else if (!ifxmode) pool.take(pat); } } @@ -225,37 +343,39 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, RTLIL::CaseRule *cs, const for (size_t i = 0; i < sw->cases.size(); i++) { int case_idx = sw->cases.size() - i - 1; RTLIL::CaseRule *cs2 = sw->cases[case_idx]; - RTLIL::SigSpec value = signal_to_mux_tree(mod, cs2, sig, initial_val); + RTLIL::SigSpec value = signal_to_mux_tree(mod, swcache, swpara, cs2, sig, initial_val, ifxmode); if (last_mux_cell && pgroups[case_idx] == pgroups[case_idx+1]) - append_pmux(mod, sw->signal, cs2->compare, value, last_mux_cell, sw); + append_pmux(mod, sw->signal, cs2->compare, value, last_mux_cell, sw, ifxmode); else - result = gen_mux(mod, sw->signal, cs2->compare, value, result, last_mux_cell, sw); + result = gen_mux(mod, sw->signal, cs2->compare, value, result, last_mux_cell, sw, ifxmode); } } return result; } -void proc_mux(RTLIL::Module *mod, RTLIL::Process *proc) +void proc_mux(RTLIL::Module *mod, RTLIL::Process *proc, bool ifxmode) { - bool first = true; - while (1) - { - RTLIL::SigSpec sig = find_any_lvalue(&proc->root_case); + log("Creating decoders for process `%s.%s'.\n", mod->name.c_str(), proc->name.c_str()); - if (sig.size() == 0) - break; + SigSnippets sigsnip; + sigsnip.insert(&proc->root_case); - if (first) { - log("Creating decoders for process `%s.%s'.\n", mod->name.c_str(), proc->name.c_str()); - first = false; - } + SnippetSwCache swcache; + swcache.snippets = &sigsnip; + swcache.insert(&proc->root_case); - extract_core_signal(&proc->root_case, sig); + dict<RTLIL::SwitchRule*, bool, hash_ptr_ops> swpara; - log(" creating decoder for signal `%s'.\n", log_signal(sig)); + int cnt = 0; + for (int idx : sigsnip.snippets) + { + swcache.current_snippet = idx; + RTLIL::SigSpec sig = sigsnip.sigidx[idx]; - RTLIL::SigSpec value = signal_to_mux_tree(mod, &proc->root_case, sig, RTLIL::SigSpec(RTLIL::State::Sx, sig.size())); + log("%6d/%d: %s\n", ++cnt, GetSize(sigsnip.snippets), log_signal(sig)); + + RTLIL::SigSpec value = signal_to_mux_tree(mod, swcache, swpara, &proc->root_case, sig, RTLIL::SigSpec(RTLIL::State::Sx, sig.size()), ifxmode); mod->connect(RTLIL::SigSig(sig, value)); } } @@ -266,24 +386,38 @@ struct ProcMuxPass : public Pass { { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" proc_mux [selection]\n"); + log(" proc_mux [options] [selection]\n"); log("\n"); log("This pass converts the decision trees in processes (originating from if-else\n"); log("and case statements) to trees of multiplexer cells.\n"); log("\n"); + log(" -ifx\n"); + log(" Use Verilog simulation behavior with respect to undef values in\n"); + log(" 'case' expressions and 'if' conditions.\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing PROC_MUX pass (convert decision trees to multiplexers).\n"); + bool ifxmode = false; + log_header(design, "Executing PROC_MUX pass (convert decision trees to multiplexers).\n"); - extra_args(args, 1, design); + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-ifx") { + ifxmode = true; + continue; + } + break; + } + extra_args(args, argidx, design); for (auto mod : design->modules()) if (design->selected(mod)) for (auto &proc_it : mod->processes) if (design->selected(mod, proc_it.second)) - proc_mux(mod, proc_it.second); + proc_mux(mod, proc_it.second, ifxmode); } } ProcMuxPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/proc/proc_rmdead.cc b/passes/proc/proc_rmdead.cc index 427e0d567..5672fb475 100644 --- a/passes/proc/proc_rmdead.cc +++ b/passes/proc/proc_rmdead.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * 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 @@ -51,8 +51,8 @@ void proc_rmdead(RTLIL::SwitchRule *sw, int &counter) counter++; continue; } - if (pool.empty()) - sw->cases[i]->compare.clear(); + // if (pool.empty()) + // sw->cases[i]->compare.clear(); } for (auto switch_it : sw->cases[i]->switches) @@ -76,7 +76,7 @@ struct ProcRmdeadPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing PROC_RMDEAD pass (remove dead branches from decision trees).\n"); + log_header(design, "Executing PROC_RMDEAD pass (remove dead branches from decision trees).\n"); extra_args(args, 1, design); @@ -100,5 +100,5 @@ struct ProcRmdeadPass : public Pass { log("Removed a total of %d dead cases.\n", total_counter); } } ProcRmdeadPass; - + PRIVATE_NAMESPACE_END |