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
This commit is contained in:
Marcelina Kościelnicka 2021-10-01 23:50:48 +02:00
parent ec2b5548fe
commit 63b9df8693
10 changed files with 565 additions and 325 deletions

View File

@ -1398,7 +1398,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
FfData ff(nullptr, cell); FfData ff(nullptr, cell);
// $ff / $_FF_ cell: not supported. // $ff / $_FF_ cell: not supported.
if (ff.has_d && !ff.has_clk && !ff.has_en) if (ff.has_gclk)
return false; return false;
std::string reg_name = cellname(cell); 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++) for (int i = 0; i < chunks; i++)
{ {
SigSpec sig_d; SigSpec sig_d, sig_ad;
Const val_arst, val_srst; 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) { if (chunky) {
reg_bit_name = stringf("%s[%d]", reg_name.c_str(), i); 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]; sig_d = ff.sig_d[i];
if (ff.has_aload)
sig_ad = ff.sig_ad[i];
} else { } else {
reg_bit_name = reg_name; 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) if (ff.has_arst)
val_arst = chunky ? ff.val_arst[i] : ff.val_arst; val_arst = chunky ? ff.val_arst[i] : ff.val_arst;
@ -1437,6 +1439,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
val_srst = chunky ? ff.val_srst[i] : ff.val_srst; 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 there are constants in the sensitivity list, replace them with an intermediate wire
if (ff.has_clk) {
if (ff.has_sr) { if (ff.has_sr) {
if (ff.sig_set[i].wire == NULL) if (ff.sig_set[i].wire == NULL)
{ {
@ -1453,13 +1456,22 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
f << stringf(";\n"); f << stringf(";\n");
} }
} else if (ff.has_arst) { } else if (ff.has_arst) {
if (ff.sig_arst[i].wire == NULL) if (ff.sig_arst[0].wire == NULL)
{ {
sig_arst_name = next_auto_id(); sig_arst_name = next_auto_id();
f << stringf("%s" "wire %s = ", indent.c_str(), sig_arst_name.c_str()); f << stringf("%s" "wire %s = ", indent.c_str(), sig_arst_name.c_str());
dump_const(f, ff.sig_arst[i].data); dump_const(f, ff.sig_arst[0].data);
f << stringf(";\n"); 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");
}
}
} }
dump_attributes(f, indent, cell->attributes); dump_attributes(f, indent, cell->attributes);
@ -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()); f << stringf("%s", sig_clr_name.c_str());
else else
dump_sigspec(f, ff.sig_clr[i]); dump_sigspec(f, ff.sig_clr[i]);
} else if (ff.has_arst) { } else if (ff.has_arst) {
f << stringf(", %sedge ", ff.pol_arst ? "pos" : "neg"); 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()); f << stringf("%s", sig_arst_name.c_str());
else else
dump_sigspec(f, ff.sig_arst); 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"); 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()); f << stringf("%s" " else ", indent.c_str());
} else if (ff.has_arst) { } else if (ff.has_arst) {
f << stringf("if (%s", ff.pol_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()); f << stringf("%s", sig_arst_name.c_str());
else else
dump_sigspec(f, ff.sig_arst); 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); dump_sigspec(f, val_arst);
f << stringf(";\n"); f << stringf(";\n");
f << stringf("%s" " else ", indent.c_str()); 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) { if (ff.has_srst && ff.has_ce && ff.ce_over_srst) {
f << stringf("if (%s", ff.pol_en ? "" : "!"); f << stringf("if (%s", ff.pol_ce ? "" : "!");
dump_sigspec(f, ff.sig_en); dump_sigspec(f, ff.sig_ce);
f << stringf(")\n"); f << stringf(")\n");
f << stringf("%s" " if (%s", indent.c_str(), ff.pol_srst ? "" : "!"); f << stringf("%s" " if (%s", indent.c_str(), ff.pol_srst ? "" : "!");
dump_sigspec(f, ff.sig_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(";\n");
f << stringf("%s" " else ", indent.c_str()); f << stringf("%s" " else ", indent.c_str());
} }
if (ff.has_en) { if (ff.has_ce) {
f << stringf("if (%s", ff.pol_en ? "" : "!"); f << stringf("if (%s", ff.pol_ce ? "" : "!");
dump_sigspec(f, ff.sig_en); dump_sigspec(f, ff.sig_ce);
f << stringf(") "); 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 ? "" : "!"); f << stringf("%s" " else if (%s", indent.c_str(), ff.pol_set ? "" : "!");
dump_sigspec(f, ff.sig_set[i]); dump_sigspec(f, ff.sig_set[i]);
f << stringf(") %s = 1'b1;\n", reg_bit_name.c_str()); 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()); f << stringf("%s" " else ", indent.c_str());
} else if (ff.has_arst) { } else if (ff.has_arst) {
f << stringf("if (%s", ff.pol_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()); f << stringf(") %s = ", reg_bit_name.c_str());
dump_sigspec(f, val_arst); dump_sigspec(f, val_arst);
f << stringf(";\n"); f << stringf(";\n");
if (ff.has_d) if (ff.has_aload)
f << stringf("%s" " else ", indent.c_str()); f << stringf("%s" " else ", indent.c_str());
} }
if (ff.has_d) { if (ff.has_aload) {
f << stringf("if (%s", ff.pol_en ? "" : "!"); f << stringf("if (%s", ff.pol_aload ? "" : "!");
dump_sigspec(f, ff.sig_en); dump_sigspec(f, ff.sig_aload);
f << stringf(") %s = ", reg_bit_name.c_str()); f << stringf(") %s = ", reg_bit_name.c_str());
dump_sigspec(f, sig_d); dump_sigspec(f, sig_ad);
f << stringf(";\n"); f << stringf(";\n");
} }
} }

View File

