From: Marcelina Kościelnicka Date: Fri, 1 Oct 2021 21:50:48 +0000 (+0200) Subject: kernel/ff: Refactor FfData to enable FFs with async load. X-Git-Tag: yosys-0.11~58 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=63b9df8693840d17def8abcb0e848112283b0231;p=yosys.git kernel/ff: Refactor FfData to enable FFs with async load. - *_en is split into *_ce (clock enable) and *_aload (async load aka latch gate enable), so both can be present at once - has_d is removed - has_gclk is added (to have a clear marker for $ff) - d_is_const and val_d leftovers are removed - async2sync, clk2fflogic, opt_dff are updated to operate correctly on FFs with async load --- diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index 47b48a460..6fb14d7fc 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -1398,7 +1398,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) FfData ff(nullptr, cell); // $ff / $_FF_ cell: not supported. - if (ff.has_d && !ff.has_clk && !ff.has_en) + if (ff.has_gclk) return false; std::string reg_name = cellname(cell); @@ -1419,17 +1419,19 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) for (int i = 0; i < chunks; i++) { - SigSpec sig_d; + SigSpec sig_d, sig_ad; Const val_arst, val_srst; - std::string reg_bit_name, sig_set_name, sig_clr_name, sig_arst_name; + std::string reg_bit_name, sig_set_name, sig_clr_name, sig_arst_name, sig_aload_name; if (chunky) { reg_bit_name = stringf("%s[%d]", reg_name.c_str(), i); - if (ff.has_d) + if (ff.has_gclk || ff.has_clk) sig_d = ff.sig_d[i]; + if (ff.has_aload) + sig_ad = ff.sig_ad[i]; } else { reg_bit_name = reg_name; - if (ff.has_d) - sig_d = ff.sig_d; + sig_d = ff.sig_d; + sig_ad = ff.sig_ad; } if (ff.has_arst) val_arst = chunky ? ff.val_arst[i] : ff.val_arst; @@ -1437,28 +1439,38 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) val_srst = chunky ? ff.val_srst[i] : ff.val_srst; // If there are constants in the sensitivity list, replace them with an intermediate wire - if (ff.has_sr) { - if (ff.sig_set[i].wire == NULL) - { - sig_set_name = next_auto_id(); - f << stringf("%s" "wire %s = ", indent.c_str(), sig_set_name.c_str()); - dump_const(f, ff.sig_set[i].data); - f << stringf(";\n"); - } - if (ff.sig_clr[i].wire == NULL) - { - sig_clr_name = next_auto_id(); - f << stringf("%s" "wire %s = ", indent.c_str(), sig_clr_name.c_str()); - dump_const(f, ff.sig_clr[i].data); - f << stringf(";\n"); - } - } else if (ff.has_arst) { - if (ff.sig_arst[i].wire == NULL) - { - sig_arst_name = next_auto_id(); - f << stringf("%s" "wire %s = ", indent.c_str(), sig_arst_name.c_str()); - dump_const(f, ff.sig_arst[i].data); - f << stringf(";\n"); + if (ff.has_clk) { + if (ff.has_sr) { + if (ff.sig_set[i].wire == NULL) + { + sig_set_name = next_auto_id(); + f << stringf("%s" "wire %s = ", indent.c_str(), sig_set_name.c_str()); + dump_const(f, ff.sig_set[i].data); + f << stringf(";\n"); + } + if (ff.sig_clr[i].wire == NULL) + { + sig_clr_name = next_auto_id(); + f << stringf("%s" "wire %s = ", indent.c_str(), sig_clr_name.c_str()); + dump_const(f, ff.sig_clr[i].data); + f << stringf(";\n"); + } + } else if (ff.has_arst) { + if (ff.sig_arst[0].wire == NULL) + { + sig_arst_name = next_auto_id(); + f << stringf("%s" "wire %s = ", indent.c_str(), sig_arst_name.c_str()); + dump_const(f, ff.sig_arst[0].data); + f << stringf(";\n"); + } + } else if (ff.has_aload) { + if (ff.sig_aload[0].wire == NULL) + { + sig_aload_name = next_auto_id(); + f << stringf("%s" "wire %s = ", indent.c_str(), sig_aload_name.c_str()); + dump_const(f, ff.sig_aload[0].data); + f << stringf(";\n"); + } } } @@ -1480,13 +1492,18 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) f << stringf("%s", sig_clr_name.c_str()); else dump_sigspec(f, ff.sig_clr[i]); - } else if (ff.has_arst) { f << stringf(", %sedge ", ff.pol_arst ? "pos" : "neg"); - if (ff.sig_arst[i].wire == NULL) + if (ff.sig_arst[0].wire == NULL) f << stringf("%s", sig_arst_name.c_str()); else dump_sigspec(f, ff.sig_arst); + } else if (ff.has_aload) { + f << stringf(", %sedge ", ff.pol_aload ? "pos" : "neg"); + if (ff.sig_aload[0].wire == NULL) + f << stringf("%s", sig_aload_name.c_str()); + else + dump_sigspec(f, ff.sig_aload); } f << stringf(")\n"); @@ -1507,7 +1524,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) f << stringf("%s" " else ", indent.c_str()); } else if (ff.has_arst) { f << stringf("if (%s", ff.pol_arst ? "" : "!"); - if (ff.sig_arst[i].wire == NULL) + if (ff.sig_arst[0].wire == NULL) f << stringf("%s", sig_arst_name.c_str()); else dump_sigspec(f, ff.sig_arst); @@ -1515,11 +1532,21 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) dump_sigspec(f, val_arst); f << stringf(";\n"); f << stringf("%s" " else ", indent.c_str()); + } else if (ff.has_aload) { + f << stringf("if (%s", ff.pol_aload ? "" : "!"); + if (ff.sig_aload[0].wire == NULL) + f << stringf("%s", sig_aload_name.c_str()); + else + dump_sigspec(f, ff.sig_aload); + f << stringf(") %s <= ", reg_bit_name.c_str()); + dump_sigspec(f, sig_ad); + f << stringf(";\n"); + f << stringf("%s" " else ", indent.c_str()); } - if (ff.has_srst && ff.has_en && ff.ce_over_srst) { - f << stringf("if (%s", ff.pol_en ? "" : "!"); - dump_sigspec(f, ff.sig_en); + if (ff.has_srst && ff.has_ce && ff.ce_over_srst) { + f << stringf("if (%s", ff.pol_ce ? "" : "!"); + dump_sigspec(f, ff.sig_ce); f << stringf(")\n"); f << stringf("%s" " if (%s", indent.c_str(), ff.pol_srst ? "" : "!"); dump_sigspec(f, ff.sig_srst); @@ -1536,9 +1563,9 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) f << stringf(";\n"); f << stringf("%s" " else ", indent.c_str()); } - if (ff.has_en) { - f << stringf("if (%s", ff.pol_en ? "" : "!"); - dump_sigspec(f, ff.sig_en); + if (ff.has_ce) { + f << stringf("if (%s", ff.pol_ce ? "" : "!"); + dump_sigspec(f, ff.sig_ce); f << stringf(") "); } } @@ -1560,7 +1587,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) f << stringf("%s" " else if (%s", indent.c_str(), ff.pol_set ? "" : "!"); dump_sigspec(f, ff.sig_set[i]); f << stringf(") %s = 1'b1;\n", reg_bit_name.c_str()); - if (ff.has_d) + if (ff.has_aload) f << stringf("%s" " else ", indent.c_str()); } else if (ff.has_arst) { f << stringf("if (%s", ff.pol_arst ? "" : "!"); @@ -1568,14 +1595,14 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) f << stringf(") %s = ", reg_bit_name.c_str()); dump_sigspec(f, val_arst); f << stringf(";\n"); - if (ff.has_d) + if (ff.has_aload) f << stringf("%s" " else ", indent.c_str()); } - if (ff.has_d) { - f << stringf("if (%s", ff.pol_en ? "" : "!"); - dump_sigspec(f, ff.sig_en); + if (ff.has_aload) { + f << stringf("if (%s", ff.pol_aload ? "" : "!"); + dump_sigspec(f, ff.sig_aload); f << stringf(") %s = ", reg_bit_name.c_str()); - dump_sigspec(f, sig_d); + dump_sigspec(f, sig_ad); f << stringf(";\n"); } } diff --git a/kernel/ff.h b/kernel/ff.h index e555e15f1..a06eb0a1c 100644 --- a/kernel/ff.h +++ b/kernel/ff.h @@ -25,55 +25,141 @@ YOSYS_NAMESPACE_BEGIN +// Describes a flip-flop or a latch. +// +// If has_gclk, this is a formal verification FF with implicit global clock: +// Q is simply previous cycle's D. +// +// Otherwise, the FF/latch can have any number of features selected by has_* +// attributes that determine Q's value (in order of decreasing priority): +// +// - on start, register is initialized to val_init +// - if has_sr is present: +// - sig_clr is per-bit async clear, and sets the corresponding bit to 0 +// if active +// - sig_set is per-bit async set, and sets the corresponding bit to 1 +// if active +// - if has_arst is present: +// - sig_arst is whole-reg async reset, and sets the whole register to val_arst +// - if has_aload is present: +// - sig_aload is whole-reg async load (aka latch gate enable), and sets the whole +// register to sig_ad +// - if has_clk is present, and we're currently on a clock edge: +// - if has_ce is present and ce_over_srst is true: +// - ignore clock edge (don't change value) unless sig_ce is active +// - if has_srst is present: +// - sig_srst is whole-reg sync reset and sets the register to val_srst +// - if has_ce is present and ce_over_srst is false: +// - ignore clock edge (don't change value) unless sig_ce is active +// - set whole reg to sig_d +// - if nothing of the above applies, the reg value remains unchanged +// +// Since the yosys FF cell library isn't fully generic, not all combinations +// of the features above can be supported: +// +// - only one of has_srst, has_arst, has_sr can be used +// - if has_clk is used together with has_aload, then has_srst, has_arst, +// has_sr cannot be used +// +// The valid feature combinations are thus: +// +// - has_clk + optional has_ce [dff/dffe] +// - has_clk + optional has_ce + has_arst [adff/adffe] +// - has_clk + optional has_ce + has_aload [aldff/aldffe] +// - has_clk + optional has_ce + has_sr [dffsr/dffsre] +// - has_clk + optional has_ce + has_srst [sdff/sdffe/sdffce] +// - has_aload [dlatch] +// - has_aload + has_arst [adlatch] +// - has_aload + has_sr [dlatchsr] +// - has_sr [sr] +// - has_arst [does not correspond to a native cell, represented as dlatch with const D input] +// - empty set [not a cell — will be emitted as a simple direct connection] + struct FfData { FfInitVals *initvals; + // The FF output. SigSpec sig_q; + // The sync data input, present if has_clk or has_gclk. SigSpec sig_d; + // The async data input, present if has_aload. + SigSpec sig_ad; + // The sync clock, present if has_clk. SigSpec sig_clk; - SigSpec sig_en; + // The clock enable, present if has_ce. + SigSpec sig_ce; + // The async load enable, present if has_aload. + SigSpec sig_aload; + // The async reset, preset if has_arst. SigSpec sig_arst; + // The sync reset, preset if has_srst. SigSpec sig_srst; + // The async clear (per-lane), present if has_sr. SigSpec sig_clr; + // The async set (per-lane), present if has_sr. SigSpec sig_set; - bool has_d; + // True if this is a clocked (edge-sensitive) flip-flop. bool has_clk; - bool has_en; + // True if this is a $ff, exclusive with every other has_*. + bool has_gclk; + // True if this FF has a clock enable. Depends on has_clk. + bool has_ce; + // True if this FF has async load function — this includes D latches. + // If this and has_clk are both set, has_arst and has_sr cannot be set. + bool has_aload; + // True if this FF has sync set/reset. Depends on has_clk, exclusive + // with has_arst, has_sr, has_aload. bool has_srst; + // True if this FF has async set/reset. Exclusive with has_srst, + // has_sr. If this and has_clk are both set, has_aload cannot be set. bool has_arst; + // True if this FF has per-bit async set + clear. Exclusive with + // has_srst, has_arst. If this and has_clk are both set, has_aload + // cannot be set. bool has_sr; + // If has_ce and has_srst are both set, determines their relative + // priorities: if true, inactive ce disables srst; if false, srst + // operates independent of ce. bool ce_over_srst; + // True if this FF is a fine cell, false if it is a coarse cell. + // If true, width must be 1. bool is_fine; + // Polarities, corresponding to sig_*. True means active-high, false + // means active-low. bool pol_clk; - bool pol_en; + bool pol_ce; + bool pol_aload; bool pol_arst; bool pol_srst; bool pol_clr; bool pol_set; + // The value loaded by sig_arst. Const val_arst; + // The value loaded by sig_srst. Const val_srst; + // The initial value at power-up. Const val_init; - Const val_d; - bool d_is_const; + // The FF data width in bits. int width; dict attributes; FfData(FfInitVals *initvals = nullptr, Cell *cell = nullptr) : initvals(initvals) { width = 0; - has_d = true; has_clk = false; - has_en = false; + has_gclk = false; + has_ce = false; + has_aload = false; has_srst = false; has_arst = false; has_sr = false; ce_over_srst = false; is_fine = false; pol_clk = false; - pol_en = false; + pol_aload = false; + pol_ce = false; pol_arst = false; pol_srst = false; pol_clr = false; pol_set = false; - d_is_const = false; if (!cell) return; @@ -88,20 +174,26 @@ struct FfData { std::string type_str = cell->type.str(); if (cell->type.in(ID($ff), ID($dff), ID($dffe), ID($dffsr), ID($dffsre), ID($adff), ID($adffe), ID($sdff), ID($sdffe), ID($sdffce), ID($dlatch), ID($adlatch), ID($dlatchsr), ID($sr))) { - if (cell->type == ID($sr)) { - has_d = false; - } else { + if (cell->type == ID($ff)) { + has_gclk = true; sig_d = cell->getPort(ID::D); - } - if (!cell->type.in(ID($ff), ID($dlatch), ID($adlatch), ID($dlatchsr), ID($sr))) { + } else if (cell->type == ID($sr)) { + // No data input at all. + } else if (cell->type.in(ID($dlatch), ID($adlatch), ID($dlatchsr))) { + has_aload = true; + sig_aload = cell->getPort(ID::EN); + pol_aload = cell->getParam(ID::EN_POLARITY).as_bool(); + sig_ad = cell->getPort(ID::D); + } else { has_clk = true; sig_clk = cell->getPort(ID::CLK); pol_clk = cell->getParam(ID::CLK_POLARITY).as_bool(); + sig_d = cell->getPort(ID::D); } - if (cell->type.in(ID($dffe), ID($dffsre), ID($adffe), ID($sdffe), ID($sdffce), ID($dlatch), ID($adlatch), ID($dlatchsr))) { - has_en = true; - sig_en = cell->getPort(ID::EN); - pol_en = cell->getParam(ID::EN_POLARITY).as_bool(); + if (cell->type.in(ID($dffe), ID($dffsre), ID($adffe), ID($sdffe), ID($sdffce))) { + has_ce = true; + sig_ce = cell->getPort(ID::EN); + pol_ce = cell->getParam(ID::EN_POLARITY).as_bool(); } if (cell->type.in(ID($dffsr), ID($dffsre), ID($dlatchsr), ID($sr))) { has_sr = true; @@ -125,10 +217,10 @@ struct FfData { } } else if (cell->type == ID($_FF_)) { is_fine = true; + has_gclk = true; sig_d = cell->getPort(ID::D); } else if (type_str.substr(0, 5) == "$_SR_") { is_fine = true; - has_d = false; has_sr = true; pol_set = type_str[5] == 'P'; pol_clr = type_str[6] == 'P'; @@ -146,9 +238,9 @@ struct FfData { has_clk = true; pol_clk = type_str[7] == 'P'; sig_clk = cell->getPort(ID::C); - has_en = true; - pol_en = type_str[8] == 'P'; - sig_en = cell->getPort(ID::E); + has_ce = true; + pol_ce = type_str[8] == 'P'; + sig_ce = cell->getPort(ID::E); } else if (type_str.substr(0, 6) == "$_DFF_" && type_str.size() == 10) { is_fine = true; sig_d = cell->getPort(ID::D); @@ -169,9 +261,9 @@ struct FfData { pol_arst = type_str[8] == 'P'; sig_arst = cell->getPort(ID::R); val_arst = type_str[9] == '1' ? State::S1 : State::S0; - has_en = true; - pol_en = type_str[10] == 'P'; - sig_en = cell->getPort(ID::E); + has_ce = true; + pol_ce = type_str[10] == 'P'; + sig_ce = cell->getPort(ID::E); } else if (type_str.substr(0, 8) == "$_DFFSR_" && type_str.size() == 12) { is_fine = true; sig_d = cell->getPort(ID::D); @@ -194,9 +286,9 @@ struct FfData { pol_clr = type_str[11] == 'P'; sig_set = cell->getPort(ID::S); sig_clr = cell->getPort(ID::R); - has_en = true; - pol_en = type_str[12] == 'P'; - sig_en = cell->getPort(ID::E); + has_ce = true; + pol_ce = type_str[12] == 'P'; + sig_ce = cell->getPort(ID::E); } else if (type_str.substr(0, 7) == "$_SDFF_" && type_str.size() == 11) { is_fine = true; sig_d = cell->getPort(ID::D); @@ -217,9 +309,9 @@ struct FfData { pol_srst = type_str[9] == 'P'; sig_srst = cell->getPort(ID::R); val_srst = type_str[10] == '1' ? State::S1 : State::S0; - has_en = true; - pol_en = type_str[11] == 'P'; - sig_en = cell->getPort(ID::E); + has_ce = true; + pol_ce = type_str[11] == 'P'; + sig_ce = cell->getPort(ID::E); } else if (type_str.substr(0, 9) == "$_SDFFCE_" && type_str.size() == 14) { is_fine = true; sig_d = cell->getPort(ID::D); @@ -230,32 +322,35 @@ struct FfData { pol_srst = type_str[10] == 'P'; sig_srst = cell->getPort(ID::R); val_srst = type_str[11] == '1' ? State::S1 : State::S0; - has_en = true; - pol_en = type_str[12] == 'P'; - sig_en = cell->getPort(ID::E); + has_ce = true; + pol_ce = type_str[12] == 'P'; + sig_ce = cell->getPort(ID::E); ce_over_srst = true; } else if (type_str.substr(0, 9) == "$_DLATCH_" && type_str.size() == 11) { is_fine = true; - sig_d = cell->getPort(ID::D); - has_en = true; - pol_en = type_str[9] == 'P'; - sig_en = cell->getPort(ID::E); + has_aload = true; + sig_ad = cell->getPort(ID::D); + has_aload = true; + pol_aload = type_str[9] == 'P'; + sig_aload = cell->getPort(ID::E); } else if (type_str.substr(0, 9) == "$_DLATCH_" && type_str.size() == 13) { is_fine = true; - sig_d = cell->getPort(ID::D); - has_en = true; - pol_en = type_str[9] == 'P'; - sig_en = cell->getPort(ID::E); + has_aload = true; + sig_ad = cell->getPort(ID::D); + has_aload = true; + pol_aload = type_str[9] == 'P'; + sig_aload = cell->getPort(ID::E); has_arst = true; pol_arst = type_str[10] == 'P'; sig_arst = cell->getPort(ID::R); val_arst = type_str[11] == '1' ? State::S1 : State::S0; } else if (type_str.substr(0, 11) == "$_DLATCHSR_" && type_str.size() == 15) { is_fine = true; - sig_d = cell->getPort(ID::D); - has_en = true; - pol_en = type_str[11] == 'P'; - sig_en = cell->getPort(ID::E); + has_aload = true; + sig_ad = cell->getPort(ID::D); + has_aload = true; + pol_aload = type_str[11] == 'P'; + sig_aload = cell->getPort(ID::E); has_sr = true; pol_set = type_str[12] == 'P'; pol_clr = type_str[13] == 'P'; @@ -264,17 +359,13 @@ struct FfData { } else { log_assert(0); } - if (has_d && sig_d.is_fully_const()) { - d_is_const = true; - val_d = sig_d.as_const(); - if (has_en && !has_clk && !has_sr && !has_arst) { - // Plain D latches with const D treated specially. - has_en = has_d = false; - has_arst = true; - sig_arst = sig_en; - pol_arst = pol_en; - val_arst = val_d; - } + if (has_aload && !has_clk && !has_sr && !has_arst && sig_ad.is_fully_const()) { + // Plain D latches with const D treated specially. + has_aload = false; + has_arst = true; + sig_arst = sig_aload; + pol_arst = pol_aload; + val_arst = sig_ad.as_const(); } } @@ -282,19 +373,22 @@ struct FfData { FfData slice(const std::vector &bits) { FfData res(initvals); res.sig_clk = sig_clk; - res.sig_en = sig_en; + res.sig_ce = sig_ce; + res.sig_aload = sig_aload; res.sig_arst = sig_arst; res.sig_srst = sig_srst; - res.has_d = has_d; res.has_clk = has_clk; - res.has_en = has_en; + res.has_gclk = has_gclk; + res.has_ce = has_ce; + res.has_aload = has_aload; res.has_arst = has_arst; res.has_srst = has_srst; res.has_sr = has_sr; res.ce_over_srst = ce_over_srst; res.is_fine = is_fine; res.pol_clk = pol_clk; - res.pol_en = pol_en; + res.pol_ce = pol_ce; + res.pol_aload = pol_aload; res.pol_arst = pol_arst; res.pol_srst = pol_srst; res.pol_clr = pol_clr; @@ -302,8 +396,10 @@ struct FfData { res.attributes = attributes; for (int i : bits) { res.sig_q.append(sig_q[i]); - if (has_d) + if (has_clk || has_gclk) res.sig_d.append(sig_d[i]); + if (has_aload) + res.sig_ad.append(sig_ad[i]); if (has_sr) { res.sig_clr.append(sig_clr[i]); res.sig_set.append(sig_set[i]); @@ -316,39 +412,34 @@ struct FfData { res.val_init.bits.push_back(val_init[i]); } res.width = GetSize(res.sig_q); - // Slicing bits out may cause D to become const. - if (has_d && res.sig_d.is_fully_const()) { - res.d_is_const = true; - res.val_d = res.sig_d.as_const(); - } return res; } void unmap_ce(Module *module) { - if (!has_en) + if (!has_ce) return; log_assert(has_clk); if (has_srst && ce_over_srst) unmap_srst(module); if (!is_fine) { - if (pol_en) - sig_d = module->Mux(NEW_ID, sig_q, sig_d, sig_en); + if (pol_ce) + sig_d = module->Mux(NEW_ID, sig_q, sig_d, sig_ce); else - sig_d = module->Mux(NEW_ID, sig_d, sig_q, sig_en); + sig_d = module->Mux(NEW_ID, sig_d, sig_q, sig_ce); } else { - if (pol_en) - sig_d = module->MuxGate(NEW_ID, sig_q, sig_d, sig_en); + if (pol_ce) + sig_d = module->MuxGate(NEW_ID, sig_q, sig_d, sig_ce); else - sig_d = module->MuxGate(NEW_ID, sig_d, sig_q, sig_en); + sig_d = module->MuxGate(NEW_ID, sig_d, sig_q, sig_ce); } - has_en = false; + has_ce = false; } void unmap_srst(Module *module) { if (!has_srst) return; - if (has_en && !ce_over_srst) + if (has_ce && !ce_over_srst) unmap_ce(module); if (!is_fine) { @@ -373,14 +464,14 @@ struct FfData { Cell *emit(Module *module, IdString name) { if (!width) return nullptr; - if (!has_d && !has_sr) { + if (!has_aload && !has_clk && !has_gclk && !has_sr) { if (has_arst) { // Convert this case to a D latch. - has_d = has_en = true; + has_aload = true; has_arst = false; - sig_d = val_arst; - sig_en = sig_arst; - pol_en = pol_arst; + sig_ad = val_arst; + sig_aload = sig_arst; + pol_aload = pol_arst; } else { // No control inputs left. Turn into a const driver. if (initvals) @@ -393,87 +484,93 @@ struct FfData { initvals->set_init(sig_q, val_init); Cell *cell; if (!is_fine) { - if (!has_d) { - log_assert(has_sr); - cell = module->addSr(name, sig_set, sig_clr, sig_q, pol_set, pol_clr); - } else if (!has_clk && !has_en) { + if (has_gclk) { + log_assert(!has_clk); + log_assert(!has_ce); + log_assert(!has_aload); log_assert(!has_arst); log_assert(!has_srst); log_assert(!has_sr); cell = module->addFf(name, sig_d, sig_q); + } else if (!has_aload && !has_clk) { + log_assert(has_sr); + cell = module->addSr(name, sig_set, sig_clr, sig_q, pol_set, pol_clr); } else if (!has_clk) { log_assert(!has_srst); if (has_sr) - cell = module->addDlatchsr(name, sig_en, sig_set, sig_clr, sig_d, sig_q, pol_en, pol_set, pol_clr); + cell = module->addDlatchsr(name, sig_aload, sig_set, sig_clr, sig_ad, sig_q, pol_aload, pol_set, pol_clr); else if (has_arst) - cell = module->addAdlatch(name, sig_en, sig_arst, sig_d, sig_q, val_arst, pol_en, pol_arst); + cell = module->addAdlatch(name, sig_aload, sig_arst, sig_ad, sig_q, val_arst, pol_aload, pol_arst); else - cell = module->addDlatch(name, sig_en, sig_d, sig_q, pol_en); + cell = module->addDlatch(name, sig_aload, sig_ad, sig_q, pol_aload); } else { if (has_sr) { - if (has_en) - cell = module->addDffsre(name, sig_clk, sig_en, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_en, pol_set, pol_clr); + if (has_ce) + cell = module->addDffsre(name, sig_clk, sig_ce, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_ce, pol_set, pol_clr); else cell = module->addDffsr(name, sig_clk, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_set, pol_clr); } else if (has_arst) { - if (has_en) - cell = module->addAdffe(name, sig_clk, sig_en, sig_arst, sig_d, sig_q, val_arst, pol_clk, pol_en, pol_arst); + if (has_ce) + cell = module->addAdffe(name, sig_clk, sig_ce, sig_arst, sig_d, sig_q, val_arst, pol_clk, pol_ce, pol_arst); else cell = module->addAdff(name, sig_clk, sig_arst, sig_d, sig_q, val_arst, pol_clk, pol_arst); } else if (has_srst) { - if (has_en) + if (has_ce) if (ce_over_srst) - cell = module->addSdffce(name, sig_clk, sig_en, sig_srst, sig_d, sig_q, val_srst, pol_clk, pol_en, pol_srst); + cell = module->addSdffce(name, sig_clk, sig_ce, sig_srst, sig_d, sig_q, val_srst, pol_clk, pol_ce, pol_srst); else - cell = module->addSdffe(name, sig_clk, sig_en, sig_srst, sig_d, sig_q, val_srst, pol_clk, pol_en, pol_srst); + cell = module->addSdffe(name, sig_clk, sig_ce, sig_srst, sig_d, sig_q, val_srst, pol_clk, pol_ce, pol_srst); else cell = module->addSdff(name, sig_clk, sig_srst, sig_d, sig_q, val_srst, pol_clk, pol_srst); } else { - if (has_en) - cell = module->addDffe(name, sig_clk, sig_en, sig_d, sig_q, pol_clk, pol_en); + if (has_ce) + cell = module->addDffe(name, sig_clk, sig_ce, sig_d, sig_q, pol_clk, pol_ce); else cell = module->addDff(name, sig_clk, sig_d, sig_q, pol_clk); } } } else { - if (!has_d) { - log_assert(has_sr); - cell = module->addSrGate(name, sig_set, sig_clr, sig_q, pol_set, pol_clr); - } else if (!has_clk && !has_en) { + if (has_gclk) { + log_assert(!has_clk); + log_assert(!has_ce); + log_assert(!has_aload); log_assert(!has_arst); log_assert(!has_srst); log_assert(!has_sr); cell = module->addFfGate(name, sig_d, sig_q); + } else if (!has_aload && !has_clk) { + log_assert(has_sr); + cell = module->addSrGate(name, sig_set, sig_clr, sig_q, pol_set, pol_clr); } else if (!has_clk) { log_assert(!has_srst); if (has_sr) - cell = module->addDlatchsrGate(name, sig_en, sig_set, sig_clr, sig_d, sig_q, pol_en, pol_set, pol_clr); + cell = module->addDlatchsrGate(name, sig_aload, sig_set, sig_clr, sig_ad, sig_q, pol_aload, pol_set, pol_clr); else if (has_arst) - cell = module->addAdlatchGate(name, sig_en, sig_arst, sig_d, sig_q, val_arst.as_bool(), pol_en, pol_arst); + cell = module->addAdlatchGate(name, sig_aload, sig_arst, sig_ad, sig_q, val_arst.as_bool(), pol_aload, pol_arst); else - cell = module->addDlatchGate(name, sig_en, sig_d, sig_q, pol_en); + cell = module->addDlatchGate(name, sig_aload, sig_ad, sig_q, pol_aload); } else { if (has_sr) { - if (has_en) - cell = module->addDffsreGate(name, sig_clk, sig_en, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_en, pol_set, pol_clr); + if (has_ce) + cell = module->addDffsreGate(name, sig_clk, sig_ce, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_ce, pol_set, pol_clr); else cell = module->addDffsrGate(name, sig_clk, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_set, pol_clr); } else if (has_arst) { - if (has_en) - cell = module->addAdffeGate(name, sig_clk, sig_en, sig_arst, sig_d, sig_q, val_arst.as_bool(), pol_clk, pol_en, pol_arst); + if (has_ce) + cell = module->addAdffeGate(name, sig_clk, sig_ce, sig_arst, sig_d, sig_q, val_arst.as_bool(), pol_clk, pol_ce, pol_arst); else cell = module->addAdffGate(name, sig_clk, sig_arst, sig_d, sig_q, val_arst.as_bool(), pol_clk, pol_arst); } else if (has_srst) { - if (has_en) + if (has_ce) if (ce_over_srst) - cell = module->addSdffceGate(name, sig_clk, sig_en, sig_srst, sig_d, sig_q, val_srst.as_bool(), pol_clk, pol_en, pol_srst); + cell = module->addSdffceGate(name, sig_clk, sig_ce, sig_srst, sig_d, sig_q, val_srst.as_bool(), pol_clk, pol_ce, pol_srst); else - cell = module->addSdffeGate(name, sig_clk, sig_en, sig_srst, sig_d, sig_q, val_srst.as_bool(), pol_clk, pol_en, pol_srst); + cell = module->addSdffeGate(name, sig_clk, sig_ce, sig_srst, sig_d, sig_q, val_srst.as_bool(), pol_clk, pol_ce, pol_srst); else cell = module->addSdffGate(name, sig_clk, sig_srst, sig_d, sig_q, val_srst.as_bool(), pol_clk, pol_srst); } else { - if (has_en) - cell = module->addDffeGate(name, sig_clk, sig_en, sig_d, sig_q, pol_clk, pol_en); + if (has_ce) + cell = module->addDffeGate(name, sig_clk, sig_ce, sig_d, sig_q, pol_clk, pol_ce); else cell = module->addDffGate(name, sig_clk, sig_d, sig_q, pol_clk); } diff --git a/kernel/ffmerge.cc b/kernel/ffmerge.cc index 6a29acc96..7d62a88cf 100644 --- a/kernel/ffmerge.cc +++ b/kernel/ffmerge.cc @@ -62,22 +62,28 @@ bool FfMergeHelper::find_output_ff(RTLIL::SigSpec sig, FfData &ff, pool undef_d; if (model_undef) undef_d = importUndefSigSpec(cell->getPort(ID::D), timestep-1); - if (ff.has_srst && ff.has_en && ff.ce_over_srst) { + if (ff.has_srst && ff.has_ce && ff.ce_over_srst) { int srst = importDefSigSpec(ff.sig_srst, timestep-1).at(0); std::vector rval = importDefSigSpec(ff.val_srst, timestep-1); int undef_srst; @@ -1108,21 +1108,21 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) else std::tie(d, undef_d) = mux(srst, undef_srst, rval, undef_rval, d, undef_d); } - if (ff.has_en) { - int en = importDefSigSpec(ff.sig_en, timestep-1).at(0); + if (ff.has_ce) { + int ce = importDefSigSpec(ff.sig_ce, timestep-1).at(0); std::vector old_q = importDefSigSpec(ff.sig_q, timestep-1); - int undef_en; + int undef_ce; std::vector undef_old_q; if (model_undef) { - undef_en = importUndefSigSpec(ff.sig_en, timestep-1).at(0); + undef_ce = importUndefSigSpec(ff.sig_ce, timestep-1).at(0); undef_old_q = importUndefSigSpec(ff.sig_q, timestep-1); } - if (ff.pol_en) - std::tie(d, undef_d) = mux(en, undef_en, old_q, undef_old_q, d, undef_d); + if (ff.pol_ce) + std::tie(d, undef_d) = mux(ce, undef_ce, old_q, undef_old_q, d, undef_d); else - std::tie(d, undef_d) = mux(en, undef_en, d, undef_d, old_q, undef_old_q); + std::tie(d, undef_d) = mux(ce, undef_ce, d, undef_d, old_q, undef_old_q); } - if (ff.has_srst && !(ff.has_en && ff.ce_over_srst)) { + if (ff.has_srst && !(ff.has_ce && ff.ce_over_srst)) { int srst = importDefSigSpec(ff.sig_srst, timestep-1).at(0); std::vector rval = importDefSigSpec(ff.val_srst, timestep-1); int undef_srst; diff --git a/passes/memory/memory_dff.cc b/passes/memory/memory_dff.cc index 21962c238..b87ecdd99 100644 --- a/passes/memory/memory_dff.cc +++ b/passes/memory/memory_dff.cc @@ -71,9 +71,9 @@ struct MemQueryCache // port_ren is an upper bound on when we care about the value fetched // from memory this cycle. int ren = ezSAT::CONST_TRUE; - if (ff.has_en) { - ren = qcsat.importSigBit(ff.sig_en); - if (!ff.pol_en) + if (ff.has_ce) { + ren = qcsat.importSigBit(ff.sig_ce); + if (!ff.pol_ce) ren = qcsat.ez->NOT(ren); } if (ff.has_srst) { @@ -347,6 +347,10 @@ struct MemoryDffWorker log("output latches are not supported.\n"); return; } + if (ff.has_aload) { + log("output FF has async load, not supported.\n"); + return; + } if (ff.has_sr) { // Latches and FFs with SR are not supported. log("output FF has both set and reset, not supported.\n"); @@ -491,8 +495,8 @@ struct MemoryDffWorker log("merging output FF to cell.\n"); merger.remove_output_ff(bits); - if (ff.has_en && !ff.pol_en) - ff.sig_en = module->LogicNot(NEW_ID, ff.sig_en); + if (ff.has_ce && !ff.pol_ce) + ff.sig_ce = module->LogicNot(NEW_ID, ff.sig_ce); if (ff.has_arst && !ff.pol_arst) ff.sig_arst = module->LogicNot(NEW_ID, ff.sig_arst); if (ff.has_srst && !ff.pol_srst) @@ -500,8 +504,8 @@ struct MemoryDffWorker port.clk = ff.sig_clk; port.clk_enable = true; port.clk_polarity = ff.pol_clk; - if (ff.has_en) - port.en = ff.sig_en; + if (ff.has_ce) + port.en = ff.sig_ce; else port.en = State::S1; if (ff.has_arst) { @@ -551,6 +555,10 @@ struct MemoryDffWorker log("address latches are not supported.\n"); return; } + if (ff.has_aload) { + log("address FF has async load, not supported.\n"); + return; + } if (ff.has_sr || ff.has_arst) { log("address FF has async set and/or reset, not supported.\n"); return; diff --git a/passes/opt/opt_dff.cc b/passes/opt/opt_dff.cc index ddf08392b..0e25484b8 100644 --- a/passes/opt/opt_dff.cc +++ b/passes/opt/opt_dff.cc @@ -382,6 +382,69 @@ struct OptDffWorker } } + if (ff.has_aload) { + if (ff.sig_aload == (ff.pol_aload ? State::S0 : State::S1) || (!opt.keepdc && ff.sig_aload == State::Sx)) { + // Always-inactive enable — remove. + log("Removing never-active async load on %s (%s) from module %s.\n", + log_id(cell), log_id(cell->type), log_id(module)); + ff.has_aload = false; + changed = true; + } else if (ff.sig_aload == (ff.pol_aload ? State::S1 : State::S0)) { + // Always-active enable. Make a comb circuit, nuke the FF/latch. + log("Handling always-active async load 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_ad, State::S1, ff.sig_set); + else + tmp = module->MuxGate(NEW_ID, State::S1, ff.sig_ad, 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_ad, ff.sig_set); + else + tmp = module->Or(NEW_ID, ff.sig_ad, 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_ad, ff.val_arst[0], ff.sig_arst, ff.sig_q); + else + module->addMuxGate(NEW_ID, ff.val_arst[0], ff.sig_ad, ff.sig_arst, ff.sig_q); + } else { + if (ff.pol_arst) + module->addMux(NEW_ID, ff.sig_ad, ff.val_arst, ff.sig_arst, ff.sig_q); + else + module->addMux(NEW_ID, ff.val_arst, ff.sig_ad, ff.sig_arst, ff.sig_q); + } + } else { + module->connect(ff.sig_q, ff.sig_ad); + } + did_something = true; + continue; + } else if (ff.sig_ad.is_fully_const() && !ff.has_arst && !ff.has_sr) { + log("Changing const-value async load to async reset on %s (%s) from module %s.\n", + log_id(cell), log_id(cell->type), log_id(module)); + ff.has_arst = true; + ff.has_aload = false; + ff.sig_arst = ff.sig_aload; + ff.pol_arst = ff.pol_aload; + ff.val_arst = ff.sig_ad.as_const(); + changed = true; + } + } + if (ff.has_arst) { if (ff.sig_arst == (ff.pol_arst ? State::S0 : State::S1)) { // Always-inactive reset — remove. @@ -414,111 +477,63 @@ struct OptDffWorker 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; + ff.has_ce = false; + ff.sig_d = ff.val_srst; changed = true; } } - if (ff.has_en) { - if (ff.sig_en == (ff.pol_en ? State::S0 : State::S1) || (!opt.keepdc && ff.sig_en == State::Sx)) { + if (ff.has_ce) { + if (ff.sig_ce == (ff.pol_ce ? State::S0 : State::S1) || (!opt.keepdc && ff.sig_ce == State::Sx)) { // Always-inactive enable — remove. - if (ff.has_clk && ff.has_srst && !ff.ce_over_srst) { + if (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.pol_ce = ff.pol_srst; + ff.sig_ce = ff.sig_srst; ff.has_srst = false; - ff.sig_d = ff.val_d = ff.val_srst; - ff.d_is_const = true; + ff.sig_d = ff.val_srst; 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; + // The D input path is effectively useless, so remove it (this will be a D latch, SR latch, or a const driver). + ff.has_ce = ff.has_clk = ff.has_srst = 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; - } + } else if (ff.sig_ce == (ff.pol_ce ? State::S1 : State::S0)) { + // Always-active enable. Just remove it. + // 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_ce = false; + changed = true; } } 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). + // Const clock — the D input path is effectively useless, so remove it (this will be a 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; + ff.has_ce = ff.has_clk = ff.has_srst = false; changed = true; } } - if (ff.has_d && ff.sig_d == ff.sig_q) { + if ((ff.has_clk || ff.has_gclk) && 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.has_ce && ff.ce_over_srst) { + if (!ff.pol_ce) { if (ff.is_fine) - ff.sig_en = module->NotGate(NEW_ID, ff.sig_en); + ff.sig_ce = module->NotGate(NEW_ID, ff.sig_ce); else - ff.sig_en = module->Not(NEW_ID, ff.sig_en); + ff.sig_ce = module->Not(NEW_ID, ff.sig_ce); } if (!ff.pol_srst) { if (ff.is_fine) @@ -527,28 +542,34 @@ struct OptDffWorker 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); + ff.sig_ce = module->AndGate(NEW_ID, ff.sig_ce, ff.sig_srst); else - ff.sig_en = module->And(NEW_ID, ff.sig_en, ff.sig_srst); - ff.pol_en = true; + ff.sig_ce = module->And(NEW_ID, ff.sig_ce, ff.sig_srst); + ff.pol_ce = true; } else { - ff.pol_en = ff.pol_srst; - ff.sig_en = ff.sig_srst; + ff.pol_ce = ff.pol_srst; + ff.sig_ce = ff.sig_srst; } - ff.has_en = true; + ff.has_ce = true; ff.has_srst = false; - ff.sig_d = ff.val_d = ff.val_srst; - ff.d_is_const = true; + ff.sig_d = ff.val_srst; 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; + ff.has_clk = ff.has_ce = ff.has_clk = false; changed = true; } } + if (ff.has_aload && !ff.has_clk && ff.sig_ad == ff.sig_q) { + log("Handling AD = Q on %s (%s) from module %s (removing async load path).\n", + log_id(cell), log_id(cell->type), log_id(module)); + ff.has_aload = false; + changed = true; + } + // Now check if any bit can be replaced by a constant. pool removed_sigbits; for (int i = 0; i < ff.width; i++) { @@ -565,7 +586,7 @@ struct OptDffWorker } if (val == State::Sm) continue; - if (ff.has_d) { + if (ff.has_clk || ff.has_gclk) { if (!ff.sig_d[i].wire) { val = combine_const(val, ff.sig_d[i].data); if (val == State::Sm) @@ -593,6 +614,34 @@ struct OptDffWorker continue; } } + if (ff.has_aload) { + if (!ff.sig_ad[i].wire) { + val = combine_const(val, ff.sig_ad[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 (!modwalker.has_drivers(ff.sig_ad.extract(i))) + continue; + if (val != State::S0 && val != State::S1) + continue; + + int init_sat_pi = qcsat.importSigBit(val); + int q_sat_pi = qcsat.importSigBit(ff.sig_q[i]); + int d_sat_pi = qcsat.importSigBit(ff.sig_ad[i]); + + qcsat.prepare(); + + // Try to find out whether the register bit can change under some circumstances + bool counter_example_found = qcsat.ez->solve(qcsat.ez->IFF(q_sat_pi, init_sat_pi), qcsat.ez->NOT(qcsat.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)); @@ -616,7 +665,7 @@ struct OptDffWorker // 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) { + if (!ff.has_arst && !ff.has_sr && (!ff.has_srst || !ff.has_ce || ff.ce_over_srst) && !opt.nosdff) { // Try to merge sync resets. std::map> groups; std::vector remaining_indices; @@ -677,7 +726,7 @@ struct OptDffWorker new_ff.has_srst = true; new_ff.sig_srst = srst.first; new_ff.pol_srst = srst.second; - if (new_ff.has_en) + if (new_ff.has_ce) new_ff.ce_over_srst = true; Cell *new_cell = new_ff.emit(module, NEW_ID); if (new_cell) @@ -695,7 +744,7 @@ struct OptDffWorker changed = true; } } - if ((!ff.has_srst || !ff.has_en || !ff.ce_over_srst) && !opt.nodffe) { + if ((!ff.has_srst || !ff.has_ce || !ff.ce_over_srst) && !opt.nodffe) { // Try to merge enables. std::map, std::vector> groups; std::vector remaining_indices; @@ -725,8 +774,8 @@ struct OptDffWorker 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)); + if (ff.has_ce) + enables.insert(ctrl_t(ff.sig_ce, ff.pol_ce)); simplify_patterns(patterns); groups[std::make_pair(patterns, enables)].push_back(i); } else @@ -737,9 +786,9 @@ struct OptDffWorker 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.has_ce = true; + new_ff.sig_ce = en.first; + new_ff.pol_ce = en.second; new_ff.ce_over_srst = false; Cell *new_cell = new_ff.emit(module, NEW_ID); if (new_cell) diff --git a/passes/sat/async2sync.cc b/passes/sat/async2sync.cc index a2b51677e..f1b93d084 100644 --- a/passes/sat/async2sync.cc +++ b/passes/sat/async2sync.cc @@ -41,8 +41,6 @@ struct Async2syncPass : public Pass { log("reset value in the next cycle regardless of the data-in value at the time of\n"); log("the clock edge.\n"); log("\n"); - log("Currently only $adff, $dffsr, and $dlatch cells are supported by this pass.\n"); - log("\n"); } void execute(std::vector args, RTLIL::Design *design) override { @@ -74,14 +72,11 @@ struct Async2syncPass : public Pass { FfData ff(&initvals, cell); // Skip for $_FF_ and $ff cells. - if (ff.has_d && !ff.has_clk && !ff.has_en) + if (ff.has_gclk) continue; if (ff.has_clk) { - if (!ff.has_sr && !ff.has_arst) - continue; - if (ff.has_sr) { ff.unmap_ce_srst(module); @@ -128,6 +123,39 @@ struct Async2syncPass : public Pass { ff.sig_d = new_d; ff.sig_q = new_q; ff.has_sr = false; + } else if (ff.has_aload) { + ff.unmap_ce_srst(module); + + log("Replacing %s.%s (%s): ALOAD=%s, AD=%s, D=%s, Q=%s\n", + log_id(module), log_id(cell), log_id(cell->type), + log_signal(ff.sig_aload), log_signal(ff.sig_ad), 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); + + if (ff.pol_aload) { + if (!ff.is_fine) { + module->addMux(NEW_ID, new_q, ff.sig_ad, ff.sig_aload, ff.sig_q); + module->addMux(NEW_ID, ff.sig_d, ff.sig_ad, ff.sig_aload, new_d); + } else { + module->addMuxGate(NEW_ID, new_q, ff.sig_ad, ff.sig_aload, ff.sig_q); + module->addMuxGate(NEW_ID, ff.sig_d, ff.sig_ad, ff.sig_aload, new_d); + } + } else { + if (!ff.is_fine) { + module->addMux(NEW_ID, ff.sig_ad, new_q, ff.sig_aload, ff.sig_q); + module->addMux(NEW_ID, ff.sig_ad, ff.sig_d, ff.sig_aload, new_d); + } else { + module->addMuxGate(NEW_ID, ff.sig_ad, new_q, ff.sig_aload, ff.sig_q); + module->addMuxGate(NEW_ID, ff.sig_ad, ff.sig_d, ff.sig_aload, new_d); + } + } + + ff.sig_d = new_d; + ff.sig_q = new_q; + ff.has_aload = false; } else if (ff.has_arst) { ff.unmap_srst(module); @@ -154,9 +182,12 @@ struct Async2syncPass : public Pass { ff.sig_q = new_q; ff.has_arst = false; ff.has_srst = true; + ff.ce_over_srst = false; ff.val_srst = ff.val_arst; ff.sig_srst = ff.sig_arst; ff.pol_srst = ff.pol_arst; + } else { + continue; } } else @@ -164,25 +195,25 @@ struct Async2syncPass : public Pass { // 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(ff.sig_en), log_signal(ff.sig_d), log_signal(ff.sig_q)); + log_signal(ff.sig_aload), log_signal(ff.sig_ad), 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) { + if (ff.has_aload) { new_d = module->addWire(NEW_ID, ff.width); - if (ff.pol_en) { + if (ff.pol_aload) { if (!ff.is_fine) - module->addMux(NEW_ID, new_q, ff.sig_d, ff.sig_en, new_d); + module->addMux(NEW_ID, new_q, ff.sig_ad, ff.sig_aload, new_d); else - module->addMuxGate(NEW_ID, new_q, ff.sig_d, ff.sig_en, new_d); + module->addMuxGate(NEW_ID, new_q, ff.sig_ad, ff.sig_aload, new_d); } else { if (!ff.is_fine) - module->addMux(NEW_ID, ff.sig_d, new_q, ff.sig_en, new_d); + module->addMux(NEW_ID, ff.sig_ad, new_q, ff.sig_aload, new_d); else - module->addMuxGate(NEW_ID, ff.sig_d, new_q, ff.sig_en, new_d); + module->addMuxGate(NEW_ID, ff.sig_ad, new_q, ff.sig_aload, new_d); } } else { new_d = new_q; @@ -231,10 +262,10 @@ struct Async2syncPass : public Pass { ff.sig_d = new_d; ff.sig_q = new_q; - ff.has_en = false; + ff.has_aload = false; ff.has_arst = false; ff.has_sr = false; - ff.has_d = true; + ff.has_gclk = true; } IdString name = cell->name; diff --git a/passes/sat/clk2fflogic.cc b/passes/sat/clk2fflogic.cc index 062083972..d90206b46 100644 --- a/passes/sat/clk2fflogic.cc +++ b/passes/sat/clk2fflogic.cc @@ -148,7 +148,7 @@ struct Clk2fflogicPass : public Pass { if (RTLIL::builtin_ff_cell_types().count(cell->type)) { FfData ff(&initvals, cell); - if (ff.has_d && !ff.has_clk && !ff.has_en) { + if (ff.has_gclk) { // Already a $ff or $_FF_ cell. continue; } @@ -202,25 +202,27 @@ struct Clk2fflogicPass : public Pass { 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) { - - 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)); + } else { + if (ff.has_aload) { + 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_aload), log_signal(ff.sig_ad), log_signal(ff.sig_q)); + } else { + // $sr. + 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)); + } + qval = past_q; + } - SigSpec sig_en = wrap_async_control(module, ff.sig_en, ff.pol_en); + if (ff.has_aload) { + SigSpec sig_aload = wrap_async_control(module, ff.sig_aload, ff.pol_aload); if (!ff.is_fine) - qval = module->Mux(NEW_ID, past_q, ff.sig_d, sig_en); + qval = module->Mux(NEW_ID, qval, ff.sig_ad, sig_aload); else - qval = module->MuxGate(NEW_ID, past_q, ff.sig_d, sig_en); - } else { - - 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)); - - qval = past_q; + qval = module->MuxGate(NEW_ID, qval, ff.sig_ad, sig_aload); } if (ff.has_sr) { diff --git a/passes/techmap/dffunmap.cc b/passes/techmap/dffunmap.cc index fb107ff75..583185e75 100644 --- a/passes/techmap/dffunmap.cc +++ b/passes/techmap/dffunmap.cc @@ -84,7 +84,7 @@ struct DffunmapPass : public Pass { continue; if (ce_only) { - if (!ff.has_en) + if (!ff.has_ce) continue; ff.unmap_ce(mod); } else if (srst_only) { @@ -92,7 +92,7 @@ struct DffunmapPass : public Pass { continue; ff.unmap_srst(mod); } else { - if (!ff.has_en && !ff.has_srst) + if (!ff.has_ce && !ff.has_srst) continue; ff.unmap_ce_srst(mod); }