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 709b5fc4c..500df142e 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 @@ -919,6 +920,386 @@ 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; + } + + std::vector> gather_initial_eq_classes(std::vector &bits, dict &ff_for_cell) + { + std::vector keys; + + // 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); + } + } + + dict> buckets; + for (int i = 0; i < GetSize(bits); i++) + buckets[keys[i]].push_back(i); + + std::vector> classes; + for (auto &kv : buckets) + if (GetSize(kv.second) >= 2) + classes.push_back(std::move(kv.second)); + + return classes; + } + + 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); + + // 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)); + } + + 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); + + // 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); + n = sat_mux(qcsat, al, qcsat.importSigBit(ff.sig_ad[eb.idx]), 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(); + std::vector worklist; + std::vector in_worklist(GetSize(classes), true); + + for (int i = 0; i < GetSize(classes); i++) + worklist.push_back(i); + + 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]])); + } + + // Split at counterexamples + int rep = cls[0]; + for (int i = 1; i < GetSize(cls); i++) { + // Trivially equivalent + 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 (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; + std::vector sub1; + + 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 + } + } + + 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 + 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); + 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 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 { @@ -987,6 +1368,8 @@ struct OptDffPass : public Pass { did_something = true; if (worker.run_constbits()) did_something = true; + if (worker.run_eqbits()) + did_something = true; } if (did_something) 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