diff options
Diffstat (limited to 'passes')
29 files changed, 1764 insertions, 1848 deletions
diff --git a/passes/equiv/equiv_induct.cc b/passes/equiv/equiv_induct.cc index 596c938fc..37aec50cd 100644 --- a/passes/equiv/equiv_induct.cc +++ b/passes/equiv/equiv_induct.cc @@ -65,8 +65,10 @@ struct EquivInductWorker int ez_a = satgen.importSigBit(bit_a, step); int ez_b = satgen.importSigBit(bit_b, step); int cond = ez->IFF(ez_a, ez_b); - if (satgen.model_undef) + if (satgen.model_undef) { + cond = ez->AND(cond, ez->NOT(satgen.importUndefSigBit(bit_b, step))); cond = ez->OR(cond, satgen.importUndefSigBit(bit_a, step)); + } ez_equal_terms.push_back(cond); } } diff --git a/passes/memory/memory_dff.cc b/passes/memory/memory_dff.cc index 947cf7b35..68023fd11 100644 --- a/passes/memory/memory_dff.cc +++ b/passes/memory/memory_dff.cc @@ -20,6 +20,7 @@ #include <algorithm> #include "kernel/yosys.h" #include "kernel/sigtools.h" +#include "kernel/ffinit.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -34,22 +35,14 @@ struct MemoryDffWorker dict<SigBit, int> sigbit_users_count; dict<SigSpec, Cell*> mux_cells_a, mux_cells_b; pool<Cell*> forward_merged_dffs, candidate_dffs; - pool<SigBit> init_bits; + FfInitVals initvals; MemoryDffWorker(Module *module) : module(module), sigmap(module) { - for (auto wire : module->wires()) { - if (wire->attributes.count(ID::init) == 0) - continue; - SigSpec sig = sigmap(wire); - Const initval = wire->attributes.at(ID::init); - for (int i = 0; i < GetSize(sig) && i < GetSize(initval); i++) - if (initval[i] == State::S0 || initval[i] == State::S1) - init_bits.insert(sig[i]); - } + initvals.set(&sigmap, module); } - bool find_sig_before_dff(RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity, bool after = false) + bool find_sig_before_dff(RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity) { sigmap.apply(sig); @@ -58,42 +51,134 @@ struct MemoryDffWorker if (bit.wire == NULL) continue; - if (!after && init_bits.count(sigmap(bit))) + if (initvals(bit) != State::Sx) return false; for (auto cell : dff_cells) { - if (after && forward_merged_dffs.count(cell)) + SigSpec this_clk = cell->getPort(ID::CLK); + bool this_clk_polarity = cell->parameters[ID::CLK_POLARITY].as_bool(); + + if (invbits.count(this_clk)) { + this_clk = invbits.at(this_clk); + this_clk_polarity = !this_clk_polarity; + } + + if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) { + if (this_clk != clk) + continue; + if (this_clk_polarity != clk_polarity) + continue; + } + + RTLIL::SigSpec q_norm = cell->getPort(ID::Q); + sigmap.apply(q_norm); + + RTLIL::SigSpec d = q_norm.extract(bit, &cell->getPort(ID::D)); + if (d.size() != 1) + continue; + + if (cell->type == ID($sdffce)) { + SigSpec rval = cell->parameters[ID::SRST_VALUE]; + SigSpec rbit = q_norm.extract(bit, &rval); + if (cell->parameters[ID::SRST_POLARITY].as_bool()) + d = module->Mux(NEW_ID, d, rbit, cell->getPort(ID::SRST)); + else + d = module->Mux(NEW_ID, rbit, d, cell->getPort(ID::SRST)); + } + + if (cell->type.in(ID($dffe), ID($sdffe), ID($sdffce))) { + if (cell->parameters[ID::EN_POLARITY].as_bool()) + d = module->Mux(NEW_ID, bit, d, cell->getPort(ID::EN)); + else + d = module->Mux(NEW_ID, d, bit, cell->getPort(ID::EN)); + } + + if (cell->type.in(ID($sdff), ID($sdffe))) { + SigSpec rval = cell->parameters[ID::SRST_VALUE]; + SigSpec rbit = q_norm.extract(bit, &rval); + if (cell->parameters[ID::SRST_POLARITY].as_bool()) + d = module->Mux(NEW_ID, d, rbit, cell->getPort(ID::SRST)); + else + d = module->Mux(NEW_ID, rbit, d, cell->getPort(ID::SRST)); + } + + bit = d; + clk = this_clk; + clk_polarity = this_clk_polarity; + candidate_dffs.insert(cell); + goto replaced_this_bit; + } + + return false; + replaced_this_bit:; + } + + return true; + } + + bool find_sig_after_dffe(RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity, RTLIL::SigSpec &en, bool &en_polarity) + { + sigmap.apply(sig); + + for (auto &bit : sig) + { + if (bit.wire == NULL) + continue; + + for (auto cell : dff_cells) + { + if (forward_merged_dffs.count(cell)) + continue; + if (!cell->type.in(ID($dff), ID($dffe))) continue; SigSpec this_clk = cell->getPort(ID::CLK); bool this_clk_polarity = cell->parameters[ID::CLK_POLARITY].as_bool(); + SigSpec this_en = State::S1; + bool this_en_polarity = true; + + if (cell->type == ID($dffe)) { + this_en = cell->getPort(ID::EN); + this_en_polarity = cell->parameters[ID::EN_POLARITY].as_bool(); + } if (invbits.count(this_clk)) { this_clk = invbits.at(this_clk); this_clk_polarity = !this_clk_polarity; } + if (invbits.count(this_en)) { + this_en = invbits.at(this_en); + this_en_polarity = !this_en_polarity; + } + if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) { if (this_clk != clk) continue; if (this_clk_polarity != clk_polarity) continue; + if (this_en != en) + continue; + if (this_en_polarity != en_polarity) + continue; } - RTLIL::SigSpec q_norm = cell->getPort(after ? ID::D : ID::Q); + RTLIL::SigSpec q_norm = cell->getPort(ID::D); sigmap.apply(q_norm); - RTLIL::SigSpec d = q_norm.extract(bit, &cell->getPort(after ? ID::Q : ID::D)); + RTLIL::SigSpec d = q_norm.extract(bit, &cell->getPort(ID::Q)); if (d.size() != 1) continue; - if (after && init_bits.count(d)) + if (initvals(d) != State::Sx) return false; bit = d; clk = this_clk; clk_polarity = this_clk_polarity; + en = this_en; + en_polarity = this_en_polarity; candidate_dffs.insert(cell); goto replaced_this_bit; } @@ -161,7 +246,7 @@ struct MemoryDffWorker RTLIL::SigSpec new_sig = module->addWire(sstr.str(), sig.size()); for (auto cell : module->cells()) - if (cell->type == ID($dff)) { + if (cell->type.in(ID($dff), ID($dffe))) { RTLIL::SigSpec new_q = cell->getPort(ID::Q); new_q.replace(sig, new_sig); cell->setPort(ID::Q, new_q); @@ -173,8 +258,10 @@ struct MemoryDffWorker log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str()); bool clk_polarity = 0; + bool en_polarity = 0; RTLIL::SigSpec clk_data = RTLIL::SigSpec(RTLIL::State::Sx); + RTLIL::SigSpec en_data; RTLIL::SigSpec sig_data = cell->getPort(ID::DATA); for (auto bit : sigmap(sig_data)) @@ -198,9 +285,14 @@ struct MemoryDffWorker if (sigbit_users_count[bit] > 1) goto skip_ff_after_read_merging; - if (find_sig_before_dff(sig_data, clk_data, clk_polarity, true) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx) && + if (find_sig_after_dffe(sig_data, clk_data, clk_polarity, en_data, en_polarity) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx) && std::all_of(check_q.begin(), check_q.end(), [&](const SigSpec &cq) {return cq == sig_data; })) { + if (en_data != State::S1 || !en_polarity) { + if (!en_polarity) + en_data = module->LogicNot(NEW_ID, en_data); + en.append(en_data); + } disconnect_dff(sig_data); cell->setPort(ID::CLK, clk_data); cell->setPort(ID::EN, en.size() > 1 ? module->ReduceAnd(NEW_ID, en) : en); @@ -214,11 +306,13 @@ struct MemoryDffWorker } else { - if (find_sig_before_dff(sig_data, clk_data, clk_polarity, true) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx)) + if (find_sig_after_dffe(sig_data, clk_data, clk_polarity, en_data, en_polarity) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx)) { + if (!en_polarity) + en_data = module->LogicNot(NEW_ID, en_data); disconnect_dff(sig_data); cell->setPort(ID::CLK, clk_data); - cell->setPort(ID::EN, State::S1); + cell->setPort(ID::EN, en_data); cell->setPort(ID::DATA, sig_data); cell->parameters[ID::CLK_ENABLE] = RTLIL::Const(1); cell->parameters[ID::CLK_POLARITY] = RTLIL::Const(clk_polarity); @@ -256,7 +350,7 @@ struct MemoryDffWorker } for (auto cell : module->cells()) { - if (cell->type == ID($dff)) + if (cell->type.in(ID($dff), ID($dffe), ID($sdff), ID($sdffe), ID($sdffce))) dff_cells.push_back(cell); if (cell->type == ID($mux)) { mux_cells_a[sigmap(cell->getPort(ID::A))] = cell; diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc index 3133927bb..64fee76b3 100644 --- a/passes/opt/Makefile.inc +++ b/passes/opt/Makefile.inc @@ -5,6 +5,7 @@ OBJS += passes/opt/opt_mem.o OBJS += passes/opt/opt_muxtree.o OBJS += passes/opt/opt_reduce.o OBJS += passes/opt/opt_rmdff.o +OBJS += passes/opt/opt_dff.o OBJS += passes/opt/opt_share.o OBJS += passes/opt/opt_clean.o OBJS += passes/opt/opt_expr.o diff --git a/passes/opt/opt.cc b/passes/opt/opt.cc index 8be94e345..4b052d9a2 100644 --- a/passes/opt/opt.cc +++ b/passes/opt/opt.cc @@ -37,7 +37,7 @@ struct OptPass : public Pass { log("a series of trivial optimizations and cleanups. This pass executes the other\n"); log("passes in the following order:\n"); log("\n"); - log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n"); + log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-noclkinv] [-fine] [-full] [-keepdc]\n"); log(" opt_merge [-share_all] -nomux\n"); log("\n"); log(" do\n"); @@ -45,19 +45,19 @@ struct OptPass : public Pass { log(" opt_reduce [-fine] [-full]\n"); log(" opt_merge [-share_all]\n"); log(" opt_share (-full only)\n"); - log(" opt_rmdff [-keepdc] [-sat] (except when called with -noff)\n"); + log(" opt_dff [-nodffe] [-nosdff] [-keepdc] [-sat] (except when called with -noff)\n"); log(" opt_clean [-purge]\n"); - log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n"); + log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-noclkinv] [-fine] [-full] [-keepdc]\n"); log(" while <changed design>\n"); log("\n"); log("When called with -fast the following script is used instead:\n"); log("\n"); log(" do\n"); - log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n"); + log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-noclkinv] [-fine] [-full] [-keepdc]\n"); log(" opt_merge [-share_all]\n"); - log(" opt_rmdff [-keepdc] [-sat] (except when called with -noff)\n"); + log(" opt_dff [-nodffe] [-nosdff] [-keepdc] [-sat] (except when called with -noff)\n"); log(" opt_clean [-purge]\n"); - log(" while <changed design in opt_rmdff>\n"); + log(" while <changed design in opt_dff>\n"); log("\n"); log("Note: Options in square brackets (such as [-keepdc]) are passed through to\n"); log("the opt_* commands when given to 'opt'.\n"); @@ -70,7 +70,7 @@ struct OptPass : public Pass { std::string opt_expr_args; std::string opt_reduce_args; std::string opt_merge_args; - std::string opt_rmdff_args; + std::string opt_dff_args; bool opt_share = false; bool fast_mode = false; bool noff_mode = false; @@ -96,8 +96,8 @@ struct OptPass : public Pass { opt_expr_args += " -undriven"; continue; } - if (args[argidx] == "-clkinv") { - opt_expr_args += " -clkinv"; + if (args[argidx] == "-noclkinv") { + opt_expr_args += " -noclkinv"; continue; } if (args[argidx] == "-fine") { @@ -113,11 +113,19 @@ struct OptPass : public Pass { } if (args[argidx] == "-keepdc") { opt_expr_args += " -keepdc"; - opt_rmdff_args += " -keepdc"; + opt_dff_args += " -keepdc"; + continue; + } + if (args[argidx] == "-nodffe") { + opt_dff_args += " -nodffe"; + continue; + } + if (args[argidx] == "-nosdff") { + opt_dff_args += " -nosdff"; continue; } if (args[argidx] == "-sat") { - opt_rmdff_args += " -sat"; + opt_dff_args += " -sat"; continue; } if (args[argidx] == "-share_all") { @@ -143,7 +151,7 @@ struct OptPass : public Pass { Pass::call(design, "opt_merge" + opt_merge_args); design->scratchpad_unset("opt.did_something"); if (!noff_mode) - Pass::call(design, "opt_rmdff" + opt_rmdff_args); + Pass::call(design, "opt_dff" + opt_dff_args); if (design->scratchpad_get_bool("opt.did_something") == false) break; Pass::call(design, "opt_clean" + opt_clean_args); @@ -163,7 +171,7 @@ struct OptPass : public Pass { if (opt_share) Pass::call(design, "opt_share"); if (!noff_mode) - Pass::call(design, "opt_rmdff" + opt_rmdff_args); + Pass::call(design, "opt_dff" + opt_dff_args); Pass::call(design, "opt_clean" + opt_clean_args); Pass::call(design, "opt_expr" + opt_expr_args); if (design->scratchpad_get_bool("opt.did_something") == false) diff --git a/passes/opt/opt_dff.cc b/passes/opt/opt_dff.cc new file mode 100644 index 000000000..a47071a30 --- /dev/null +++ b/passes/opt/opt_dff.cc @@ -0,0 +1,875 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2020 Marcelina Kościelnicka <mwk@0x04.net> + * + * 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/log.h" +#include "kernel/register.h" +#include "kernel/rtlil.h" +#include "kernel/satgen.h" +#include "kernel/sigtools.h" +#include "kernel/ffinit.h" +#include "kernel/ff.h" +#include "passes/techmap/simplemap.h" +#include <stdio.h> +#include <stdlib.h> + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct OptDffOptions +{ + bool nosdff; + bool nodffe; + bool simple_dffe; + bool sat; + bool keepdc; +}; + +struct OptDffWorker +{ + const OptDffOptions &opt; + + Module *module; + typedef std::pair<RTLIL::Cell*, int> cell_int_t; + SigMap sigmap; + FfInitVals initvals; + dict<SigBit, int> bitusers; + dict<SigBit, cell_int_t> bit2mux; + dict<SigBit, RTLIL::Cell*> bit2driver; + + typedef std::map<RTLIL::SigBit, bool> pattern_t; + typedef std::set<pattern_t> patterns_t; + typedef std::pair<RTLIL::SigBit, bool> ctrl_t; + typedef std::set<ctrl_t> ctrls_t; + + ezSatPtr ez; + SatGen satgen; + pool<Cell*> sat_cells; + + // Used as a queue. + std::vector<Cell *> dff_cells; + + OptDffWorker(const OptDffOptions &opt, Module *mod) : opt(opt), module(mod), sigmap(mod), initvals(&sigmap, mod), ez(), satgen(ez.get(), &sigmap) { + // Gathering three kinds of information here for every sigmapped SigBit: + // + // - bitusers: how many users it has (muxes will only be merged into FFs if this is 1, making the FF the only user) + // - bit2mux: the mux cell and bit index that drives it, if any + // - bit2driver: the cell driving it, if any + + for (auto wire : module->wires()) + { + if (wire->port_output) + for (auto bit : sigmap(wire)) + bitusers[bit]++; + } + + for (auto cell : module->cells()) { + if (cell->type.in(ID($mux), ID($pmux), ID($_MUX_))) { + RTLIL::SigSpec sig_y = sigmap(cell->getPort(ID::Y)); + for (int i = 0; i < GetSize(sig_y); i++) + bit2mux[sig_y[i]] = cell_int_t(cell, i); + } + + for (auto conn : cell->connections()) { + bool is_output = cell->output(conn.first); + if (is_output) { + for (auto bit : sigmap(conn.second)) + bit2driver[bit] = cell; + } + if (!is_output || !cell->known()) { + for (auto bit : sigmap(conn.second)) + bitusers[bit]++; + } + } + + if (module->design->selected(module, cell) && RTLIL::builtin_ff_cell_types().count(cell->type)) + dff_cells.push_back(cell); + } + + } + + std::function<void(Cell*)> sat_import_cell = [&](Cell *c) { + if (!sat_cells.insert(c).second) + return; + if (!satgen.importCell(c)) + return; + for (auto &conn : c->connections()) { + if (!c->input(conn.first)) + continue; + for (auto bit : sigmap(conn.second)) + if (bit2driver.count(bit)) + sat_import_cell(bit2driver.at(bit)); + } + }; + + State combine_const(State a, State b) { + if (a == State::Sx && !opt.keepdc) + return b; + if (b == State::Sx && !opt.keepdc) + return a; + if (a == b) + return a; + return State::Sm; + } + + patterns_t find_muxtree_feedback_patterns(RTLIL::SigBit d, RTLIL::SigBit q, pattern_t path) + { + patterns_t ret; + + if (d == q) { + ret.insert(path); + return ret; + } + + if (bit2mux.count(d) == 0 || bitusers[d] > 1) + return ret; + + cell_int_t mbit = bit2mux.at(d); + RTLIL::SigSpec sig_a = sigmap(mbit.first->getPort(ID::A)); + RTLIL::SigSpec sig_b = sigmap(mbit.first->getPort(ID::B)); + RTLIL::SigSpec sig_s = sigmap(mbit.first->getPort(ID::S)); + int width = GetSize(sig_a), index = mbit.second; + + for (int i = 0; i < GetSize(sig_s); i++) + if (path.count(sig_s[i]) && path.at(sig_s[i])) + { + ret = find_muxtree_feedback_patterns(sig_b[i*width + index], q, path); + + if (sig_b[i*width + index] == q) { + RTLIL::SigSpec s = mbit.first->getPort(ID::B); + s[i*width + index] = RTLIL::Sx; + mbit.first->setPort(ID::B, s); + } + + return ret; + } + + pattern_t path_else = path; + + for (int i = 0; i < GetSize(sig_s); i++) + { + if (path.count(sig_s[i])) + continue; + + pattern_t path_this = path; + path_else[sig_s[i]] = false; + path_this[sig_s[i]] = true; + + for (auto &pat : find_muxtree_feedback_patterns(sig_b[i*width + index], q, path_this)) + ret.insert(pat); + + if (sig_b[i*width + index] == q) { + RTLIL::SigSpec s = mbit.first->getPort(ID::B); + s[i*width + index] = RTLIL::Sx; + mbit.first->setPort(ID::B, s); + } + } + + for (auto &pat : find_muxtree_feedback_patterns(sig_a[index], q, path_else)) + ret.insert(pat); + + if (sig_a[index] == q) { + RTLIL::SigSpec s = mbit.first->getPort(ID::A); + s[index] = RTLIL::Sx; + mbit.first->setPort(ID::A, s); + } + + return ret; + } + + void simplify_patterns(patterns_t&) + { + // TBD + } + + ctrl_t make_patterns_logic(const patterns_t &patterns, const ctrls_t &ctrls, bool make_gates) + { + if (patterns.empty() && GetSize(ctrls) == 1) { + return *ctrls.begin(); + } + + RTLIL::SigSpec or_input; + + for (auto pat : patterns) + { + RTLIL::SigSpec s1, s2; + for (auto it : pat) { + s1.append(it.first); + s2.append(it.second); + } + + RTLIL::SigSpec y = module->addWire(NEW_ID); + RTLIL::Cell *c = module->addNe(NEW_ID, s1, s2, y); + + if (make_gates) { + simplemap(module, c); + module->remove(c); + } + + or_input.append(y); + } + for (auto item : ctrls) { + if (item.second) + or_input.append(item.first); + else if (make_gates) + or_input.append(module->NotGate(NEW_ID, item.first)); + else + or_input.append(module->Not(NEW_ID, item.first)); + } + + if (GetSize(or_input) == 0) + return ctrl_t(State::S1, true); + + if (GetSize(or_input) == 1) + return ctrl_t(or_input, true); + + RTLIL::SigSpec y = module->addWire(NEW_ID); + RTLIL::Cell *c = module->addReduceAnd(NEW_ID, or_input, y); + + if (make_gates) { + simplemap(module, c); + module->remove(c); + } + + return ctrl_t(y, true); + } + + ctrl_t combine_resets(const ctrls_t &ctrls, bool make_gates) + { + if (GetSize(ctrls) == 1) { + return *ctrls.begin(); + } + + RTLIL::SigSpec or_input; + + bool final_pol = false; + for (auto item : ctrls) { + if (item.second) + final_pol = true; + } + + for (auto item : ctrls) { + if (item.second == final_pol) + or_input.append(item.first); + else if (make_gates) + or_input.append(module->NotGate(NEW_ID, item.first)); + else + or_input.append(module->Not(NEW_ID, item.first)); + } + + RTLIL::SigSpec y = module->addWire(NEW_ID); + RTLIL::Cell *c = final_pol ? module->addReduceOr(NEW_ID, or_input, y) : module->addReduceAnd(NEW_ID, or_input, y); + + if (make_gates) { + simplemap(module, c); + module->remove(c); + } + + return ctrl_t(y, final_pol); + } + + bool run() { + // We have all the information we need, and the list of FFs to process as well. Do it. + bool did_something = false; + while (!dff_cells.empty()) { + Cell *cell = dff_cells.back(); + dff_cells.pop_back(); + // Break down the FF into pieces. + FfData ff(&initvals, cell); + bool changed = false; + + if (!ff.width) { + module->remove(cell); + did_something = true; + continue; + } + + if (ff.has_sr) { + bool sr_removed = false; + std::vector<int> keep_bits; + // Check for always-active S/R bits. + for (int i = 0; i < ff.width; i++) { + if (ff.sig_clr[i] == (ff.pol_clr ? State::S1 : State::S0) || (!opt.keepdc && ff.sig_clr[i] == State::Sx)) { + // Always-active clear — connect Q bit to 0. + initvals.remove_init(ff.sig_q[i]); + module->connect(ff.sig_q[i], State::S0); + log("Handling always-active CLR at position %d on %s (%s) from module %s (changing to const driver).\n", + i, log_id(cell), log_id(cell->type), log_id(module)); + sr_removed = true; + } else if (ff.sig_set[i] == (ff.pol_set ? State::S1 : State::S0) || (!opt.keepdc && ff.sig_set[i] == State::Sx)) { + // Always-active set — connect Q bit to 1 if clear inactive, 0 if reset active. + initvals.remove_init(ff.sig_q[i]); + if (!ff.pol_clr) { + module->connect(ff.sig_q[i], ff.sig_clr[i]); + } else if (ff.is_fine) { + module->addNotGate(NEW_ID, ff.sig_q[i], ff.sig_clr[i]); + } else { + module->addNot(NEW_ID, ff.sig_q[i], ff.sig_clr[i]); + } + log("Handling always-active SET at position %d on %s (%s) from module %s (changing to combinatorial circuit).\n", + i, log_id(cell), log_id(cell->type), log_id(module)); + sr_removed = true; + } else { + keep_bits.push_back(i); + } + } + if (sr_removed) { + if (keep_bits.empty()) { + module->remove(cell); + did_something = true; + continue; + } + ff = ff.slice(keep_bits); + changed = true; + } + + if (ff.pol_clr ? ff.sig_clr.is_fully_zero() : ff.sig_clr.is_fully_ones()) { + // CLR is useless, try to kill it. + bool failed = false; + for (int i = 0; i < ff.width; i++) + if (ff.sig_set[i] != ff.sig_set[0]) + failed = true; + if (!failed) { + log("Removing never-active CLR on %s (%s) from module %s.\n", + log_id(cell), log_id(cell->type), log_id(module)); + ff.has_sr = false; + ff.has_arst = true; + ff.pol_arst = ff.pol_set; + ff.sig_arst = ff.sig_set[0]; + ff.val_arst = Const(State::S1, ff.width); + changed = true; + } + } else if (ff.pol_set ? ff.sig_set.is_fully_zero() : ff.sig_set.is_fully_ones()) { + // SET is useless, try to kill it. + bool failed = false; + for (int i = 0; i < ff.width; i++) + if (ff.sig_clr[i] != ff.sig_clr[0]) + failed = true; + if (!failed) { + log("Removing never-active SET on %s (%s) from module %s.\n", + log_id(cell), log_id(cell->type), log_id(module)); + ff.has_sr = false; + ff.has_arst = true; + ff.pol_arst = ff.pol_clr; + ff.sig_arst = ff.sig_clr[0]; + ff.val_arst = Const(State::S0, ff.width); + changed = true; + } + } else if (ff.pol_clr == ff.pol_set) { + // Try a more complex conversion to plain async reset. + State val_neutral = ff.pol_set ? State::S0 : State::S1; + Const val_arst; + SigSpec sig_arst; + if (ff.sig_clr[0] == val_neutral) + sig_arst = ff.sig_set[0]; + else + sig_arst = ff.sig_clr[0]; + bool failed = false; + for (int i = 0; i < ff.width; i++) { + if (ff.sig_clr[i] == sig_arst && ff.sig_set[i] == val_neutral) + val_arst.bits.push_back(State::S0); + else if (ff.sig_set[i] == sig_arst && ff.sig_clr[i] == val_neutral) + val_arst.bits.push_back(State::S1); + else + failed = true; + } + if (!failed) { + log("Converting CLR/SET to ARST on %s (%s) from module %s.\n", + log_id(cell), log_id(cell->type), log_id(module)); + ff.has_sr = false; + ff.has_arst = true; + ff.val_arst = val_arst; + ff.sig_arst = sig_arst; + ff.pol_arst = ff.pol_clr; + changed = true; + } + } + } + + if (ff.has_arst) { + if (ff.sig_arst == (ff.pol_arst ? State::S0 : State::S1)) { + // Always-inactive reset — remove. + log("Removing never-active ARST on %s (%s) from module %s.\n", + log_id(cell), log_id(cell->type), log_id(module)); + ff.has_arst = false; + changed = true; + } else if (ff.sig_arst == (ff.pol_arst ? State::S1 : State::S0) || (!opt.keepdc && ff.sig_arst == State::Sx)) { + // Always-active async reset — change to const driver. + log("Handling always-active ARST on %s (%s) from module %s (changing to const driver).\n", + log_id(cell), log_id(cell->type), log_id(module)); + initvals.remove_init(ff.sig_q); + module->remove(cell); + module->connect(ff.sig_q, ff.val_arst); + did_something = true; + continue; + } + } + + if (ff.has_srst) { + if (ff.sig_srst == (ff.pol_srst ? State::S0 : State::S1)) { + // Always-inactive reset — remove. + log("Removing never-active SRST on %s (%s) from module %s.\n", + log_id(cell), log_id(cell->type), log_id(module)); + ff.has_srst = false; + changed = true; + } else if (ff.sig_srst == (ff.pol_srst ? State::S1 : State::S0) || (!opt.keepdc && ff.sig_srst == State::Sx)) { + // Always-active sync reset — connect to D instead. + log("Handling always-active SRST on %s (%s) from module %s (changing to const D).\n", + log_id(cell), log_id(cell->type), log_id(module)); + ff.has_srst = false; + if (!ff.ce_over_srst) + ff.has_en = false; + ff.sig_d = ff.val_d = ff.val_srst; + ff.d_is_const = true; + changed = true; + } + } + + if (ff.has_en) { + if (ff.sig_en == (ff.pol_en ? State::S0 : State::S1) || (!opt.keepdc && ff.sig_en == State::Sx)) { + // Always-inactive enable — remove. + if (ff.has_clk && ff.has_srst && !ff.ce_over_srst) { + log("Handling never-active EN on %s (%s) from module %s (connecting SRST instead).\n", + log_id(cell), log_id(cell->type), log_id(module)); + // FF with sync reset — connect the sync reset to D instead. + ff.pol_en = ff.pol_srst; + ff.sig_en = ff.sig_srst; + ff.has_srst = false; + ff.sig_d = ff.val_d = ff.val_srst; + ff.d_is_const = true; + changed = true; + } else { + log("Handling never-active EN on %s (%s) from module %s (removing D path).\n", + log_id(cell), log_id(cell->type), log_id(module)); + // The D input path is effectively useless, so remove it (this will be a const-input D latch, SR latch, or a const driver). + ff.has_d = ff.has_en = ff.has_clk = false; + changed = true; + } + } else if (ff.sig_en == (ff.pol_en ? State::S1 : State::S0)) { + // Always-active enable. + if (ff.has_clk) { + // For FF, just remove the useless enable. + log("Removing always-active EN on %s (%s) from module %s.\n", + log_id(cell), log_id(cell->type), log_id(module)); + ff.has_en = false; + changed = true; + } else { + // For latches, make a comb circuit, nuke the latch. + log("Handling always-active EN on %s (%s) from module %s (changing to combinatorial circuit).\n", + log_id(cell), log_id(cell->type), log_id(module)); + initvals.remove_init(ff.sig_q); + module->remove(cell); + if (ff.has_sr) { + SigSpec tmp; + if (ff.is_fine) { + if (ff.pol_set) + tmp = module->MuxGate(NEW_ID, ff.sig_d, State::S1, ff.sig_set); + else + tmp = module->MuxGate(NEW_ID, State::S1, ff.sig_d, ff.sig_set); + if (ff.pol_clr) + module->addMuxGate(NEW_ID, tmp, State::S0, ff.sig_clr, ff.sig_q); + else + module->addMuxGate(NEW_ID, State::S0, tmp, ff.sig_clr, ff.sig_q); + } else { + if (ff.pol_set) + tmp = module->Or(NEW_ID, ff.sig_d, ff.sig_set); + else + tmp = module->Or(NEW_ID, ff.sig_d, module->Not(NEW_ID, ff.sig_set)); + if (ff.pol_clr) + module->addAnd(NEW_ID, tmp, module->Not(NEW_ID, ff.sig_clr), ff.sig_q); + else + module->addAnd(NEW_ID, tmp, ff.sig_clr, ff.sig_q); + } + } else if (ff.has_arst) { + if (ff.is_fine) { + if (ff.pol_arst) + module->addMuxGate(NEW_ID, ff.sig_d, ff.val_arst[0], ff.sig_arst, ff.sig_q); + else + module->addMuxGate(NEW_ID, ff.val_arst[0], ff.sig_d, ff.sig_arst, ff.sig_q); + } else { + if (ff.pol_arst) + module->addMux(NEW_ID, ff.sig_d, ff.val_arst, ff.sig_arst, ff.sig_q); + else + module->addMux(NEW_ID, ff.val_arst, ff.sig_d, ff.sig_arst, ff.sig_q); + } + } else { + module->connect(ff.sig_q, ff.sig_d); + } + did_something = true; + continue; + } + } + } + + if (ff.has_clk) { + if (ff.sig_clk.is_fully_const()) { + // Const clock — the D input path is effectively useless, so remove it (this will be a const-input D latch, SR latch, or a const driver). + log("Handling const CLK on %s (%s) from module %s (removing D path).\n", + log_id(cell), log_id(cell->type), log_id(module)); + ff.has_d = ff.has_en = ff.has_clk = ff.has_srst = false; + changed = true; + } + } + + if (ff.has_d && ff.sig_d == ff.sig_q) { + // Q wrapped back to D, can be removed. + if (ff.has_clk && ff.has_srst) { + // FF with sync reset — connect the sync reset to D instead. + log("Handling D = Q on %s (%s) from module %s (conecting SRST instead).\n", + log_id(cell), log_id(cell->type), log_id(module)); + if (ff.has_en && ff.ce_over_srst) { + if (!ff.pol_en) { + if (ff.is_fine) + ff.sig_en = module->NotGate(NEW_ID, ff.sig_en); + else + ff.sig_en = module->Not(NEW_ID, ff.sig_en); + } + if (!ff.pol_srst) { + if (ff.is_fine) + ff.sig_srst = module->NotGate(NEW_ID, ff.sig_srst); + else + ff.sig_srst = module->Not(NEW_ID, ff.sig_srst); + } + if (ff.is_fine) + ff.sig_en = module->AndGate(NEW_ID, ff.sig_en, ff.sig_srst); + else + ff.sig_en = module->And(NEW_ID, ff.sig_en, ff.sig_srst); + ff.pol_en = true; + } else { + ff.pol_en = ff.pol_srst; + ff.sig_en = ff.sig_srst; + } + ff.has_en = true; + ff.has_srst = false; + ff.sig_d = ff.val_d = ff.val_srst; + ff.d_is_const = true; + changed = true; + } else { + // The D input path is effectively useless, so remove it (this will be a const-input D latch, SR latch, or a const driver). + log("Handling D = Q on %s (%s) from module %s (removing D path).\n", + log_id(cell), log_id(cell->type), log_id(module)); + ff.has_d = ff.has_en = ff.has_clk = false; + changed = true; + } + } + + // Now check if any bit can be replaced by a constant. + pool<int> removed_sigbits; + for (int i = 0; i < ff.width; i++) { + State val = ff.val_init[i]; + if (ff.has_arst) + val = combine_const(val, ff.val_arst[i]); + if (ff.has_srst) + val = combine_const(val, ff.val_srst[i]); + if (ff.has_sr) { + if (ff.sig_clr[i] != (ff.pol_clr ? State::S0 : State::S1)) + val = combine_const(val, State::S0); + if (ff.sig_set[i] != (ff.pol_set ? State::S0 : State::S1)) + val = combine_const(val, State::S1); + } + if (val == State::Sm) + continue; + if (ff.has_d) { + if (!ff.sig_d[i].wire) { + val = combine_const(val, ff.sig_d[i].data); + if (val == State::Sm) + continue; + } else { + if (!opt.sat) + continue; + // For each register bit, try to prove that it cannot change from the initial value. If so, remove it + if (!bit2driver.count(ff.sig_d[i])) + continue; + if (val != State::S0 && val != State::S1) + continue; + + sat_import_cell(bit2driver.at(ff.sig_d[i])); + + int init_sat_pi = satgen.importSigSpec(val).front(); + int q_sat_pi = satgen.importSigBit(ff.sig_q[i]); + int d_sat_pi = satgen.importSigBit(ff.sig_d[i]); + + // Try to find out whether the register bit can change under some circumstances + bool counter_example_found = ez->solve(ez->IFF(q_sat_pi, init_sat_pi), ez->NOT(ez->IFF(d_sat_pi, init_sat_pi))); + + // If the register bit cannot change, we can replace it with a constant + if (counter_example_found) + continue; + } + } + log("Setting constant %d-bit at position %d on %s (%s) from module %s.\n", val ? 1 : 0, + i, log_id(cell), log_id(cell->type), log_id(module)); + + initvals.remove_init(ff.sig_q[i]); + module->connect(ff.sig_q[i], val); + removed_sigbits.insert(i); + } + if (!removed_sigbits.empty()) { + std::vector<int> keep_bits; + for (int i = 0; i < ff.width; i++) + if (!removed_sigbits.count(i)) + keep_bits.push_back(i); + if (keep_bits.empty()) { + module->remove(cell); + did_something = true; + continue; + } + ff = ff.slice(keep_bits); + changed = true; + } + + // The cell has been simplified as much as possible already. Now try to spice it up with enables / sync resets. + if (ff.has_clk) { + if (!ff.has_arst && !ff.has_sr && (!ff.has_srst || !ff.has_en || ff.ce_over_srst) && !opt.nosdff) { + // Try to merge sync resets. + std::map<ctrls_t, std::vector<int>> groups; + std::vector<int> remaining_indices; + Const val_srst; + + for (int i = 0 ; i < ff.width; i++) { + ctrls_t resets; + State reset_val = State::Sx; + if (ff.has_srst) + reset_val = ff.val_srst[i]; + while (bit2mux.count(ff.sig_d[i]) && bitusers[ff.sig_d[i]] == 1) { + cell_int_t mbit = bit2mux.at(ff.sig_d[i]); + if (GetSize(mbit.first->getPort(ID::S)) != 1) + break; + SigBit s = mbit.first->getPort(ID::S); + SigBit a = mbit.first->getPort(ID::A)[mbit.second]; + SigBit b = mbit.first->getPort(ID::B)[mbit.second]; + // Workaround for funny memory WE pattern. + if ((a == State::S0 || a == State::S1) && (b == State::S0 || b == State::S1)) + break; + if ((b == State::S0 || b == State::S1) && (b == reset_val || reset_val == State::Sx)) { + // This is better handled by CE pattern. + if (a == ff.sig_q[i]) + break; + reset_val = b.data; + resets.insert(ctrl_t(s, true)); + ff.sig_d[i] = a; + } else if ((a == State::S0 || a == State::S1) && (a == reset_val || reset_val == State::Sx)) { + // This is better handled by CE pattern. + if (b == ff.sig_q[i]) + break; + reset_val = a.data; + resets.insert(ctrl_t(s, false)); + ff.sig_d[i] = b; + } else { + break; + } + } + + if (!resets.empty()) { + if (ff.has_srst) + resets.insert(ctrl_t(ff.sig_srst, ff.pol_srst)); + groups[resets].push_back(i); + } else + remaining_indices.push_back(i); + val_srst.bits.push_back(reset_val); + } + + for (auto &it : groups) { + FfData new_ff = ff.slice(it.second); + new_ff.val_srst = Const(); + for (int i = 0; i < new_ff.width; i++) { + int j = it.second[i]; + new_ff.val_srst.bits.push_back(val_srst[j]); + } + ctrl_t srst = combine_resets(it.first, ff.is_fine); + + new_ff.has_srst = true; + new_ff.sig_srst = srst.first; + new_ff.pol_srst = srst.second; + if (new_ff.has_en) + new_ff.ce_over_srst = true; + Cell *new_cell = new_ff.emit(module, NEW_ID); + if (new_cell) + dff_cells.push_back(new_cell); + log("Adding SRST signal on %s (%s) from module %s (D = %s, Q = %s, rval = %s).\n", + log_id(cell), log_id(cell->type), log_id(module), log_signal(new_ff.sig_d), log_signal(new_ff.sig_q), log_signal(new_ff.val_srst)); + } + + if (remaining_indices.empty()) { + module->remove(cell); + did_something = true; + continue; + } else if (GetSize(remaining_indices) != ff.width) { + ff = ff.slice(remaining_indices); + changed = true; + } + } + if ((!ff.has_srst || !ff.has_en || !ff.ce_over_srst) && !opt.nodffe) { + // Try to merge enables. + std::map<std::pair<patterns_t, ctrls_t>, std::vector<int>> groups; + std::vector<int> remaining_indices; + + for (int i = 0 ; i < ff.width; i++) { + // First, eat up as many simple muxes as possible. + ctrls_t enables; + while (bit2mux.count(ff.sig_d[i]) && bitusers[ff.sig_d[i]] == 1) { + cell_int_t mbit = bit2mux.at(ff.sig_d[i]); + if (GetSize(mbit.first->getPort(ID::S)) != 1) + break; + SigBit s = mbit.first->getPort(ID::S); + SigBit a = mbit.first->getPort(ID::A)[mbit.second]; + SigBit b = mbit.first->getPort(ID::B)[mbit.second]; + if (a == ff.sig_q[i]) { + enables.insert(ctrl_t(s, true)); + ff.sig_d[i] = b; + } else if (b == ff.sig_q[i]) { + enables.insert(ctrl_t(s, false)); + ff.sig_d[i] = a; + } else { + break; + } + } + + patterns_t patterns; + if (!opt.simple_dffe) + patterns = find_muxtree_feedback_patterns(ff.sig_d[i], ff.sig_q[i], pattern_t()); + if (!patterns.empty() || !enables.empty()) { + if (ff.has_en) + enables.insert(ctrl_t(ff.sig_en, ff.pol_en)); + simplify_patterns(patterns); + groups[std::make_pair(patterns, enables)].push_back(i); + } else + remaining_indices.push_back(i); + } + + for (auto &it : groups) { + FfData new_ff = ff.slice(it.second); + ctrl_t en = make_patterns_logic(it.first.first, it.first.second, ff.is_fine); + + new_ff.has_en = true; + new_ff.sig_en = en.first; + new_ff.pol_en = en.second; + new_ff.ce_over_srst = false; + Cell *new_cell = new_ff.emit(module, NEW_ID); + if (new_cell) + dff_cells.push_back(new_cell); + log("Adding EN signal on %s (%s) from module %s (D = %s, Q = %s).\n", + log_id(cell), log_id(cell->type), log_id(module), log_signal(new_ff.sig_d), log_signal(new_ff.sig_q)); + } + + if (remaining_indices.empty()) { + module->remove(cell); + did_something = true; + continue; + } else if (GetSize(remaining_indices) != ff.width) { + ff = ff.slice(remaining_indices); + changed = true; + } + } + } + + if (changed) { + // Rebuild the FF. + IdString name = cell->name; + module->remove(cell); + ff.emit(module, name); + did_something = true; + } + } + return did_something; + } +}; + +struct OptDffPass : public Pass { + OptDffPass() : Pass("opt_dff", "perform DFF optimizations") { } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" opt_dff [-nodffe] [-nosdff] [-keepdc] [-sat] [selection]\n"); + log("\n"); + log("This pass converts flip-flops to a more suitable type by merging clock enables\n"); + log("and synchronous reset multiplexers, removing unused control inputs, or potentially\n"); + log("removes the flip-flop altogether, converting it to a constant driver.\n"); + log("\n"); + log(" -nodffe\n"); + log(" disables dff -> dffe conversion, and other transforms recognizing clock enable\n"); + log("\n"); + log(" -nosdff\n"); + log(" disables dff -> sdff conversion, and other transforms recognizing sync resets\n"); + log("\n"); + log(" -simple-dffe\n"); + log(" only enables clock enable recognition transform for obvious cases\n"); + log("\n"); + log(" -sat\n"); + log(" additionally invoke SAT solver to detect and remove flip-flops (with\n"); + log(" non-constant inputs) that can also be replaced with a constant driver\n"); + log("\n"); + log(" -keepdc\n"); + log(" some optimizations change the behavior of the circuit with respect to\n"); + log(" don't-care bits. for example in 'a+0' a single x-bit in 'a' will cause\n"); + log(" all result bits to be set to x. this behavior changes when 'a+0' is\n"); + log(" replaced by 'a'. the -keepdc option disables all such optimizations.\n"); + log("\n"); + } + + void execute(std::vector<std::string> args, RTLIL::Design *design) override + { + log_header(design, "Executing OPT_DFF pass (perform DFF optimizations).\n"); + OptDffOptions opt; + opt.nodffe = false; + opt.nosdff = false; + opt.simple_dffe = false; + opt.keepdc = false; + opt.sat = false; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-nodffe") { + opt.nodffe = true; + continue; + } + if (args[argidx] == "-nosdff") { + opt.nosdff = true; + continue; + } + if (args[argidx] == "-simple-dffe") { + opt.simple_dffe = true; + continue; + } + if (args[argidx] == "-keepdc") { + opt.keepdc = true; + continue; + } + if (args[argidx] == "-sat") { + opt.sat = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + bool did_something = false; + for (auto mod : design->selected_modules()) { + OptDffWorker worker(opt, mod); + if (worker.run()) + did_something = true; + } + + if (did_something) + design->scratchpad_set_bool("opt.did_something", true); + } +} OptDffPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc index 649ad83a6..e36e4419d 100644 --- a/passes/opt/opt_expr.cc +++ b/passes/opt/opt_expr.cc @@ -416,7 +416,7 @@ int get_onehot_bit_index(RTLIL::SigSpec signal) return bit_index; } -void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool consume_x, bool mux_undef, bool mux_bool, bool do_fine, bool keepdc, bool clkinv) +void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool consume_x, bool mux_undef, bool mux_bool, bool do_fine, bool keepdc, bool noclkinv) { if (!design->selected(module)) return; @@ -465,7 +465,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons #define ACTION_DO(_p_, _s_) do { cover("opt.opt_expr.action_" S__LINE__); replace_cell(assign_map, module, cell, input.as_string(), _p_, _s_); goto next_cell; } while (0) #define ACTION_DO_Y(_v_) ACTION_DO(ID::Y, RTLIL::SigSpec(RTLIL::State::S ## _v_)) - if (clkinv) + if (!noclkinv) { if (cell->type.in(ID($dff), ID($dffe), ID($dffsr), ID($dffsre), ID($adff), ID($adffe), ID($sdff), ID($sdffe), ID($sdffce), ID($fsm), ID($memrd), ID($memwr))) handle_polarity_inv(cell, ID::CLK, ID::CLK_POLARITY, assign_map, invert_map); @@ -604,7 +604,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (cell->type.in(ID($xnor), ID($_XNOR_))) { cover("opt.opt_expr.const_xnor"); // For consistency since simplemap does $xnor -> $_XOR_ + $_NOT_ - int width = cell->getParam(ID::Y_WIDTH).as_int(); + int width = GetSize(cell->getPort(ID::Y)); replace_cell(assign_map, module, cell, "const_xnor", ID::Y, SigSpec(RTLIL::State::S1, width)); goto next_cell; } @@ -2064,8 +2064,8 @@ struct OptExprPass : public Pass { log(" -undriven\n"); log(" replace undriven nets with undef (x) constants\n"); log("\n"); - log(" -clkinv\n"); - log(" optimize clock inverters by changing FF types\n"); + log(" -noclkinv\n"); + log(" do not optimize clock inverters by changing FF types\n"); log("\n"); log(" -fine\n"); log(" perform fine-grain optimizations\n"); @@ -2085,7 +2085,7 @@ struct OptExprPass : public Pass { bool mux_undef = false; bool mux_bool = false; bool undriven = false; - bool clkinv = false; + bool noclkinv = false; bool do_fine = false; bool keepdc = false; @@ -2106,8 +2106,8 @@ struct OptExprPass : public Pass { undriven = true; continue; } - if (args[argidx] == "-clkinv") { - clkinv = true; + if (args[argidx] == "-noclkinv") { + noclkinv = true; continue; } if (args[argidx] == "-fine") { @@ -2144,12 +2144,12 @@ struct OptExprPass : public Pass { do { do { did_something = false; - replace_const_cells(design, module, false /* consume_x */, mux_undef, mux_bool, do_fine, keepdc, clkinv); + replace_const_cells(design, module, false /* consume_x */, mux_undef, mux_bool, do_fine, keepdc, noclkinv); if (did_something) design->scratchpad_set_bool("opt.did_something", true); } while (did_something); if (!keepdc) - replace_const_cells(design, module, true /* consume_x */, mux_undef, mux_bool, do_fine, keepdc, clkinv); + replace_const_cells(design, module, true /* consume_x */, mux_undef, mux_bool, do_fine, keepdc, noclkinv); if (did_something) design->scratchpad_set_bool("opt.did_something", true); } while (did_something); diff --git a/passes/opt/pmux2shiftx.cc b/passes/opt/pmux2shiftx.cc index 9f226e12d..f3b1fd377 100644 --- a/passes/opt/pmux2shiftx.cc +++ b/passes/opt/pmux2shiftx.cc @@ -19,6 +19,7 @@ #include "kernel/yosys.h" #include "kernel/sigtools.h" +#include "kernel/ffinit.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -30,7 +31,7 @@ struct OnehotDatabase bool verbose = false; bool initialized = false; - pool<SigBit> init_ones; + FfInitVals initvals; dict<SigSpec, pool<SigSpec>> sig_sources_db; dict<SigSpec, bool> sig_onehot_cache; pool<SigSpec> recursion_guard; @@ -44,19 +45,7 @@ struct OnehotDatabase log_assert(!initialized); initialized = true; - for (auto wire : module->wires()) - { - auto it = wire->attributes.find(ID::init); - if (it == wire->attributes.end()) - continue; - - auto &val = it->second; - int width = std::max(GetSize(wire), GetSize(val)); - - for (int i = 0; i < width; i++) - if (val[i] == State::S1) - init_ones.insert(sigmap(SigBit(wire, i))); - } + initvals.set(&sigmap, module); for (auto cell : module->cells()) { @@ -119,7 +108,7 @@ struct OnehotDatabase bool found_init_ones = false; for (auto bit : sig) { - if (init_ones.count(bit)) { + if (initvals(bit) == State::S1) { if (found_init_ones) { if (verbose) log("%*s - non-onehot init value\n", indent, ""); diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc index 78e2bcbea..a216f36d4 100644 --- a/passes/opt/wreduce.cc +++ b/passes/opt/wreduce.cc @@ -20,6 +20,7 @@ #include "kernel/yosys.h" #include "kernel/sigtools.h" #include "kernel/modtools.h" +#include "kernel/ffinit.h" USING_YOSYS_NAMESPACE @@ -54,8 +55,7 @@ struct WreduceWorker std::set<Cell*, IdString::compare_ptr_by_name<Cell>> work_queue_cells; std::set<SigBit> work_queue_bits; pool<SigBit> keep_bits; - dict<SigBit, State> init_bits; - pool<SigBit> remove_init_bits; + FfInitVals initvals; WreduceWorker(WreduceConfig *config, Module *module) : config(config), module(module), mi(module) { } @@ -145,7 +145,7 @@ struct WreduceWorker SigSpec sig_d = mi.sigmap(cell->getPort(ID::D)); SigSpec sig_q = mi.sigmap(cell->getPort(ID::Q)); bool has_reset = false; - Const initval, rst_value; + Const initval = initvals(sig_q), rst_value; int width_before = GetSize(sig_q); @@ -163,20 +163,12 @@ struct WreduceWorker bool zero_ext = sig_d[GetSize(sig_d)-1] == State::S0; bool sign_ext = !zero_ext; - for (int i = 0; i < GetSize(sig_q); i++) { - SigBit bit = sig_q[i]; - if (init_bits.count(bit)) - initval.bits.push_back(init_bits.at(bit)); - else - initval.bits.push_back(State::Sx); - } - for (int i = GetSize(sig_q)-1; i >= 0; i--) { if (zero_ext && sig_d[i] == State::S0 && (initval[i] == State::S0 || initval[i] == State::Sx) && (!has_reset || i >= GetSize(rst_value) || rst_value[i] == State::S0 || rst_value[i] == State::Sx)) { module->connect(sig_q[i], State::S0); - remove_init_bits.insert(sig_q[i]); + initvals.remove_init(sig_q[i]); sig_d.remove(i); sig_q.remove(i); continue; @@ -185,7 +177,7 @@ struct WreduceWorker if (sign_ext && i > 0 && sig_d[i] == sig_d[i-1] && initval[i] == initval[i-1] && (!has_reset || i >= GetSize(rst_value) || rst_value[i] == rst_value[i-1])) { module->connect(sig_q[i], sig_q[i-1]); - remove_init_bits.insert(sig_q[i]); + initvals.remove_init(sig_q[i]); sig_d.remove(i); sig_q.remove(i); continue; @@ -195,7 +187,7 @@ struct WreduceWorker if (info == nullptr) return; if (!info->is_output && GetSize(info->ports) == 1 && !keep_bits.count(mi.sigmap(sig_q[i]))) { - remove_init_bits.insert(sig_q[i]); + initvals.remove_init(sig_q[i]); sig_d.remove(i); sig_q.remove(i); zero_ext = false; @@ -409,18 +401,12 @@ struct WreduceWorker { // create a copy as mi.sigmap will be updated as we process the module SigMap init_attr_sigmap = mi.sigmap; + initvals.set(&init_attr_sigmap, module); for (auto w : module->wires()) { if (w->get_bool_attribute(ID::keep)) for (auto bit : mi.sigmap(w)) keep_bits.insert(bit); - if (w->attributes.count(ID::init)) { - Const initval = w->attributes.at(ID::init); - SigSpec initsig = init_attr_sigmap(w); - int width = std::min(GetSize(initval), GetSize(initsig)); - for (int i = 0; i < width; i++) - init_bits[initsig[i]] = initval[i]; - } } for (auto c : module->selected_cells()) @@ -469,22 +455,6 @@ struct WreduceWorker module->connect(nw, SigSpec(w).extract(0, GetSize(nw))); module->swap_names(w, nw); } - - if (!remove_init_bits.empty()) { - for (auto w : module->wires()) { - if (w->attributes.count(ID::init)) { - Const initval = w->attributes.at(ID::init); - Const new_initval(State::Sx, GetSize(w)); - SigSpec initsig = init_attr_sigmap(w); - int width = std::min(GetSize(initval), GetSize(initsig)); - for (int i = 0; i < width; i++) { - if (!remove_init_bits.count(initsig[i])) - new_initval[i] = initval[i]; - } - w->attributes.at(ID::init) = new_initval; - } - } - } } }; diff --git a/passes/pmgen/ice40_dsp.cc b/passes/pmgen/ice40_dsp.cc index fff04074b..c46f5d58f 100644 --- a/passes/pmgen/ice40_dsp.cc +++ b/passes/pmgen/ice40_dsp.cc @@ -31,15 +31,15 @@ void create_ice40_dsp(ice40_dsp_pm &pm) log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(st.mul)); - log_debug("ffA: %s %s %s\n", log_id(st.ffA, "--"), log_id(st.ffAholdmux, "--"), log_id(st.ffArstmux, "--")); - log_debug("ffB: %s %s %s\n", log_id(st.ffB, "--"), log_id(st.ffBholdmux, "--"), log_id(st.ffBrstmux, "--")); - log_debug("ffCD: %s %s\n", log_id(st.ffCD, "--"), log_id(st.ffCDholdmux, "--")); + log_debug("ffA: %s\n", log_id(st.ffA, "--")); + log_debug("ffB: %s\n", log_id(st.ffB, "--")); + log_debug("ffCD: %s\n", log_id(st.ffCD, "--")); log_debug("mul: %s\n", log_id(st.mul, "--")); log_debug("ffFJKG: %s\n", log_id(st.ffFJKG, "--")); log_debug("ffH: %s\n", log_id(st.ffH, "--")); log_debug("add: %s\n", log_id(st.add, "--")); log_debug("mux: %s\n", log_id(st.mux, "--")); - log_debug("ffO: %s %s %s\n", log_id(st.ffO, "--"), log_id(st.ffOholdmux, "--"), log_id(st.ffOrstmux, "--")); + log_debug("ffO: %s\n", log_id(st.ffO, "--")); log_debug("\n"); if (GetSize(st.sigA) > 16) { @@ -97,16 +97,16 @@ void create_ice40_dsp(ice40_dsp_pm &pm) cell->setParam(ID(D_REG), st.ffCD ? State::S1 : State::S0); SigSpec AHOLD, BHOLD, CDHOLD; - if (st.ffAholdmux) - AHOLD = st.ffAholdpol ? st.ffAholdmux->getPort(ID::S) : pm.module->Not(NEW_ID, st.ffAholdmux->getPort(ID::S)); + if (st.ffA && st.ffA->hasPort(ID::EN)) + AHOLD = st.ffA->getParam(ID::EN_POLARITY).as_bool() ? pm.module->Not(NEW_ID, st.ffA->getPort(ID::EN)) : st.ffA->getPort(ID::EN); else AHOLD = State::S0; - if (st.ffBholdmux) - BHOLD = st.ffBholdpol ? st.ffBholdmux->getPort(ID::S) : pm.module->Not(NEW_ID, st.ffBholdmux->getPort(ID::S)); + if (st.ffB && st.ffB->hasPort(ID::EN)) + BHOLD = st.ffB->getParam(ID::EN_POLARITY).as_bool() ? pm.module->Not(NEW_ID, st.ffB->getPort(ID::EN)) : st.ffB->getPort(ID::EN); else BHOLD = State::S0; - if (st.ffCDholdmux) - CDHOLD = st.ffCDholdpol ? st.ffCDholdmux->getPort(ID::S) : pm.module->Not(NEW_ID, st.ffCDholdmux->getPort(ID::S)); + if (st.ffCD && st.ffCD->hasPort(ID::EN)) + CDHOLD = st.ffCD->getParam(ID::EN_POLARITY).as_bool() ? pm.module->Not(NEW_ID, st.ffCD->getPort(ID::EN)) : st.ffCD->getPort(ID::EN); else CDHOLD = State::S0; cell->setPort(ID(AHOLD), AHOLD); @@ -115,12 +115,12 @@ void create_ice40_dsp(ice40_dsp_pm &pm) cell->setPort(ID(DHOLD), CDHOLD); SigSpec IRSTTOP, IRSTBOT; - if (st.ffArstmux) - IRSTTOP = st.ffArstpol ? st.ffArstmux->getPort(ID::S) : pm.module->Not(NEW_ID, st.ffArstmux->getPort(ID::S)); + if (st.ffA && st.ffA->hasPort(ID::ARST)) + IRSTTOP = st.ffA->getParam(ID::ARST_POLARITY).as_bool() ? st.ffA->getPort(ID::ARST) : pm.module->Not(NEW_ID, st.ffA->getPort(ID::ARST)); else IRSTTOP = State::S0; - if (st.ffBrstmux) - IRSTBOT = st.ffBrstpol ? st.ffBrstmux->getPort(ID::S) : pm.module->Not(NEW_ID, st.ffBrstmux->getPort(ID::S)); + if (st.ffB && st.ffB->hasPort(ID::ARST)) + IRSTBOT = st.ffB->getParam(ID::ARST_POLARITY).as_bool() ? st.ffB->getPort(ID::ARST) : pm.module->Not(NEW_ID, st.ffB->getPort(ID::ARST)); else IRSTBOT = State::S0; cell->setPort(ID(IRSTTOP), IRSTTOP); @@ -207,16 +207,16 @@ void create_ice40_dsp(ice40_dsp_pm &pm) } SigSpec OHOLD; - if (st.ffOholdmux) - OHOLD = st.ffOholdpol ? st.ffOholdmux->getPort(ID::S) : pm.module->Not(NEW_ID, st.ffOholdmux->getPort(ID::S)); + if (st.ffO && st.ffO->hasPort(ID::EN)) + OHOLD = st.ffO->getParam(ID::EN_POLARITY).as_bool() ? pm.module->Not(NEW_ID, st.ffO->getPort(ID::EN)) : st.ffO->getPort(ID::EN); else OHOLD = State::S0; cell->setPort(ID(OHOLDTOP), OHOLD); cell->setPort(ID(OHOLDBOT), OHOLD); SigSpec ORST; - if (st.ffOrstmux) - ORST = st.ffOrstpol ? st.ffOrstmux->getPort(ID::S) : pm.module->Not(NEW_ID, st.ffOrstmux->getPort(ID::S)); + if (st.ffO && st.ffO->hasPort(ID::ARST)) + ORST = st.ffO->getParam(ID::ARST_POLARITY).as_bool() ? st.ffO->getPort(ID::ARST) : pm.module->Not(NEW_ID, st.ffO->getPort(ID::ARST)); else ORST = State::S0; cell->setPort(ID(ORSTTOP), ORST); @@ -228,6 +228,8 @@ void create_ice40_dsp(ice40_dsp_pm &pm) acc_reset = st.mux->getPort(ID::S); else acc_reset = pm.module->Not(NEW_ID, st.mux->getPort(ID::S)); + } else if (st.ffO && st.ffO->hasPort(ID::SRST)) { + acc_reset = st.ffO->getParam(ID::SRST_POLARITY).as_bool() ? st.ffO->getPort(ID::SRST) : pm.module->Not(NEW_ID, st.ffO->getPort(ID::SRST)); } cell->setPort(ID(OLOADTOP), acc_reset); cell->setPort(ID(OLOADBOT), acc_reset); diff --git a/passes/pmgen/ice40_dsp.pmg b/passes/pmgen/ice40_dsp.pmg index 2456a49dc..7a01cbd51 100644 --- a/passes/pmgen/ice40_dsp.pmg +++ b/passes/pmgen/ice40_dsp.pmg @@ -6,20 +6,16 @@ state <SigSpec> sigA sigB sigCD sigH sigO state <Cell*> add mux state <IdString> addAB muxAB -state <bool> ffAholdpol ffBholdpol ffCDholdpol ffOholdpol -state <bool> ffArstpol ffBrstpol ffCDrstpol ffOrstpol - -state <Cell*> ffA ffAholdmux ffArstmux ffB ffBholdmux ffBrstmux ffCD ffCDholdmux -state <Cell*> ffFJKG ffH ffO ffOholdmux ffOrstmux +state <Cell*> ffA ffB ffCD +state <Cell*> ffFJKG ffH ffO // subpattern +state <bool> argSdff state <SigSpec> argQ argD -state <bool> ffholdpol ffrstpol -state <int> ffoffset udata <SigSpec> dffD dffQ udata <SigBit> dffclock -udata <Cell*> dff dffholdmux dffrstmux -udata <bool> dffholdpol dffrstpol dffclock_pol +udata <Cell*> dff +udata <bool> dffclock_pol match mul select mul->type.in($mul, \SB_MAC16) @@ -64,7 +60,7 @@ code sigA sigB sigH log_assert(nusers(O.extract_end(i)) <= 1); endcode -code argQ ffA ffAholdmux ffArstmux ffAholdpol ffArstpol sigA clock clock_pol +code argQ ffA sigA clock clock_pol if (mul->type != \SB_MAC16 || !param(mul, \A_REG).as_bool()) { argQ = sigA; subpattern(in_dffe); @@ -72,20 +68,12 @@ code argQ ffA ffAholdmux ffArstmux ffAholdpol ffArstpol sigA clock clock_pol ffA = dff; clock = dffclock; clock_pol = dffclock_pol; - if (dffrstmux) { - ffArstmux = dffrstmux; - ffArstpol = dffrstpol; - } - if (dffholdmux) { - ffAholdmux = dffholdmux; - ffAholdpol = dffholdpol; - } sigA = dffD; } } endcode -code argQ ffB ffBholdmux ffBrstmux ffBholdpol ffBrstpol sigB clock clock_pol +code argQ ffB sigB clock clock_pol if (mul->type != \SB_MAC16 || !param(mul, \B_REG).as_bool()) { argQ = sigB; subpattern(in_dffe); @@ -93,47 +81,44 @@ code argQ ffB ffBholdmux ffBrstmux ffBholdpol ffBrstpol sigB clock clock_pol ffB = dff; clock = dffclock; clock_pol = dffclock_pol; - if (dffrstmux) { - ffBrstmux = dffrstmux; - ffBrstpol = dffrstpol; - } - if (dffholdmux) { - ffBholdmux = dffholdmux; - ffBholdpol = dffholdpol; - } sigB = dffD; } } endcode -code argD ffFJKG sigH clock clock_pol +code argD argSdff ffFJKG sigH clock clock_pol if (nusers(sigH) == 2 && (mul->type != \SB_MAC16 || (!param(mul, \TOP_8x8_MULT_REG).as_bool() && !param(mul, \BOT_8x8_MULT_REG).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1).as_bool()))) { argD = sigH; + argSdff = false; subpattern(out_dffe); if (dff) { // F/J/K/G do not have a CE-like (hold) input - if (dffholdmux) + if (dff->hasPort(\EN)) goto reject_ffFJKG; // Reset signal of F/J (IRSTTOP) and K/G (IRSTBOT) // shared with A and B - if ((ffArstmux != NULL) != (dffrstmux != NULL)) - goto reject_ffFJKG; - if ((ffBrstmux != NULL) != (dffrstmux != NULL)) - goto reject_ffFJKG; - if (ffArstmux) { - if (port(ffArstmux, \S) != port(dffrstmux, \S)) - goto reject_ffFJKG; - if (ffArstpol != dffrstpol) + if (ffA) { + if (ffA->hasPort(\ARST) != dff->hasPort(\ARST)) goto reject_ffFJKG; + if (ffA->hasPort(\ARST)) { + if (port(ffA, \ARST) != port(dff, \ARST)) + goto reject_ffFJKG; + if (param(ffA, \ARST_POLARITY) != param(dff, \ARST_POLARITY)) + goto reject_ffFJKG; + } } - if (ffBrstmux) { - if (port(ffBrstmux, \S) != port(dffrstmux, \S)) - goto reject_ffFJKG; - if (ffBrstpol != dffrstpol) + if (ffB) { + if (ffB->hasPort(\ARST) != dff->hasPort(\ARST)) goto reject_ffFJKG; + if (ffB->hasPort(\ARST)) { + if (port(ffB, \ARST) != port(dff, \ARST)) + goto reject_ffFJKG; + if (param(ffB, \ARST_POLARITY) != param(dff, \ARST_POLARITY)) + goto reject_ffFJKG; + } } ffFJKG = dff; @@ -146,23 +131,24 @@ reject_ffFJKG: ; } endcode -code argD ffH sigH sigO clock clock_pol +code argD argSdff ffH sigH sigO clock clock_pol if (ffFJKG && nusers(sigH) == 2 && (mul->type != \SB_MAC16 || !param(mul, \PIPELINE_16x16_MULT_REG2).as_bool())) { argD = sigH; + argSdff = false; subpattern(out_dffe); if (dff) { // H does not have a CE-like (hold) input - if (dffholdmux) + if (dff->hasPort(\EN)) goto reject_ffH; // Reset signal of H (IRSTBOT) shared with B - if ((ffBrstmux != NULL) != (dffrstmux != NULL)) + if (ffB->hasPort(\ARST) != dff->hasPort(\ARST)) goto reject_ffH; - if (ffBrstmux) { - if (port(ffBrstmux, \S) != port(dffrstmux, \S)) + if (ffB->hasPort(\ARST)) { + if (port(ffB, \ARST) != port(dff, \ARST)) goto reject_ffH; - if (ffBrstpol != dffrstpol) + if (param(ffB, \ARST_POLARITY) != param(dff, \ARST_POLARITY)) goto reject_ffH; } @@ -226,7 +212,7 @@ code sigO sigO = port(mux, \Y); endcode -code argD ffO ffOholdmux ffOrstmux ffOholdpol ffOrstpol sigO sigCD clock clock_pol cd_signed o_lo +code argD argSdff ffO sigO sigCD clock clock_pol cd_signed o_lo if (mul->type != \SB_MAC16 || // Ensure that register is not already used ((param(mul, \TOPOUTPUT_SELECT).as_int() != 1 && param(mul, \BOTOUTPUT_SELECT).as_int() != 1) && @@ -238,6 +224,7 @@ code argD ffO ffOholdmux ffOrstmux ffOholdpol ffOrstpol sigO sigCD clock clock_p // First try entire sigO if (nusers(sigO) == 2) { argD = sigO; + argSdff = !mux; subpattern(out_dffe); } @@ -245,6 +232,7 @@ code argD ffO ffOholdmux ffOrstmux ffOholdpol ffOrstpol sigO sigCD clock clock_p if (!dff && GetSize(sigO) > 16) { argD = sigO.extract(0, 16); if (nusers(argD) == 2) { + argSdff = !mux; subpattern(out_dffe); o_lo = dff; } @@ -254,14 +242,6 @@ code argD ffO ffOholdmux ffOrstmux ffOholdpol ffOrstpol sigO sigCD clock clock_p ffO = dff; clock = dffclock; clock_pol = dffclock_pol; - if (dffrstmux) { - ffOrstmux = dffrstmux; - ffOrstpol = dffrstpol; - } - if (dffholdmux) { - ffOholdmux = dffholdmux; - ffOholdpol = dffholdpol; - } sigO.replace(sigO.extract(0, GetSize(dffQ)), dffQ); } @@ -274,38 +254,43 @@ code argD ffO ffOholdmux ffOrstmux ffOholdpol ffOrstpol sigO sigCD clock clock_p sigCD = port(mux, muxAB == \B ? \A : \B); cd_signed = add && param(add, \A_SIGNED).as_bool() && param(add, \B_SIGNED).as_bool(); + } else if (dff && dff->hasPort(\SRST)) { + if (sigCD != sigO) + reject; + sigCD = param(dff, \SRST_VALUE); + + cd_signed = add && param(add, \A_SIGNED).as_bool() && param(add, \B_SIGNED).as_bool(); } } endcode -code argQ ffCD ffCDholdmux ffCDholdpol ffCDrstpol sigCD clock clock_pol +code argQ ffCD sigCD clock clock_pol if (!sigCD.empty() && sigCD != sigO && (mul->type != \SB_MAC16 || (!param(mul, \C_REG).as_bool() && !param(mul, \D_REG).as_bool()))) { argQ = sigCD; subpattern(in_dffe); if (dff) { - if (dffholdmux) { - ffCDholdmux = dffholdmux; - ffCDholdpol = dffholdpol; - } - // Reset signal of C (IRSTTOP) and D (IRSTBOT) // shared with A and B - if ((ffArstmux != NULL) != (dffrstmux != NULL)) - goto reject_ffCD; - if ((ffBrstmux != NULL) != (dffrstmux != NULL)) - goto reject_ffCD; - if (ffArstmux) { - if (port(ffArstmux, \S) != port(dffrstmux, \S)) - goto reject_ffCD; - if (ffArstpol != dffrstpol) + if (ffA) { + if (ffA->hasPort(\ARST) != dff->hasPort(\ARST)) goto reject_ffCD; + if (ffA->hasPort(\ARST)) { + if (port(ffA, \ARST) != port(dff, \ARST)) + goto reject_ffCD; + if (param(ffA, \ARST_POLARITY) != param(dff, \ARST_POLARITY)) + goto reject_ffCD; + } } - if (ffBrstmux) { - if (port(ffBrstmux, \S) != port(dffrstmux, \S)) - goto reject_ffCD; - if (ffBrstpol != dffrstpol) + if (ffB) { + if (ffB->hasPort(\ARST) != dff->hasPort(\ARST)) goto reject_ffCD; + if (ffB->hasPort(\ARST)) { + if (port(ffB, \ARST) != port(dff, \ARST)) + goto reject_ffCD; + if (param(ffB, \ARST_POLARITY) != param(dff, \ARST_POLARITY)) + goto reject_ffCD; + } } ffCD = dff; @@ -347,7 +332,7 @@ code endcode match ff - select ff->type.in($dff) + select ff->type.in($dff, $dffe) // DSP48E1 does not support clock inversion select param(ff, \CLK_POLARITY).as_bool() @@ -357,8 +342,6 @@ match ff // Check that the rest of argQ is present filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ) filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ - - set ffoffset offset endmatch code argQ argD @@ -378,72 +361,13 @@ code argQ argD argD = port(ff, \D); argQ = Q; dffD.replace(argQ, argD); - // Only search for ffrstmux if dffD only - // has two (ff, ffrstmux) users - if (nusers(dffD) > 2) - argD = SigSpec(); } endcode -match ffrstmux - if false /* TODO: ice40 resets are actually async */ - - if !argD.empty() - select ffrstmux->type.in($mux) - index <SigSpec> port(ffrstmux, \Y) === argD - - choice <IdString> BA {\B, \A} - // DSP48E1 only supports reset to zero - select port(ffrstmux, BA).is_fully_zero() - - define <bool> pol (BA == \B) - set ffrstpol pol - semioptional -endmatch - -code argD - if (ffrstmux) { - dffrstmux = ffrstmux; - dffrstpol = ffrstpol; - argD = port(ffrstmux, ffrstpol ? \A : \B); - dffD.replace(port(ffrstmux, \Y), argD); - - // Only search for ffholdmux if argQ has at - // least 3 users (ff, <upstream>, ffrstmux) and - // dffD only has two (ff, ffrstmux) - if (!(nusers(argQ) >= 3 && nusers(dffD) == 2)) - argD = SigSpec(); - } - else - dffrstmux = nullptr; -endcode - -match ffholdmux - if !argD.empty() - select ffholdmux->type.in($mux) - index <SigSpec> port(ffholdmux, \Y) === argD - choice <IdString> BA {\B, \A} - index <SigSpec> port(ffholdmux, BA) === argQ - define <bool> pol (BA == \B) - set ffholdpol pol - semioptional -endmatch - -code argD - if (ffholdmux) { - dffholdmux = ffholdmux; - dffholdpol = ffholdpol; - argD = port(ffholdmux, ffholdpol ? \A : \B); - dffD.replace(port(ffholdmux, \Y), argD); - } - else - dffholdmux = nullptr; -endcode - // ####################### subpattern out_dffe -arg argD argQ clock clock_pol +arg argD argSdff argQ clock clock_pol code dff = nullptr; @@ -452,101 +376,19 @@ code reject; endcode -match ffholdmux - select ffholdmux->type.in($mux) - // ffholdmux output must have two users: ffholdmux and ff.D - select nusers(port(ffholdmux, \Y)) == 2 - - choice <IdString> BA {\B, \A} - // keep-last-value net must have at least three users: ffholdmux, ff, downstream sink(s) - select nusers(port(ffholdmux, BA)) >= 3 - - slice offset GetSize(port(ffholdmux, \Y)) - define <IdString> AB (BA == \B ? \A : \B) - index <SigBit> port(ffholdmux, AB)[offset] === argD[0] - - // Check that the rest of argD is present - filter GetSize(port(ffholdmux, AB)) >= offset + GetSize(argD) - filter port(ffholdmux, AB).extract(offset, GetSize(argD)) == argD - - set ffoffset offset - define <bool> pol (BA == \B) - set ffholdpol pol - - semioptional -endmatch - -code argD argQ - dffholdmux = ffholdmux; - if (ffholdmux) { - SigSpec AB = port(ffholdmux, ffholdpol ? \A : \B); - SigSpec Y = port(ffholdmux, \Y); - argQ = argD; - argD.replace(AB, Y); - argQ.replace(AB, port(ffholdmux, ffholdpol ? \B : \A)); - - dffholdmux = ffholdmux; - dffholdpol = ffholdpol; - } -endcode - -match ffrstmux - if false /* TODO: ice40 resets are actually async */ - - select ffrstmux->type.in($mux) - // ffrstmux output must have two users: ffrstmux and ff.D - select nusers(port(ffrstmux, \Y)) == 2 - - choice <IdString> BA {\B, \A} - // DSP48E1 only supports reset to zero - select port(ffrstmux, BA).is_fully_zero() - - slice offset GetSize(port(ffrstmux, \Y)) - define <IdString> AB (BA == \B ? \A : \B) - index <SigBit> port(ffrstmux, AB)[offset] === argD[0] - - // Check that offset is consistent - filter !ffholdmux || ffoffset == offset - // Check that the rest of argD is present - filter GetSize(port(ffrstmux, AB)) >= offset + GetSize(argD) - filter port(ffrstmux, AB).extract(offset, GetSize(argD)) == argD - - set ffoffset offset - define <bool> pol (AB == \A) - set ffrstpol pol - - semioptional -endmatch - -code argD argQ - dffrstmux = ffrstmux; - if (ffrstmux) { - SigSpec AB = port(ffrstmux, ffrstpol ? \A : \B); - SigSpec Y = port(ffrstmux, \Y); - argD.replace(AB, Y); - - dffrstmux = ffrstmux; - dffrstpol = ffrstpol; - } -endcode - match ff - select ff->type.in($dff) + select ff->type.in($dff, $dffe, $sdff, $sdffce) // SB_MAC16 does not support clock inversion select param(ff, \CLK_POLARITY).as_bool() slice offset GetSize(port(ff, \D)) index <SigBit> port(ff, \D)[offset] === argD[0] - // Check that offset is consistent - filter (!ffholdmux && !ffrstmux) || ffoffset == offset + // Only allow sync reset if requested. + filter argSdff || ff->type.in($dff, $dffe) // Check that the rest of argD is present filter GetSize(port(ff, \D)) >= offset + GetSize(argD) filter port(ff, \D).extract(offset, GetSize(argD)) == argD - // Check that FF.Q is connected to CE-mux - filter !ffholdmux || port(ff, \Q).extract(offset, GetSize(argQ)) == argQ - - set ffoffset offset endmatch code argQ @@ -559,10 +401,8 @@ code argQ } SigSpec D = port(ff, \D); SigSpec Q = port(ff, \Q); - if (!ffholdmux) { - argQ = argD; - argQ.replace(D, Q); - } + argQ = argD; + argQ.replace(D, Q); for (auto c : argQ.chunks()) { Const init = c.wire->attributes.at(\init, State::Sx); @@ -575,7 +415,4 @@ code argQ dffclock = port(ff, \CLK); dffclock_pol = param(ff, \CLK_POLARITY).as_bool(); } - // No enable/reset mux possible without flop - else if (dffholdmux || dffrstmux) - reject; endcode diff --git a/passes/pmgen/xilinx_dsp.cc b/passes/pmgen/xilinx_dsp.cc index d05157270..cf7703d36 100644 --- a/passes/pmgen/xilinx_dsp.cc +++ b/passes/pmgen/xilinx_dsp.cc @@ -263,17 +263,17 @@ void xilinx_dsp_pack(xilinx_dsp_pm &pm) log("Analysing %s.%s for Xilinx DSP packing.\n", log_id(pm.module), log_id(st.dsp)); log_debug("preAdd: %s\n", log_id(st.preAdd, "--")); - log_debug("ffAD: %s %s %s\n", log_id(st.ffAD, "--"), log_id(st.ffADcemux, "--"), log_id(st.ffADrstmux, "--")); - log_debug("ffA2: %s %s %s\n", log_id(st.ffA2, "--"), log_id(st.ffA2cemux, "--"), log_id(st.ffA2rstmux, "--")); - log_debug("ffA1: %s %s %s\n", log_id(st.ffA1, "--"), log_id(st.ffA1cemux, "--"), log_id(st.ffA1rstmux, "--")); - log_debug("ffB2: %s %s %s\n", log_id(st.ffB2, "--"), log_id(st.ffB2cemux, "--"), log_id(st.ffB2rstmux, "--")); - log_debug("ffB1: %s %s %s\n", log_id(st.ffB1, "--"), log_id(st.ffB1cemux, "--"), log_id(st.ffB1rstmux, "--")); - log_debug("ffD: %s %s %s\n", log_id(st.ffD, "--"), log_id(st.ffDcemux, "--"), log_id(st.ffDrstmux, "--")); + log_debug("ffAD: %s\n", log_id(st.ffAD, "--")); + log_debug("ffA2: %s\n", log_id(st.ffA2, "--")); + log_debug("ffA1: %s\n", log_id(st.ffA1, "--")); + log_debug("ffB2: %s\n", log_id(st.ffB2, "--")); + log_debug("ffB1: %s\n", log_id(st.ffB1, "--")); + log_debug("ffD: %s\n", log_id(st.ffD, "--")); log_debug("dsp: %s\n", log_id(st.dsp, "--")); - log_debug("ffM: %s %s %s\n", log_id(st.ffM, "--"), log_id(st.ffMcemux, "--"), log_id(st.ffMrstmux, "--")); + log_debug("ffM: %s\n", log_id(st.ffM, "--")); log_debug("postAdd: %s\n", log_id(st.postAdd, "--")); log_debug("postAddMux: %s\n", log_id(st.postAddMux, "--")); - log_debug("ffP: %s %s %s\n", log_id(st.ffP, "--"), log_id(st.ffPcemux, "--"), log_id(st.ffPrstmux, "--")); + log_debug("ffP: %s\n", log_id(st.ffP, "--")); log_debug("overflow: %s\n", log_id(st.overflow, "--")); Cell *cell = st.dsp; @@ -291,9 +291,10 @@ void xilinx_dsp_pack(xilinx_dsp_pm &pm) cell->setPort(ID(INMODE), Const::from_string("00100")); if (st.ffAD) { - if (st.ffADcemux) { - SigSpec S = st.ffADcemux->getPort(ID::S); - cell->setPort(ID(CEAD), st.ffADcepol ? S : pm.module->Not(NEW_ID, S)); + if (st.ffAD->type.in(ID($dffe), ID($sdffe))) { + bool pol = st.ffAD->getParam(ID::EN_POLARITY).as_bool(); + SigSpec S = st.ffAD->getPort(ID::EN); + cell->setPort(ID(CEAD), pol ? S : pm.module->Not(NEW_ID, S)); } else cell->setPort(ID(CEAD), State::S1); @@ -363,30 +364,24 @@ void xilinx_dsp_pack(xilinx_dsp_pm &pm) { cell->setPort(ID::CLK, st.clock); - auto f = [&pm,cell](SigSpec &A, Cell* ff, Cell* cemux, bool cepol, IdString ceport, Cell* rstmux, bool rstpol, IdString rstport) { + auto f = [&pm,cell](SigSpec &A, Cell* ff, IdString ceport, IdString rstport) { SigSpec D = ff->getPort(ID::D); SigSpec Q = pm.sigmap(ff->getPort(ID::Q)); if (!A.empty()) A.replace(Q, D); - if (rstmux) { - SigSpec Y = rstmux->getPort(ID::Y); - SigSpec AB = rstmux->getPort(rstpol ? ID::A : ID::B); - if (!A.empty()) - A.replace(Y, AB); - if (rstport != IdString()) { - SigSpec S = rstmux->getPort(ID::S); - cell->setPort(rstport, rstpol ? S : pm.module->Not(NEW_ID, S)); + if (rstport != IdString()) { + if (ff->type.in(ID($sdff), ID($sdffe))) { + SigSpec srst = ff->getPort(ID::SRST); + bool rstpol = ff->getParam(ID::SRST_POLARITY).as_bool(); + cell->setPort(rstport, rstpol ? srst : pm.module->Not(NEW_ID, srst)); + } else { + cell->setPort(rstport, State::S0); } } - else if (rstport != IdString()) - cell->setPort(rstport, State::S0); - if (cemux) { - SigSpec Y = cemux->getPort(ID::Y); - SigSpec BA = cemux->getPort(cepol ? ID::B : ID::A); - SigSpec S = cemux->getPort(ID::S); - if (!A.empty()) - A.replace(Y, BA); - cell->setPort(ceport, cepol ? S : pm.module->Not(NEW_ID, S)); + if (ff->type.in(ID($dffe), ID($sdffe))) { + SigSpec ce = ff->getPort(ID::EN); + bool cepol = ff->getParam(ID::EN_POLARITY).as_bool(); + cell->setPort(ceport, cepol ? ce : pm.module->Not(NEW_ID, ce)); } else cell->setPort(ceport, State::S1); @@ -404,9 +399,9 @@ void xilinx_dsp_pack(xilinx_dsp_pm &pm) if (st.ffA2) { SigSpec A = cell->getPort(ID::A); - f(A, st.ffA2, st.ffA2cemux, st.ffA2cepol, ID(CEA2), st.ffA2rstmux, st.ffArstpol, ID(RSTA)); + f(A, st.ffA2, ID(CEA2), ID(RSTA)); if (st.ffA1) { - f(A, st.ffA1, st.ffA1cemux, st.ffA1cepol, ID(CEA1), st.ffA1rstmux, st.ffArstpol, IdString()); + f(A, st.ffA1, ID(CEA1), IdString()); cell->setParam(ID(AREG), 2); cell->setParam(ID(ACASCREG), 2); } @@ -419,9 +414,9 @@ void xilinx_dsp_pack(xilinx_dsp_pm &pm) } if (st.ffB2) { SigSpec B = cell->getPort(ID::B); - f(B, st.ffB2, st.ffB2cemux, st.ffB2cepol, ID(CEB2), st.ffB2rstmux, st.ffBrstpol, ID(RSTB)); + f(B, st.ffB2, ID(CEB2), ID(RSTB)); if (st.ffB1) { - f(B, st.ffB1, st.ffB1cemux, st.ffB1cepol, ID(CEB1), st.ffB1rstmux, st.ffBrstpol, IdString()); + f(B, st.ffB1, ID(CEB1), IdString()); cell->setParam(ID(BREG), 2); cell->setParam(ID(BCASCREG), 2); } @@ -434,20 +429,20 @@ void xilinx_dsp_pack(xilinx_dsp_pm &pm) } if (st.ffD) { SigSpec D = cell->getPort(ID::D); - f(D, st.ffD, st.ffDcemux, st.ffDcepol, ID(CED), st.ffDrstmux, st.ffDrstpol, ID(RSTD)); + f(D, st.ffD, ID(CED), ID(RSTD)); pm.add_siguser(D, cell); cell->setPort(ID::D, D); cell->setParam(ID(DREG), 1); } if (st.ffM) { SigSpec M; // unused - f(M, st.ffM, st.ffMcemux, st.ffMcepol, ID(CEM), st.ffMrstmux, st.ffMrstpol, ID(RSTM)); + f(M, st.ffM, ID(CEM), ID(RSTM)); st.ffM->connections_.at(ID::Q).replace(st.sigM, pm.module->addWire(NEW_ID, GetSize(st.sigM))); cell->setParam(ID(MREG), State::S1); } if (st.ffP) { SigSpec P; // unused - f(P, st.ffP, st.ffPcemux, st.ffPcepol, ID(CEP), st.ffPrstmux, st.ffPrstpol, ID(RSTP)); + f(P, st.ffP, ID(CEP), ID(RSTP)); st.ffP->connections_.at(ID::Q).replace(st.sigP, pm.module->addWire(NEW_ID, GetSize(st.sigP))); cell->setParam(ID(PREG), State::S1); } @@ -495,16 +490,16 @@ void xilinx_dsp48a_pack(xilinx_dsp48a_pm &pm) log("Analysing %s.%s for Xilinx DSP48A/DSP48A1 packing.\n", log_id(pm.module), log_id(st.dsp)); log_debug("preAdd: %s\n", log_id(st.preAdd, "--")); - log_debug("ffA1: %s %s %s\n", log_id(st.ffA1, "--"), log_id(st.ffA1cemux, "--"), log_id(st.ffA1rstmux, "--")); - log_debug("ffA0: %s %s %s\n", log_id(st.ffA0, "--"), log_id(st.ffA0cemux, "--"), log_id(st.ffA0rstmux, "--")); - log_debug("ffB1: %s %s %s\n", log_id(st.ffB1, "--"), log_id(st.ffB1cemux, "--"), log_id(st.ffB1rstmux, "--")); - log_debug("ffB0: %s %s %s\n", log_id(st.ffB0, "--"), log_id(st.ffB0cemux, "--"), log_id(st.ffB0rstmux, "--")); - log_debug("ffD: %s %s %s\n", log_id(st.ffD, "--"), log_id(st.ffDcemux, "--"), log_id(st.ffDrstmux, "--")); + log_debug("ffA1: %s\n", log_id(st.ffA1, "--")); + log_debug("ffA0: %s\n", log_id(st.ffA0, "--")); + log_debug("ffB1: %s\n", log_id(st.ffB1, "--")); + log_debug("ffB0: %s\n", log_id(st.ffB0, "--")); + log_debug("ffD: %s\n", log_id(st.ffD, "--")); log_debug("dsp: %s\n", log_id(st.dsp, "--")); - log_debug("ffM: %s %s %s\n", log_id(st.ffM, "--"), log_id(st.ffMcemux, "--"), log_id(st.ffMrstmux, "--")); + log_debug("ffM: %s\n", log_id(st.ffM, "--")); log_debug("postAdd: %s\n", log_id(st.postAdd, "--")); log_debug("postAddMux: %s\n", log_id(st.postAddMux, "--")); - log_debug("ffP: %s %s %s\n", log_id(st.ffP, "--"), log_id(st.ffPcemux, "--"), log_id(st.ffPrstmux, "--")); + log_debug("ffP: %s\n", log_id(st.ffP, "--")); Cell *cell = st.dsp; SigSpec &opmode = cell->connections_.at(ID(OPMODE)); @@ -556,30 +551,24 @@ void xilinx_dsp48a_pack(xilinx_dsp48a_pm &pm) { cell->setPort(ID::CLK, st.clock); - auto f = [&pm,cell](SigSpec &A, Cell* ff, Cell* cemux, bool cepol, IdString ceport, Cell* rstmux, bool rstpol, IdString rstport) { + auto f = [&pm,cell](SigSpec &A, Cell* ff, IdString ceport, IdString rstport) { SigSpec D = ff->getPort(ID::D); SigSpec Q = pm.sigmap(ff->getPort(ID::Q)); if (!A.empty()) A.replace(Q, D); - if (rstmux) { - SigSpec Y = rstmux->getPort(ID::Y); - SigSpec AB = rstmux->getPort(rstpol ? ID::A : ID::B); - if (!A.empty()) - A.replace(Y, AB); - if (rstport != IdString()) { - SigSpec S = rstmux->getPort(ID::S); - cell->setPort(rstport, rstpol ? S : pm.module->Not(NEW_ID, S)); + if (rstport != IdString()) { + if (ff->type.in(ID($sdff), ID($sdffe))) { + SigSpec srst = ff->getPort(ID::SRST); + bool rstpol = ff->getParam(ID::SRST_POLARITY).as_bool(); + cell->setPort(rstport, rstpol ? srst : pm.module->Not(NEW_ID, srst)); + } else { + cell->setPort(rstport, State::S0); } } - else if (rstport != IdString()) - cell->setPort(rstport, State::S0); - if (cemux) { - SigSpec Y = cemux->getPort(ID::Y); - SigSpec BA = cemux->getPort(cepol ? ID::B : ID::A); - SigSpec S = cemux->getPort(ID::S); - if (!A.empty()) - A.replace(Y, BA); - cell->setPort(ceport, cepol ? S : pm.module->Not(NEW_ID, S)); + if (ff->type.in(ID($dffe), ID($sdffe))) { + SigSpec ce = ff->getPort(ID::EN); + bool cepol = ff->getParam(ID::EN_POLARITY).as_bool(); + cell->setPort(ceport, cepol ? ce : pm.module->Not(NEW_ID, ce)); } else cell->setPort(ceport, State::S1); @@ -598,11 +587,11 @@ void xilinx_dsp48a_pack(xilinx_dsp48a_pm &pm) if (st.ffA0 || st.ffA1) { SigSpec A = cell->getPort(ID::A); if (st.ffA1) { - f(A, st.ffA1, st.ffA1cemux, st.ffAcepol, ID(CEA), st.ffA1rstmux, st.ffArstpol, ID(RSTA)); + f(A, st.ffA1, ID(CEA), ID(RSTA)); cell->setParam(ID(A1REG), 1); } if (st.ffA0) { - f(A, st.ffA0, st.ffA0cemux, st.ffAcepol, ID(CEA), st.ffA0rstmux, st.ffArstpol, ID(RSTA)); + f(A, st.ffA0, ID(CEA), ID(RSTA)); cell->setParam(ID(A0REG), 1); } pm.add_siguser(A, cell); @@ -611,11 +600,11 @@ void xilinx_dsp48a_pack(xilinx_dsp48a_pm &pm) if (st.ffB0 || st.ffB1) { SigSpec B = cell->getPort(ID::B); if (st.ffB1) { - f(B, st.ffB1, st.ffB1cemux, st.ffBcepol, ID(CEB), st.ffB1rstmux, st.ffBrstpol, ID(RSTB)); + f(B, st.ffB1, ID(CEB), ID(RSTB)); cell->setParam(ID(B1REG), 1); } if (st.ffB0) { - f(B, st.ffB0, st.ffB0cemux, st.ffBcepol, ID(CEB), st.ffB0rstmux, st.ffBrstpol, ID(RSTB)); + f(B, st.ffB0, ID(CEB), ID(RSTB)); cell->setParam(ID(B0REG), 1); } pm.add_siguser(B, cell); @@ -623,20 +612,20 @@ void xilinx_dsp48a_pack(xilinx_dsp48a_pm &pm) } if (st.ffD) { SigSpec D = cell->getPort(ID::D); - f(D, st.ffD, st.ffDcemux, st.ffDcepol, ID(CED), st.ffDrstmux, st.ffDrstpol, ID(RSTD)); + f(D, st.ffD, ID(CED), ID(RSTD)); pm.add_siguser(D, cell); cell->setPort(ID::D, D); cell->setParam(ID(DREG), 1); } if (st.ffM) { SigSpec M; // unused - f(M, st.ffM, st.ffMcemux, st.ffMcepol, ID(CEM), st.ffMrstmux, st.ffMrstpol, ID(RSTM)); + f(M, st.ffM, ID(CEM), ID(RSTM)); st.ffM->connections_.at(ID::Q).replace(st.sigM, pm.module->addWire(NEW_ID, GetSize(st.sigM))); cell->setParam(ID(MREG), State::S1); } if (st.ffP) { SigSpec P; // unused - f(P, st.ffP, st.ffPcemux, st.ffPcepol, ID(CEP), st.ffPrstmux, st.ffPrstpol, ID(RSTP)); + f(P, st.ffP, ID(CEP), ID(RSTP)); st.ffP->connections_.at(ID::Q).replace(st.sigP, pm.module->addWire(NEW_ID, GetSize(st.sigP))); cell->setParam(ID(PREG), State::S1); } @@ -677,7 +666,7 @@ void xilinx_dsp_packC(xilinx_dsp_CREG_pm &pm) auto &st = pm.st_xilinx_dsp_packC; log_debug("Analysing %s.%s for Xilinx DSP packing (CREG).\n", log_id(pm.module), log_id(st.dsp)); - log_debug("ffC: %s %s %s\n", log_id(st.ffC, "--"), log_id(st.ffCcemux, "--"), log_id(st.ffCrstmux, "--")); + log_debug("ffC: %s\n", log_id(st.ffC, "--")); Cell *cell = st.dsp; @@ -685,30 +674,24 @@ void xilinx_dsp_packC(xilinx_dsp_CREG_pm &pm) { cell->setPort(ID::CLK, st.clock); - auto f = [&pm,cell](SigSpec &A, Cell* ff, Cell* cemux, bool cepol, IdString ceport, Cell* rstmux, bool rstpol, IdString rstport) { + auto f = [&pm,cell](SigSpec &A, Cell* ff, IdString ceport, IdString rstport) { SigSpec D = ff->getPort(ID::D); SigSpec Q = pm.sigmap(ff->getPort(ID::Q)); if (!A.empty()) A.replace(Q, D); - if (rstmux) { - SigSpec Y = rstmux->getPort(ID::Y); - SigSpec AB = rstmux->getPort(rstpol ? ID::A : ID::B); - if (!A.empty()) - A.replace(Y, AB); - if (rstport != IdString()) { - SigSpec S = rstmux->getPort(ID::S); - cell->setPort(rstport, rstpol ? S : pm.module->Not(NEW_ID, S)); + if (rstport != IdString()) { + if (ff->type.in(ID($sdff), ID($sdffe))) { + SigSpec srst = ff->getPort(ID::SRST); + bool rstpol = ff->getParam(ID::SRST_POLARITY).as_bool(); + cell->setPort(rstport, rstpol ? srst : pm.module->Not(NEW_ID, srst)); + } else { + cell->setPort(rstport, State::S0); } } - else if (rstport != IdString()) - cell->setPort(rstport, State::S0); - if (cemux) { - SigSpec Y = cemux->getPort(ID::Y); - SigSpec BA = cemux->getPort(cepol ? ID::B : ID::A); - SigSpec S = cemux->getPort(ID::S); - if (!A.empty()) - A.replace(Y, BA); - cell->setPort(ceport, cepol ? S : pm.module->Not(NEW_ID, S)); + if (ff->type.in(ID($dffe), ID($sdffe))) { + SigSpec ce = ff->getPort(ID::EN); + bool cepol = ff->getParam(ID::EN_POLARITY).as_bool(); + cell->setPort(ceport, cepol ? ce : pm.module->Not(NEW_ID, ce)); } else cell->setPort(ceport, State::S1); @@ -726,7 +709,7 @@ void xilinx_dsp_packC(xilinx_dsp_CREG_pm &pm) if (st.ffC) { SigSpec C = cell->getPort(ID::C); - f(C, st.ffC, st.ffCcemux, st.ffCcepol, ID(CEC), st.ffCrstmux, st.ffCrstpol, ID(RSTC)); + f(C, st.ffC, ID(CEC), ID(RSTC)); pm.add_siguser(C, cell); cell->setPort(ID::C, C); cell->setParam(ID(CREG), 1); diff --git a/passes/pmgen/xilinx_dsp.pmg b/passes/pmgen/xilinx_dsp.pmg index d40f073c9..0cd23c09d 100644 --- a/passes/pmgen/xilinx_dsp.pmg +++ b/passes/pmgen/xilinx_dsp.pmg @@ -2,9 +2,7 @@ // forms the `xilinx_dsp` pass described in xilinx_dsp.cc // At a high level, it works as follows: // ( 1) Starting from a DSP48E1 cell -// ( 2) Match the driver of the 'A' input to a possible $dff cell (ADREG) -// (attached to at most two $mux cells that implement clock-enable or -// reset functionality, using a subpattern discussed below) +// ( 2) Match the driver of the 'A' input to a possible $sdffe cell (ADREG) // If ADREG matched, treat 'A' input as input of ADREG // ( 3) Match the driver of the 'A' and 'D' inputs for a possible $add cell // (pre-adder) @@ -44,7 +42,7 @@ // DSP48E1 cells inferred from multiply operations by Yosys, as well as for // user instantiations that may already contain the cells being packed... // (though the latter is currently untested) -// - Since the $dff-with-optional-clock-enable-or-reset-mux pattern is used +// - Since the $sdffe pattern is used // for each *REG match, it has been factored out into two subpatterns: // in_dffe and out_dffe located at the bottom of this file. // - Matching for pattern detector features is currently incomplete. For @@ -57,20 +55,15 @@ pattern xilinx_dsp_pack state <SigBit> clock state <SigSpec> sigA sigB sigC sigD sigM sigP state <IdString> postAddAB postAddMuxAB -state <bool> ffA1cepol ffA2cepol ffADcepol ffB1cepol ffB2cepol ffDcepol ffMcepol ffPcepol -state <bool> ffArstpol ffADrstpol ffBrstpol ffDrstpol ffMrstpol ffPrstpol -state <Cell*> ffAD ffADcemux ffADrstmux ffA1 ffA1cemux ffA1rstmux ffA2 ffA2cemux ffA2rstmux -state <Cell*> ffB1 ffB1cemux ffB1rstmux ffB2 ffB2cemux ffB2rstmux -state <Cell*> ffD ffDcemux ffDrstmux ffM ffMcemux ffMrstmux ffP ffPcemux ffPrstmux +state <Cell*> ffAD ffA1 ffA2 +state <Cell*> ffB1 ffB2 +state <Cell*> ffD ffM ffP // Variables used for subpatterns state <SigSpec> argQ argD -state <bool> ffcepol ffrstpol -state <int> ffoffset udata <SigSpec> dffD dffQ udata <SigBit> dffclock -udata <Cell*> dff dffcemux dffrstmux -udata <bool> dffcepol dffrstpol +udata <Cell*> dff // (1) Starting from a DSP48E1 cell match dsp @@ -115,25 +108,15 @@ code sigA sigB sigC sigD sigM clock clock = port(dsp, \CLK, SigBit()); endcode -// (2) Match the driver of the 'A' input to a possible $dff cell (ADREG) -// (attached to at most two $mux cells that implement clock-enable or -// reset functionality, using a subpattern discussed above) +// (2) Match the driver of the 'A' input to a possible $sdffe cell (ADREG) // If matched, treat 'A' input as input of ADREG -code argQ ffAD ffADcemux ffADrstmux ffADcepol ffADrstpol sigA clock +code argQ ffAD sigA clock if (param(dsp, \ADREG).as_int() == 0) { argQ = sigA; subpattern(in_dffe); if (dff) { ffAD = dff; clock = dffclock; - if (dffrstmux) { - ffADrstmux = dffrstmux; - ffADrstpol = dffrstpol; - } - if (dffcemux) { - ffADcemux = dffcemux; - ffADcepol = dffcepol; - } sigA = dffD; } } @@ -172,7 +155,7 @@ endcode // (4) If pre-adder was present, find match 'A' input for A2REG // If pre-adder was not present, move ADREG to A2REG // Then match 'A' input for A1REG -code argQ ffAD ffADcemux ffADrstmux ffADcepol ffADrstpol sigA clock ffA2 ffA2cemux ffA2rstmux ffA2cepol ffArstpol ffA1 ffA1cemux ffA1rstmux ffA1cepol +code argQ ffAD sigA clock ffA2 ffA1 // Only search for ffA2 if there was a pre-adder // (otherwise ffA2 would have been matched as ffAD) if (preAdd) { @@ -182,14 +165,6 @@ code argQ ffAD ffADcemux ffADrstmux ffADcepol ffADrstpol sigA clock ffA2 ffA2cem if (dff) { ffA2 = dff; clock = dffclock; - if (dffrstmux) { - ffA2rstmux = dffrstmux; - ffArstpol = dffrstpol; - } - if (dffcemux) { - ffA2cepol = dffcepol; - ffA2cemux = dffcemux; - } sigA = dffD; } } @@ -197,12 +172,8 @@ code argQ ffAD ffADcemux ffADrstmux ffADcepol ffADrstpol sigA clock ffA2 ffA2cem // And if there wasn't a pre-adder, // move AD register to A else if (ffAD) { - log_assert(!ffA2 && !ffA2cemux && !ffA2rstmux); + log_assert(!ffA2); std::swap(ffA2, ffAD); - std::swap(ffA2cemux, ffADcemux); - std::swap(ffA2rstmux, ffADrstmux); - ffA2cepol = ffADcepol; - ffArstpol = ffADrstpol; } // Now attempt to match A1 @@ -210,23 +181,23 @@ code argQ ffAD ffADcemux ffADrstmux ffADcepol ffADrstpol sigA clock ffA2 ffA2cem argQ = sigA; subpattern(in_dffe); if (dff) { - if ((ffA2rstmux != nullptr) ^ (dffrstmux != nullptr)) + if (dff->type != ffA2->type) goto ffA1_end; - if (dffrstmux) { - if (ffArstpol != dffrstpol) + if (dff->type.in($sdff, $sdffe, $sdffce)) { + if (param(dff, \SRST_POLARITY) != param(ffA2, \SRST_POLARITY)) goto ffA1_end; - if (port(ffA2rstmux, \S) != port(dffrstmux, \S)) + if (port(dff, \SRST) != port(ffA2, \SRST)) + goto ffA1_end; + } + if (dff->type.in($dffe, $sdffe, $sdffce)) { + if (param(dff, \EN_POLARITY) != param(ffA2, \EN_POLARITY)) + goto ffA1_end; + if (port(dff, \EN) != port(ffA2, \EN)) goto ffA1_end; - ffA1rstmux = dffrstmux; } ffA1 = dff; clock = dffclock; - - if (dffcemux) { - ffA1cemux = dffcemux; - ffA1cepol = dffcepol; - } sigA = dffD; ffA1_end: ; @@ -236,21 +207,13 @@ endcode // (5) Match 'B' input for B2REG // If B2REG, then match 'B' input for B1REG -code argQ ffB2 ffB2cemux ffB2rstmux ffB2cepol ffBrstpol sigB clock ffB1 ffB1cemux ffB1rstmux ffB1cepol +code argQ ffB2 sigB clock ffB1 if (param(dsp, \BREG).as_int() == 0) { argQ = sigB; subpattern(in_dffe); if (dff) { ffB2 = dff; clock = dffclock; - if (dffrstmux) { - ffB2rstmux = dffrstmux; - ffBrstpol = dffrstpol; - } - if (dffcemux) { - ffB2cemux = dffcemux; - ffB2cepol = dffcepol; - } sigB = dffD; // Now attempt to match B1 @@ -258,23 +221,23 @@ code argQ ffB2 ffB2cemux ffB2rstmux ffB2cepol ffBrstpol sigB clock ffB1 ffB1cemu argQ = sigB; subpattern(in_dffe); if (dff) { - if ((ffB2rstmux != nullptr) ^ (dffrstmux != nullptr)) + if (dff->type != ffB2->type) goto ffB1_end; - if (dffrstmux) { - if (ffBrstpol != dffrstpol) + if (dff->type.in($sdff, $sdffe, $sdffce)) { + if (param(dff, \SRST_POLARITY) != param(ffB2, \SRST_POLARITY)) + goto ffB1_end; + if (port(dff, \SRST) != port(ffB2, \SRST)) + goto ffB1_end; + } + if (dff->type.in($dffe, $sdffe, $sdffce)) { + if (param(dff, \EN_POLARITY) != param(ffB2, \EN_POLARITY)) goto ffB1_end; - if (port(ffB2rstmux, \S) != port(dffrstmux, \S)) + if (port(dff, \EN) != port(ffB2, \EN)) goto ffB1_end; - ffB1rstmux = dffrstmux; } ffB1 = dff; clock = dffclock; - - if (dffcemux) { - ffB1cemux = dffcemux; - ffB1cepol = dffcepol; - } sigB = dffD; ffB1_end: ; @@ -286,42 +249,26 @@ ffB1_end: ; endcode // (6) Match 'D' input for DREG -code argQ ffD ffDcemux ffDrstmux ffDcepol ffDrstpol sigD clock +code argQ ffD sigD clock if (param(dsp, \DREG).as_int() == 0) { argQ = sigD; subpattern(in_dffe); if (dff) { ffD = dff; clock = dffclock; - if (dffrstmux) { - ffDrstmux = dffrstmux; - ffDrstpol = dffrstpol; - } - if (dffcemux) { - ffDcemux = dffcemux; - ffDcepol = dffcepol; - } sigD = dffD; } } endcode // (7) Match 'P' output that exclusively drives an MREG -code argD ffM ffMcemux ffMrstmux ffMcepol ffMrstpol sigM sigP clock +code argD ffM sigM sigP clock if (param(dsp, \MREG).as_int() == 0 && nusers(sigM) == 2) { argD = sigM; subpattern(out_dffe); if (dff) { ffM = dff; clock = dffclock; - if (dffrstmux) { - ffMrstmux = dffrstmux; - ffMrstpol = dffrstpol; - } - if (dffcemux) { - ffMcemux = dffcemux; - ffMcepol = dffcepol; - } sigM = dffQ; } } @@ -340,9 +287,7 @@ match postAdd select postAdd->type.in($add) select GetSize(port(postAdd, \Y)) <= 48 choice <IdString> AB {\A, \B} - select nusers(port(postAdd, AB)) <= 3 - filter ffMcemux || nusers(port(postAdd, AB)) == 2 - filter !ffMcemux || nusers(port(postAdd, AB)) == 3 + select nusers(port(postAdd, AB)) == 2 index <SigBit> port(postAdd, AB)[0] === sigP[0] filter GetSize(port(postAdd, AB)) >= GetSize(sigP) @@ -362,25 +307,14 @@ code sigC sigP endcode // (9) Match 'P' output that exclusively drives a PREG -code argD ffP ffPcemux ffPrstmux ffPcepol ffPrstpol sigP clock +code argD ffP sigP clock if (param(dsp, \PREG).as_int() == 0) { - int users = 2; - // If ffMcemux and no postAdd new-value net must have three users: ffMcemux, ffM and ffPcemux - if (ffMcemux && !postAdd) users++; - if (nusers(sigP) == users) { + if (nusers(sigP) == 2) { argD = sigP; subpattern(out_dffe); if (dff) { ffP = dff; clock = dffclock; - if (dffrstmux) { - ffPrstmux = dffrstmux; - ffPrstpol = dffrstpol; - } - if (dffcemux) { - ffPcemux = dffcemux; - ffPcepol = dffcepol; - } sigP = dffQ; } } @@ -441,22 +375,9 @@ endcode // ####################### // Subpattern for matching against input registers, based on knowledge of the -// 'Q' input. Typically, identifying registers with clock-enable and reset -// capability would be a task would be handled by other Yosys passes such as -// dff2dffe, but since DSP inference happens much before this, these patterns -// have to be manually identified. -// At a high level: -// (1) Starting from a $dff cell that (partially or fully) drives the given -// 'Q' argument -// (2) Match for a $mux cell implementing synchronous reset semantics --- -// one that exclusively drives the 'D' input of the $dff, with one of its -// $mux inputs being fully zero -// (3) Match for a $mux cell implement clock enable semantics --- one that -// exclusively drives the 'D' input of the $dff (or the other input of -// the reset $mux) and where one of this $mux's inputs is connected to -// the 'Q' output of the $dff +// 'Q' input. subpattern in_dffe -arg argD argQ clock +arg argQ clock code dff = nullptr; @@ -479,13 +400,14 @@ code } endcode -// (1) Starting from a $dff cell that (partially or fully) drives the given -// 'Q' argument match ff - select ff->type.in($dff) + select ff->type.in($dff, $dffe, $sdff, $sdffe) // DSP48E1 does not support clock inversion select param(ff, \CLK_POLARITY).as_bool() + // Check that reset value, if present, is fully 0. + filter ff->type.in($dff, $dffe) || param(ff, \SRST_VALUE).is_fully_zero() + slice offset GetSize(port(ff, \D)) index <SigBit> port(ff, \Q)[offset] === argQ[0] @@ -494,82 +416,16 @@ match ff filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ filter clock == SigBit() || port(ff, \CLK) == clock - - set ffoffset offset endmatch -code argQ argD +code argQ SigSpec Q = port(ff, \Q); dff = ff; dffclock = port(ff, \CLK); dffD = argQ; - argD = port(ff, \D); + SigSpec D = port(ff, \D); argQ = Q; - dffD.replace(argQ, argD); - // Only search for ffrstmux if dffD only - // has two (ff, ffrstmux) users - if (nusers(dffD) > 2) - argD = SigSpec(); -endcode - -// (2) Match for a $mux cell implementing synchronous reset semantics --- -// exclusively drives the 'D' input of the $dff, with one of the $mux -// inputs being fully zero -match ffrstmux - if !argD.empty() - select ffrstmux->type.in($mux) - index <SigSpec> port(ffrstmux, \Y) === argD - - choice <IdString> BA {\B, \A} - // DSP48E1 only supports reset to zero - select port(ffrstmux, BA).is_fully_zero() - - define <bool> pol (BA == \B) - set ffrstpol pol - semioptional -endmatch - -code argD - if (ffrstmux) { - dffrstmux = ffrstmux; - dffrstpol = ffrstpol; - argD = port(ffrstmux, ffrstpol ? \A : \B); - dffD.replace(port(ffrstmux, \Y), argD); - - // Only search for ffcemux if argQ has at - // least 3 users (ff, <upstream>, ffrstmux) and - // dffD only has two (ff, ffrstmux) - if (!(nusers(argQ) >= 3 && nusers(dffD) == 2)) - argD = SigSpec(); - } - else - dffrstmux = nullptr; -endcode - -// (3) Match for a $mux cell implement clock enable semantics --- one that -// exclusively drives the 'D' input of the $dff (or the other input of -// the reset $mux) and where one of this $mux's inputs is connected to -// the 'Q' output of the $dff -match ffcemux - if !argD.empty() - select ffcemux->type.in($mux) - index <SigSpec> port(ffcemux, \Y) === argD - choice <IdString> AB {\A, \B} - index <SigSpec> port(ffcemux, AB) === argQ - define <bool> pol (AB == \A) - set ffcepol pol - semioptional -endmatch - -code argD - if (ffcemux) { - dffcemux = ffcemux; - dffcepol = ffcepol; - argD = port(ffcemux, ffcepol ? \B : \A); - dffD.replace(port(ffcemux, \Y), argD); - } - else - dffcemux = nullptr; + dffD.replace(argQ, D); endcode // ####################### @@ -597,119 +453,26 @@ code reject; endcode -// (1) Starting from an optional $mux cell that implements clock enable -// semantics --- one where the given 'D' argument (partially or fully) -// drives one of its two inputs -match ffcemux - select ffcemux->type.in($mux) - // ffcemux output must have two users: ffcemux and ff.D - select nusers(port(ffcemux, \Y)) == 2 - - choice <IdString> AB {\A, \B} - // keep-last-value net must have at least three users: ffcemux, ff, downstream sink(s) - select nusers(port(ffcemux, AB)) >= 3 - - slice offset GetSize(port(ffcemux, \Y)) - define <IdString> BA (AB == \A ? \B : \A) - index <SigBit> port(ffcemux, BA)[offset] === argD[0] - - // Check that the rest of argD is present - filter GetSize(port(ffcemux, BA)) >= offset + GetSize(argD) - filter port(ffcemux, BA).extract(offset, GetSize(argD)) == argD - - set ffoffset offset - define <bool> pol (AB == \A) - set ffcepol pol - - semioptional -endmatch - -code argD argQ - dffcemux = ffcemux; - if (ffcemux) { - SigSpec BA = port(ffcemux, ffcepol ? \B : \A); - SigSpec Y = port(ffcemux, \Y); - argQ = argD; - argD.replace(BA, Y); - argQ.replace(BA, port(ffcemux, ffcepol ? \A : \B)); - - dffcemux = ffcemux; - dffcepol = ffcepol; - } -endcode - -// (2) Starting from, or continuing onto, another optional $mux cell that -// implements synchronous reset semantics --- one where the given 'D' -// argument (or the clock enable $mux output) drives one of its two inputs -// and where the other input is fully zero -match ffrstmux - select ffrstmux->type.in($mux) - // ffrstmux output must have two users: ffrstmux and ff.D - select nusers(port(ffrstmux, \Y)) == 2 - - choice <IdString> BA {\B, \A} - // DSP48E1 only supports reset to zero - select port(ffrstmux, BA).is_fully_zero() - - slice offset GetSize(port(ffrstmux, \Y)) - define <IdString> AB (BA == \B ? \A : \B) - index <SigBit> port(ffrstmux, AB)[offset] === argD[0] - - // Check that offset is consistent - filter !ffcemux || ffoffset == offset - // Check that the rest of argD is present - filter GetSize(port(ffrstmux, AB)) >= offset + GetSize(argD) - filter port(ffrstmux, AB).extract(offset, GetSize(argD)) == argD - - set ffoffset offset - define <bool> pol (AB == \A) - set ffrstpol pol - - semioptional -endmatch - -code argD argQ - dffrstmux = ffrstmux; - if (ffrstmux) { - SigSpec AB = port(ffrstmux, ffrstpol ? \A : \B); - SigSpec Y = port(ffrstmux, \Y); - argD.replace(AB, Y); - - dffrstmux = ffrstmux; - dffrstpol = ffrstpol; - } -endcode - -// (3) Match for a $dff cell (whose 'D' input is the 'D' argument, or the -// output of the previous clock enable or reset $mux cells) match ff - select ff->type.in($dff) + select ff->type.in($dff, $dffe, $sdff, $sdffe) // DSP48E1 does not support clock inversion select param(ff, \CLK_POLARITY).as_bool() slice offset GetSize(port(ff, \D)) index <SigBit> port(ff, \D)[offset] === argD[0] - // Check that offset is consistent - filter (!ffcemux && !ffrstmux) || ffoffset == offset // Check that the rest of argD is present filter GetSize(port(ff, \D)) >= offset + GetSize(argD) filter port(ff, \D).extract(offset, GetSize(argD)) == argD - // Check that FF.Q is connected to CE-mux - filter !ffcemux || port(ff, \Q).extract(offset, GetSize(argQ)) == argQ filter clock == SigBit() || port(ff, \CLK) == clock - - set ffoffset offset endmatch code argQ SigSpec D = port(ff, \D); SigSpec Q = port(ff, \Q); - if (!ffcemux) { - argQ = argD; - argQ.replace(D, Q); - } + argQ = argD; + argQ.replace(D, Q); // Abandon matches when 'Q' has a non-zero init attribute set // (not supported by DSP48E1) diff --git a/passes/pmgen/xilinx_dsp48a.pmg b/passes/pmgen/xilinx_dsp48a.pmg index 16f5e598d..dce1b61b0 100644 --- a/passes/pmgen/xilinx_dsp48a.pmg +++ b/passes/pmgen/xilinx_dsp48a.pmg @@ -4,8 +4,6 @@ // At a high level, it works as follows: // ( 1) Starting from a DSP48A/DSP48A1 cell // ( 2) Match the driver of the 'B' input to a possible $dff cell (B1REG) -// (attached to at most two $mux cells that implement clock-enable or -// reset functionality, using a subpattern discussed below) // If B1REG matched, treat 'B' input as input of B1REG // ( 3) Match the driver of the 'B' and 'D' inputs for a possible $add cell // (pre-adder) @@ -40,20 +38,15 @@ pattern xilinx_dsp48a_pack state <SigBit> clock state <SigSpec> sigA sigB sigC sigD sigM sigP state <IdString> postAddAB postAddMuxAB -state <bool> ffAcepol ffBcepol ffDcepol ffMcepol ffPcepol -state <bool> ffArstpol ffBrstpol ffDrstpol ffMrstpol ffPrstpol -state <Cell*> ffA0 ffA0cemux ffA0rstmux ffA1 ffA1cemux ffA1rstmux -state <Cell*> ffB0 ffB0cemux ffB0rstmux ffB1 ffB1cemux ffB1rstmux -state <Cell*> ffD ffDcemux ffDrstmux ffM ffMcemux ffMrstmux ffP ffPcemux ffPrstmux +state <Cell*> ffA0 ffA1 +state <Cell*> ffB0 ffB1 +state <Cell*> ffD ffM ffP // Variables used for subpatterns state <SigSpec> argQ argD -state <bool> ffcepol ffrstpol -state <int> ffoffset udata <SigSpec> dffD dffQ udata <SigBit> dffclock -udata <Cell*> dff dffcemux dffrstmux -udata <bool> dffcepol dffrstpol +udata <Cell*> dff // (1) Starting from a DSP48A/DSP48A1 cell match dsp @@ -98,21 +91,13 @@ endcode // (attached to at most two $mux cells that implement clock-enable or // reset functionality, using a subpattern discussed above) // If matched, treat 'B' input as input of B1REG -code argQ ffB1 ffB1cemux ffB1rstmux ffBcepol ffBrstpol sigB clock +code argQ ffB1 sigB clock if (param(dsp, \B1REG).as_int() == 0 && param(dsp, \B0REG).as_int() == 0 && port(dsp, \OPMODE, SigSpec()).extract(4, 1).is_fully_zero()) { argQ = sigB; subpattern(in_dffe); if (dff) { ffB1 = dff; clock = dffclock; - if (dffrstmux) { - ffB1rstmux = dffrstmux; - ffBrstpol = dffrstpol; - } - if (dffcemux) { - ffB1cemux = dffcemux; - ffBcepol = dffcepol; - } sigB = dffD; } } @@ -147,41 +132,29 @@ code sigB sigD endcode // (4) Match 'B' input for B0REG -code argQ ffB0 ffB0cemux ffB0rstmux ffBcepol ffBrstpol sigB clock +code argQ ffB0 sigB clock if (param(dsp, \B0REG).as_int() == 0) { argQ = sigB; subpattern(in_dffe); if (dff) { if (ffB1) { - if ((ffB1rstmux != nullptr) ^ (dffrstmux != nullptr)) + if (dff->type != ffB1->type) goto ffB0_end; - if ((ffB1cemux != nullptr) ^ (dffcemux != nullptr)) - goto ffB0_end; - if (dffrstmux) { - if (ffBrstpol != dffrstpol) + if (dff->type.in($sdff, $sdffe, $sdffce)) { + if (param(dff, \SRST_POLARITY) != param(ffB1, \SRST_POLARITY)) goto ffB0_end; - if (port(ffB1rstmux, \S) != port(dffrstmux, \S)) + if (port(dff, \SRST) != port(ffB1, \SRST)) goto ffB0_end; - ffB0rstmux = dffrstmux; } - if (dffcemux) { - if (ffBcepol != dffcepol) + if (dff->type.in($dffe, $sdffe, $sdffce)) { + if (param(dff, \EN_POLARITY) != param(ffB1, \EN_POLARITY)) goto ffB0_end; - if (port(ffB1cemux, \S) != port(dffcemux, \S)) + if (port(dff, \EN) != port(ffB1, \EN)) goto ffB0_end; - ffB0cemux = dffcemux; } } ffB0 = dff; clock = dffclock; - if (dffrstmux) { - ffB0rstmux = dffrstmux; - ffBrstpol = dffrstpol; - } - if (dffcemux) { - ffB0cemux = dffcemux; - ffBcepol = dffcepol; - } sigB = dffD; } } @@ -190,21 +163,13 @@ endcode // (5) Match 'A' input for A1REG // If A1REG, then match 'A' input for A0REG -code argQ ffA1 ffA1cemux ffA1rstmux ffAcepol ffArstpol sigA clock ffA0 ffA0cemux ffA0rstmux +code argQ ffA1 sigA clock ffA0 if (param(dsp, \A0REG).as_int() == 0 && param(dsp, \A1REG).as_int() == 0) { argQ = sigA; subpattern(in_dffe); if (dff) { ffA1 = dff; clock = dffclock; - if (dffrstmux) { - ffA1rstmux = dffrstmux; - ffArstpol = dffrstpol; - } - if (dffcemux) { - ffA1cemux = dffcemux; - ffAcepol = dffcepol; - } sigA = dffD; // Now attempt to match A0 @@ -212,32 +177,23 @@ code argQ ffA1 ffA1cemux ffA1rstmux ffAcepol ffArstpol sigA clock ffA0 ffA0cemux argQ = sigA; subpattern(in_dffe); if (dff) { - if ((ffA1rstmux != nullptr) ^ (dffrstmux != nullptr)) + if (dff->type != ffA1->type) goto ffA0_end; - if ((ffA1cemux != nullptr) ^ (dffcemux != nullptr)) - goto ffA0_end; - if (dffrstmux) { - if (ffArstpol != dffrstpol) + if (dff->type.in($sdff, $sdffe, $sdffce)) { + if (param(dff, \SRST_POLARITY) != param(ffA1, \SRST_POLARITY)) goto ffA0_end; - if (port(ffA1rstmux, \S) != port(dffrstmux, \S)) + if (port(dff, \SRST) != port(ffA1, \SRST)) goto ffA0_end; - ffA0rstmux = dffrstmux; } - if (dffcemux) { - if (ffAcepol != dffcepol) + if (dff->type.in($dffe, $sdffe, $sdffce)) { + if (param(dff, \EN_POLARITY) != param(ffA1, \EN_POLARITY)) goto ffA0_end; - if (port(ffA1cemux, \S) != port(dffcemux, \S)) + if (port(dff, \EN) != port(ffA1, \EN)) goto ffA0_end; - ffA0cemux = dffcemux; } ffA0 = dff; clock = dffclock; - - if (dffcemux) { - ffA0cemux = dffcemux; - ffAcepol = dffcepol; - } sigA = dffD; ffA0_end: ; @@ -249,42 +205,26 @@ ffA0_end: ; endcode // (6) Match 'D' input for DREG -code argQ ffD ffDcemux ffDrstmux ffDcepol ffDrstpol sigD clock +code argQ ffD sigD clock if (param(dsp, \DREG).as_int() == 0) { argQ = sigD; subpattern(in_dffe); if (dff) { ffD = dff; clock = dffclock; - if (dffrstmux) { - ffDrstmux = dffrstmux; - ffDrstpol = dffrstpol; - } - if (dffcemux) { - ffDcemux = dffcemux; - ffDcepol = dffcepol; - } sigD = dffD; } } endcode // (7) Match 'P' output that exclusively drives an MREG -code argD ffM ffMcemux ffMrstmux ffMcepol ffMrstpol sigM sigP clock +code argD ffM sigM sigP clock if (param(dsp, \MREG).as_int() == 0 && nusers(sigM) == 2) { argD = sigM; subpattern(out_dffe); if (dff) { ffM = dff; clock = dffclock; - if (dffrstmux) { - ffMrstmux = dffrstmux; - ffMrstpol = dffrstpol; - } - if (dffcemux) { - ffMcemux = dffcemux; - ffMcepol = dffcepol; - } sigM = dffQ; } } @@ -303,9 +243,7 @@ match postAdd select postAdd->type.in($add) select GetSize(port(postAdd, \Y)) <= 48 choice <IdString> AB {\A, \B} - select nusers(port(postAdd, AB)) <= 3 - filter ffMcemux || nusers(port(postAdd, AB)) == 2 - filter !ffMcemux || nusers(port(postAdd, AB)) == 3 + select nusers(port(postAdd, AB)) == 2 index <SigBit> port(postAdd, AB)[0] === sigP[0] filter GetSize(port(postAdd, AB)) >= GetSize(sigP) @@ -325,25 +263,14 @@ code sigC sigP endcode // (9) Match 'P' output that exclusively drives a PREG -code argD ffP ffPcemux ffPrstmux ffPcepol ffPrstpol sigP clock +code argD ffP sigP clock if (param(dsp, \PREG).as_int() == 0) { - int users = 2; - // If ffMcemux and no postAdd new-value net must have three users: ffMcemux, ffM and ffPcemux - if (ffMcemux && !postAdd) users++; - if (nusers(sigP) == users) { + if (nusers(sigP) == 2) { argD = sigP; subpattern(out_dffe); if (dff) { ffP = dff; clock = dffclock; - if (dffrstmux) { - ffPrstmux = dffrstmux; - ffPrstpol = dffrstpol; - } - if (dffcemux) { - ffPcemux = dffcemux; - ffPcepol = dffcepol; - } sigP = dffQ; } } @@ -387,26 +314,13 @@ endcode // ####################### // Subpattern for matching against input registers, based on knowledge of the -// 'Q' input. Typically, identifying registers with clock-enable and reset -// capability would be a task would be handled by other Yosys passes such as -// dff2dffe, but since DSP inference happens much before this, these patterns -// have to be manually identified. -// At a high level: -// (1) Starting from a $dff cell that (partially or fully) drives the given -// 'Q' argument -// (2) Match for a $mux cell implementing synchronous reset semantics --- -// one that exclusively drives the 'D' input of the $dff, with one of its -// $mux inputs being fully zero -// (3) Match for a $mux cell implement clock enable semantics --- one that -// exclusively drives the 'D' input of the $dff (or the other input of -// the reset $mux) and where one of this $mux's inputs is connected to -// the 'Q' output of the $dff +// 'Q' input. subpattern in_dffe -arg argD argQ clock +arg argQ clock code dff = nullptr; - if (GetSize(argQ) == 0) + if (argQ.empty()) reject; for (const auto &c : argQ.chunks()) { // Abandon matches when 'Q' is a constant @@ -425,13 +339,14 @@ code } endcode -// (1) Starting from a $dff cell that (partially or fully) drives the given -// 'Q' argument match ff - select ff->type.in($dff) + select ff->type.in($dff, $dffe, $sdff, $sdffe) // DSP48E1 does not support clock inversion select param(ff, \CLK_POLARITY).as_bool() + // Check that reset value, if present, is fully 0. + filter ff->type.in($dff, $dffe) || param(ff, \SRST_VALUE).is_fully_zero() + slice offset GetSize(port(ff, \D)) index <SigBit> port(ff, \Q)[offset] === argQ[0] @@ -440,82 +355,16 @@ match ff filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ filter clock == SigBit() || port(ff, \CLK) == clock - - set ffoffset offset endmatch -code argQ argD +code argQ SigSpec Q = port(ff, \Q); dff = ff; dffclock = port(ff, \CLK); dffD = argQ; - argD = port(ff, \D); + SigSpec D = port(ff, \D); argQ = Q; - dffD.replace(argQ, argD); - // Only search for ffrstmux if dffD only - // has two (ff, ffrstmux) users - if (nusers(dffD) > 2) - argD = SigSpec(); -endcode - -// (2) Match for a $mux cell implementing synchronous reset semantics --- -// exclusively drives the 'D' input of the $dff, with one of the $mux -// inputs being fully zero -match ffrstmux - if !argD.empty() - select ffrstmux->type.in($mux) - index <SigSpec> port(ffrstmux, \Y) === argD - - choice <IdString> BA {\B, \A} - // DSP48E1 only supports reset to zero - select port(ffrstmux, BA).is_fully_zero() - - define <bool> pol (BA == \B) - set ffrstpol pol - semioptional -endmatch - -code argD - if (ffrstmux) { - dffrstmux = ffrstmux; - dffrstpol = ffrstpol; - argD = port(ffrstmux, ffrstpol ? \A : \B); - dffD.replace(port(ffrstmux, \Y), argD); - - // Only search for ffcemux if argQ has at - // least 3 users (ff, <upstream>, ffrstmux) and - // dffD only has two (ff, ffrstmux) - if (!(nusers(argQ) >= 3 && nusers(dffD) == 2)) - argD = SigSpec(); - } - else - dffrstmux = nullptr; -endcode - -// (3) Match for a $mux cell implement clock enable semantics --- one that -// exclusively drives the 'D' input of the $dff (or the other input of -// the reset $mux) and where one of this $mux's inputs is connected to -// the 'Q' output of the $dff -match ffcemux - if !argD.empty() - select ffcemux->type.in($mux) - index <SigSpec> port(ffcemux, \Y) === argD - choice <IdString> AB {\A, \B} - index <SigSpec> port(ffcemux, AB) === argQ - define <bool> pol (AB == \A) - set ffcepol pol - semioptional -endmatch - -code argD - if (ffcemux) { - dffcemux = ffcemux; - dffcepol = ffcepol; - argD = port(ffcemux, ffcepol ? \B : \A); - dffD.replace(port(ffcemux, \Y), argD); - } - else - dffcemux = nullptr; + dffD.replace(argQ, D); endcode // ####################### @@ -543,119 +392,26 @@ code reject; endcode -// (1) Starting from an optional $mux cell that implements clock enable -// semantics --- one where the given 'D' argument (partially or fully) -// drives one of its two inputs -match ffcemux - select ffcemux->type.in($mux) - // ffcemux output must have two users: ffcemux and ff.D - select nusers(port(ffcemux, \Y)) == 2 - - choice <IdString> AB {\A, \B} - // keep-last-value net must have at least three users: ffcemux, ff, downstream sink(s) - select nusers(port(ffcemux, AB)) >= 3 - - slice offset GetSize(port(ffcemux, \Y)) - define <IdString> BA (AB == \A ? \B : \A) - index <SigBit> port(ffcemux, BA)[offset] === argD[0] - - // Check that the rest of argD is present - filter GetSize(port(ffcemux, BA)) >= offset + GetSize(argD) - filter port(ffcemux, BA).extract(offset, GetSize(argD)) == argD - - set ffoffset offset - define <bool> pol (AB == \A) - set ffcepol pol - - semioptional -endmatch - -code argD argQ - dffcemux = ffcemux; - if (ffcemux) { - SigSpec BA = port(ffcemux, ffcepol ? \B : \A); - SigSpec Y = port(ffcemux, \Y); - argQ = argD; - argD.replace(BA, Y); - argQ.replace(BA, port(ffcemux, ffcepol ? \A : \B)); - - dffcemux = ffcemux; - dffcepol = ffcepol; - } -endcode - -// (2) Starting from, or continuing onto, another optional $mux cell that -// implements synchronous reset semantics --- one where the given 'D' -// argument (or the clock enable $mux output) drives one of its two inputs -// and where the other input is fully zero -match ffrstmux - select ffrstmux->type.in($mux) - // ffrstmux output must have two users: ffrstmux and ff.D - select nusers(port(ffrstmux, \Y)) == 2 - - choice <IdString> BA {\B, \A} - // DSP48E1 only supports reset to zero - select port(ffrstmux, BA).is_fully_zero() - - slice offset GetSize(port(ffrstmux, \Y)) - define <IdString> AB (BA == \B ? \A : \B) - index <SigBit> port(ffrstmux, AB)[offset] === argD[0] - - // Check that offset is consistent - filter !ffcemux || ffoffset == offset - // Check that the rest of argD is present - filter GetSize(port(ffrstmux, AB)) >= offset + GetSize(argD) - filter port(ffrstmux, AB).extract(offset, GetSize(argD)) == argD - - set ffoffset offset - define <bool> pol (AB == \A) - set ffrstpol pol - - semioptional -endmatch - -code argD argQ - dffrstmux = ffrstmux; - if (ffrstmux) { - SigSpec AB = port(ffrstmux, ffrstpol ? \A : \B); - SigSpec Y = port(ffrstmux, \Y); - argD.replace(AB, Y); - - dffrstmux = ffrstmux; - dffrstpol = ffrstpol; - } -endcode - -// (3) Match for a $dff cell (whose 'D' input is the 'D' argument, or the -// output of the previous clock enable or reset $mux cells) match ff - select ff->type.in($dff) + select ff->type.in($dff, $dffe, $sdff, $sdffe) // DSP48E1 does not support clock inversion select param(ff, \CLK_POLARITY).as_bool() slice offset GetSize(port(ff, \D)) index <SigBit> port(ff, \D)[offset] === argD[0] - // Check that offset is consistent - filter (!ffcemux && !ffrstmux) || ffoffset == offset // Check that the rest of argD is present filter GetSize(port(ff, \D)) >= offset + GetSize(argD) filter port(ff, \D).extract(offset, GetSize(argD)) == argD - // Check that FF.Q is connected to CE-mux - filter !ffcemux || port(ff, \Q).extract(offset, GetSize(argQ)) == argQ filter clock == SigBit() || port(ff, \CLK) == clock - - set ffoffset offset endmatch code argQ SigSpec D = port(ff, \D); SigSpec Q = port(ff, \Q); - if (!ffcemux) { - argQ = argD; - argQ.replace(D, Q); - } + argQ = argD; + argQ.replace(D, Q); // Abandon matches when 'Q' has a non-zero init attribute set // (not supported by DSP48E1) diff --git a/passes/pmgen/xilinx_dsp_CREG.pmg b/passes/pmgen/xilinx_dsp_CREG.pmg index 42d4d1b9b..95379771a 100644 --- a/passes/pmgen/xilinx_dsp_CREG.pmg +++ b/passes/pmgen/xilinx_dsp_CREG.pmg @@ -26,17 +26,14 @@ pattern xilinx_dsp_packC udata <std::function<SigSpec(const SigSpec&)>> unextend state <SigBit> clock state <SigSpec> sigC sigP -state <bool> ffCcepol ffCrstpol -state <Cell*> ffC ffCcemux ffCrstmux +state <Cell*> ffC // Variables used for subpatterns state <SigSpec> argQ argD -state <bool> ffcepol ffrstpol state <int> ffoffset udata <SigSpec> dffD dffQ udata <SigBit> dffclock -udata <Cell*> dff dffcemux dffrstmux -udata <bool> dffcepol dffrstpol +udata <Cell*> dff // (1) Starting from a DSP48* cell that (a) doesn't have a CREG already, // and (b) uses the 'C' port @@ -80,20 +77,12 @@ endcode // (2) Match the driver of the 'C' input to a possible $dff cell (CREG) // (attached to at most two $mux cells that implement clock-enable or // reset functionality, using the in_dffe subpattern) -code argQ ffC ffCcemux ffCrstmux ffCcepol ffCrstpol sigC clock +code argQ ffC sigC clock argQ = sigC; subpattern(in_dffe); if (dff) { ffC = dff; clock = dffclock; - if (dffrstmux) { - ffCrstmux = dffrstmux; - ffCrstpol = dffrstpol; - } - if (dffcemux) { - ffCcemux = dffcemux; - ffCcepol = dffcepol; - } sigC = dffD; } endcode @@ -106,25 +95,14 @@ endcode // ####################### // Subpattern for matching against input registers, based on knowledge of the -// 'Q' input. Typically, identifying registers with clock-enable and reset -// capability would be a task would be handled by other Yosys passes such as -// dff2dffe, but since DSP inference happens much before this, these patterns -// have to be manually identified. -// At a high level: -// (1) Starting from a $dff cell that (partially or fully) drives the given -// 'Q' argument -// (2) Match for a $mux cell implementing synchronous reset semantics --- -// one that exclusively drives the 'D' input of the $dff, with one of its -// $mux inputs being fully zero -// (3) Match for a $mux cell implement clock enable semantics --- one that -// exclusively drives the 'D' input of the $dff (or the other input of -// the reset $mux) and where one of this $mux's inputs is connected to -// the 'Q' output of the $dff +// 'Q' input. subpattern in_dffe -arg argD argQ clock +arg argQ clock code dff = nullptr; + if (argQ.empty()) + reject; for (const auto &c : argQ.chunks()) { // Abandon matches when 'Q' is a constant if (!c.wire) @@ -135,19 +113,21 @@ code // Abandon matches when 'Q' has a non-zero init attribute set // (not supported by DSP48E1) Const init = c.wire->attributes.at(\init, Const()); - for (auto b : init.extract(c.offset, c.width)) - if (b != State::Sx && b != State::S0) - reject; + if (!init.empty()) + for (auto b : init.extract(c.offset, c.width)) + if (b != State::Sx && b != State::S0) + reject; } endcode -// (1) Starting from a $dff cell that (partially or fully) drives the given -// 'Q' argument match ff - select ff->type.in($dff) + select ff->type.in($dff, $dffe, $sdff, $sdffe) // DSP48E1 does not support clock inversion select param(ff, \CLK_POLARITY).as_bool() + // Check that reset value, if present, is fully 0. + filter ff->type.in($dff, $dffe) || param(ff, \SRST_VALUE).is_fully_zero() + slice offset GetSize(port(ff, \D)) index <SigBit> port(ff, \Q)[offset] === argQ[0] @@ -156,80 +136,14 @@ match ff filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ filter clock == SigBit() || port(ff, \CLK) == clock - - set ffoffset offset endmatch -code argQ argD +code argQ SigSpec Q = port(ff, \Q); dff = ff; dffclock = port(ff, \CLK); dffD = argQ; - argD = port(ff, \D); + SigSpec D = port(ff, \D); argQ = Q; - dffD.replace(argQ, argD); - // Only search for ffrstmux if dffD only - // has two (ff, ffrstmux) users - if (nusers(dffD) > 2) - argD = SigSpec(); -endcode - -// (2) Match for a $mux cell implementing synchronous reset semantics --- -// exclusively drives the 'D' input of the $dff, with one of the $mux -// inputs being fully zero -match ffrstmux - if !argD.empty() - select ffrstmux->type.in($mux) - index <SigSpec> port(ffrstmux, \Y) === argD - - choice <IdString> BA {\B, \A} - // DSP48E1 only supports reset to zero - select port(ffrstmux, BA).is_fully_zero() - - define <bool> pol (BA == \B) - set ffrstpol pol - semioptional -endmatch - -code argD - if (ffrstmux) { - dffrstmux = ffrstmux; - dffrstpol = ffrstpol; - argD = port(ffrstmux, ffrstpol ? \A : \B); - dffD.replace(port(ffrstmux, \Y), argD); - - // Only search for ffcemux if argQ has at - // least 3 users (ff, <upstream>, ffrstmux) and - // dffD only has two (ff, ffrstmux) - if (!(nusers(argQ) >= 3 && nusers(dffD) == 2)) - argD = SigSpec(); - } - else - dffrstmux = nullptr; -endcode - -// (3) Match for a $mux cell implement clock enable semantics --- one that -// exclusively drives the 'D' input of the $dff (or the other input of -// the reset $mux) and where one of this $mux's inputs is connected to -// the 'Q' output of the $dff -match ffcemux - if !argD.empty() - select ffcemux->type.in($mux) - index <SigSpec> port(ffcemux, \Y) === argD - choice <IdString> AB {\A, \B} - index <SigSpec> port(ffcemux, AB) === argQ - define <bool> pol (AB == \A) - set ffcepol pol - semioptional -endmatch - -code argD - if (ffcemux) { - dffcemux = ffcemux; - dffcepol = ffcepol; - argD = port(ffcemux, ffcepol ? \B : \A); - dffD.replace(port(ffcemux, \Y), argD); - } - else - dffcemux = nullptr; + dffD.replace(argQ, D); endcode diff --git a/passes/pmgen/xilinx_dsp_cascade.pmg b/passes/pmgen/xilinx_dsp_cascade.pmg index 8babb88e6..06601554c 100644 --- a/passes/pmgen/xilinx_dsp_cascade.pmg +++ b/passes/pmgen/xilinx_dsp_cascade.pmg @@ -51,12 +51,10 @@ state <int> AREG BREG // Variables used for subpatterns state <SigSpec> argQ argD -state <bool> ffcepol ffrstpol state <int> ffoffset udata <SigSpec> dffD dffQ udata <SigBit> dffclock -udata <Cell*> dff dffcemux dffrstmux -udata <bool> dffcepol dffrstpol +udata <Cell*> dff code #define MAX_DSP_CASCADE 20 @@ -254,9 +252,9 @@ code argQ clock AREG clock = port(prev, \CLK); subpattern(in_dffe); if (dff) { - if (!dffrstmux && port(prev, \RSTA, State::S0) != State::S0) + if (!dff->type.in($sdff, $sdffe) && port(prev, \RSTA, State::S0) != State::S0) goto reject_AREG; - if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTA, State::S0)) + if (dff->type.in($sdff, $sdffe) && (port(dff, \SRST) != port(prev, \RSTA, State::S0) || !param(dff, \SRST_POLARITY).as_bool())) goto reject_AREG; IdString CEA; if (param(prev, \AREG) == 1) @@ -264,9 +262,9 @@ code argQ clock AREG else if (param(prev, \AREG) == 2) CEA = \CEA1; else log_abort(); - if (!dffcemux && port(prev, CEA, State::S0) != State::S1) + if (!dff->type.in($dffe, $sdffe) && port(prev, CEA, State::S0) != State::S1) goto reject_AREG; - if (dffcemux && port(dffcemux, \S) != port(prev, CEA, State::S0)) + if (dff->type.in($dffe, $sdffe) && (port(dff, \EN) != port(prev, CEA, State::S0) || !param(dff, \EN_POLARITY).as_bool())) goto reject_AREG; if (dffD == unextend(port(prev, \A))) AREG = 1; @@ -295,9 +293,9 @@ code argQ clock BREG clock = port(prev, \CLK); subpattern(in_dffe); if (dff) { - if (!dffrstmux && port(prev, \RSTB, State::S0) != State::S0) + if (!dff->type.in($sdff, $sdffe) && port(prev, \RSTB, State::S0) != State::S0) goto reject_BREG; - if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTB, State::S0)) + if (dff->type.in($sdff, $sdffe) && (port(dff, \SRST) != port(prev, \RSTB, State::S0) || !param(dff, \SRST_POLARITY).as_bool())) goto reject_BREG; IdString CEB; if (next->type.in(\DSP48A, \DSP48A1)) @@ -310,9 +308,9 @@ code argQ clock BREG else log_abort(); } else log_abort(); - if (!dffcemux && port(prev, CEB, State::S0) != State::S1) + if (!dff->type.in($dffe, $sdffe) && port(prev, CEB, State::S0) != State::S1) goto reject_BREG; - if (dffcemux && port(dffcemux, \S) != port(prev, CEB, State::S0)) + if (dff->type.in($dffe, $sdffe) && (port(dff, \EN) != port(prev, CEB, State::S0) || !param(dff, \EN_POLARITY).as_bool())) goto reject_BREG; if (dffD == unextend(port(prev, \B))) { if (next->type.in(\DSP48A, \DSP48A1) && param(prev, \B0REG) != 0) @@ -357,25 +355,14 @@ endcode // ####################### // Subpattern for matching against input registers, based on knowledge of the -// 'Q' input. Typically, identifying registers with clock-enable and reset -// capability would be a task would be handled by other Yosys passes such as -// dff2dffe, but since DSP inference happens much before this, these patterns -// have to be manually identified. -// At a high level: -// (1) Starting from a $dff cell that (partially or fully) drives the given -// 'Q' argument -// (2) Match for a $mux cell implementing synchronous reset semantics --- -// one that exclusively drives the 'D' input of the $dff, with one of its -// $mux inputs being fully zero -// (3) Match for a $mux cell implement clock enable semantics --- one that -// exclusively drives the 'D' input of the $dff (or the other input of -// the reset $mux) and where one of this $mux's inputs is connected to -// the 'Q' output of the $dff +// 'Q' input. subpattern in_dffe -arg argD argQ clock +arg argQ clock code dff = nullptr; + if (argQ.empty()) + reject; for (const auto &c : argQ.chunks()) { // Abandon matches when 'Q' is a constant if (!c.wire) @@ -386,19 +373,21 @@ code // Abandon matches when 'Q' has a non-zero init attribute set // (not supported by DSP48E1) Const init = c.wire->attributes.at(\init, Const()); - for (auto b : init.extract(c.offset, c.width)) - if (b != State::Sx && b != State::S0) - reject; + if (!init.empty()) + for (auto b : init.extract(c.offset, c.width)) + if (b != State::Sx && b != State::S0) + reject; } endcode -// (1) Starting from a $dff cell that (partially or fully) drives the given -// 'Q' argument match ff - select ff->type.in($dff) + select ff->type.in($dff, $dffe, $sdff, $sdffe) // DSP48E1 does not support clock inversion select param(ff, \CLK_POLARITY).as_bool() + // Check that reset value, if present, is fully 0. + filter ff->type.in($dff, $dffe) || param(ff, \SRST_VALUE).is_fully_zero() + slice offset GetSize(port(ff, \D)) index <SigBit> port(ff, \Q)[offset] === argQ[0] @@ -407,80 +396,14 @@ match ff filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ filter clock == SigBit() || port(ff, \CLK) == clock - - set ffoffset offset endmatch -code argQ argD +code argQ SigSpec Q = port(ff, \Q); dff = ff; dffclock = port(ff, \CLK); dffD = argQ; - argD = port(ff, \D); + SigSpec D = port(ff, \D); argQ = Q; - dffD.replace(argQ, argD); - // Only search for ffrstmux if dffD only - // has two (ff, ffrstmux) users - if (nusers(dffD) > 2) - argD = SigSpec(); -endcode - -// (2) Match for a $mux cell implementing synchronous reset semantics --- -// exclusively drives the 'D' input of the $dff, with one of the $mux -// inputs being fully zero -match ffrstmux - if !argD.empty() - select ffrstmux->type.in($mux) - index <SigSpec> port(ffrstmux, \Y) === argD - - choice <IdString> BA {\B, \A} - // DSP48E1 only supports reset to zero - select port(ffrstmux, BA).is_fully_zero() - - define <bool> pol (BA == \B) - set ffrstpol pol - semioptional -endmatch - -code argD - if (ffrstmux) { - dffrstmux = ffrstmux; - dffrstpol = ffrstpol; - argD = port(ffrstmux, ffrstpol ? \A : \B); - dffD.replace(port(ffrstmux, \Y), argD); - - // Only search for ffcemux if argQ has at - // least 3 users (ff, <upstream>, ffrstmux) and - // dffD only has two (ff, ffrstmux) - if (!(nusers(argQ) >= 3 && nusers(dffD) == 2)) - argD = SigSpec(); - } - else - dffrstmux = nullptr; -endcode - -// (3) Match for a $mux cell implement clock enable semantics --- one that -// exclusively drives the 'D' input of the $dff (or the other input of -// the reset $mux) and where one of this $mux's inputs is connected to -// the 'Q' output of the $dff -match ffcemux - if !argD.empty() - select ffcemux->type.in($mux) - index <SigSpec> port(ffcemux, \Y) === argD - choice <IdString> AB {\A, \B} - index <SigSpec> port(ffcemux, AB) === argQ - define <bool> pol (AB == \A) - set ffcepol pol - semioptional -endmatch - -code argD - if (ffcemux) { - dffcemux = ffcemux; - dffcepol = ffcepol; - argD = port(ffcemux, ffcepol ? \B : \A); - dffD.replace(port(ffcemux, \Y), argD); - } - else - dffcemux = nullptr; + dffD.replace(argQ, D); endcode diff --git a/passes/proc/proc_dlatch.cc b/passes/proc/proc_dlatch.cc index 3ed158f37..7b8c05b21 100644 --- a/passes/proc/proc_dlatch.cc +++ b/passes/proc/proc_dlatch.cc @@ -19,6 +19,7 @@ #include "kernel/register.h" #include "kernel/sigtools.h" +#include "kernel/ffinit.h" #include "kernel/consteval.h" #include "kernel/log.h" #include <sstream> @@ -32,15 +33,17 @@ struct proc_dlatch_db_t { Module *module; SigMap sigmap; + FfInitVals initvals; pool<Cell*> generated_dlatches; dict<Cell*, vector<SigBit>> mux_srcbits; dict<SigBit, pair<Cell*, int>> mux_drivers; dict<SigBit, int> sigusers; - dict<SigBit, std::pair<State,SigBit>> initbits; proc_dlatch_db_t(Module *module) : module(module), sigmap(module) { + initvals.set(&sigmap, module); + for (auto cell : module->cells()) { if (cell->type.in(ID($mux), ID($pmux))) @@ -74,29 +77,6 @@ struct proc_dlatch_db_t if (wire->port_input) for (auto bit : sigmap(wire)) sigusers[bit]++; - if (wire->attributes.count(ID::init)) { - SigSpec wirebits = sigmap(wire); - Const initval = wire->attributes.at(ID::init); - - for (int i = 0; i < GetSize(wirebits) && i < GetSize(initval); i++) - { - SigBit bit = wirebits[i]; - State val = initval[i]; - - if (val != State::S0 && val != State::S1 && bit.wire != nullptr) - continue; - - if (initbits.count(bit)) { - if (initbits.at(bit).first != val) - log_error("Conflicting init values for signal %s (%s = %s != %s).\n", - log_signal(bit), log_signal(SigBit(wire, i)), - log_signal(val), log_signal(initbits.at(bit).first)); - continue; - } - - initbits[bit] = std::make_pair(val,SigBit(wire,i)); - } - } } } @@ -420,11 +400,11 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc) 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()); for (auto &bit : lhs) { - auto it = db.initbits.find(bit); - if (it != db.initbits.end()) { - log("Removing init bit %s for non-memory siginal `%s.%s` in process `%s.%s`.\n", log_signal(it->second.first), db.module->name.c_str(), log_signal(bit), db.module->name.c_str(), proc->name.c_str()); - it->second.second.wire->attributes.at(ID::init)[it->second.second.offset] = State::Sx; + State val = db.initvals(bit); + if (db.initvals(bit) != State::Sx) { + log("Removing init bit %s for non-memory siginal `%s.%s` in process `%s.%s`.\n", log_signal(val), db.module->name.c_str(), log_signal(bit), db.module->name.c_str(), proc->name.c_str()); } + db.initvals.remove_init(bit); } db.module->connect(lhs, rhs); offset += chunk.width; diff --git a/passes/sat/async2sync.cc b/passes/sat/async2sync.cc index 6fc480925..3fa5a614c 100644 --- a/passes/sat/async2sync.cc +++ b/passes/sat/async2sync.cc @@ -19,6 +19,8 @@ #include "kernel/yosys.h" #include "kernel/sigtools.h" +#include "kernel/ffinit.h" +#include "kernel/ff.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -62,169 +64,183 @@ struct Async2syncPass : public Pass { for (auto module : design->selected_modules()) { SigMap sigmap(module); - dict<SigBit, State> initbits; - pool<SigBit> del_initbits; - - for (auto wire : module->wires()) - if (wire->attributes.count(ID::init) > 0) - { - Const initval = wire->attributes.at(ID::init); - SigSpec initsig = sigmap(wire); - - for (int i = 0; i < GetSize(initval) && i < GetSize(initsig); i++) - if (initval[i] == State::S0 || initval[i] == State::S1) - initbits[initsig[i]] = initval[i]; - } + FfInitVals initvals(&sigmap, module); for (auto cell : vector<Cell*>(module->selected_cells())) { - if (cell->type.in(ID($adff))) - { - // bool clk_pol = cell->parameters[ID::CLK_POLARITY].as_bool(); - bool arst_pol = cell->parameters[ID::ARST_POLARITY].as_bool(); - Const arst_val = cell->parameters[ID::ARST_VALUE]; - - // SigSpec sig_clk = cell->getPort(ID::CLK); - SigSpec sig_arst = cell->getPort(ID::ARST); - SigSpec sig_d = cell->getPort(ID::D); - SigSpec sig_q = cell->getPort(ID::Q); - - log("Replacing %s.%s (%s): ARST=%s, D=%s, Q=%s\n", - log_id(module), log_id(cell), log_id(cell->type), - log_signal(sig_arst), log_signal(sig_d), log_signal(sig_q)); - - Const init_val; - for (int i = 0; i < GetSize(sig_q); i++) { - SigBit bit = sigmap(sig_q[i]); - init_val.bits.push_back(initbits.count(bit) ? initbits.at(bit) : State::Sx); - del_initbits.insert(bit); - } + if (!RTLIL::builtin_ff_cell_types().count(cell->type)) + continue; - Wire *new_d = module->addWire(NEW_ID, GetSize(sig_d)); - Wire *new_q = module->addWire(NEW_ID, GetSize(sig_q)); - new_q->attributes[ID::init] = init_val; + FfData ff(&initvals, cell); - if (arst_pol) { - module->addMux(NEW_ID, sig_d, arst_val, sig_arst, new_d); - module->addMux(NEW_ID, new_q, arst_val, sig_arst, sig_q); - } else { - module->addMux(NEW_ID, arst_val, sig_d, sig_arst, new_d); - module->addMux(NEW_ID, arst_val, new_q, sig_arst, sig_q); - } - - cell->setPort(ID::D, new_d); - cell->setPort(ID::Q, new_q); - cell->unsetPort(ID::ARST); - cell->unsetParam(ID::ARST_POLARITY); - cell->unsetParam(ID::ARST_VALUE); - cell->type = ID($dff); + // Skip for $_FF_ and $ff cells. + if (ff.has_d && !ff.has_clk && !ff.has_en) continue; - } - if (cell->type.in(ID($dffsr))) + if (ff.has_clk) { - // bool clk_pol = cell->parameters[ID::CLK_POLARITY].as_bool(); - bool set_pol = cell->parameters[ID::SET_POLARITY].as_bool(); - bool clr_pol = cell->parameters[ID::CLR_POLARITY].as_bool(); - - // SigSpec sig_clk = cell->getPort(ID::CLK); - SigSpec sig_set = cell->getPort(ID::SET); - SigSpec sig_clr = cell->getPort(ID::CLR); - SigSpec sig_d = cell->getPort(ID::D); - SigSpec sig_q = cell->getPort(ID::Q); - - log("Replacing %s.%s (%s): SET=%s, CLR=%s, D=%s, Q=%s\n", - log_id(module), log_id(cell), log_id(cell->type), - log_signal(sig_set), log_signal(sig_clr), log_signal(sig_d), log_signal(sig_q)); - - Const init_val; - for (int i = 0; i < GetSize(sig_q); i++) { - SigBit bit = sigmap(sig_q[i]); - init_val.bits.push_back(initbits.count(bit) ? initbits.at(bit) : State::Sx); - del_initbits.insert(bit); + if (!ff.has_sr && !ff.has_arst) + continue; + + if (ff.has_sr) { + ff.unmap_ce_srst(module); + + log("Replacing %s.%s (%s): SET=%s, CLR=%s, D=%s, Q=%s\n", + log_id(module), log_id(cell), log_id(cell->type), + log_signal(ff.sig_set), log_signal(ff.sig_clr), log_signal(ff.sig_d), log_signal(ff.sig_q)); + + initvals.remove_init(ff.sig_q); + + Wire *new_d = module->addWire(NEW_ID, ff.width); + Wire *new_q = module->addWire(NEW_ID, ff.width); + + SigSpec sig_set = ff.sig_set; + SigSpec sig_clr = ff.sig_clr; + + if (!ff.pol_set) { + if (!ff.is_fine) + sig_set = module->Not(NEW_ID, sig_set); + else + sig_set = module->NotGate(NEW_ID, sig_set); + } + + if (ff.pol_clr) { + if (!ff.is_fine) + sig_clr = module->Not(NEW_ID, sig_clr); + else + sig_clr = module->NotGate(NEW_ID, sig_clr); + } + + if (!ff.is_fine) { + SigSpec tmp = module->Or(NEW_ID, ff.sig_d, sig_set); + module->addAnd(NEW_ID, tmp, sig_clr, new_d); + + tmp = module->Or(NEW_ID, new_q, sig_set); + module->addAnd(NEW_ID, tmp, sig_clr, ff.sig_q); + } else { + SigSpec tmp = module->OrGate(NEW_ID, ff.sig_d, sig_set); + module->addAndGate(NEW_ID, tmp, sig_clr, new_d); + + tmp = module->OrGate(NEW_ID, new_q, sig_set); + module->addAndGate(NEW_ID, tmp, sig_clr, ff.sig_q); + } + + ff.sig_d = new_d; + ff.sig_q = new_q; + ff.has_sr = false; + } else if (ff.has_arst) { + ff.unmap_srst(module); + + log("Replacing %s.%s (%s): ARST=%s, D=%s, Q=%s\n", + log_id(module), log_id(cell), log_id(cell->type), + log_signal(ff.sig_arst), log_signal(ff.sig_d), log_signal(ff.sig_q)); + + initvals.remove_init(ff.sig_q); + + Wire *new_q = module->addWire(NEW_ID, ff.width); + + if (ff.pol_arst) { + if (!ff.is_fine) + module->addMux(NEW_ID, new_q, ff.val_arst, ff.sig_arst, ff.sig_q); + else + module->addMuxGate(NEW_ID, new_q, ff.val_arst[0], ff.sig_arst, ff.sig_q); + } else { + if (!ff.is_fine) + module->addMux(NEW_ID, ff.val_arst, new_q, ff.sig_arst, ff.sig_q); + else + module->addMuxGate(NEW_ID, ff.val_arst[0], new_q, ff.sig_arst, ff.sig_q); + } + + ff.sig_q = new_q; + ff.has_arst = false; + ff.has_srst = true; + ff.val_srst = ff.val_arst; + ff.sig_srst = ff.sig_arst; + ff.pol_srst = ff.pol_arst; } - - Wire *new_d = module->addWire(NEW_ID, GetSize(sig_d)); - Wire *new_q = module->addWire(NEW_ID, GetSize(sig_q)); - new_q->attributes[ID::init] = init_val; - - if (!set_pol) - sig_set = module->Not(NEW_ID, sig_set); - - if (clr_pol) - sig_clr = module->Not(NEW_ID, sig_clr); - - SigSpec tmp = module->Or(NEW_ID, sig_d, sig_set); - module->addAnd(NEW_ID, tmp, sig_clr, new_d); - - tmp = module->Or(NEW_ID, new_q, sig_set); - module->addAnd(NEW_ID, tmp, sig_clr, sig_q); - - cell->setPort(ID::D, new_d); - cell->setPort(ID::Q, new_q); - cell->unsetPort(ID::SET); - cell->unsetPort(ID::CLR); - cell->unsetParam(ID::SET_POLARITY); - cell->unsetParam(ID::CLR_POLARITY); - cell->type = ID($dff); - continue; } - - if (cell->type.in(ID($dlatch))) + else { - bool en_pol = cell->parameters[ID::EN_POLARITY].as_bool(); - - SigSpec sig_en = cell->getPort(ID::EN); - SigSpec sig_d = cell->getPort(ID::D); - SigSpec sig_q = cell->getPort(ID::Q); - + // Latch. log("Replacing %s.%s (%s): EN=%s, D=%s, Q=%s\n", log_id(module), log_id(cell), log_id(cell->type), - log_signal(sig_en), log_signal(sig_d), log_signal(sig_q)); - - Const init_val; - for (int i = 0; i < GetSize(sig_q); i++) { - SigBit bit = sigmap(sig_q[i]); - init_val.bits.push_back(initbits.count(bit) ? initbits.at(bit) : State::Sx); - del_initbits.insert(bit); + log_signal(ff.sig_en), log_signal(ff.sig_d), log_signal(ff.sig_q)); + + initvals.remove_init(ff.sig_q); + + Wire *new_q = module->addWire(NEW_ID, ff.width); + Wire *new_d; + + if (ff.has_d) { + new_d = module->addWire(NEW_ID, ff.width); + if (ff.pol_en) { + if (!ff.is_fine) + module->addMux(NEW_ID, new_q, ff.sig_d, ff.sig_en, new_d); + else + module->addMuxGate(NEW_ID, new_q, ff.sig_d, ff.sig_en, new_d); + } else { + if (!ff.is_fine) + module->addMux(NEW_ID, ff.sig_d, new_q, ff.sig_en, new_d); + else + module->addMuxGate(NEW_ID, ff.sig_d, new_q, ff.sig_en, new_d); + } + } else { + new_d = new_q; } - Wire *new_q = module->addWire(NEW_ID, GetSize(sig_q)); - new_q->attributes[ID::init] = init_val; - - if (en_pol) { - module->addMux(NEW_ID, new_q, sig_d, sig_en, sig_q); + if (ff.has_sr) { + SigSpec sig_set = ff.sig_set; + SigSpec sig_clr = ff.sig_clr; + + if (!ff.pol_set) { + if (!ff.is_fine) + sig_set = module->Not(NEW_ID, sig_set); + else + sig_set = module->NotGate(NEW_ID, sig_set); + } + + if (ff.pol_clr) { + if (!ff.is_fine) + sig_clr = module->Not(NEW_ID, sig_clr); + else + sig_clr = module->NotGate(NEW_ID, sig_clr); + } + + if (!ff.is_fine) { + SigSpec tmp = module->Or(NEW_ID, new_d, sig_set); + module->addAnd(NEW_ID, tmp, sig_clr, ff.sig_q); + } else { + SigSpec tmp = module->OrGate(NEW_ID, new_d, sig_set); + module->addAndGate(NEW_ID, tmp, sig_clr, ff.sig_q); + } + } else if (ff.has_arst) { + if (ff.pol_arst) { + if (!ff.is_fine) + module->addMux(NEW_ID, new_d, ff.val_arst, ff.sig_arst, ff.sig_q); + else + module->addMuxGate(NEW_ID, new_d, ff.val_arst[0], ff.sig_arst, ff.sig_q); + } else { + if (!ff.is_fine) + module->addMux(NEW_ID, ff.val_arst, new_d, ff.sig_arst, ff.sig_q); + else + module->addMuxGate(NEW_ID, ff.val_arst[0], new_d, ff.sig_arst, ff.sig_q); + } } else { - module->addMux(NEW_ID, sig_d, new_q, sig_en, sig_q); + module->connect(ff.sig_q, new_d); } - cell->setPort(ID::D, sig_q); - cell->setPort(ID::Q, new_q); - cell->unsetPort(ID::EN); - cell->unsetParam(ID::EN_POLARITY); - cell->type = ID($ff); - continue; + ff.sig_d = new_d; + ff.sig_q = new_q; + ff.has_en = false; + ff.has_arst = false; + ff.has_sr = false; + ff.has_d = true; } - } - for (auto wire : module->wires()) - if (wire->attributes.count(ID::init) > 0) - { - bool delete_initattr = true; - Const initval = wire->attributes.at(ID::init); - SigSpec initsig = sigmap(wire); - - for (int i = 0; i < GetSize(initval) && i < GetSize(initsig); i++) - if (del_initbits.count(initsig[i]) > 0) - initval[i] = State::Sx; - else if (initval[i] != State::Sx) - delete_initattr = false; - - if (delete_initattr) - wire->attributes.erase(ID::init); - else - wire->attributes.at(ID::init) = initval; - } + IdString name = cell->name; + module->remove(cell); + ff.emit(module, name); + } } } } Async2syncPass; diff --git a/passes/sat/clk2fflogic.cc b/passes/sat/clk2fflogic.cc index cc24db6d4..2cb91c009 100644 --- a/passes/sat/clk2fflogic.cc +++ b/passes/sat/clk2fflogic.cc @@ -19,6 +19,8 @@ #include "kernel/yosys.h" #include "kernel/sigtools.h" +#include "kernel/ffinit.h" +#include "kernel/ff.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -80,19 +82,7 @@ struct Clk2fflogicPass : public Pass { for (auto module : design->selected_modules()) { SigMap sigmap(module); - dict<SigBit, State> initbits; - pool<SigBit> del_initbits; - - for (auto wire : module->wires()) - if (wire->attributes.count(ID::init) > 0) - { - Const initval = wire->attributes.at(ID::init); - SigSpec initsig = sigmap(wire); - - for (int i = 0; i < GetSize(initval) && i < GetSize(initsig); i++) - if (initval[i] == State::S0 || initval[i] == State::S1) - initbits[initsig[i]] = initval[i]; - } + FfInitVals initvals(&sigmap, module); for (auto cell : vector<Cell*>(module->selected_cells())) { @@ -177,221 +167,112 @@ struct Clk2fflogicPass : public Pass { cell->setPort(ID::WR_DATA, wr_data_port); } - if (cell->type.in(ID($dlatch), ID($adlatch), ID($dlatchsr))) - { - bool enpol = cell->parameters[ID::EN_POLARITY].as_bool(); - - SigSpec sig_en = cell->getPort(ID::EN); - SigSpec sig_d = cell->getPort(ID::D); - SigSpec sig_q = cell->getPort(ID::Q); - - log("Replacing %s.%s (%s): EN=%s, D=%s, Q=%s\n", - log_id(module), log_id(cell), log_id(cell->type), - log_signal(sig_en), log_signal(sig_d), log_signal(sig_q)); - - sig_en = wrap_async_control(module, sig_en, enpol); + SigSpec qval; + if (RTLIL::builtin_ff_cell_types().count(cell->type)) { + FfData ff(&initvals, cell); - Wire *past_q = module->addWire(NEW_ID, GetSize(sig_q)); - module->addFf(NEW_ID, sig_q, past_q); - - if (cell->type == ID($dlatch)) - { - module->addMux(NEW_ID, past_q, sig_d, sig_en, sig_q); + if (ff.has_d && !ff.has_clk && !ff.has_en) { + // Already a $ff or $_FF_ cell. + continue; } - else if (cell->type == ID($adlatch)) - { - SigSpec t = module->Mux(NEW_ID, past_q, sig_d, sig_en); - SigSpec arst = wrap_async_control(module, cell->getPort(ID::ARST), cell->parameters[ID::ARST_POLARITY].as_bool()); - Const rstval = cell->parameters[ID::ARST_VALUE]; - - module->addMux(NEW_ID, t, rstval, arst, sig_q); - } - else - { - SigSpec t = module->Mux(NEW_ID, past_q, sig_d, sig_en); - - SigSpec s = wrap_async_control(module, cell->getPort(ID::SET), cell->parameters[ID::SET_POLARITY].as_bool()); - t = module->Or(NEW_ID, t, s); - SigSpec c = wrap_async_control(module, cell->getPort(ID::CLR), cell->parameters[ID::CLR_POLARITY].as_bool()); - c = module->Not(NEW_ID, c); - module->addAnd(NEW_ID, t, c, sig_q); - } - - Const initval; - bool assign_initval = false; - for (int i = 0; i < GetSize(sig_d); i++) { - SigBit qbit = sigmap(sig_q[i]); - if (initbits.count(qbit)) { - initval.bits.push_back(initbits.at(qbit)); - del_initbits.insert(qbit); - } else - initval.bits.push_back(State::Sx); - if (initval.bits.back() != State::Sx) - assign_initval = true; + Wire *past_q = module->addWire(NEW_ID, ff.width); + if (!ff.is_fine) { + module->addFf(NEW_ID, ff.sig_q, past_q); + } else { + module->addFfGate(NEW_ID, ff.sig_q, past_q); } + if (!ff.val_init.is_fully_undef()) + initvals.set_init(past_q, ff.val_init); - if (assign_initval) - past_q->attributes[ID::init] = initval; - - module->remove(cell); - continue; - } - - bool word_dff = cell->type.in(ID($dff), ID($adff), ID($dffsr)); - if (word_dff || cell->type.in(ID($_DFF_N_), ID($_DFF_P_), - ID($_DFF_NN0_), ID($_DFF_NN1_), ID($_DFF_NP0_), ID($_DFF_NP1_), - ID($_DFF_PP0_), ID($_DFF_PP1_), ID($_DFF_PN0_), ID($_DFF_PN1_), - ID($_DFFSR_NNN_), ID($_DFFSR_NNP_), ID($_DFFSR_NPN_), ID($_DFFSR_NPP_), - ID($_DFFSR_PNN_), ID($_DFFSR_PNP_), ID($_DFFSR_PPN_), ID($_DFFSR_PPP_))) - { - bool clkpol; - SigSpec clk; - if (word_dff) { - clkpol = cell->parameters[ID::CLK_POLARITY].as_bool(); - clk = cell->getPort(ID::CLK); - } - else { - if (cell->type.in(ID($_DFF_P_), ID($_DFF_N_), - ID($_DFF_NN0_), ID($_DFF_NN1_), ID($_DFF_NP0_), ID($_DFF_NP1_), - ID($_DFF_PP0_), ID($_DFF_PP1_), ID($_DFF_PN0_), ID($_DFF_PN1_))) - clkpol = cell->type[6] == 'P'; - else if (cell->type.in(ID($_DFFSR_NNN_), ID($_DFFSR_NNP_), ID($_DFFSR_NPN_), ID($_DFFSR_NPP_), - ID($_DFFSR_PNN_), ID($_DFFSR_PNP_), ID($_DFFSR_PPN_), ID($_DFFSR_PPP_))) - clkpol = cell->type[8] == 'P'; - else log_abort(); - clk = cell->getPort(ID::C); - } + if (ff.has_clk) { + ff.unmap_ce_srst(module); - Wire *past_clk = module->addWire(NEW_ID); - past_clk->attributes[ID::init] = clkpol ? State::S1 : State::S0; + Wire *past_clk = module->addWire(NEW_ID); + initvals.set_init(past_clk, ff.pol_clk ? State::S1 : State::S0); - if (word_dff) - module->addFf(NEW_ID, clk, past_clk); - else - module->addFfGate(NEW_ID, clk, past_clk); + if (!ff.is_fine) + module->addFf(NEW_ID, ff.sig_clk, past_clk); + else + module->addFfGate(NEW_ID, ff.sig_clk, past_clk); - SigSpec sig_d = cell->getPort(ID::D); - SigSpec sig_q = cell->getPort(ID::Q); + log("Replacing %s.%s (%s): CLK=%s, D=%s, Q=%s\n", + log_id(module), log_id(cell), log_id(cell->type), + log_signal(ff.sig_clk), log_signal(ff.sig_d), log_signal(ff.sig_q)); - log("Replacing %s.%s (%s): CLK=%s, D=%s, Q=%s\n", - log_id(module), log_id(cell), log_id(cell->type), - log_signal(clk), log_signal(sig_d), log_signal(sig_q)); + SigSpec clock_edge_pattern; - SigSpec clock_edge_pattern; + if (ff.pol_clk) { + clock_edge_pattern.append(State::S0); + clock_edge_pattern.append(State::S1); + } else { + clock_edge_pattern.append(State::S1); + clock_edge_pattern.append(State::S0); + } - if (clkpol) { - clock_edge_pattern.append(State::S0); - clock_edge_pattern.append(State::S1); - } else { - clock_edge_pattern.append(State::S1); - clock_edge_pattern.append(State::S0); - } + SigSpec clock_edge = module->Eqx(NEW_ID, {ff.sig_clk, SigSpec(past_clk)}, clock_edge_pattern); - SigSpec clock_edge = module->Eqx(NEW_ID, {clk, SigSpec(past_clk)}, clock_edge_pattern); + Wire *past_d = module->addWire(NEW_ID, ff.width); + if (!ff.is_fine) + module->addFf(NEW_ID, ff.sig_d, past_d); + else + module->addFfGate(NEW_ID, ff.sig_d, past_d); - Wire *past_d = module->addWire(NEW_ID, GetSize(sig_d)); - Wire *past_q = module->addWire(NEW_ID, GetSize(sig_q)); - if (word_dff) { - module->addFf(NEW_ID, sig_d, past_d); - module->addFf(NEW_ID, sig_q, past_q); - } - else { - module->addFfGate(NEW_ID, sig_d, past_d); - module->addFfGate(NEW_ID, sig_q, past_q); - } + if (!ff.val_init.is_fully_undef()) + initvals.set_init(past_d, ff.val_init); - if (cell->type == ID($adff)) - { - SigSpec arst = wrap_async_control(module, cell->getPort(ID::ARST), cell->parameters[ID::ARST_POLARITY].as_bool()); - SigSpec qval = module->Mux(NEW_ID, past_q, past_d, clock_edge); - Const rstval = cell->parameters[ID::ARST_VALUE]; + if (!ff.is_fine) + qval = module->Mux(NEW_ID, past_q, past_d, clock_edge); + else + qval = module->MuxGate(NEW_ID, past_q, past_d, clock_edge); + } else if (ff.has_d) { - module->addMux(NEW_ID, qval, rstval, arst, sig_q); - } - else - if (cell->type.in(ID($_DFF_NN0_), ID($_DFF_NN1_), ID($_DFF_NP0_), ID($_DFF_NP1_), - ID($_DFF_PP0_), ID($_DFF_PP1_), ID($_DFF_PN0_), ID($_DFF_PN1_))) - { - SigSpec arst = wrap_async_control_gate(module, cell->getPort(ID::R), cell->type[7] == 'P'); - SigSpec qval = module->MuxGate(NEW_ID, past_q, past_d, clock_edge); - SigBit rstval = (cell->type[8] == '1'); + log("Replacing %s.%s (%s): EN=%s, D=%s, Q=%s\n", + log_id(module), log_id(cell), log_id(cell->type), + log_signal(ff.sig_en), log_signal(ff.sig_d), log_signal(ff.sig_q)); - module->addMuxGate(NEW_ID, qval, rstval, arst, sig_q); - } - else - if (cell->type == ID($dffsr)) - { - SigSpec qval = module->Mux(NEW_ID, past_q, past_d, clock_edge); - SigSpec setval = wrap_async_control(module, cell->getPort(ID::SET), cell->parameters[ID::SET_POLARITY].as_bool()); - SigSpec clrval = wrap_async_control(module, cell->getPort(ID::CLR), cell->parameters[ID::CLR_POLARITY].as_bool()); + SigSpec sig_en = wrap_async_control(module, ff.sig_en, ff.pol_en); - clrval = module->Not(NEW_ID, clrval); - qval = module->Or(NEW_ID, qval, setval); - module->addAnd(NEW_ID, qval, clrval, sig_q); - } - else - if (cell->type.in(ID($_DFFSR_NNN_), ID($_DFFSR_NNP_), ID($_DFFSR_NPN_), ID($_DFFSR_NPP_), - ID($_DFFSR_PNN_), ID($_DFFSR_PNP_), ID($_DFFSR_PPN_), ID($_DFFSR_PPP_))) - { - SigSpec qval = module->MuxGate(NEW_ID, past_q, past_d, clock_edge); - SigSpec setval = wrap_async_control_gate(module, cell->getPort(ID::S), cell->type[9] == 'P'); - SigSpec clrval = wrap_async_control_gate(module, cell->getPort(ID::R), cell->type[10] == 'P'); + if (!ff.is_fine) + qval = module->Mux(NEW_ID, past_q, ff.sig_d, sig_en); + else + qval = module->MuxGate(NEW_ID, past_q, ff.sig_d, sig_en); + } else { - clrval = module->NotGate(NEW_ID, clrval); - qval = module->OrGate(NEW_ID, qval, setval); - module->addAndGate(NEW_ID, qval, clrval, sig_q); - } - else if (cell->type == ID($dff)) - { - module->addMux(NEW_ID, past_q, past_d, clock_edge, sig_q); - } - else - { - module->addMuxGate(NEW_ID, past_q, past_d, clock_edge, sig_q); - } + log("Replacing %s.%s (%s): SET=%s, CLR=%s, Q=%s\n", + log_id(module), log_id(cell), log_id(cell->type), + log_signal(ff.sig_set), log_signal(ff.sig_clr), log_signal(ff.sig_q)); - Const initval; - bool assign_initval = false; - for (int i = 0; i < GetSize(sig_d); i++) { - SigBit qbit = sigmap(sig_q[i]); - if (initbits.count(qbit)) { - initval.bits.push_back(initbits.at(qbit)); - del_initbits.insert(qbit); - } else - initval.bits.push_back(State::Sx); - if (initval.bits.back() != State::Sx) - assign_initval = true; + qval = past_q; } - if (assign_initval) { - past_d->attributes[ID::init] = initval; - past_q->attributes[ID::init] = initval; + if (ff.has_sr) { + SigSpec setval = wrap_async_control(module, ff.sig_set, ff.pol_set); + SigSpec clrval = wrap_async_control(module, ff.sig_clr, ff.pol_clr); + if (!ff.is_fine) { + clrval = module->Not(NEW_ID, clrval); + qval = module->Or(NEW_ID, qval, setval); + module->addAnd(NEW_ID, qval, clrval, ff.sig_q); + } else { + clrval = module->NotGate(NEW_ID, clrval); + qval = module->OrGate(NEW_ID, qval, setval); + module->addAndGate(NEW_ID, qval, clrval, ff.sig_q); + } + } else if (ff.has_arst) { + SigSpec arst = wrap_async_control(module, ff.sig_arst, ff.pol_arst); + if (!ff.is_fine) + module->addMux(NEW_ID, qval, ff.val_arst, arst, ff.sig_q); + else + module->addMuxGate(NEW_ID, qval, ff.val_arst[0], arst, ff.sig_q); + } else { + module->connect(ff.sig_q, qval); } + initvals.remove_init(ff.sig_q); module->remove(cell); continue; } } - - for (auto wire : module->wires()) - if (wire->attributes.count(ID::init) > 0) - { - bool delete_initattr = true; - Const initval = wire->attributes.at(ID::init); - SigSpec initsig = sigmap(wire); - - for (int i = 0; i < GetSize(initval) && i < GetSize(initsig); i++) - if (del_initbits.count(initsig[i]) > 0) - initval[i] = State::Sx; - else if (initval[i] != State::Sx) - delete_initattr = false; - - if (delete_initattr) - wire->attributes.erase(ID::init); - else - wire->attributes.at(ID::init) = initval; - } } } diff --git a/passes/sat/qbfsat.cc b/passes/sat/qbfsat.cc index 46f7f5070..6db7d4b64 100644 --- a/passes/sat/qbfsat.cc +++ b/passes/sat/qbfsat.cc @@ -215,7 +215,6 @@ QbfSolutionType call_qbf_solver(RTLIL::Module *mod, const QbfSolveOptions &opt, //Execute and capture stdout from `yosys-smtbmc -s z3 -t 1 -g --binary [--dump-smt2 <file>]` QbfSolutionType ret; const std::string yosys_smtbmc_exe = proc_self_dirname() + "yosys-smtbmc"; - const std::string smt2_command = stringf("write_smt2 -stbv -wires %s/problem%d.smt2", tempdir_name.c_str(), iter_num); const std::string smtbmc_warning = "z3: WARNING:"; const std::string smtbmc_cmd = stringf("%s -s %s %s -t 1 -g --binary %s %s/problem%d.smt2 2>&1", yosys_smtbmc_exe.c_str(), opt.get_solver_name().c_str(), @@ -223,6 +222,10 @@ QbfSolutionType call_qbf_solver(RTLIL::Module *mod, const QbfSolveOptions &opt, (opt.dump_final_smt2? "--dump-smt2 " + opt.dump_final_smt2_file : "").c_str(), tempdir_name.c_str(), iter_num); + std::string smt2_command = "write_smt2 -stbv -wires "; + for (auto &solver_opt : opt.solver_options) + smt2_command += stringf("-solver-option %s %s ", solver_opt.first.c_str(), solver_opt.second.c_str()); + smt2_command += stringf("%s/problem%d.smt2", tempdir_name.c_str(), iter_num); Pass::call(mod->design, smt2_command); auto process_line = [&ret, &smtbmc_warning, &opt, &quiet](const std::string &line) { @@ -419,6 +422,13 @@ QbfSolveOptions parse_args(const std::vector<std::string> &args) { } continue; } + else if (args[opt.argidx] == "-solver-option") { + if (args.size() <= opt.argidx + 2) + log_cmd_error("solver option name and value not fully specified.\n"); + opt.solver_options.emplace(args[opt.argidx+1], args[opt.argidx+2]); + opt.argidx += 2; + continue; + } else if (args[opt.argidx] == "-timeout") { if (args.size() <= opt.argidx + 1) log_cmd_error("timeout not specified.\n"); @@ -533,6 +543,9 @@ struct QbfSatPass : public Pass { log(" Use a particular solver. Choose one of: \"z3\", \"yices\", and \"cvc4\".\n"); log(" (default: yices)\n"); log("\n"); + log(" -solver-option <name> <value>\n"); + log(" Set the specified solver option in the SMT-LIBv2 problem file.\n"); + log("\n"); log(" -timeout <value>\n"); log(" Set the per-iteration timeout in seconds.\n"); log(" (default: no timeout)\n"); diff --git a/passes/sat/qbfsat.h b/passes/sat/qbfsat.h index 401f9c7a6..c96c6f818 100644 --- a/passes/sat/qbfsat.h +++ b/passes/sat/qbfsat.h @@ -31,6 +31,7 @@ struct QbfSolveOptions { bool nobisection = false, sat = false, unsat = false, show_smtbmc = false; enum Solver{Z3, Yices, CVC4} solver = Yices; enum OptimizationLevel{O0, O1, O2} oflag = O0; + dict<std::string, std::string> solver_options; int timeout = 0; std::string specialize_soln_file = ""; std::string write_soln_soln_file = ""; diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index e3b3d8dea..5a4d84f94 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -43,6 +43,7 @@ OBJS += passes/techmap/attrmap.o OBJS += passes/techmap/zinit.o OBJS += passes/techmap/dfflegalize.o OBJS += passes/techmap/dff2dffs.o +OBJS += passes/techmap/dffunmap.o OBJS += passes/techmap/flowmap.o OBJS += passes/techmap/extractinv.o endif diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc index 0a58fdcc0..ce50e9a5b 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -44,6 +44,7 @@ #include "kernel/register.h" #include "kernel/sigtools.h" #include "kernel/celltypes.h" +#include "kernel/ffinit.h" #include "kernel/cost.h" #include "kernel/log.h" #include <stdlib.h> @@ -111,7 +112,7 @@ SigMap assign_map; RTLIL::Module *module; std::vector<gate_t> signal_list; std::map<RTLIL::SigBit, int> signal_map; -std::map<RTLIL::SigBit, RTLIL::State> signal_init; +FfInitVals initvals; pool<std::string> enabled_gates; bool recover_init, cmos_cost; @@ -133,10 +134,7 @@ int map_signal(RTLIL::SigBit bit, gate_type_t gate_type = G(NONE), int in1 = -1, gate.in4 = -1; gate.is_port = false; gate.bit = bit; - if (signal_init.count(bit)) - gate.init = signal_init.at(bit); - else - gate.init = State::Sx; + gate.init = initvals(bit); signal_list.push_back(gate); signal_map[bit] = gate.id; } @@ -1468,7 +1466,7 @@ struct AbcPass : public Pass { assign_map.clear(); signal_list.clear(); signal_map.clear(); - signal_init.clear(); + initvals.clear(); pi_map.clear(); po_map.clear(); @@ -1854,24 +1852,7 @@ struct AbcPass : public Pass { } assign_map.set(mod); - signal_init.clear(); - - for (Wire *wire : mod->wires()) - if (wire->attributes.count(ID::init)) { - SigSpec initsig = assign_map(wire); - Const initval = wire->attributes.at(ID::init); - for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++) - switch (initval[i]) { - case State::S0: - signal_init[initsig[i]] = State::S0; - break; - case State::S1: - signal_init[initsig[i]] = State::S1; - break; - default: - break; - } - } + initvals.set(&assign_map, mod); if (!dff_mode || !clk_str.empty()) { abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_costs, dff_mode, clk_str, keepff, @@ -2028,7 +2009,7 @@ struct AbcPass : public Pass { assign_map.clear(); signal_list.clear(); signal_map.clear(); - signal_init.clear(); + initvals.clear(); pi_map.clear(); po_map.clear(); diff --git a/passes/techmap/abc9.cc b/passes/techmap/abc9.cc index e99c56d8d..7d017ac40 100644 --- a/passes/techmap/abc9.cc +++ b/passes/techmap/abc9.cc @@ -295,7 +295,7 @@ struct Abc9Pass : public ScriptPass run("proc"); run("wbflip"); run("techmap -wb -map %$abc9 -map +/techmap.v A:abc9_flop"); - run("opt"); + run("opt -nodffe -nosdff"); if (dff_mode || help_mode) { if (!help_mode) active_design->scratchpad_unset("abc9_ops.prep_dff_submod.did_something"); diff --git a/passes/techmap/dffinit.cc b/passes/techmap/dffinit.cc index c60a901c1..44af043db 100644 --- a/passes/techmap/dffinit.cc +++ b/passes/techmap/dffinit.cc @@ -19,6 +19,7 @@ #include "kernel/yosys.h" #include "kernel/sigtools.h" +#include "kernel/ffinit.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -94,29 +95,10 @@ struct DffinitPass : public Pass { for (auto module : design->selected_modules()) { SigMap sigmap(module); - dict<SigBit, State> init_bits; - pool<SigBit> cleanup_bits; - pool<SigBit> used_bits; - - for (auto wire : module->selected_wires()) { - if (wire->attributes.count(ID::init)) { - Const value = wire->attributes.at(ID::init); - for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++) - if (value[i] != State::Sx) - init_bits[sigmap(SigBit(wire, i))] = value[i]; - } - if (wire->port_output) - for (auto bit : sigmap(wire)) - used_bits.insert(bit); - } + FfInitVals initvals(&sigmap, module); for (auto cell : module->selected_cells()) { - for (auto it : cell->connections()) - if (!cell->known() || cell->input(it.first)) - for (auto bit : sigmap(it.second)) - used_bits.insert(bit); - if (ff_types.count(cell->type) == 0) continue; @@ -131,17 +113,18 @@ struct DffinitPass : public Pass { if (cell->hasParam(it.second)) value = cell->getParam(it.second); + Const initval = initvals(sig); + initvals.remove_init(sig); for (int i = 0; i < GetSize(sig); i++) { - if (init_bits.count(sig[i]) == 0) + if (initval[i] == State::Sx) continue; while (GetSize(value.bits) <= i) value.bits.push_back(State::S0); - if (noreinit && value.bits[i] != State::Sx && value.bits[i] != init_bits.at(sig[i])) + if (noreinit && value.bits[i] != State::Sx && value.bits[i] != initval[i]) log_error("Trying to assign a different init value for %s.%s.%s which technically " "have a conflicted init value.\n", log_id(module), log_id(cell), log_id(it.second)); - value.bits[i] = init_bits.at(sig[i]); - cleanup_bits.insert(sig[i]); + value.bits[i] = initval[i]; } if (highlow_mode && GetSize(value) != 0) { @@ -161,23 +144,6 @@ struct DffinitPass : public Pass { } } } - - for (auto wire : module->selected_wires()) - if (wire->attributes.count(ID::init)) { - Const &value = wire->attributes.at(ID::init); - bool do_cleanup = true; - for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++) { - SigBit bit = sigmap(SigBit(wire, i)); - if (cleanup_bits.count(bit) || !used_bits.count(bit)) - value[i] = State::Sx; - else if (value[i] != State::Sx) - do_cleanup = false; - } - if (do_cleanup) { - log("Removing init attribute from wire %s.%s.\n", log_id(module), log_id(wire)); - wire->attributes.erase(ID::init); - } - } } } } DffinitPass; diff --git a/passes/techmap/dfflegalize.cc b/passes/techmap/dfflegalize.cc index 13ce4f49a..8ad65493f 100644 --- a/passes/techmap/dfflegalize.cc +++ b/passes/techmap/dfflegalize.cc @@ -19,6 +19,7 @@ #include "kernel/yosys.h" #include "kernel/sigtools.h" +#include "kernel/ffinit.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -170,7 +171,7 @@ struct DffLegalizePass : public Pass { dict<SigBit, int> srst_used; SigMap sigmap; - dict<SigBit, std::pair<State,SigBit>> initbits; + FfInitVals initvals; int flip_initmask(int mask) { int res = mask & INIT_X; @@ -303,13 +304,7 @@ struct DffLegalizePass : public Pass { return; } - State initval = State::Sx; - SigBit initbit; - if (GetSize(sig_q) > 0 && initbits.count(sigmap(sig_q[0]))) { - const auto &d = initbits.at(sigmap(sig_q[0])); - initval = d.first; - initbit = d.second; - } + State initval = initvals(sig_q[0]); FfInit initmask = INIT_X; if (initval == State::S0) @@ -345,12 +340,8 @@ flip_dqi: sig_d = cell->module->NotGate(NEW_ID, sig_d[0]); SigBit new_q = SigSpec(cell->module->addWire(NEW_ID))[0]; cell->module->addNotGate(NEW_ID, new_q, sig_q[0]); - if (initbit.wire) { - initbit.wire->attributes.at(ID::init)[initbit.offset] = State::Sx; - initbit = new_q; - new_q.wire->attributes[ID::init] = initval; - initbits[new_q] = std::make_pair(initval, new_q); - } + initvals.remove_init(sig_q[0]); + initvals.set_init(new_q, initval); sig_q = new_q; continue; } @@ -484,15 +475,12 @@ unmap_enable: } log_warning("Emulating mismatched async reset and init with several FFs and a mux for %s.%s\n", log_id(cell->module->name), log_id(cell->name)); - if (initbit.wire) - initbit.wire->attributes.at(ID::init)[initbit.offset] = State::Sx; + initvals.remove_init(sig_q[0]); Wire *adff_q = cell->module->addWire(NEW_ID); Wire *dff_q = cell->module->addWire(NEW_ID); Wire *sel_q = cell->module->addWire(NEW_ID); - dff_q->attributes[ID::init] = initval; - initbits[SigBit(dff_q, 0)] = std::make_pair(initval, SigBit(dff_q, 0)); - sel_q->attributes[ID::init] = State::S0; - initbits[SigBit(sel_q, 0)] = std::make_pair(State::S0, SigBit(sel_q, 0)); + initvals.set_init(SigBit(dff_q, 0), initval); + initvals.set_init(SigBit(sel_q, 0), State::S0); Cell *cell_dff; Cell *cell_adff; Cell *cell_sel; @@ -588,21 +576,15 @@ flip_dqisr:; } log_warning("Emulating async set + reset with several FFs and a mux for %s.%s\n", log_id(cell->module->name), log_id(cell->name)); - if (initbit.wire) - initbit.wire->attributes.at(ID::init)[initbit.offset] = State::Sx; + initvals.remove_init(sig_q[0]); Wire *adff0_q = cell->module->addWire(NEW_ID); Wire *adff1_q = cell->module->addWire(NEW_ID); Wire *sel_q = cell->module->addWire(NEW_ID); - if (init0) { - adff0_q->attributes[ID::init] = initval; - initbits[SigBit(adff0_q, 0)] = std::make_pair(initval, SigBit(adff0_q, 0)); - } - if (init1) { - adff1_q->attributes[ID::init] = initval; - initbits[SigBit(adff1_q, 0)] = std::make_pair(initval, SigBit(adff1_q, 0)); - } - sel_q->attributes[ID::init] = initsel; - initbits[SigBit(sel_q, 0)] = std::make_pair(initsel, SigBit(sel_q, 0)); + if (init0) + initvals.set_init(SigBit(adff0_q, 0), initval); + if (init1) + initvals.set_init(SigBit(adff1_q, 0), initval); + initvals.set_init(SigBit(sel_q, 0), initsel); Cell *cell_adff0; Cell *cell_adff1; Cell *cell_sel; @@ -741,15 +723,12 @@ flip_dqisr:; // The only hope left is breaking down to adff + dff + dlatch + mux. log_warning("Emulating mismatched async reset and init with several latches and a mux for %s.%s\n", log_id(cell->module->name), log_id(cell->name)); - if (initbit.wire) - initbit.wire->attributes.at(ID::init)[initbit.offset] = State::Sx; + initvals.remove_init(sig_q[0]); Wire *adlatch_q = cell->module->addWire(NEW_ID); Wire *dlatch_q = cell->module->addWire(NEW_ID); Wire *sel_q = cell->module->addWire(NEW_ID); - dlatch_q->attributes[ID::init] = initval; - initbits[SigBit(dlatch_q, 0)] = std::make_pair(initval, SigBit(dlatch_q, 0)); - sel_q->attributes[ID::init] = State::S0; - initbits[SigBit(sel_q, 0)] = std::make_pair(State::S0, SigBit(sel_q, 0)); + initvals.set_init(SigBit(dlatch_q, 0), initval); + initvals.set_init(SigBit(sel_q, 0), State::S0); Cell *cell_dlatch; Cell *cell_adlatch; Cell *cell_sel; @@ -797,21 +776,15 @@ flip_dqisr:; } log_warning("Emulating async set + reset with several latches and a mux for %s.%s\n", log_id(cell->module->name), log_id(cell->name)); - if (initbit.wire) - initbit.wire->attributes.at(ID::init)[initbit.offset] = State::Sx; + initvals.remove_init(sig_q[0]); Wire *adlatch0_q = cell->module->addWire(NEW_ID); Wire *adlatch1_q = cell->module->addWire(NEW_ID); Wire *sel_q = cell->module->addWire(NEW_ID); - if (init0) { - adlatch0_q->attributes[ID::init] = initval; - initbits[SigBit(adlatch0_q, 0)] = std::make_pair(initval, SigBit(adlatch0_q, 0)); - } - if (init1) { - adlatch1_q->attributes[ID::init] = initval; - initbits[SigBit(adlatch1_q, 0)] = std::make_pair(initval, SigBit(adlatch1_q, 0)); - } - sel_q->attributes[ID::init] = initsel; - initbits[SigBit(sel_q, 0)] = std::make_pair(initsel, SigBit(sel_q, 0)); + if (init0) + initvals.set_init(SigBit(adlatch0_q, 0), initval); + if (init1) + initvals.set_init(SigBit(adlatch1_q, 0), initval); + initvals.set_init(SigBit(sel_q, 0), initsel); Cell *cell_adlatch0; Cell *cell_adlatch1; Cell *cell_sel; @@ -1294,35 +1267,7 @@ unrecognized: for (auto module : design->selected_modules()) { sigmap.set(module); - initbits.clear(); - - for (auto wire : module->wires()) - { - if (wire->attributes.count(ID::init) == 0) - continue; - - SigSpec wirebits = sigmap(wire); - Const initval = wire->attributes.at(ID::init); - - for (int i = 0; i < GetSize(wirebits) && i < GetSize(initval); i++) - { - SigBit bit = wirebits[i]; - State val = initval[i]; - - if (val != State::S0 && val != State::S1 && bit.wire != nullptr) - continue; - - if (initbits.count(bit)) { - if (initbits.at(bit).first != val) - log_error("Conflicting init values for signal %s (%s = %s != %s).\n", - log_signal(bit), log_signal(SigBit(wire, i)), - log_signal(val), log_signal(initbits.at(bit).first)); - continue; - } - - initbits[bit] = std::make_pair(val,SigBit(wire,i)); - } - } + initvals.set(&sigmap, module); if (mince || minsrst) { ce_used.clear(); @@ -1365,7 +1310,7 @@ unrecognized: } sigmap.clear(); - initbits.clear(); + initvals.clear(); ce_used.clear(); srst_used.clear(); } diff --git a/passes/techmap/dffunmap.cc b/passes/techmap/dffunmap.cc new file mode 100644 index 000000000..fb107ff75 --- /dev/null +++ b/passes/techmap/dffunmap.cc @@ -0,0 +1,107 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2020 Marcelina Kościelnicka <mwk@0x04.net> + * + * 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" +#include "kernel/ff.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct DffunmapPass : public Pass { + DffunmapPass() : Pass("dffunmap", "unmap clock enable and synchronous reset from FFs") { } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" dffunmap [options] [selection]\n"); + log("\n"); + log("This pass transforms FF types with clock enable and/or synchronous reset into\n"); + log("their base type (with neither clock enable nor sync reset) by emulating the clock\n"); + log("enable and synchronous reset with multiplexers on the cell input.\n"); + log("\n"); + log(" -ce-only\n"); + log(" unmap only clock enables, leave synchronous resets alone.\n"); + log("\n"); + log(" -srst-only\n"); + log(" unmap only synchronous resets, leave clock enables alone.\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) override + { + log_header(design, "Executing DFFUNMAP pass (unmap clock enable and synchronous reset from FFs).\n"); + + bool ce_only = false; + bool srst_only = false; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-ce-only") { + ce_only = true; + continue; + } + if (args[argidx] == "-srst-only") { + srst_only = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + if (ce_only && srst_only) + log_cmd_error("Options -ce-only and -srst-only are mutually exclusive!\n"); + + for (auto mod : design->selected_modules()) + { + SigMap sigmap(mod); + FfInitVals initvals(&sigmap, mod); + + for (auto cell : mod->selected_cells()) + { + if (!RTLIL::builtin_ff_cell_types().count(cell->type)) + continue; + + FfData ff(&initvals, cell); + IdString name = cell->name; + + if (!ff.has_clk) + continue; + + if (ce_only) { + if (!ff.has_en) + continue; + ff.unmap_ce(mod); + } else if (srst_only) { + if (!ff.has_srst) + continue; + ff.unmap_srst(mod); + } else { + if (!ff.has_en && !ff.has_srst) + continue; + ff.unmap_ce_srst(mod); + } + + mod->remove(cell); + ff.emit(mod, name); + } + } + } +} DffunmapPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/shregmap.cc b/passes/techmap/shregmap.cc index 237c261ae..b971068f7 100644 --- a/passes/techmap/shregmap.cc +++ b/passes/techmap/shregmap.cc @@ -19,6 +19,7 @@ #include "kernel/yosys.h" #include "kernel/sigtools.h" +#include "kernel/ffinit.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -100,9 +101,8 @@ struct ShregmapWorker int dff_count, shreg_count; pool<Cell*> remove_cells; - pool<SigBit> remove_init; - dict<SigBit, bool> sigbit_init; + FfInitVals initvals; dict<SigBit, Cell*> sigbit_chain_next; dict<SigBit, Cell*> sigbit_chain_prev; pool<SigBit> sigbit_with_non_chain_users; @@ -116,16 +116,6 @@ struct ShregmapWorker for (auto bit : sigmap(wire)) sigbit_with_non_chain_users.insert(bit); } - - if (wire->attributes.count(ID::init)) { - SigSpec initsig = sigmap(wire); - Const initval = wire->attributes.at(ID::init); - for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++) - if (initval[i] == State::S0 && !opts.zinit) - sigbit_init[initsig[i]] = false; - else if (initval[i] == State::S1) - sigbit_init[initsig[i]] = true; - } } for (auto cell : module->cells()) @@ -137,8 +127,9 @@ struct ShregmapWorker SigBit d_bit = sigmap(cell->getPort(d_port).as_bit()); SigBit q_bit = sigmap(cell->getPort(q_port).as_bit()); + State initval = initvals(q_bit); - if (opts.init || sigbit_init.count(q_bit) == 0) + if (opts.init || initval == State::Sx || (opts.zinit && initval == State::S0)) { auto r = sigbit_chain_next.insert(std::make_pair(d_bit, cell)); if (!r.second) { @@ -310,22 +301,17 @@ struct ShregmapWorker if (opts.init) { vector<State> initval; for (int i = depth-1; i >= 0; i--) { - SigBit bit = sigmap(chain[cursor+i]->getPort(q_port).as_bit()); - if (sigbit_init.count(bit) == 0) - initval.push_back(State::Sx); - else if (sigbit_init.at(bit)) - initval.push_back(State::S1); - else - initval.push_back(State::S0); - remove_init.insert(bit); + SigBit bit = chain[cursor+i]->getPort(q_port).as_bit(); + initval.push_back(initvals(bit)); + initvals.remove_init(bit); } first_cell->setParam(ID::INIT, initval); } if (opts.zinit) for (int i = depth-1; i >= 0; i--) { - SigBit bit = sigmap(chain[cursor+i]->getPort(q_port).as_bit()); - remove_init.insert(bit); + SigBit bit = chain[cursor+i]->getPort(q_port).as_bit(); + initvals.remove_init(bit); } if (opts.params) @@ -364,22 +350,6 @@ struct ShregmapWorker for (auto cell : remove_cells) module->remove(cell); - for (auto wire : module->wires()) - { - if (wire->attributes.count(ID::init) == 0) - continue; - - SigSpec initsig = sigmap(wire); - Const &initval = wire->attributes.at(ID::init); - - for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++) - if (remove_init.count(initsig[i])) - initval[i] = State::Sx; - - if (SigSpec(initval).is_fully_undef()) - wire->attributes.erase(ID::init); - } - remove_cells.clear(); sigbit_chain_next.clear(); sigbit_chain_prev.clear(); @@ -389,6 +359,7 @@ struct ShregmapWorker ShregmapWorker(Module *module, const ShregmapOptions &opts) : module(module), sigmap(module), opts(opts), dff_count(0), shreg_count(0) { + initvals.set(&sigmap, module); make_sigbit_chain_next_prev(); find_chain_start_cells(); diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc index f98d1564a..c22ae8ef0 100644 --- a/passes/techmap/techmap.cc +++ b/passes/techmap/techmap.cc @@ -20,6 +20,7 @@ #include "kernel/yosys.h" #include "kernel/utils.h" #include "kernel/sigtools.h" +#include "kernel/ffinit.h" #include "libs/sha1/sha1.h" #include <stdlib.h> @@ -426,18 +427,7 @@ struct TechmapWorker LogMakeDebugHdl mkdebug; SigMap sigmap(module); - - dict<SigBit, State> init_bits; - pool<SigBit> remove_init_bits; - - for (auto wire : module->wires()) { - if (wire->attributes.count(ID::init)) { - Const value = wire->attributes.at(ID::init); - for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++) - if (value[i] != State::Sx) - init_bits[sigmap(SigBit(wire, i))] = value[i]; - } - } + FfInitVals initvals(&sigmap, module); TopoSort<RTLIL::Cell*, IdString::compare_ptr_by_name<RTLIL::Cell>> cells; dict<RTLIL::Cell*, pool<RTLIL::SigBit>> cell_to_inbit; @@ -643,6 +633,8 @@ struct TechmapWorker if (tpl->avail_parameters.count(ID::_TECHMAP_CELLTYPE_) != 0) parameters.emplace(ID::_TECHMAP_CELLTYPE_, RTLIL::unescape_id(cell->type)); + if (tpl->avail_parameters.count(ID::_TECHMAP_CELLNAME_) != 0) + parameters.emplace(ID::_TECHMAP_CELLNAME_, RTLIL::unescape_id(cell->name)); for (auto &conn : cell->connections()) { if (tpl->avail_parameters.count(stringf("\\_TECHMAP_CONSTMSK_%s_", log_id(conn.first))) != 0) { @@ -659,15 +651,7 @@ struct TechmapWorker parameters.emplace(stringf("\\_TECHMAP_CONSTVAL_%s_", log_id(conn.first)), RTLIL::SigSpec(v).as_const()); } if (tpl->avail_parameters.count(stringf("\\_TECHMAP_WIREINIT_%s_", log_id(conn.first))) != 0) { - auto sig = sigmap(conn.second); - RTLIL::Const value(State::Sx, sig.size()); - for (int i = 0; i < sig.size(); i++) { - auto it = init_bits.find(sig[i]); - if (it != init_bits.end()) { - value[i] = it->second; - } - } - parameters.emplace(stringf("\\_TECHMAP_WIREINIT_%s_", log_id(conn.first)), value); + parameters.emplace(stringf("\\_TECHMAP_WIREINIT_%s_", log_id(conn.first)), initvals(conn.second)); } } @@ -912,7 +896,7 @@ struct TechmapWorker auto sig = sigmap(it->second); for (int i = 0; i < sig.size(); i++) if (val[i] == State::S1) - remove_init_bits.insert(sig[i]); + initvals.remove_init(sig[i]); } } } @@ -961,25 +945,6 @@ struct TechmapWorker handled_cells.insert(cell); } - if (!remove_init_bits.empty()) { - for (auto wire : module->wires()) - if (wire->attributes.count(ID::init)) { - Const &value = wire->attributes.at(ID::init); - bool do_cleanup = true; - for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++) { - SigBit bit = sigmap(SigBit(wire, i)); - if (remove_init_bits.count(bit)) - value[i] = State::Sx; - else if (value[i] != State::Sx) - do_cleanup = false; - } - if (do_cleanup) { - log("Removing init attribute from wire %s.%s.\n", log_id(module), log_id(wire)); - wire->attributes.erase(ID::init); - } - } - } - if (log_continue) { log_header(design, "Continuing TECHMAP pass.\n"); log_continue = false; @@ -1111,6 +1076,10 @@ struct TechmapPass : public Pass { log(" When a parameter with this name exists, it will be set to the type name\n"); log(" of the cell that matches the module.\n"); log("\n"); + log(" _TECHMAP_CELLNAME_\n"); + log(" When a parameter with this name exists, it will be set to the name\n"); + log(" of the cell that matches the module.\n"); + log("\n"); log(" _TECHMAP_CONSTMSK_<port-name>_\n"); log(" _TECHMAP_CONSTVAL_<port-name>_\n"); log(" When this pair of parameters is available in a module for a port, then\n"); diff --git a/passes/techmap/zinit.cc b/passes/techmap/zinit.cc index cc0b26bcc..e3b4ae573 100644 --- a/passes/techmap/zinit.cc +++ b/passes/techmap/zinit.cc @@ -19,6 +19,7 @@ #include "kernel/yosys.h" #include "kernel/sigtools.h" +#include "kernel/ffinit.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -57,35 +58,7 @@ struct ZinitPass : public Pass { for (auto module : design->selected_modules()) { SigMap sigmap(module); - dict<SigBit, std::pair<State,SigBit>> initbits; - - for (auto wire : module->selected_wires()) - { - if (wire->attributes.count(ID::init) == 0) - continue; - - SigSpec wirebits = sigmap(wire); - Const initval = wire->attributes.at(ID::init); - - for (int i = 0; i < GetSize(wirebits) && i < GetSize(initval); i++) - { - SigBit bit = wirebits[i]; - State val = initval[i]; - - if (val != State::S0 && val != State::S1 && bit.wire != nullptr) - continue; - - if (initbits.count(bit)) { - if (initbits.at(bit).first != val) - log_error("Conflicting init values for signal %s (%s = %s != %s).\n", - log_signal(bit), log_signal(SigBit(wire, i)), - log_signal(val), log_signal(initbits.at(bit).first)); - continue; - } - - initbits[bit] = std::make_pair(val,SigBit(wire,i)); - } - } + FfInitVals initvals(&sigmap, module); pool<IdString> dff_types = { // FIXME: It would appear that supporting @@ -127,33 +100,28 @@ struct ZinitPass : public Pass { if (GetSize(sig_d) < 1 || GetSize(sig_q) < 1) continue; - Const initval; + Const initval = initvals(sig_q); + Const newval = initval; + initvals.remove_init(sig_q); - for (int i = 0; i < GetSize(sig_q); i++) { - if (initbits.count(sig_q[i])) { - const auto &d = initbits.at(sig_q[i]); - initval.bits.push_back(d.first); - const auto &b = d.second; - b.wire->attributes.at(ID::init)[b.offset] = State::Sx; - } else - initval.bits.push_back(all_mode ? State::S0 : State::Sx); - } - - Wire *initwire = module->addWire(NEW_ID, GetSize(initval)); - initwire->attributes[ID::init] = initval; + Wire *initwire = module->addWire(NEW_ID, GetSize(sig_q)); for (int i = 0; i < GetSize(initwire); i++) if (initval[i] == State::S1) { sig_d[i] = module->NotGate(NEW_ID, sig_d[i]); module->addNotGate(NEW_ID, SigSpec(initwire, i), sig_q[i]); - initwire->attributes[ID::init][i] = State::S0; + newval[i] = State::S0; } else { module->connect(sig_q[i], SigSpec(initwire, i)); + if (all_mode) + newval[i] = State::S0; } + initvals.set_init(initwire, newval); + log("FF init value for cell %s (%s): %s = %s\n", log_id(cell), log_id(cell->type), log_signal(sig_q), log_signal(initval)); |