From f69a5fc0779ba91eb92a6152dc33d29523e43347 Mon Sep 17 00:00:00 2001 From: nella Date: Wed, 29 Apr 2026 11:21:26 +0200 Subject: [PATCH 1/7] Elim equiv bits. --- passes/opt/opt_dff.cc | 284 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 283 insertions(+), 1 deletion(-) diff --git a/passes/opt/opt_dff.cc b/passes/opt/opt_dff.cc index 709b5fc4c..cc669f902 100644 --- a/passes/opt/opt_dff.cc +++ b/passes/opt/opt_dff.cc @@ -919,6 +919,286 @@ struct OptDffWorker return did_something; } + + struct EqBit { + Cell *cell; + int idx; + SigBit q; + }; + + struct SigKey { + enum Flag : uint16_t { + InitOne = 1u << 0, + InitX = 1u << 1, + PolClk = 1u << 2, + PolCe = 1u << 3, + PolSrst = 1u << 4, + PolArst = 1u << 5, + PolAload = 1u << 6, + PolClr = 1u << 7, + PolSet = 1u << 8, + CeOverSrst = 1u << 9, + }; + + SigBit clk, ce, srst, arst, aload, clr, set; + IdString cell_type; // for SR + uint16_t flags; + + bool operator==(const SigKey &o) const { + return flags == o.flags && clk == o.clk && ce == o.ce && srst == o.srst && arst == o.arst + && aload == o.aload && clr == o.clr && set == o.set && cell_type == o.cell_type; + } + + Hasher hash_into(Hasher h) const { + h.eat(flags); + h.eat(clk); + h.eat(ce); + h.eat(srst); + h.eat(arst); + h.eat(aload); + h.eat(clr); + h.eat(set); + h.eat(cell_type); + return h; + } + }; + + bool is_def(State s) { + return s == State::S0 || s == State::S1; + } + + int sat_mux(QuickConeSat &qcsat, int s, int a, int b) { + return qcsat.ez->OR(qcsat.ez->AND(s, a), qcsat.ez->AND(qcsat.ez->NOT(s), b)); + } + + int sat_const(QuickConeSat &qcsat, State v) { + return v == State::S1 ? qcsat.ez->CONST_TRUE : qcsat.ez->CONST_FALSE; + } + + bool run_eqbits() + { + std::vector bits; + std::vector keys; + dict ff_for_cell; + + // Collect FF bits eligible for merging + for (auto cell : module->selected_cells()) { + if (!cell->is_builtin_ff()) + continue; + + FfData ff(&initvals, cell); + if (!ff.has_clk && !ff.has_gclk) + continue; + + ff_for_cell.emplace(cell, ff); + + for (int i = 0; i < ff.width; i++) { + // X value + if (ff.has_srst && !is_def(ff.val_srst[i])) continue; + if (ff.has_arst && !is_def(ff.val_arst[i])) continue; + + // Missing anchor + bool def_init = is_def(ff.val_init[i]); + if (!def_init && !ff.has_srst && !ff.has_arst) + continue; + + SigKey k = {}; + + // Flags + if (def_init && ff.val_init[i] == State::S1) + k.flags |= SigKey::InitOne; + else if (!def_init) + k.flags |= SigKey::InitX; + + if (ff.has_clk) { + k.clk = ff.sig_clk; + if (ff.pol_clk) k.flags |= SigKey::PolClk; + } + if (ff.has_ce) { + k.ce = ff.sig_ce; + if (ff.pol_ce) k.flags |= SigKey::PolCe; + } + if (ff.has_srst) { + k.srst = ff.sig_srst; + if (ff.pol_srst) k.flags |= SigKey::PolSrst; + if (ff.ce_over_srst) k.flags |= SigKey::CeOverSrst; + } + if (ff.has_arst) { + k.arst = ff.sig_arst; + if (ff.pol_arst) k.flags |= SigKey::PolArst; + } + if (ff.has_aload) { + k.aload = ff.sig_aload; + if (ff.pol_aload) k.flags |= SigKey::PolAload; + } + if (ff.has_sr) { + k.clr = ff.sig_clr[i]; + k.set = ff.sig_set[i]; + k.cell_type = cell->type; + if (ff.pol_clr) k.flags |= SigKey::PolClr; + if (ff.pol_set) k.flags |= SigKey::PolSet; + } + + bits.push_back({cell, i, ff.sig_q[i]}); + keys.push_back(k); + } + } + + if (GetSize(bits) < 2) + return false; + + // Group bits by control signature + dict> buckets; + for (int i = 0; i < GetSize(bits); i++) + buckets[keys[i]].push_back(i); + + std::vector> classes; + classes.reserve(GetSize(buckets)); + for (auto &kv : buckets) + if (GetSize(kv.second) >= 2) + classes.push_back(std::move(kv.second)); + + if (classes.empty()) + return false; + + ModWalker modwalker(module->design, module); + QuickConeSat qcsat(modwalker); + std::vector q_lit(bits.size(), -1); + std::vector n_lit(bits.size(), -1); + + // Per candidate SAT for its next state, model difference + for (auto &cls : classes) { + for (int idx : cls) { + const EqBit &eb = bits[idx]; + const FfData &ff = ff_for_cell.at(eb.cell); + q_lit[idx] = qcsat.importSigBit(eb.q); + int n = qcsat.importSigBit(ff.sig_d[eb.idx]); + + if (ff.has_aload) { + int al = qcsat.importSigBit(ff.sig_aload); + if (!ff.pol_aload) al = qcsat.ez->NOT(al); + int ad = qcsat.importSigBit(ff.sig_ad[eb.idx]); + n = sat_mux(qcsat, al, ad, n); + } + if (ff.has_arst) { + int ar = qcsat.importSigBit(ff.sig_arst); + if (!ff.pol_arst) ar = qcsat.ez->NOT(ar); + n = sat_mux(qcsat, ar, sat_const(qcsat, ff.val_arst[eb.idx]), n); + } + if (ff.has_sr) { + int clr = qcsat.importSigBit(ff.sig_clr[eb.idx]); + if (!ff.pol_clr) clr = qcsat.ez->NOT(clr); + int set = qcsat.importSigBit(ff.sig_set[eb.idx]); + if (!ff.pol_set) set = qcsat.ez->NOT(set); + n = qcsat.ez->AND(qcsat.ez->NOT(clr), qcsat.ez->OR(set, n)); + } + if (ff.has_srst) { + int srst = qcsat.importSigBit(ff.sig_srst); + if (!ff.pol_srst) srst = qcsat.ez->NOT(srst); + n = sat_mux(qcsat,srst, sat_const(qcsat, ff.val_srst[eb.idx]), n); + } + + n_lit[idx] = n; + } + } + + qcsat.prepare(); + bool any_change = false; + bool changed = true; + + // Bit = class rep, split classes whenever two next states differ + while (changed) { + changed = false; + int joint = qcsat.ez->CONST_TRUE; + + for (auto &cls : classes) { + int rep = cls[0]; + for (int k = 1; k < GetSize(cls); k++) + joint = qcsat.ez->AND(joint, qcsat.ez->IFF(q_lit[rep], q_lit[cls[k]])); + } + + std::vector> new_classes; + new_classes.reserve(classes.size()); + + for (auto &cls : classes) { + std::vector> subs; + for (int b : cls) { + bool placed = false; + + // Identical literal - trivially eq + for (auto &sub : subs) { + if (n_lit[sub[0]] == n_lit[b]) { + sub.push_back(b); + placed = true; + break; + } + } + + if (placed) continue; + + for (auto &sub : subs) { + int rep = sub[0]; + int query = qcsat.ez->NOT(qcsat.ez->IFF(n_lit[rep], n_lit[b])); + if (!qcsat.ez->solve(joint, query)) { + sub.push_back(b); + placed = true; + break; + } + } + + if (!placed) + subs.push_back({b}); + } + + if (GetSize(subs) > 1) + changed = true; + for (auto &sub : subs) + if (GetSize(sub) >= 2) + new_classes.push_back(std::move(sub)); + } + + classes = std::move(new_classes); + if (changed) + any_change = true; + } + + if (classes.empty()) + return any_change; + + dict> remove_bits; + + // Drive every non-rep Q from its class rep, drop merged bits from their FFs + for (auto &cls : classes) { + SigBit rep_q = bits[cls[0]].q; + for (int k = 1; k < GetSize(cls); k++) { + const EqBit &eb = bits[cls[k]]; + initvals.remove_init(eb.q); + module->connect(eb.q, rep_q); + remove_bits[eb.cell].insert(eb.idx); + } + } + + for (auto &kv : remove_bits) { + Cell *cell = kv.first; + const std::set &drop = kv.second; + FfData &ff = ff_for_cell.at(cell); + std::vector keep; + + for (int i = 0; i < ff.width; i++) + if (!drop.count(i)) + keep.push_back(i); + + if (keep.empty()) { + module->remove(cell); + } else { + FfData new_ff = ff.slice(keep); + new_ff.cell = cell; + new_ff.emit(); + } + } + + return true; + } }; struct OptDffPass : public Pass { @@ -946,7 +1226,7 @@ struct OptDffPass : public Pass { log(" -simple-dffe\n"); log(" only enables clock enable recognition transform for obvious cases\n"); log("\n"); - log(" -sat\n"); + log(" -sat AAA\n"); log(" additionally invoke SAT solver to detect and remove flip-flops (with\n"); log(" non-constant inputs) that can also be replaced with a constant driver\n"); log("\n"); @@ -987,6 +1267,8 @@ struct OptDffPass : public Pass { did_something = true; if (worker.run_constbits()) did_something = true; + if (opt.sat && worker.run_eqbits()) + did_something = true; } if (did_something) From d85e3f10de301d1bd594042cf71a3d8f15cf4d8a Mon Sep 17 00:00:00 2001 From: nella Date: Wed, 29 Apr 2026 15:55:45 +0200 Subject: [PATCH 2/7] Add tests. --- passes/opt/opt_dff.cc | 4 +- tests/opt/opt_dff_eqbits.ys | 56 ++++++++ tests/opt/opt_dff_eqbits_large.sv | 231 ++++++++++++++++++++++++++++++ tests/opt/opt_dff_eqbits_small.sv | 30 ++++ 4 files changed, 319 insertions(+), 2 deletions(-) create mode 100644 tests/opt/opt_dff_eqbits.ys create mode 100644 tests/opt/opt_dff_eqbits_large.sv create mode 100644 tests/opt/opt_dff_eqbits_small.sv diff --git a/passes/opt/opt_dff.cc b/passes/opt/opt_dff.cc index cc669f902..241ea6888 100644 --- a/passes/opt/opt_dff.cc +++ b/passes/opt/opt_dff.cc @@ -1095,7 +1095,7 @@ struct OptDffWorker if (ff.has_srst) { int srst = qcsat.importSigBit(ff.sig_srst); if (!ff.pol_srst) srst = qcsat.ez->NOT(srst); - n = sat_mux(qcsat,srst, sat_const(qcsat, ff.val_srst[eb.idx]), n); + n = sat_mux(qcsat, srst, sat_const(qcsat, ff.val_srst[eb.idx]), n); } n_lit[idx] = n; @@ -1226,7 +1226,7 @@ struct OptDffPass : public Pass { log(" -simple-dffe\n"); log(" only enables clock enable recognition transform for obvious cases\n"); log("\n"); - log(" -sat AAA\n"); + log(" -sat\n"); log(" additionally invoke SAT solver to detect and remove flip-flops (with\n"); log(" non-constant inputs) that can also be replaced with a constant driver\n"); log("\n"); diff --git a/tests/opt/opt_dff_eqbits.ys b/tests/opt/opt_dff_eqbits.ys new file mode 100644 index 000000000..10e9045e4 --- /dev/null +++ b/tests/opt/opt_dff_eqbits.ys @@ -0,0 +1,56 @@ +# small test case +design -reset +read_verilog -sv opt_dff_eqbits_small.sv +hierarchy -top test_case +techmap +opt_dff -sat +synth +opt_dff -sat +opt_clean -purge + +select -assert-count 2 t:$_SDFF_PN0_ + +# equivalence +design -reset +read_verilog -sv opt_dff_eqbits_small.sv +hierarchy -top test_case +prep +design -save gold + +opt_dff -sat +design -save gate + +design -copy-from gold -as gold test_case +design -copy-from gate -as gate test_case +equiv_make gold gate equiv +equiv_induct equiv +equiv_status -assert + + +# large test case +design -reset +read_verilog -sv opt_dff_eqbits_large.sv +hierarchy -top test_case +techmap +opt_dff -sat +synth +opt_dff -sat +opt_clean -purge + +select -assert-count 6 t:$_SDFFE_PN0P_ + +# equivalence +design -reset +read_verilog -sv opt_dff_eqbits_large.sv +hierarchy -top test_case +prep +design -save gold + +opt_dff -sat +design -save gate + +design -copy-from gold -as gold test_case +design -copy-from gate -as gate test_case +equiv_make gold gate equiv +equiv_induct equiv +equiv_status -assert diff --git a/tests/opt/opt_dff_eqbits_large.sv b/tests/opt/opt_dff_eqbits_large.sv new file mode 100644 index 000000000..4b32c7f8e --- /dev/null +++ b/tests/opt/opt_dff_eqbits_large.sv @@ -0,0 +1,231 @@ +module test_case ( + input wire clk, + input wire rst_n, + input wire [3:0] chan_0_data, + input wire chan_0_vld, + input wire chan_1_rdy, + output wire chan_0_rdy, + output wire [207:0] chan_1_data, + output wire chan_1_vld, + output wire idle +); + wire [12:0] state_init[0:15]; + assign state_init[0] = 13'h0000; + assign state_init[1] = 13'h0000; + assign state_init[2] = 13'h0000; + assign state_init[3] = 13'h0000; + assign state_init[4] = 13'h0000; + assign state_init[5] = 13'h0000; + assign state_init[6] = 13'h0000; + assign state_init[7] = 13'h0000; + assign state_init[8] = 13'h0000; + assign state_init[9] = 13'h0000; + assign state_init[10] = 13'h0000; + assign state_init[11] = 13'h0000; + assign state_init[12] = 13'h0000; + assign state_init[13] = 13'h0000; + assign state_init[14] = 13'h0000; + assign state_init[15] = 13'h0000; + + wire [12:0] ch1_init[0:15]; + assign ch1_init[0] = 13'h0000; + assign ch1_init[1] = 13'h0000; + assign ch1_init[2] = 13'h0000; + assign ch1_init[3] = 13'h0000; + assign ch1_init[4] = 13'h0000; + assign ch1_init[5] = 13'h0000; + assign ch1_init[6] = 13'h0000; + assign ch1_init[7] = 13'h0000; + assign ch1_init[8] = 13'h0000; + assign ch1_init[9] = 13'h0000; + assign ch1_init[10] = 13'h0000; + assign ch1_init[11] = 13'h0000; + assign ch1_init[12] = 13'h0000; + assign ch1_init[13] = 13'h0000; + assign ch1_init[14] = 13'h0000; + assign ch1_init[15] = 13'h0000; + + wire [12:0] mask_1fff[0:15]; + assign mask_1fff[0] = 13'h1fff; + assign mask_1fff[1] = 13'h1fff; + assign mask_1fff[2] = 13'h1fff; + assign mask_1fff[3] = 13'h1fff; + assign mask_1fff[4] = 13'h1fff; + assign mask_1fff[5] = 13'h1fff; + assign mask_1fff[6] = 13'h1fff; + assign mask_1fff[7] = 13'h1fff; + assign mask_1fff[8] = 13'h1fff; + assign mask_1fff[9] = 13'h1fff; + assign mask_1fff[10] = 13'h1fff; + assign mask_1fff[11] = 13'h1fff; + assign mask_1fff[12] = 13'h1fff; + assign mask_1fff[13] = 13'h1fff; + assign mask_1fff[14] = 13'h1fff; + assign mask_1fff[15] = 13'h1fff; + + reg [12:0] state_array[0:15]; + reg [3:0] ch0_in_buf; + reg ch0_in_buf_vld; + reg [12:0] ch1_out_buf[0:15]; + reg ch1_out_buf_vld; + reg stg1_vld; + + wire ch1_not_vld; + wire [3:0] ch0_sel_data; + wire ch0_is_vld; + wire ch1_vld_we; + wire ch1_data_we; + wire stg0_vld_out; + wire ch0_buf_ready; + wire ch0_pipe_stall; + wire [1:0] sel_concat; + wire ch0_buf_data_we; + wire ch0_buf_vld_rst; + wire stg0_idle; + wire stg1_idle; + wire ch0_is_inactive; + wire ch1_is_inactive; + wire [12:0] next_state_val[0:15]; + wire state_we; + wire ch0_buf_vld_we; + wire stg1_vld_we; + wire pipe_idle; + + assign ch1_not_vld = ~ch1_out_buf_vld; + assign ch0_sel_data = ch0_in_buf_vld ? ch0_in_buf : chan_0_data; + assign ch0_is_vld = chan_0_vld | ch0_in_buf_vld; + assign ch1_vld_we = chan_1_rdy | ch1_not_vld; + assign ch1_data_we = ch0_is_vld & ch1_vld_we; + assign stg0_vld_out = ch0_is_vld & ch1_data_we; + assign ch0_buf_ready = ~ch0_in_buf_vld; + assign ch0_pipe_stall = ~stg0_vld_out; + assign sel_concat = {ch0_is_vld & ch0_sel_data[0], ch0_is_vld & ~ch0_sel_data[0]}; + assign ch0_buf_data_we = chan_0_vld & ch0_buf_ready & ch0_pipe_stall; + assign ch0_buf_vld_rst = ch0_in_buf_vld & stg0_vld_out; + assign stg0_idle = ~ch0_is_vld; + assign stg1_idle = ~stg1_vld; + assign ch0_is_inactive = ~(chan_0_vld & ch0_buf_ready); + assign ch1_is_inactive = ~(ch1_out_buf_vld & chan_1_rdy); + + assign next_state_val[0] = state_array[0] & {13{sel_concat[0]}} | mask_1fff[0] & {13{sel_concat[1]}}; + assign next_state_val[1] = state_array[1] & {13{sel_concat[0]}} | mask_1fff[1] & {13{sel_concat[1]}}; + assign next_state_val[2] = state_array[2] & {13{sel_concat[0]}} | mask_1fff[2] & {13{sel_concat[1]}}; + assign next_state_val[3] = state_array[3] & {13{sel_concat[0]}} | mask_1fff[3] & {13{sel_concat[1]}}; + assign next_state_val[4] = state_array[4] & {13{sel_concat[0]}} | mask_1fff[4] & {13{sel_concat[1]}}; + assign next_state_val[5] = state_array[5] & {13{sel_concat[0]}} | mask_1fff[5] & {13{sel_concat[1]}}; + assign next_state_val[6] = state_array[6] & {13{sel_concat[0]}} | mask_1fff[6] & {13{sel_concat[1]}}; + assign next_state_val[7] = state_array[7] & {13{sel_concat[0]}} | mask_1fff[7] & {13{sel_concat[1]}}; + assign next_state_val[8] = state_array[8] & {13{sel_concat[0]}} | mask_1fff[8] & {13{sel_concat[1]}}; + assign next_state_val[9] = state_array[9] & {13{sel_concat[0]}} | mask_1fff[9] & {13{sel_concat[1]}}; + assign next_state_val[10] = state_array[10] & {13{sel_concat[0]}} | mask_1fff[10] & {13{sel_concat[1]}}; + assign next_state_val[11] = state_array[11] & {13{sel_concat[0]}} | mask_1fff[11] & {13{sel_concat[1]}}; + assign next_state_val[12] = state_array[12] & {13{sel_concat[0]}} | mask_1fff[12] & {13{sel_concat[1]}}; + assign next_state_val[13] = state_array[13] & {13{sel_concat[0]}} | mask_1fff[13] & {13{sel_concat[1]}}; + assign next_state_val[14] = state_array[14] & {13{sel_concat[0]}} | mask_1fff[14] & {13{sel_concat[1]}}; + assign next_state_val[15] = state_array[15] & {13{sel_concat[0]}} | mask_1fff[15] & {13{sel_concat[1]}}; + + assign state_we = stg0_vld_out & ch0_sel_data[0] | stg0_vld_out & ~ch0_sel_data[0]; + assign ch0_buf_vld_we = ch0_buf_data_we | ch0_buf_vld_rst; + assign stg1_vld_we = stg0_vld_out | stg1_vld; + assign pipe_idle = stg0_idle & stg1_idle & ch0_is_inactive & ch1_is_inactive; + + always @(posedge clk) begin + if (!rst_n) begin + state_array[0] <= state_init[0]; + state_array[1] <= state_init[1]; + state_array[2] <= state_init[2]; + state_array[3] <= state_init[3]; + state_array[4] <= state_init[4]; + state_array[5] <= state_init[5]; + state_array[6] <= state_init[6]; + state_array[7] <= state_init[7]; + state_array[8] <= state_init[8]; + state_array[9] <= state_init[9]; + state_array[10] <= state_init[10]; + state_array[11] <= state_init[11]; + state_array[12] <= state_init[12]; + state_array[13] <= state_init[13]; + state_array[14] <= state_init[14]; + state_array[15] <= state_init[15]; + ch0_in_buf <= 4'h0; + ch0_in_buf_vld <= 1'h0; + ch1_out_buf[0] <= ch1_init[0]; + ch1_out_buf[1] <= ch1_init[1]; + ch1_out_buf[2] <= ch1_init[2]; + ch1_out_buf[3] <= ch1_init[3]; + ch1_out_buf[4] <= ch1_init[4]; + ch1_out_buf[5] <= ch1_init[5]; + ch1_out_buf[6] <= ch1_init[6]; + ch1_out_buf[7] <= ch1_init[7]; + ch1_out_buf[8] <= ch1_init[8]; + ch1_out_buf[9] <= ch1_init[9]; + ch1_out_buf[10] <= ch1_init[10]; + ch1_out_buf[11] <= ch1_init[11]; + ch1_out_buf[12] <= ch1_init[12]; + ch1_out_buf[13] <= ch1_init[13]; + ch1_out_buf[14] <= ch1_init[14]; + ch1_out_buf[15] <= ch1_init[15]; + ch1_out_buf_vld <= 1'h0; + stg1_vld <= 1'h0; + end else begin + state_array[0] <= state_we ? next_state_val[0] : state_array[0]; + state_array[1] <= state_we ? next_state_val[1] : state_array[1]; + state_array[2] <= state_we ? next_state_val[2] : state_array[2]; + state_array[3] <= state_we ? next_state_val[3] : state_array[3]; + state_array[4] <= state_we ? next_state_val[4] : state_array[4]; + state_array[5] <= state_we ? next_state_val[5] : state_array[5]; + state_array[6] <= state_we ? next_state_val[6] : state_array[6]; + state_array[7] <= state_we ? next_state_val[7] : state_array[7]; + state_array[8] <= state_we ? next_state_val[8] : state_array[8]; + state_array[9] <= state_we ? next_state_val[9] : state_array[9]; + state_array[10] <= state_we ? next_state_val[10] : state_array[10]; + state_array[11] <= state_we ? next_state_val[11] : state_array[11]; + state_array[12] <= state_we ? next_state_val[12] : state_array[12]; + state_array[13] <= state_we ? next_state_val[13] : state_array[13]; + state_array[14] <= state_we ? next_state_val[14] : state_array[14]; + state_array[15] <= state_we ? next_state_val[15] : state_array[15]; + ch0_in_buf <= ch0_buf_data_we ? chan_0_data : ch0_in_buf; + ch0_in_buf_vld <= ch0_buf_vld_we ? ch0_buf_ready : ch0_in_buf_vld; + ch1_out_buf[0] <= ch1_data_we ? state_array[0] : ch1_out_buf[0]; + ch1_out_buf[1] <= ch1_data_we ? state_array[1] : ch1_out_buf[1]; + ch1_out_buf[2] <= ch1_data_we ? state_array[2] : ch1_out_buf[2]; + ch1_out_buf[3] <= ch1_data_we ? state_array[3] : ch1_out_buf[3]; + ch1_out_buf[4] <= ch1_data_we ? state_array[4] : ch1_out_buf[4]; + ch1_out_buf[5] <= ch1_data_we ? state_array[5] : ch1_out_buf[5]; + ch1_out_buf[6] <= ch1_data_we ? state_array[6] : ch1_out_buf[6]; + ch1_out_buf[7] <= ch1_data_we ? state_array[7] : ch1_out_buf[7]; + ch1_out_buf[8] <= ch1_data_we ? state_array[8] : ch1_out_buf[8]; + ch1_out_buf[9] <= ch1_data_we ? state_array[9] : ch1_out_buf[9]; + ch1_out_buf[10] <= ch1_data_we ? state_array[10] : ch1_out_buf[10]; + ch1_out_buf[11] <= ch1_data_we ? state_array[11] : ch1_out_buf[11]; + ch1_out_buf[12] <= ch1_data_we ? state_array[12] : ch1_out_buf[12]; + ch1_out_buf[13] <= ch1_data_we ? state_array[13] : ch1_out_buf[13]; + ch1_out_buf[14] <= ch1_data_we ? state_array[14] : ch1_out_buf[14]; + ch1_out_buf[15] <= ch1_data_we ? state_array[15] : ch1_out_buf[15]; + ch1_out_buf_vld <= ch1_vld_we ? ch0_is_vld : ch1_out_buf_vld; + stg1_vld <= stg1_vld_we ? stg0_vld_out : stg1_vld; + end + end + + assign chan_0_rdy = ch0_buf_ready; + assign chan_1_data = { + ch1_out_buf[15], + ch1_out_buf[14], + ch1_out_buf[13], + ch1_out_buf[12], + ch1_out_buf[11], + ch1_out_buf[10], + ch1_out_buf[9], + ch1_out_buf[8], + ch1_out_buf[7], + ch1_out_buf[6], + ch1_out_buf[5], + ch1_out_buf[4], + ch1_out_buf[3], + ch1_out_buf[2], + ch1_out_buf[1], + ch1_out_buf[0] + }; + assign chan_1_vld = ch1_out_buf_vld; + assign idle = pipe_idle; +endmodule diff --git a/tests/opt/opt_dff_eqbits_small.sv b/tests/opt/opt_dff_eqbits_small.sv new file mode 100644 index 000000000..7c6aeba7f --- /dev/null +++ b/tests/opt/opt_dff_eqbits_small.sv @@ -0,0 +1,30 @@ +module test_case ( + input wire clk, + input wire rst_n, + input wire in_val, + output wire out_a, + output wire out_b, + output wire out_c, + output wire out_d +); + reg a, b, c, d; + + always @(posedge clk) begin + if (!rst_n) begin + a <= 1'b0; + b <= 1'b0; + c <= 1'b0; + d <= 1'b0; + end else begin + a <= c & in_val; + b <= d & in_val; + c <= b | in_val; + d <= a | in_val; + end + end + + assign out_a = a; + assign out_b = b; + assign out_c = c; + assign out_d = d; +endmodule From c6bf13bb94ed8b3f5f7eee14eb67823f497f0e4a Mon Sep 17 00:00:00 2001 From: nella Date: Wed, 13 May 2026 10:49:12 +0200 Subject: [PATCH 3/7] Implement worklist and SAT counterexample splitting. --- passes/opt/opt_dff.cc | 113 ++++++++++++++++++++++++------------------ 1 file changed, 65 insertions(+), 48 deletions(-) diff --git a/passes/opt/opt_dff.cc b/passes/opt/opt_dff.cc index 241ea6888..ef5c56896 100644 --- a/passes/opt/opt_dff.cc +++ b/passes/opt/opt_dff.cc @@ -1104,62 +1104,76 @@ struct OptDffWorker qcsat.prepare(); bool any_change = false; - bool changed = true; + std::vector worklist; + std::vector in_worklist(GetSize(classes), true); - // Bit = class rep, split classes whenever two next states differ - while (changed) { - changed = false; - int joint = qcsat.ez->CONST_TRUE; + for (int i = 0; i < GetSize(classes); i++) { + worklist.push_back(i); + } - for (auto &cls : classes) { - int rep = cls[0]; - for (int k = 1; k < GetSize(cls); k++) - joint = qcsat.ez->AND(joint, qcsat.ez->IFF(q_lit[rep], q_lit[cls[k]])); + while (!worklist.empty()) { + int cls_idx = worklist.back(); + worklist.pop_back(); + in_worklist[cls_idx] = false; + + auto &cls = classes[cls_idx]; + if (GetSize(cls) < 2) continue; + + std::vector assumptions; + for (auto &c : classes) { + if (GetSize(c) < 2) continue; + int rep = c[0]; + for (int k = 1; k < GetSize(c); k++) { + assumptions.push_back(qcsat.ez->IFF(q_lit[rep], q_lit[c[k]])); + } } - std::vector> new_classes; - new_classes.reserve(classes.size()); + // Split at counterexamples + int rep = cls[0]; + for (int i = 1; i < GetSize(cls); i++) { + // Trivially eqivalent + if (n_lit[rep] == n_lit[cls[i]]) + continue; + + int query = qcsat.ez->NOT(qcsat.ez->IFF(n_lit[rep], n_lit[cls[i]])); + std::vector modelExprs; - for (auto &cls : classes) { - std::vector> subs; for (int b : cls) { - bool placed = false; - - // Identical literal - trivially eq - for (auto &sub : subs) { - if (n_lit[sub[0]] == n_lit[b]) { - sub.push_back(b); - placed = true; - break; - } - } - - if (placed) continue; - - for (auto &sub : subs) { - int rep = sub[0]; - int query = qcsat.ez->NOT(qcsat.ez->IFF(n_lit[rep], n_lit[b])); - if (!qcsat.ez->solve(joint, query)) { - sub.push_back(b); - placed = true; - break; - } - } - - if (!placed) - subs.push_back({b}); + modelExprs.push_back(n_lit[b]); } - if (GetSize(subs) > 1) - changed = true; - for (auto &sub : subs) - if (GetSize(sub) >= 2) - new_classes.push_back(std::move(sub)); - } + std::vector modelVals; + assumptions.push_back(query); + + if (qcsat.ez->solve(modelExprs, modelVals, assumptions)) { + // SAT -> partition entire class + std::vector sub0; + std::vector sub1; - classes = std::move(new_classes); - if (changed) - any_change = true; + for (size_t b_idx = 0; b_idx < cls.size(); b_idx++) { + if (modelVals[b_idx]) + sub1.push_back(cls[b_idx]); + else + sub0.push_back(cls[b_idx]); + } + + classes[cls_idx] = std::move(sub0); + classes.push_back(std::move(sub1)); + in_worklist.push_back(false); + + // Partition was split -> the induction hypo weakened + for (int j = 0; j < GetSize(classes); j++) { + if (GetSize(classes[j]) >= 2 && !in_worklist[j]) { + worklist.push_back(j); + in_worklist[j] = true; + } + } + + break; // Process new splits + } + + assumptions.pop_back(); // Remove query for the next pairwise check if UNSAT + } } if (classes.empty()) @@ -1169,7 +1183,10 @@ struct OptDffWorker // Drive every non-rep Q from its class rep, drop merged bits from their FFs for (auto &cls : classes) { + if (GetSize(cls) < 2) + continue; SigBit rep_q = bits[cls[0]].q; + any_change = true; for (int k = 1; k < GetSize(cls); k++) { const EqBit &eb = bits[cls[k]]; initvals.remove_init(eb.q); @@ -1197,7 +1214,7 @@ struct OptDffWorker } } - return true; + return any_change; } }; From bbec8d2902d3bdf65fb90a99881a94c8dfed520b Mon Sep 17 00:00:00 2001 From: nella Date: Wed, 20 May 2026 15:51:04 +0200 Subject: [PATCH 4/7] Gate behind flag. --- passes/opt/opt_dff.cc | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/passes/opt/opt_dff.cc b/passes/opt/opt_dff.cc index ef5c56896..e657a8a2d 100644 --- a/passes/opt/opt_dff.cc +++ b/passes/opt/opt_dff.cc @@ -41,6 +41,7 @@ struct OptDffOptions bool simple_dffe; bool sat; bool keepdc; + bool eqbits; }; struct OptDffWorker @@ -977,6 +978,10 @@ struct OptDffWorker bool run_eqbits() { + if(!opt.eqbits) { + return false; + } + std::vector bits; std::vector keys; dict ff_for_cell; @@ -1253,6 +1258,11 @@ struct OptDffPass : public Pass { log(" all result bits to be set to x. this behavior changes when 'a+0' is\n"); log(" replaced by 'a'. the -keepdc option disables all such optimizations.\n"); log("\n"); + log(" -eqbits\n"); + log(" finds groups of flip flop bits provably holding always-equal values\n"); + log(" across cycles and collapses each group to a single bit, potentially\n"); + log(" reducing the number of required flip flops.\n"); + log("\n"); } void execute(std::vector args, RTLIL::Design *design) override @@ -1265,6 +1275,7 @@ struct OptDffPass : public Pass { opt.simple_dffe = false; opt.keepdc = false; opt.sat = false; + opt.eqbits = false; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -1273,6 +1284,7 @@ struct OptDffPass : public Pass { if (args[argidx] == "-simple-dffe") { opt.simple_dffe = true; continue; } if (args[argidx] == "-keepdc") { opt.keepdc = true; continue; } if (args[argidx] == "-sat") { opt.sat = true; continue; } + if (args[argidx] == "-eqbits") { opt.eqbits = true; continue; } break; } extra_args(args, argidx, design); @@ -1284,7 +1296,7 @@ struct OptDffPass : public Pass { did_something = true; if (worker.run_constbits()) did_something = true; - if (opt.sat && worker.run_eqbits()) + if (worker.run_eqbits()) did_something = true; } From 04a1611346afbd7ffbed8b4d625c674694a1f972 Mon Sep 17 00:00:00 2001 From: nella Date: Wed, 20 May 2026 15:58:27 +0200 Subject: [PATCH 5/7] Tests. --- tests/opt/opt_dff_eqbits.ys | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/opt/opt_dff_eqbits.ys b/tests/opt/opt_dff_eqbits.ys index 10e9045e4..181b5d0a7 100644 --- a/tests/opt/opt_dff_eqbits.ys +++ b/tests/opt/opt_dff_eqbits.ys @@ -3,9 +3,9 @@ design -reset read_verilog -sv opt_dff_eqbits_small.sv hierarchy -top test_case techmap -opt_dff -sat +opt_dff -sat -eqbits synth -opt_dff -sat +opt_dff -sat -eqbits opt_clean -purge select -assert-count 2 t:$_SDFF_PN0_ @@ -17,7 +17,7 @@ hierarchy -top test_case prep design -save gold -opt_dff -sat +opt_dff -sat -eqbits design -save gate design -copy-from gold -as gold test_case @@ -32,9 +32,9 @@ design -reset read_verilog -sv opt_dff_eqbits_large.sv hierarchy -top test_case techmap -opt_dff -sat +opt_dff -sat -eqbits synth -opt_dff -sat +opt_dff -sat -eqbits opt_clean -purge select -assert-count 6 t:$_SDFFE_PN0P_ @@ -46,7 +46,7 @@ hierarchy -top test_case prep design -save gold -opt_dff -sat +opt_dff -sat -eqbits design -save gate design -copy-from gold -as gold test_case From 386e63ae20465e401d749ffe160098e7be97f799 Mon Sep 17 00:00:00 2001 From: nella Date: Mon, 25 May 2026 12:49:29 +0200 Subject: [PATCH 6/7] Add prepass for bit simulation. --- kernel/bitsim.h | 79 +++++++++++++++++++++++++++++++++++++++++++ passes/opt/opt_dff.cc | 64 ++++++++++++++++++++++++++++++++++- 2 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 kernel/bitsim.h diff --git a/kernel/bitsim.h b/kernel/bitsim.h new file mode 100644 index 000000000..a0915e28b --- /dev/null +++ b/kernel/bitsim.h @@ -0,0 +1,79 @@ +#ifndef BITSIM_H +#define BITSIM_H + +#include "kernel/modtools.h" + +YOSYS_NAMESPACE_BEGIN + +struct BitSim { + Module *module; + SigMap &sigmap; + ModWalker &modwalker; + dict sim_vals; + uint64_t rng_state; + + BitSim(Module *m, SigMap &sm, ModWalker &mw) + : module(m), sigmap(sm), modwalker(mw), rng_state(1337) {} + + uint64_t xorshift64() { + rng_state ^= rng_state << 13; + rng_state ^= rng_state >> 7; + rng_state ^= rng_state << 17; + return rng_state; + } + + uint64_t eval_bit(SigBit b) { + SigBit mapped = sigmap(b); + if (mapped == State::S0) return 0ULL; + if (mapped == State::S1) return ~0ULL; + if (mapped == State::Sx || mapped == State::Sz) return 0ULL; + + auto it = sim_vals.find(mapped); + if (it != sim_vals.end()) return it->second; + sim_vals[mapped] = 0; + uint64_t res = 0; + + if (!modwalker.has_drivers(mapped)) { + res = xorshift64(); + } else { + auto &drivers = modwalker.signal_drivers[mapped]; + if (drivers.empty()) { + res = xorshift64(); + } else { + auto driver = *drivers.begin(); + Cell *cell = driver.cell; + + if (cell->is_builtin_ff()) { + res = xorshift64(); + } else if (cell->type == ID($_AND_)) { + res = eval_bit(cell->getPort(ID::A)[0]) & eval_bit(cell->getPort(ID::B)[0]); + } else if (cell->type == ID($_OR_)) { + res = eval_bit(cell->getPort(ID::A)[0]) | eval_bit(cell->getPort(ID::B)[0]); + } else if (cell->type == ID($_XOR_)) { + res = eval_bit(cell->getPort(ID::A)[0]) ^ eval_bit(cell->getPort(ID::B)[0]); + } else if (cell->type == ID($_NOT_)) { + res = ~eval_bit(cell->getPort(ID::A)[0]); + } else if (cell->type == ID($_MUX_)) { + uint64_t s = eval_bit(cell->getPort(ID::S)[0]); + uint64_t a = eval_bit(cell->getPort(ID::A)[0]); + uint64_t b = eval_bit(cell->getPort(ID::B)[0]); + res = (a & ~s) | (b & s); + } else if (cell->type == ID($mux)) { + uint64_t s = eval_bit(cell->getPort(ID::S)[0]); + uint64_t a = eval_bit(cell->getPort(ID::A)[driver.offset]); + uint64_t b = eval_bit(cell->getPort(ID::B)[driver.offset]); + res = (a & ~s) | (b & s); + } else { + res = xorshift64(); + } + } + } + + sim_vals[mapped] = res; + return res; + } +}; + +YOSYS_NAMESPACE_END + +#endif diff --git a/passes/opt/opt_dff.cc b/passes/opt/opt_dff.cc index e657a8a2d..dbe0e521a 100644 --- a/passes/opt/opt_dff.cc +++ b/passes/opt/opt_dff.cc @@ -26,6 +26,7 @@ #include "kernel/sigtools.h" #include "kernel/ffinit.h" #include "kernel/ff.h" +#include "kernel/bitsim.h" #include "kernel/pattern.h" #include "passes/techmap/simplemap.h" #include @@ -1067,6 +1068,67 @@ struct OptDffWorker return false; ModWalker modwalker(module->design, module); + BitSim sim(module, sigmap, modwalker); + + // Simulation prepass + // Assume same class + for (auto &cls : classes) { + uint64_t class_q_val = sim.xorshift64(); + for (int idx : cls) { + sim.sim_vals[sigmap(bits[idx].q)] = class_q_val; + } + } + + std::vector> refined_classes; + + for (auto &cls : classes) { + dict> sim_buckets; + for (int idx : cls) { + const EqBit &eb = bits[idx]; + const FfData &ff = ff_for_cell.at(eb.cell); + + uint64_t n_val = sim.eval_bit(ff.sig_d[eb.idx]); + + if (ff.has_aload) { + uint64_t al = sim.eval_bit(ff.sig_aload); + if (!ff.pol_aload) al = ~al; + uint64_t ad = sim.eval_bit(ff.sig_ad[eb.idx]); + n_val = (n_val & ~al) | (ad & al); + } + if (ff.has_arst) { + uint64_t ar = sim.eval_bit(ff.sig_arst); + if (!ff.pol_arst) ar = ~ar; + uint64_t ar_val = (ff.val_arst[eb.idx] == State::S1) ? ~0ULL : 0ULL; + n_val = (n_val & ~ar) | (ar_val & ar); + } + if (ff.has_sr) { + uint64_t clr = sim.eval_bit(ff.sig_clr[eb.idx]); + if (!ff.pol_clr) clr = ~clr; + uint64_t set = sim.eval_bit(ff.sig_set[eb.idx]); + if (!ff.pol_set) set = ~set; + n_val = ~clr & (set | n_val); + } + if (ff.has_srst) { + uint64_t srst = sim.eval_bit(ff.sig_srst); + if (!ff.pol_srst) srst = ~srst; + uint64_t srst_val = (ff.val_srst[eb.idx] == State::S1) ? ~0ULL : 0ULL; + n_val = (n_val & ~srst) | (srst_val & srst); + } + + sim_buckets[n_val].push_back(idx); + } + + for (auto &kv : sim_buckets) { + if (GetSize(kv.second) >= 2) { + refined_classes.push_back(std::move(kv.second)); + } + } + } + + classes = std::move(refined_classes); + if (classes.empty()) + return false; + QuickConeSat qcsat(modwalker); std::vector q_lit(bits.size(), -1); std::vector n_lit(bits.size(), -1); @@ -1136,7 +1198,7 @@ struct OptDffWorker // Split at counterexamples int rep = cls[0]; for (int i = 1; i < GetSize(cls); i++) { - // Trivially eqivalent + // Trivially equivalent if (n_lit[rep] == n_lit[cls[i]]) continue; From 68df0be7d2ef7b3a0dd54abe469b81b9494a48e1 Mon Sep 17 00:00:00 2001 From: nella Date: Mon, 25 May 2026 14:16:55 +0200 Subject: [PATCH 7/7] Remove eqbits flag. --- passes/opt/opt_dff.cc | 106 ++++++++++++++++++++---------------- tests/opt/opt_dff_eqbits.ys | 12 ++-- 2 files changed, 64 insertions(+), 54 deletions(-) diff --git a/passes/opt/opt_dff.cc b/passes/opt/opt_dff.cc index dbe0e521a..500df142e 100644 --- a/passes/opt/opt_dff.cc +++ b/passes/opt/opt_dff.cc @@ -42,7 +42,6 @@ struct OptDffOptions bool simple_dffe; bool sat; bool keepdc; - bool eqbits; }; struct OptDffWorker @@ -977,15 +976,9 @@ struct OptDffWorker return v == State::S1 ? qcsat.ez->CONST_TRUE : qcsat.ez->CONST_FALSE; } - bool run_eqbits() + std::vector> gather_initial_eq_classes(std::vector &bits, dict &ff_for_cell) { - if(!opt.eqbits) { - return false; - } - - std::vector bits; std::vector keys; - dict ff_for_cell; // Collect FF bits eligible for merging for (auto cell : module->selected_cells()) { @@ -1050,27 +1043,26 @@ struct OptDffWorker } } - if (GetSize(bits) < 2) - return false; - - // Group bits by control signature dict> buckets; for (int i = 0; i < GetSize(bits); i++) buckets[keys[i]].push_back(i); std::vector> classes; - classes.reserve(GetSize(buckets)); for (auto &kv : buckets) if (GetSize(kv.second) >= 2) classes.push_back(std::move(kv.second)); - if (classes.empty()) - return false; + return classes; + } - ModWalker modwalker(module->design, module); + std::vector> filter_classes_sim( + const std::vector> &classes, + const std::vector &bits, + const dict &ff_for_cell, + ModWalker &modwalker + ) { BitSim sim(module, sigmap, modwalker); - - // Simulation prepass + // Assume same class for (auto &cls : classes) { uint64_t class_q_val = sim.xorshift64(); @@ -1080,15 +1072,13 @@ struct OptDffWorker } std::vector> refined_classes; - for (auto &cls : classes) { dict> sim_buckets; for (int idx : cls) { const EqBit &eb = bits[idx]; const FfData &ff = ff_for_cell.at(eb.cell); - uint64_t n_val = sim.eval_bit(ff.sig_d[eb.idx]); - + if (ff.has_aload) { uint64_t al = sim.eval_bit(ff.sig_aload); if (!ff.pol_aload) al = ~al; @@ -1118,17 +1108,20 @@ struct OptDffWorker sim_buckets[n_val].push_back(idx); } - for (auto &kv : sim_buckets) { - if (GetSize(kv.second) >= 2) { + for (auto &kv : sim_buckets) + if (GetSize(kv.second) >= 2) refined_classes.push_back(std::move(kv.second)); - } - } } - classes = std::move(refined_classes); - if (classes.empty()) - return false; + return refined_classes; + } + std::vector> filter_classes_sat( + std::vector> classes, + const std::vector &bits, + const dict &ff_for_cell, + ModWalker &modwalker + ) { QuickConeSat qcsat(modwalker); std::vector q_lit(bits.size(), -1); std::vector n_lit(bits.size(), -1); @@ -1144,8 +1137,7 @@ struct OptDffWorker if (ff.has_aload) { int al = qcsat.importSigBit(ff.sig_aload); if (!ff.pol_aload) al = qcsat.ez->NOT(al); - int ad = qcsat.importSigBit(ff.sig_ad[eb.idx]); - n = sat_mux(qcsat, al, ad, n); + n = sat_mux(qcsat, al, qcsat.importSigBit(ff.sig_ad[eb.idx]), n); } if (ff.has_arst) { int ar = qcsat.importSigBit(ff.sig_arst); @@ -1170,13 +1162,11 @@ struct OptDffWorker } qcsat.prepare(); - bool any_change = false; std::vector worklist; std::vector in_worklist(GetSize(classes), true); - for (int i = 0; i < GetSize(classes); i++) { + for (int i = 0; i < GetSize(classes); i++) worklist.push_back(i); - } while (!worklist.empty()) { int cls_idx = worklist.back(); @@ -1190,9 +1180,8 @@ struct OptDffWorker for (auto &c : classes) { if (GetSize(c) < 2) continue; int rep = c[0]; - for (int k = 1; k < GetSize(c); k++) { + for (int k = 1; k < GetSize(c); k++) assumptions.push_back(qcsat.ez->IFF(q_lit[rep], q_lit[c[k]])); - } } // Split at counterexamples @@ -1204,14 +1193,12 @@ struct OptDffWorker int query = qcsat.ez->NOT(qcsat.ez->IFF(n_lit[rep], n_lit[cls[i]])); std::vector modelExprs; - - for (int b : cls) { + for (int b : cls) modelExprs.push_back(n_lit[b]); - } std::vector modelVals; assumptions.push_back(query); - + if (qcsat.ez->solve(modelExprs, modelVals, assumptions)) { // SAT -> partition entire class std::vector sub0; @@ -1243,9 +1230,12 @@ struct OptDffWorker } } - if (classes.empty()) - return any_change; + return classes; + } + bool apply_eq_merges(const std::vector> &classes, const std::vector &bits, dict &ff_for_cell) + { + bool any_change = false; dict> remove_bits; // Drive every non-rep Q from its class rep, drop merged bits from their FFs @@ -1283,6 +1273,33 @@ struct OptDffWorker return any_change; } + + bool run_eqbits() + { + if (!opt.sat) + return false; + + std::vector bits; + dict ff_for_cell; + + std::vector> classes = gather_initial_eq_classes(bits, ff_for_cell); + if (classes.empty()) + return false; + + ModWalker modwalker(module->design, module); + + // Simulation prepass + classes = filter_classes_sim(classes, bits, ff_for_cell, modwalker); + if (classes.empty()) + return false; + + // SAT prove + classes = filter_classes_sat(std::move(classes), bits, ff_for_cell, modwalker); + if (classes.empty()) + return false; + + return apply_eq_merges(classes, bits, ff_for_cell); + } }; struct OptDffPass : public Pass { @@ -1320,11 +1337,6 @@ struct OptDffPass : public Pass { log(" all result bits to be set to x. this behavior changes when 'a+0' is\n"); log(" replaced by 'a'. the -keepdc option disables all such optimizations.\n"); log("\n"); - log(" -eqbits\n"); - log(" finds groups of flip flop bits provably holding always-equal values\n"); - log(" across cycles and collapses each group to a single bit, potentially\n"); - log(" reducing the number of required flip flops.\n"); - log("\n"); } void execute(std::vector args, RTLIL::Design *design) override @@ -1337,7 +1349,6 @@ struct OptDffPass : public Pass { opt.simple_dffe = false; opt.keepdc = false; opt.sat = false; - opt.eqbits = false; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -1346,7 +1357,6 @@ struct OptDffPass : public Pass { if (args[argidx] == "-simple-dffe") { opt.simple_dffe = true; continue; } if (args[argidx] == "-keepdc") { opt.keepdc = true; continue; } if (args[argidx] == "-sat") { opt.sat = true; continue; } - if (args[argidx] == "-eqbits") { opt.eqbits = true; continue; } break; } extra_args(args, argidx, design); diff --git a/tests/opt/opt_dff_eqbits.ys b/tests/opt/opt_dff_eqbits.ys index 181b5d0a7..10e9045e4 100644 --- a/tests/opt/opt_dff_eqbits.ys +++ b/tests/opt/opt_dff_eqbits.ys @@ -3,9 +3,9 @@ design -reset read_verilog -sv opt_dff_eqbits_small.sv hierarchy -top test_case techmap -opt_dff -sat -eqbits +opt_dff -sat synth -opt_dff -sat -eqbits +opt_dff -sat opt_clean -purge select -assert-count 2 t:$_SDFF_PN0_ @@ -17,7 +17,7 @@ hierarchy -top test_case prep design -save gold -opt_dff -sat -eqbits +opt_dff -sat design -save gate design -copy-from gold -as gold test_case @@ -32,9 +32,9 @@ design -reset read_verilog -sv opt_dff_eqbits_large.sv hierarchy -top test_case techmap -opt_dff -sat -eqbits +opt_dff -sat synth -opt_dff -sat -eqbits +opt_dff -sat opt_clean -purge select -assert-count 6 t:$_SDFFE_PN0P_ @@ -46,7 +46,7 @@ hierarchy -top test_case prep design -save gold -opt_dff -sat -eqbits +opt_dff -sat design -save gate design -copy-from gold -as gold test_case