@ -25,55 +25,141 @@
YOSYS_NAMESPACE_BEGIN 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 { struct FfData {
FfInitVals *initvals; FfInitVals *initvals;
// The FF output.
SigSpec sig_q; SigSpec sig_q;
// The sync data input, present if has_clk or has_gclk.
SigSpec sig_d; 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_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; SigSpec sig_arst;
// The sync reset, preset if has_srst.
SigSpec sig_srst; SigSpec sig_srst;
// The async clear (per-lane), present if has_sr.
SigSpec sig_clr; SigSpec sig_clr;
// The async set (per-lane), present if has_sr.
SigSpec sig_set; SigSpec sig_set;
bool has_d; // True if this is a clocked (edge-sensitive) flip-flop.
bool has_clk; 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; 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; 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; 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; 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; bool is_fine;
// Polarities, corresponding to sig_*. True means active-high, false
// means active-low.
bool pol_clk; bool pol_clk;
bool pol_en; bool pol_ce;
bool pol_aload;
bool pol_arst; bool pol_arst;
bool pol_srst; bool pol_srst;
bool pol_clr; bool pol_clr;
bool pol_set; bool pol_set;
// The value loaded by sig_arst.
Const val_arst; Const val_arst;
// The value loaded by sig_srst.
Const val_srst; Const val_srst;
// The initial value at power-up.
Const val_init; Const val_init;
Const val_d; // The FF data width in bits.
bool d_is_const;
int width; int width;
dict<IdString, Const> attributes; dict<IdString, Const> attributes;
FfData(FfInitVals *initvals = nullptr, Cell *cell = nullptr) : initvals(initvals) { FfData(FfInitVals *initvals = nullptr, Cell *cell = nullptr) : initvals(initvals) {
width = 0; width = 0;
has_d = true;
has_clk = false; has_clk = false;
has_en = false; has_gclk = false;
has_ce = false;
has_aload = false;
has_srst = false; has_srst = false;
has_arst = false; has_arst = false;
has_sr = false; has_sr = false;
ce_over_srst = false; ce_over_srst = false;
is_fine = false; is_fine = false;
pol_clk = false; pol_clk = false;
pol_en = false; pol_aload = false;
pol_ce = false;
pol_arst = false; pol_arst = false;
pol_srst = false; pol_srst = false;
pol_clr = false; pol_clr = false;
pol_set = false; pol_set = false;
d_is_const = false;
if (!cell) if (!cell)
return; return;
@ -88,20 +174,26 @@ struct FfData {
std::string type_str = cell->type.str(); 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.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)) { if (cell->type == ID($ff)) {
has_d = false; has_gclk = true;
} else {
sig_d = cell->getPort(ID::D); sig_d = cell->getPort(ID::D);
} } else if (cell->type == ID($sr)) {
if (!cell->type.in(ID($ff), ID($dlatch), ID($adlatch), ID($dlatchsr), 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; has_clk = true;
sig_clk = cell->getPort(ID::CLK); sig_clk = cell->getPort(ID::CLK);
pol_clk = cell->getParam(ID::CLK_POLARITY).as_bool(); 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))) { if (cell->type.in(ID($dffe), ID($dffsre), ID($adffe), ID($sdffe), ID($sdffce))) {
has_en = true; has_ce = true;
sig_en = cell->getPort(ID::EN); sig_ce = cell->getPort(ID::EN);
pol_en = cell->getParam(ID::EN_POLARITY).as_bool(); pol_ce = cell->getParam(ID::EN_POLARITY).as_bool();
} }
if (cell->type.in(ID($dffsr), ID($dffsre), ID($dlatchsr), ID($sr))) { if (cell->type.in(ID($dffsr), ID($dffsre), ID($dlatchsr), ID($sr))) {
has_sr = true; has_sr = true;
@ -125,10 +217,10 @@ struct FfData {
} }
} else if (cell->type == ID($_FF_)) { } else if (cell->type == ID($_FF_)) {
is_fine = true; is_fine = true;
has_gclk = true;
sig_d = cell->getPort(ID::D); sig_d = cell->getPort(ID::D);
} else if (type_str.substr(0, 5) == "$_SR_") { } else if (type_str.substr(0, 5) == "$_SR_") {
is_fine = true; is_fine = true;
has_d = false;
has_sr = true; has_sr = true;
pol_set = type_str[5] == 'P'; pol_set = type_str[5] == 'P';
pol_clr = type_str[6] == 'P'; pol_clr = type_str[6] == 'P';
@ -146,9 +238,9 @@ struct FfData {
has_clk = true; has_clk = true;
pol_clk = type_str[7] == 'P'; pol_clk = type_str[7] == 'P';
sig_clk = cell->getPort(ID::C); sig_clk = cell->getPort(ID::C);
has_en = true; has_ce = true;
pol_en = type_str[8] == 'P'; pol_ce = type_str[8] == 'P';
sig_en = cell->getPort(ID::E); sig_ce = cell->getPort(ID::E);
} else if (type_str.substr(0, 6) == "$_DFF_" && type_str.size() == 10) { } else if (type_str.substr(0, 6) == "$_DFF_" && type_str.size() == 10) {
is_fine = true; is_fine = true;
sig_d = cell->getPort(ID::D); sig_d = cell->getPort(ID::D);
@ -169,9 +261,9 @@ struct FfData {
pol_arst = type_str[8] == 'P'; pol_arst = type_str[8] == 'P';
sig_arst = cell->getPort(ID::R); sig_arst = cell->getPort(ID::R);
val_arst = type_str[9] == '1' ? State::S1 : State::S0; val_arst = type_str[9] == '1' ? State::S1 : State::S0;
has_en = true; has_ce = true;
pol_en = type_str[10] == 'P'; pol_ce = type_str[10] == 'P';
sig_en = cell->getPort(ID::E); sig_ce = cell->getPort(ID::E);
} else if (type_str.substr(0, 8) == "$_DFFSR_" && type_str.size() == 12) { } else if (type_str.substr(0, 8) == "$_DFFSR_" && type_str.size() == 12) {
is_fine = true; is_fine = true;
sig_d = cell->getPort(ID::D); sig_d = cell->getPort(ID::D);
@ -194,9 +286,9 @@ struct FfData {
pol_clr = type_str[11] == 'P'; pol_clr = type_str[11] == 'P';
sig_set = cell->getPort(ID::S); sig_set = cell->getPort(ID::S);
sig_clr = cell->getPort(ID::R); sig_clr = cell->getPort(ID::R);
has_en = true; has_ce = true;
pol_en = type_str[12] == 'P'; pol_ce = type_str[12] == 'P';
sig_en = cell->getPort(ID::E); sig_ce = cell->getPort(ID::E);
} else if (type_str.substr(0, 7) == "$_SDFF_" && type_str.size() == 11) { } else if (type_str.substr(0, 7) == "$_SDFF_" && type_str.size() == 11) {
is_fine = true; is_fine = true;
sig_d = cell->getPort(ID::D); sig_d = cell->getPort(ID::D);
@ -217,9 +309,9 @@ struct FfData {
pol_srst = type_str[9] == 'P'; pol_srst = type_str[9] == 'P';
sig_srst = cell->getPort(ID::R); sig_srst = cell->getPort(ID::R);
val_srst = type_str[10] == '1' ? State::S1 : State::S0; val_srst = type_str[10] == '1' ? State::S1 : State::S0;
has_en = true; has_ce = true;
pol_en = type_str[11] == 'P'; pol_ce = type_str[11] == 'P';
sig_en = cell->getPort(ID::E); sig_ce = cell->getPort(ID::E);
} else if (type_str.substr(0, 9) == "$_SDFFCE_" && type_str.size() == 14) { } else if (type_str.substr(0, 9) == "$_SDFFCE_" && type_str.size() == 14) {
is_fine = true; is_fine = true;
sig_d = cell->getPort(ID::D); sig_d = cell->getPort(ID::D);
@ -230,32 +322,35 @@ struct FfData {
pol_srst = type_str[10] == 'P'; pol_srst = type_str[10] == 'P';
sig_srst = cell->getPort(ID::R); sig_srst = cell->getPort(ID::R);
val_srst = type_str[11] == '1' ? State::S1 : State::S0; val_srst = type_str[11] == '1' ? State::S1 : State::S0;
has_en = true; has_ce = true;
pol_en = type_str[12] == 'P'; pol_ce = type_str[12] == 'P';
sig_en = cell->getPort(ID::E); sig_ce = cell->getPort(ID::E);
ce_over_srst = true; ce_over_srst = true;
} else if (type_str.substr(0, 9) == "$_DLATCH_" && type_str.size() == 11) { } else if (type_str.substr(0, 9) == "$_DLATCH_" && type_str.size() == 11) {
is_fine = true; is_fine = true;
sig_d = cell->getPort(ID::D); has_aload = true;
has_en = true; sig_ad = cell->getPort(ID::D);
pol_en = type_str[9] == 'P'; has_aload = true;
sig_en = cell->getPort(ID::E); pol_aload = type_str[9] == 'P';
sig_aload = cell->getPort(ID::E);
} else if (type_str.substr(0, 9) == "$_DLATCH_" && type_str.size() == 13) { } else if (type_str.substr(0, 9) == "$_DLATCH_" && type_str.size() == 13) {
is_fine = true; is_fine = true;
sig_d = cell->getPort(ID::D); has_aload = true;
has_en = true; sig_ad = cell->getPort(ID::D);
pol_en = type_str[9] == 'P'; has_aload = true;
sig_en = cell->getPort(ID::E); pol_aload = type_str[9] == 'P';
sig_aload = cell->getPort(ID::E);
has_arst = true; has_arst = true;
pol_arst = type_str[10] == 'P'; pol_arst = type_str[10] == 'P';
sig_arst = cell->getPort(ID::R); sig_arst = cell->getPort(ID::R);
val_arst = type_str[11] == '1' ? State::S1 : State::S0; val_arst = type_str[11] == '1' ? State::S1 : State::S0;
} else if (type_str.substr(0, 11) == "$_DLATCHSR_" && type_str.size() == 15) { } else if (type_str.substr(0, 11) == "$_DLATCHSR_" && type_str.size() == 15) {
is_fine = true; is_fine = true;
sig_d = cell->getPort(ID::D); has_aload = true;
has_en = true; sig_ad = cell->getPort(ID::D);
pol_en = type_str[11] == 'P'; has_aload = true;
sig_en = cell->getPort(ID::E); pol_aload = type_str[11] == 'P';
sig_aload = cell->getPort(ID::E);
has_sr = true; has_sr = true;
pol_set = type_str[12] == 'P'; pol_set = type_str[12] == 'P';
pol_clr = type_str[13] == 'P'; pol_clr = type_str[13] == 'P';
@ -264,17 +359,13 @@ struct FfData {
} else { } else {
log_assert(0); log_assert(0);
} }
if (has_d && sig_d.is_fully_const()) { if (has_aload && !has_clk && !has_sr && !has_arst && sig_ad.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. // Plain D latches with const D treated specially.
has_en = has_d = false; has_aload = false;
has_arst = true; has_arst = true;
sig_arst = sig_en; sig_arst = sig_aload;
pol_arst = pol_en; pol_arst = pol_aload;
val_arst = val_d; val_arst = sig_ad.as_const();
}
} }
} }
@ -282,19 +373,22 @@ struct FfData {
FfData slice(const std::vector<int> &bits) { FfData slice(const std::vector<int> &bits) {
FfData res(initvals); FfData res(initvals);
res.sig_clk = sig_clk; 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_arst = sig_arst;
res.sig_srst = sig_srst; res.sig_srst = sig_srst;
res.has_d = has_d;
res.has_clk = has_clk; 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_arst = has_arst;
res.has_srst = has_srst; res.has_srst = has_srst;
res.has_sr = has_sr; res.has_sr = has_sr;
res.ce_over_srst = ce_over_srst; res.ce_over_srst = ce_over_srst;
res.is_fine = is_fine; res.is_fine = is_fine;
res.pol_clk = pol_clk; 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_arst = pol_arst;
res.pol_srst = pol_srst; res.pol_srst = pol_srst;
res.pol_clr = pol_clr; res.pol_clr = pol_clr;
@ -302,8 +396,10 @@ struct FfData {
res.attributes = attributes; res.attributes = attributes;
for (int i : bits) { for (int i : bits) {
res.sig_q.append(sig_q[i]); res.sig_q.append(sig_q[i]);
if (has_d) if (has_clk || has_gclk)
res.sig_d.append(sig_d[i]); res.sig_d.append(sig_d[i]);
if (has_aload)
res.sig_ad.append(sig_ad[i]);
if (has_sr) { if (has_sr) {
res.sig_clr.append(sig_clr[i]); res.sig_clr.append(sig_clr[i]);
res.sig_set.append(sig_set[i]); res.sig_set.append(sig_set[i]);
@ -316,39 +412,34 @@ struct FfData {
res.val_init.bits.push_back(val_init[i]); res.val_init.bits.push_back(val_init[i]);
} }
res.width = GetSize(res.sig_q); 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; return res;
} }
void unmap_ce(Module *module) { void unmap_ce(Module *module) {
if (!has_en) if (!has_ce)
return; return;
log_assert(has_clk); log_assert(has_clk);
if (has_srst && ce_over_srst) if (has_srst && ce_over_srst)
unmap_srst(module); unmap_srst(module);
if (!is_fine) { if (!is_fine) {
if (pol_en) if (pol_ce)
sig_d = module->Mux(NEW_ID, sig_q, sig_d, sig_en); sig_d = module->Mux(NEW_ID, sig_q, sig_d, sig_ce);
else 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 { } else {
if (pol_en) if (pol_ce)
sig_d = module->MuxGate(NEW_ID, sig_q, sig_d, sig_en); sig_d = module->MuxGate(NEW_ID, sig_q, sig_d, sig_ce);
else 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) { void unmap_srst(Module *module) {
if (!has_srst) if (!has_srst)
return; return;
if (has_en && !ce_over_srst) if (has_ce && !ce_over_srst)
unmap_ce(module); unmap_ce(module);
if (!is_fine) { if (!is_fine) {
@ -373,14 +464,14 @@ struct FfData {
Cell *emit(Module *module, IdString name) { Cell *emit(Module *module, IdString name) {
if (!width) if (!width)
return nullptr; return nullptr;
if (!has_d && !has_sr) { if (!has_aload && !has_clk && !has_gclk && !has_sr) {
if (has_arst) { if (has_arst) {
// Convert this case to a D latch. // Convert this case to a D latch.
has_d = has_en = true; has_aload = true;
has_arst = false; has_arst = false;
sig_d = val_arst; sig_ad = val_arst;
sig_en = sig_arst; sig_aload = sig_arst;
pol_en = pol_arst; pol_aload = pol_arst;
} else { } else {
// No control inputs left. Turn into a const driver. // No control inputs left. Turn into a const driver.
if (initvals) if (initvals)
@ -393,87 +484,93 @@ struct FfData {
initvals->set_init(sig_q, val_init); initvals->set_init(sig_q, val_init);
Cell *cell; Cell *cell;
if (!is_fine) { if (!is_fine) {
if (!has_d) { if (has_gclk) {
log_assert(has_sr); log_assert(!has_clk);
cell = module->addSr(name, sig_set, sig_clr, sig_q, pol_set, pol_clr); log_assert(!has_ce);
} else if (!has_clk && !has_en) { log_assert(!has_aload);
log_assert(!has_arst); log_assert(!has_arst);
log_assert(!has_srst); log_assert(!has_srst);
log_assert(!has_sr); log_assert(!has_sr);
cell = module->addFf(name, sig_d, sig_q); 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) { } else if (!has_clk) {
log_assert(!has_srst); log_assert(!has_srst);
if (has_sr) 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) 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 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 { } else {
if (has_sr) { if (has_sr) {
if (has_en) if (has_ce)
cell = module->addDffsre(name, sig_clk, sig_en, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_en, pol_set, pol_clr); 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 else
cell = module->addDffsr(name, sig_clk, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_set, pol_clr); cell = module->addDffsr(name, sig_clk, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_set, pol_clr);
} else if (has_arst) { } else if (has_arst) {
if (has_en) if (has_ce)
cell = module->addAdffe(name, sig_clk, sig_en, sig_arst, sig_d, sig_q, val_arst, pol_clk, pol_en, pol_arst); cell = module->addAdffe(name, sig_clk, sig_ce, sig_arst, sig_d, sig_q, val_arst, pol_clk, pol_ce, pol_arst);
else else
cell = module->addAdff(name, sig_clk, sig_arst, sig_d, sig_q, val_arst, pol_clk, pol_arst); cell = module->addAdff(name, sig_clk, sig_arst, sig_d, sig_q, val_arst, pol_clk, pol_arst);
} else if (has_srst) { } else if (has_srst) {
if (has_en) if (has_ce)
if (ce_over_srst) 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 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 else
cell = module->addSdff(name, sig_clk, sig_srst, sig_d, sig_q, val_srst, pol_clk, pol_srst); cell = module->addSdff(name, sig_clk, sig_srst, sig_d, sig_q, val_srst, pol_clk, pol_srst);
} else { } else {
if (has_en) if (has_ce)
cell = module->addDffe(name, sig_clk, sig_en, sig_d, sig_q, pol_clk, pol_en); cell = module->addDffe(name, sig_clk, sig_ce, sig_d, sig_q, pol_clk, pol_ce);
else else
cell = module->addDff(name, sig_clk, sig_d, sig_q, pol_clk); cell = module->addDff(name, sig_clk, sig_d, sig_q, pol_clk);
} }
} }
} else { } else {
if (!has_d) { if (has_gclk) {
log_assert(has_sr); log_assert(!has_clk);
cell = module->addSrGate(name, sig_set, sig_clr, sig_q, pol_set, pol_clr); log_assert(!has_ce);
} else if (!has_clk && !has_en) { log_assert(!has_aload);
log_assert(!has_arst); log_assert(!has_arst);
log_assert(!has_srst); log_assert(!has_srst);
log_assert(!has_sr); log_assert(!has_sr);
cell = module->addFfGate(name, sig_d, sig_q); 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) { } else if (!has_clk) {
log_assert(!has_srst); log_assert(!has_srst);
if (has_sr) 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) 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 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 { } else {
if (has_sr) { if (has_sr) {
if (has_en) if (has_ce)
cell = module->addDffsreGate(name, sig_clk, sig_en, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_en, pol_set, pol_clr); 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 else
cell = module->addDffsrGate(name, sig_clk, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_set, pol_clr); cell = module->addDffsrGate(name, sig_clk, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_set, pol_clr);
} else if (has_arst) { } else if (has_arst) {
if (has_en) if (has_ce)
cell = module->addAdffeGate(name, sig_clk, sig_en, sig_arst, sig_d, sig_q, val_arst.as_bool(), pol_clk, pol_en, pol_arst); 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 else
cell = module->addAdffGate(name, sig_clk, sig_arst, sig_d, sig_q, val_arst.as_bool(), pol_clk, pol_arst); cell = module->addAdffGate(name, sig_clk, sig_arst, sig_d, sig_q, val_arst.as_bool(), pol_clk, pol_arst);
} else if (has_srst) { } else if (has_srst) {
if (has_en) if (has_ce)
if (ce_over_srst) 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 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 else
cell = module->addSdffGate(name, sig_clk, sig_srst, sig_d, sig_q, val_srst.as_bool(), pol_clk, pol_srst); cell = module->addSdffGate(name, sig_clk, sig_srst, sig_d, sig_q, val_srst.as_bool(), pol_clk, pol_srst);
} else { } else {
if (has_en) if (has_ce)
cell = module->addDffeGate(name, sig_clk, sig_en, sig_d, sig_q, pol_clk, pol_en); cell = module->addDffeGate(name, sig_clk, sig_ce, sig_d, sig_q, pol_clk, pol_ce);
else else
cell = module->addDffGate(name, sig_clk, sig_d, sig_q, pol_clk); cell = module->addDffGate(name, sig_clk, sig_d, sig_q, pol_clk);
} }

View File

@ -62,22 +62,28 @@ bool FfMergeHelper::find_output_ff(RTLIL::SigSpec sig, FfData &ff, pool<std::pai
FfData cur_ff(initvals, cell); FfData cur_ff(initvals, cell);
log_assert(cur_ff.has_d); // Reject latches and $ff.
if (!cur_ff.has_clk)
return false;
log_assert((*sigmap)(cur_ff.sig_d[idx]) == bit); log_assert((*sigmap)(cur_ff.sig_d[idx]) == bit);
if (!found) { if (!found) {
ff.sig_clk = cur_ff.sig_clk; ff.sig_clk = cur_ff.sig_clk;
ff.sig_en = cur_ff.sig_en; ff.sig_ce = cur_ff.sig_ce;
ff.sig_aload = cur_ff.sig_aload;
ff.sig_srst = cur_ff.sig_srst; ff.sig_srst = cur_ff.sig_srst;
ff.sig_arst = cur_ff.sig_arst; ff.sig_arst = cur_ff.sig_arst;
ff.has_clk = cur_ff.has_clk; ff.has_clk = cur_ff.has_clk;
ff.has_en = cur_ff.has_en; ff.has_ce = cur_ff.has_ce;
ff.has_aload = cur_ff.has_aload;
ff.has_srst = cur_ff.has_srst; ff.has_srst = cur_ff.has_srst;
ff.has_arst = cur_ff.has_arst; ff.has_arst = cur_ff.has_arst;
ff.has_sr = cur_ff.has_sr; ff.has_sr = cur_ff.has_sr;
ff.ce_over_srst = cur_ff.ce_over_srst; ff.ce_over_srst = cur_ff.ce_over_srst;
ff.pol_clk = cur_ff.pol_clk; ff.pol_clk = cur_ff.pol_clk;
ff.pol_en = cur_ff.pol_en; ff.pol_ce = cur_ff.pol_ce;
ff.pol_aload = cur_ff.pol_aload;
ff.pol_arst = cur_ff.pol_arst; ff.pol_arst = cur_ff.pol_arst;
ff.pol_srst = cur_ff.pol_srst; ff.pol_srst = cur_ff.pol_srst;
ff.pol_clr = cur_ff.pol_clr; ff.pol_clr = cur_ff.pol_clr;
@ -85,7 +91,9 @@ bool FfMergeHelper::find_output_ff(RTLIL::SigSpec sig, FfData &ff, pool<std::pai
} else { } else {
if (ff.has_clk != cur_ff.has_clk) if (ff.has_clk != cur_ff.has_clk)
return false; return false;
if (ff.has_en != cur_ff.has_en) if (ff.has_ce != cur_ff.has_ce)
return false;
if (ff.has_aload != cur_ff.has_aload)
return false; return false;
if (ff.has_srst != cur_ff.has_srst) if (ff.has_srst != cur_ff.has_srst)
return false; return false;
@ -99,10 +107,16 @@ bool FfMergeHelper::find_output_ff(RTLIL::SigSpec sig, FfData &ff, pool<std::pai
if (ff.pol_clk != cur_ff.pol_clk) if (ff.pol_clk != cur_ff.pol_clk)
return false; return false;
} }
if (ff.has_en) { if (ff.has_ce) {
if (ff.sig_en != cur_ff.sig_en) if (ff.sig_ce != cur_ff.sig_ce)
return false; return false;
if (ff.pol_en != cur_ff.pol_en) if (ff.pol_ce != cur_ff.pol_ce)
return false;
}
if (ff.has_aload) {
if (ff.sig_aload != cur_ff.sig_aload)
return false;
if (ff.pol_aload != cur_ff.pol_aload)
return false; return false;
} }
if (ff.has_srst) { if (ff.has_srst) {
@ -110,7 +124,7 @@ bool FfMergeHelper::find_output_ff(RTLIL::SigSpec sig, FfData &ff, pool<std::pai
return false; return false;
if (ff.pol_srst != cur_ff.pol_srst) if (ff.pol_srst != cur_ff.pol_srst)
return false; return false;
if (ff.has_en && ff.ce_over_srst != cur_ff.ce_over_srst) if (ff.has_ce && ff.ce_over_srst != cur_ff.ce_over_srst)
return false; return false;
} }
if (ff.has_arst) { if (ff.has_arst) {
@ -129,6 +143,7 @@ bool FfMergeHelper::find_output_ff(RTLIL::SigSpec sig, FfData &ff, pool<std::pai
ff.width++; ff.width++;
ff.sig_d.append(cur_ff.sig_d[idx]); ff.sig_d.append(cur_ff.sig_d[idx]);
ff.sig_ad.append(ff.has_aload ? cur_ff.sig_ad[idx] : State::Sx);
ff.sig_q.append(cur_ff.sig_q[idx]); ff.sig_q.append(cur_ff.sig_q[idx]);
ff.sig_clr.append(ff.has_sr ? cur_ff.sig_clr[idx] : State::S0); ff.sig_clr.append(ff.has_sr ? cur_ff.sig_clr[idx] : State::S0);
ff.sig_set.append(ff.has_sr ? cur_ff.sig_set[idx] : State::S0); ff.sig_set.append(ff.has_sr ? cur_ff.sig_set[idx] : State::S0);
@ -179,28 +194,33 @@ bool FfMergeHelper::find_input_ff(RTLIL::SigSpec sig, FfData &ff, pool<std::pair
if (!found) { if (!found) {
ff.sig_clk = cur_ff.sig_clk; ff.sig_clk = cur_ff.sig_clk;
ff.sig_en = cur_ff.sig_en; ff.sig_ce = cur_ff.sig_ce;
ff.sig_aload = cur_ff.sig_aload;
ff.sig_srst = cur_ff.sig_srst; ff.sig_srst = cur_ff.sig_srst;
ff.sig_arst = cur_ff.sig_arst; ff.sig_arst = cur_ff.sig_arst;
ff.has_d = cur_ff.has_d;
ff.has_clk = cur_ff.has_clk; ff.has_clk = cur_ff.has_clk;
ff.has_en = cur_ff.has_en; ff.has_gclk = cur_ff.has_gclk;
ff.has_ce = cur_ff.has_ce;
ff.has_aload = cur_ff.has_aload;
ff.has_srst = cur_ff.has_srst; ff.has_srst = cur_ff.has_srst;
ff.has_arst = cur_ff.has_arst; ff.has_arst = cur_ff.has_arst;
ff.has_sr = cur_ff.has_sr; ff.has_sr = cur_ff.has_sr;
ff.ce_over_srst = cur_ff.ce_over_srst; ff.ce_over_srst = cur_ff.ce_over_srst;
ff.pol_clk = cur_ff.pol_clk; ff.pol_clk = cur_ff.pol_clk;
ff.pol_en = cur_ff.pol_en; ff.pol_ce = cur_ff.pol_ce;
ff.pol_aload = cur_ff.pol_aload;
ff.pol_arst = cur_ff.pol_arst; ff.pol_arst = cur_ff.pol_arst;
ff.pol_srst = cur_ff.pol_srst; ff.pol_srst = cur_ff.pol_srst;
ff.pol_clr = cur_ff.pol_clr; ff.pol_clr = cur_ff.pol_clr;
ff.pol_set = cur_ff.pol_set; ff.pol_set = cur_ff.pol_set;
} else { } else {
if (ff.has_d != cur_ff.has_d) if (ff.has_gclk != cur_ff.has_gclk)
return false; return false;
if (ff.has_clk != cur_ff.has_clk) if (ff.has_clk != cur_ff.has_clk)
return false; return false;
if (ff.has_en != cur_ff.has_en) if (ff.has_ce != cur_ff.has_ce)
return false;
if (ff.has_aload != cur_ff.has_aload)
return false; return false;
if (ff.has_srst != cur_ff.has_srst) if (ff.has_srst != cur_ff.has_srst)
return false; return false;
@ -214,10 +234,16 @@ bool FfMergeHelper::find_input_ff(RTLIL::SigSpec sig, FfData &ff, pool<std::pair
if (ff.pol_clk != cur_ff.pol_clk) if (ff.pol_clk != cur_ff.pol_clk)
return false; return false;
} }
if (ff.has_en) { if (ff.has_ce) {
if (ff.sig_en != cur_ff.sig_en) if (ff.sig_ce != cur_ff.sig_ce)
return false; return false;
if (ff.pol_en != cur_ff.pol_en) if (ff.pol_ce != cur_ff.pol_ce)
return false;
}
if (ff.has_aload) {
if (ff.sig_aload != cur_ff.sig_aload)
return false;
if (ff.pol_aload != cur_ff.pol_aload)
return false; return false;
} }
if (ff.has_srst) { if (ff.has_srst) {
@ -225,7 +251,7 @@ bool FfMergeHelper::find_input_ff(RTLIL::SigSpec sig, FfData &ff, pool<std::pair
return false; return false;
if (ff.pol_srst != cur_ff.pol_srst) if (ff.pol_srst != cur_ff.pol_srst)
return false; return false;
if (ff.has_en && ff.ce_over_srst != cur_ff.ce_over_srst) if (ff.has_ce && ff.ce_over_srst != cur_ff.ce_over_srst)
return false; return false;
} }
if (ff.has_arst) { if (ff.has_arst) {
@ -243,7 +269,8 @@ bool FfMergeHelper::find_input_ff(RTLIL::SigSpec sig, FfData &ff, pool<std::pair
} }
ff.width++; ff.width++;
ff.sig_d.append(ff.has_d ? cur_ff.sig_d[idx] : State::Sx); ff.sig_d.append((ff.has_clk || ff.has_gclk) ? cur_ff.sig_d[idx] : State::Sx);
ff.sig_ad.append(ff.has_aload ? cur_ff.sig_ad[idx] : State::Sx);
ff.sig_q.append(cur_ff.sig_q[idx]); ff.sig_q.append(cur_ff.sig_q[idx]);
ff.sig_clr.append(ff.has_sr ? cur_ff.sig_clr[idx] : State::S0); ff.sig_clr.append(ff.has_sr ? cur_ff.sig_clr[idx] : State::S0);
ff.sig_set.append(ff.has_sr ? cur_ff.sig_set[idx] : State::S0); ff.sig_set.append(ff.has_sr ? cur_ff.sig_set[idx] : State::S0);

View File

@ -961,9 +961,9 @@ Cell *Mem::extract_rdff(int idx, FfInitVals *initvals) {
ff.sig_clk = port.clk; ff.sig_clk = port.clk;
ff.pol_clk = port.clk_polarity; ff.pol_clk = port.clk_polarity;
if (port.en != State::S1) { if (port.en != State::S1) {
ff.has_en = true; ff.has_ce = true;
ff.pol_en = true; ff.pol_ce = true;
ff.sig_en = port.en; ff.sig_ce = port.en;
} }
if (port.arst != State::S0) { if (port.arst != State::S0) {
ff.has_arst = true; ff.has_arst = true;
@ -976,7 +976,7 @@ Cell *Mem::extract_rdff(int idx, FfInitVals *initvals) {
ff.pol_srst = true; ff.pol_srst = true;
ff.sig_srst = port.srst; ff.sig_srst = port.srst;
ff.val_srst = port.srst_value; ff.val_srst = port.srst_value;
ff.ce_over_srst = ff.has_en && port.ce_over_srst; ff.ce_over_srst = ff.has_ce && port.ce_over_srst;
} }
ff.sig_d = sig_d; ff.sig_d = sig_d;
ff.sig_q = port.data; ff.sig_q = port.data;
@ -1163,15 +1163,14 @@ void Mem::emulate_transparency(int widx, int ridx, FfInitVals *initvals) {
FfData ff(initvals); FfData ff(initvals);
ff.width = 1; ff.width = 1;
ff.sig_q = cond_q; ff.sig_q = cond_q;
ff.has_d = true;
ff.sig_d = cond; ff.sig_d = cond;
ff.has_clk = true; ff.has_clk = true;
ff.sig_clk = rport.clk; ff.sig_clk = rport.clk;
ff.pol_clk = rport.clk_polarity; ff.pol_clk = rport.clk_polarity;
if (rport.en != State::S1) { if (rport.en != State::S1) {
ff.has_en = true; ff.has_ce = true;
ff.sig_en = rport.en; ff.sig_ce = rport.en;
ff.pol_en = true; ff.pol_ce = true;
} }
if (rport.arst != State::S0) { if (rport.arst != State::S0) {
ff.has_arst = true; ff.has_arst = true;

View File

@ -1081,7 +1081,7 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep)
FfData ff(nullptr, cell); FfData ff(nullptr, cell);
// Latches and FFs with async inputs are not supported — use clk2fflogic or async2sync first. // Latches and FFs with async inputs are not supported — use clk2fflogic or async2sync first.
if (!ff.has_d || ff.has_arst || ff.has_sr || (ff.has_en && !ff.has_clk)) if (ff.has_aload || ff.has_arst || ff.has_sr)
return false; return false;
if (timestep == 1) if (timestep == 1)
@ -1094,7 +1094,7 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep)
std::vector<int> undef_d; std::vector<int> undef_d;
if (model_undef) if (model_undef)
undef_d = importUndefSigSpec(cell->getPort(ID::D), timestep-1); 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); int srst = importDefSigSpec(ff.sig_srst, timestep-1).at(0);
std::vector<int> rval = importDefSigSpec(ff.val_srst, timestep-1); std::vector<int> rval = importDefSigSpec(ff.val_srst, timestep-1);
int undef_srst; int undef_srst;
@ -1108,21 +1108,21 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep)
else else
std::tie(d, undef_d) = mux(srst, undef_srst, rval, undef_rval, d, undef_d); std::tie(d, undef_d) = mux(srst, undef_srst, rval, undef_rval, d, undef_d);
} }
if (ff.has_en) { if (ff.has_ce) {
int en = importDefSigSpec(ff.sig_en, timestep-1).at(0); int ce = importDefSigSpec(ff.sig_ce, timestep-1).at(0);
std::vector<int> old_q = importDefSigSpec(ff.sig_q, timestep-1); std::vector<int> old_q = importDefSigSpec(ff.sig_q, timestep-1);
int undef_en; int undef_ce;
std::vector<int> undef_old_q; std::vector<int> undef_old_q;
if (model_undef) { 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); undef_old_q = importUndefSigSpec(ff.sig_q, timestep-1);
} }
if (ff.pol_en) if (ff.pol_ce)
std::tie(d, undef_d) = mux(en, undef_en, old_q, undef_old_q, d, undef_d); std::tie(d, undef_d) = mux(ce, undef_ce, old_q, undef_old_q, d, undef_d);
else 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); int srst = importDefSigSpec(ff.sig_srst, timestep-1).at(0);
std::vector<int> rval = importDefSigSpec(ff.val_srst, timestep-1); std::vector<int> rval = importDefSigSpec(ff.val_srst, timestep-1);
int undef_srst; int undef_srst;

View File

@ -71,9 +71,9 @@ struct MemQueryCache
// port_ren is an upper bound on when we care about the value fetched // port_ren is an upper bound on when we care about the value fetched
// from memory this cycle. // from memory this cycle.
int ren = ezSAT::CONST_TRUE; int ren = ezSAT::CONST_TRUE;
if (ff.has_en) { if (ff.has_ce) {
ren = qcsat.importSigBit(ff.sig_en); ren = qcsat.importSigBit(ff.sig_ce);
if (!ff.pol_en) if (!ff.pol_ce)
ren = qcsat.ez->NOT(ren); ren = qcsat.ez->NOT(ren);
} }
if (ff.has_srst) { if (ff.has_srst) {
@ -347,6 +347,10 @@ struct MemoryDffWorker
log("output latches are not supported.\n"); log("output latches are not supported.\n");
return; return;
} }
if (ff.has_aload) {
log("output FF has async load, not supported.\n");
return;
}
if (ff.has_sr) { if (ff.has_sr) {
// Latches and FFs with SR are not supported. // Latches and FFs with SR are not supported.
log("output FF has both set and reset, not supported.\n"); log("output FF has both set and reset, not supported.\n");
@ -491,8 +495,8 @@ struct MemoryDffWorker
log("merging output FF to cell.\n"); log("merging output FF to cell.\n");
merger.remove_output_ff(bits); merger.remove_output_ff(bits);
if (ff.has_en && !ff.pol_en) if (ff.has_ce && !ff.pol_ce)
ff.sig_en = module->LogicNot(NEW_ID, ff.sig_en); ff.sig_ce = module->LogicNot(NEW_ID, ff.sig_ce);
if (ff.has_arst && !ff.pol_arst) if (ff.has_arst && !ff.pol_arst)
ff.sig_arst = module->LogicNot(NEW_ID, ff.sig_arst); ff.sig_arst = module->LogicNot(NEW_ID, ff.sig_arst);
if (ff.has_srst && !ff.pol_srst) if (ff.has_srst && !ff.pol_srst)
@ -500,8 +504,8 @@ struct MemoryDffWorker
port.clk = ff.sig_clk; port.clk = ff.sig_clk;
port.clk_enable = true; port.clk_enable = true;
port.clk_polarity = ff.pol_clk; port.clk_polarity = ff.pol_clk;
if (ff.has_en) if (ff.has_ce)
port.en = ff.sig_en; port.en = ff.sig_ce;
else else
port.en = State::S1; port.en = State::S1;
if (ff.has_arst) { if (ff.has_arst) {
@ -551,6 +555,10 @@ struct MemoryDffWorker
log("address latches are not supported.\n"); log("address latches are not supported.\n");
return; return;
} }
if (ff.has_aload) {
log("address FF has async load, not supported.\n");
return;
}
if (ff.has_sr || ff.has_arst) { if (ff.has_sr || ff.has_arst) {
log("address FF has async set and/or reset, not supported.\n"); log("address FF has async set and/or reset, not supported.\n");
return; return;

View File

@ -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.has_arst) {
if (ff.sig_arst == (ff.pol_arst ? State::S0 : State::S1)) { if (ff.sig_arst == (ff.pol_arst ? State::S0 : State::S1)) {
// Always-inactive reset — remove. // Always-inactive reset — remove.
@ -414,111 +477,63 @@ struct OptDffWorker
log_id(cell), log_id(cell->type), log_id(module)); log_id(cell), log_id(cell->type), log_id(module));
ff.has_srst = false; ff.has_srst = false;
if (!ff.ce_over_srst) if (!ff.ce_over_srst)
ff.has_en = false; ff.has_ce = false;
ff.sig_d = ff.val_d = ff.val_srst; ff.sig_d = ff.val_srst;
ff.d_is_const = true;
changed = true; changed = true;
} }
} }
if (ff.has_en) { if (ff.has_ce) {
if (ff.sig_en == (ff.pol_en ? State::S0 : State::S1) || (!opt.keepdc && ff.sig_en == State::Sx)) { if (ff.sig_ce == (ff.pol_ce ? State::S0 : State::S1) || (!opt.keepdc && ff.sig_ce == State::Sx)) {
// Always-inactive enable — remove. // 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("Handling never-active EN on %s (%s) from module %s (connecting SRST instead).\n",
log_id(cell), log_id(cell->type), log_id(module)); log_id(cell), log_id(cell->type), log_id(module));
// FF with sync reset — connect the sync reset to D instead. // FF with sync reset — connect the sync reset to D instead.
ff.pol_en = ff.pol_srst; ff.pol_ce = ff.pol_srst;
ff.sig_en = ff.sig_srst; ff.sig_ce = ff.sig_srst;
ff.has_srst = false; ff.has_srst = false;
ff.sig_d = ff.val_d = ff.val_srst; ff.sig_d = ff.val_srst;
ff.d_is_const = true;
changed = true; changed = true;
} else { } else {
log("Handling never-active EN on %s (%s) from module %s (removing D path).\n", 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)); 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). // The D input path is effectively useless, so remove it (this will be a D latch, SR latch, or a const driver).
ff.has_d = ff.has_en = ff.has_clk = false; ff.has_ce = ff.has_clk = ff.has_srst = false;
changed = true; changed = true;
} }
} else if (ff.sig_en == (ff.pol_en ? State::S1 : State::S0)) { } else if (ff.sig_ce == (ff.pol_ce ? State::S1 : State::S0)) {
// Always-active enable. // Always-active enable. Just remove it.
if (ff.has_clk) {
// For FF, just remove the useless enable. // For FF, just remove the useless enable.
log("Removing always-active EN on %s (%s) from module %s.\n", log("Removing always-active EN on %s (%s) from module %s.\n",
log_id(cell), log_id(cell->type), log_id(module)); log_id(cell), log_id(cell->type), log_id(module));
ff.has_en = false; ff.has_ce = false;
changed = true; 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.has_clk) {
if (ff.sig_clk.is_fully_const()) { 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("Handling const CLK on %s (%s) from module %s (removing D path).\n",
log_id(cell), log_id(cell->type), log_id(module)); 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; 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. // Q wrapped back to D, can be removed.
if (ff.has_clk && ff.has_srst) { if (ff.has_clk && ff.has_srst) {
// FF with sync reset — connect the sync reset to D instead. // 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("Handling D = Q on %s (%s) from module %s (conecting SRST instead).\n",
log_id(cell), log_id(cell->type), log_id(module)); log_id(cell), log_id(cell->type), log_id(module));
if (ff.has_en && ff.ce_over_srst) { if (ff.has_ce && ff.ce_over_srst) {
if (!ff.pol_en) { if (!ff.pol_ce) {
if (ff.is_fine) 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 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.pol_srst) {
if (ff.is_fine) if (ff.is_fine)
@ -527,28 +542,34 @@ struct OptDffWorker
ff.sig_srst = module->Not(NEW_ID, ff.sig_srst); ff.sig_srst = module->Not(NEW_ID, ff.sig_srst);
} }
if (ff.is_fine) 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 else
ff.sig_en = module->And(NEW_ID, ff.sig_en, ff.sig_srst); ff.sig_ce = module->And(NEW_ID, ff.sig_ce, ff.sig_srst);
ff.pol_en = true; ff.pol_ce = true;
} else { } else {
ff.pol_en = ff.pol_srst; ff.pol_ce = ff.pol_srst;
ff.sig_en = ff.sig_srst; ff.sig_ce = ff.sig_srst;
} }
ff.has_en = true; ff.has_ce = true;
ff.has_srst = false; ff.has_srst = false;
ff.sig_d = ff.val_d = ff.val_srst; ff.sig_d = ff.val_srst;
ff.d_is_const = true;
changed = true; changed = true;
} else { } 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). // 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("Handling D = Q on %s (%s) from module %s (removing D path).\n",
log_id(cell), log_id(cell->type), log_id(module)); 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; 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. // Now check if any bit can be replaced by a constant.
pool<int> removed_sigbits; pool<int> removed_sigbits;
for (int i = 0; i < ff.width; i++) { for (int i = 0; i < ff.width; i++) {
@ -565,7 +586,7 @@ struct OptDffWorker
} }
if (val == State::Sm) if (val == State::Sm)
continue; continue;
if (ff.has_d) { if (ff.has_clk || ff.has_gclk) {
if (!ff.sig_d[i].wire) { if (!ff.sig_d[i].wire) {
val = combine_const(val, ff.sig_d[i].data); val = combine_const(val, ff.sig_d[i].data);
if (val == State::Sm) if (val == State::Sm)
@ -593,6 +614,34 @@ struct OptDffWorker
continue; 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, 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)); 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. // 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_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. // Try to merge sync resets.
std::map<ctrls_t, std::vector<int>> groups; std::map<ctrls_t, std::vector<int>> groups;
std::vector<int> remaining_indices; std::vector<int> remaining_indices;
@ -677,7 +726,7 @@ struct OptDffWorker
new_ff.has_srst = true; new_ff.has_srst = true;
new_ff.sig_srst = srst.first; new_ff.sig_srst = srst.first;
new_ff.pol_srst = srst.second; new_ff.pol_srst = srst.second;
if (new_ff.has_en) if (new_ff.has_ce)
new_ff.ce_over_srst = true; new_ff.ce_over_srst = true;
Cell *new_cell = new_ff.emit(module, NEW_ID); Cell *new_cell = new_ff.emit(module, NEW_ID);
if (new_cell) if (new_cell)
@ -695,7 +744,7 @@ struct OptDffWorker
changed = true; 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. // Try to merge enables.
std::map<std::pair<patterns_t, ctrls_t>, std::vector<int>> groups; std::map<std::pair<patterns_t, ctrls_t>, std::vector<int>> groups;
std::vector<int> remaining_indices; std::vector<int> remaining_indices;
@ -725,8 +774,8 @@ struct OptDffWorker
if (!opt.simple_dffe) if (!opt.simple_dffe)
patterns = find_muxtree_feedback_patterns(ff.sig_d[i], ff.sig_q[i], pattern_t()); patterns = find_muxtree_feedback_patterns(ff.sig_d[i], ff.sig_q[i], pattern_t());
if (!patterns.empty() || !enables.empty()) { if (!patterns.empty() || !enables.empty()) {
if (ff.has_en) if (ff.has_ce)
enables.insert(ctrl_t(ff.sig_en, ff.pol_en)); enables.insert(ctrl_t(ff.sig_ce, ff.pol_ce));
simplify_patterns(patterns); simplify_patterns(patterns);
groups[std::make_pair(patterns, enables)].push_back(i); groups[std::make_pair(patterns, enables)].push_back(i);
} else } else
@ -737,9 +786,9 @@ struct OptDffWorker
FfData new_ff = ff.slice(it.second); FfData new_ff = ff.slice(it.second);
ctrl_t en = make_patterns_logic(it.first.first, it.first.second, ff.is_fine); ctrl_t en = make_patterns_logic(it.first.first, it.first.second, ff.is_fine);
new_ff.has_en = true; new_ff.has_ce = true;
new_ff.sig_en = en.first; new_ff.sig_ce = en.first;
new_ff.pol_en = en.second; new_ff.pol_ce = en.second;
new_ff.ce_over_srst = false; new_ff.ce_over_srst = false;
Cell *new_cell = new_ff.emit(module, NEW_ID); Cell *new_cell = new_ff.emit(module, NEW_ID);
if (new_cell) if (new_cell)

View File

@ -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("reset value in the next cycle regardless of the data-in value at the time of\n");
log("the clock edge.\n"); log("the clock edge.\n");
log("\n"); log("\n");
log("Currently only $adff, $dffsr, and $dlatch cells are supported by this pass.\n");
log("\n");
} }
void execute(std::vector<std::string> args, RTLIL::Design *design) override void execute(std::vector<std::string> args, RTLIL::Design *design) override
{ {
@ -74,14 +72,11 @@ struct Async2syncPass : public Pass {
FfData ff(&initvals, cell); FfData ff(&initvals, cell);
// Skip for $_FF_ and $ff cells. // Skip for $_FF_ and $ff cells.
if (ff.has_d && !ff.has_clk && !ff.has_en) if (ff.has_gclk)
continue; continue;
if (ff.has_clk) if (ff.has_clk)
{ {
if (!ff.has_sr && !ff.has_arst)
continue;
if (ff.has_sr) { if (ff.has_sr) {
ff.unmap_ce_srst(module); ff.unmap_ce_srst(module);
@ -128,6 +123,39 @@ struct Async2syncPass : public Pass {
ff.sig_d = new_d; ff.sig_d = new_d;
ff.sig_q = new_q; ff.sig_q = new_q;
ff.has_sr = false; 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) { } else if (ff.has_arst) {
ff.unmap_srst(module); ff.unmap_srst(module);
@ -154,9 +182,12 @@ struct Async2syncPass : public Pass {
ff.sig_q = new_q; ff.sig_q = new_q;
ff.has_arst = false; ff.has_arst = false;
ff.has_srst = true; ff.has_srst = true;
ff.ce_over_srst = false;
ff.val_srst = ff.val_arst; ff.val_srst = ff.val_arst;
ff.sig_srst = ff.sig_arst; ff.sig_srst = ff.sig_arst;
ff.pol_srst = ff.pol_arst; ff.pol_srst = ff.pol_arst;
} else {
continue;
} }
} }
else else
@ -164,25 +195,25 @@ struct Async2syncPass : public Pass {
// Latch. // Latch.
log("Replacing %s.%s (%s): EN=%s, D=%s, Q=%s\n", log("Replacing %s.%s (%s): EN=%s, D=%s, Q=%s\n",
log_id(module), log_id(cell), log_id(cell->type), 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); initvals.remove_init(ff.sig_q);
Wire *new_q = module->addWire(NEW_ID, ff.width); Wire *new_q = module->addWire(NEW_ID, ff.width);
Wire *new_d; Wire *new_d;
if (ff.has_d) { if (ff.has_aload) {
new_d = module->addWire(NEW_ID, ff.width); new_d = module->addWire(NEW_ID, ff.width);
if (ff.pol_en) { if (ff.pol_aload) {
if (!ff.is_fine) 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 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 { } else {
if (!ff.is_fine) 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 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 { } else {
new_d = new_q; new_d = new_q;
@ -231,10 +262,10 @@ struct Async2syncPass : public Pass {
ff.sig_d = new_d; ff.sig_d = new_d;
ff.sig_q = new_q; ff.sig_q = new_q;
ff.has_en = false; ff.has_aload = false;
ff.has_arst = false; ff.has_arst = false;
ff.has_sr = false; ff.has_sr = false;
ff.has_d = true; ff.has_gclk = true;
} }
IdString name = cell->name; IdString name = cell->name;

View File

@ -148,7 +148,7 @@ struct Clk2fflogicPass : public Pass {
if (RTLIL::builtin_ff_cell_types().count(cell->type)) { if (RTLIL::builtin_ff_cell_types().count(cell->type)) {
FfData ff(&initvals, cell); FfData ff(&initvals, cell);
if (ff.has_d && !ff.has_clk && !ff.has_en) { if (ff.has_gclk) {
// Already a $ff or $_FF_ cell. // Already a $ff or $_FF_ cell.
continue; continue;
} }
@ -202,27 +202,29 @@ struct Clk2fflogicPass : public Pass {
qval = module->Mux(NEW_ID, past_q, past_d, clock_edge); qval = module->Mux(NEW_ID, past_q, past_d, clock_edge);
else else
qval = module->MuxGate(NEW_ID, past_q, past_d, clock_edge); qval = module->MuxGate(NEW_ID, past_q, past_d, clock_edge);
} else if (ff.has_d) { } else {
if (ff.has_aload) {
log("Replacing %s.%s (%s): EN=%s, D=%s, Q=%s\n", log("Replacing %s.%s (%s): EN=%s, D=%s, Q=%s\n",
log_id(module), log_id(cell), log_id(cell->type), 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));
SigSpec sig_en = wrap_async_control(module, ff.sig_en, ff.pol_en);
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 { } else {
// $sr.
log("Replacing %s.%s (%s): SET=%s, CLR=%s, Q=%s\n", log("Replacing %s.%s (%s): SET=%s, CLR=%s, Q=%s\n",
log_id(module), log_id(cell), log_id(cell->type), 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)); log_signal(ff.sig_set), log_signal(ff.sig_clr), log_signal(ff.sig_q));
}
qval = past_q; qval = past_q;
} }
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, qval, ff.sig_ad, sig_aload);
else
qval = module->MuxGate(NEW_ID, qval, ff.sig_ad, sig_aload);
}
if (ff.has_sr) { if (ff.has_sr) {
SigSpec setval = wrap_async_control(module, ff.sig_set, ff.pol_set); SigSpec setval = wrap_async_control(module, ff.sig_set, ff.pol_set);
SigSpec clrval = wrap_async_control(module, ff.sig_clr, ff.pol_clr); SigSpec clrval = wrap_async_control(module, ff.sig_clr, ff.pol_clr);

View File

@ -84,7 +84,7 @@ struct DffunmapPass : public Pass {
continue; continue;
if (ce_only) { if (ce_only) {
if (!ff.has_en) if (!ff.has_ce)
continue; continue;
ff.unmap_ce(mod); ff.unmap_ce(mod);
} else if (srst_only) { } else if (srst_only) {
@ -92,7 +92,7 @@ struct DffunmapPass : public Pass {
continue; continue;
ff.unmap_srst(mod); ff.unmap_srst(mod);
} else { } else {
if (!ff.has_en && !ff.has_srst) if (!ff.has_ce && !ff.has_srst)
continue; continue;
ff.unmap_ce_srst(mod); ff.unmap_ce_srst(mod);
} }