This commit is contained in:
nella 2026-06-12 09:43:24 -07:00 committed by GitHub
commit c875f3223c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 779 additions and 0 deletions

79
kernel/bitsim.h Normal file
View File

@ -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<SigBit, uint64_t> 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

View File

@ -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 <stdio.h>
@ -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<std::vector<int>> gather_initial_eq_classes(std::vector<EqBit> &bits, dict<Cell *, FfData> &ff_for_cell)
{
std::vector<SigKey> 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<SigKey, std::vector<int>> buckets;
for (int i = 0; i < GetSize(bits); i++)
buckets[keys[i]].push_back(i);
std::vector<std::vector<int>> classes;
for (auto &kv : buckets)
if (GetSize(kv.second) >= 2)
classes.push_back(std::move(kv.second));
return classes;
}
std::vector<std::vector<int>> filter_classes_sim(
const std::vector<std::vector<int>> &classes,
const std::vector<EqBit> &bits,
const dict<Cell *, FfData> &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<std::vector<int>> refined_classes;
for (auto &cls : classes) {
dict<uint64_t, std::vector<int>> 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<std::vector<int>> filter_classes_sat(
std::vector<std::vector<int>> classes,
const std::vector<EqBit> &bits,
const dict<Cell *, FfData> &ff_for_cell,
ModWalker &modwalker
) {
QuickConeSat qcsat(modwalker);
std::vector<int> q_lit(bits.size(), -1);
std::vector<int> 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<int> worklist;
std::vector<bool> 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<int> 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<int> modelExprs;
for (int b : cls)
modelExprs.push_back(n_lit[b]);
std::vector<bool> modelVals;
assumptions.push_back(query);
if (qcsat.ez->solve(modelExprs, modelVals, assumptions)) {
// SAT -> partition entire class
std::vector<int> sub0;
std::vector<int> 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<std::vector<int>> &classes, const std::vector<EqBit> &bits, dict<Cell *, FfData> &ff_for_cell)
{
bool any_change = false;
dict<Cell *, std::set<int>> 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<int> &drop = kv.second;
FfData &ff = ff_for_cell.at(cell);
std::vector<int> 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<EqBit> bits;
dict<Cell *, FfData> ff_for_cell;
std::vector<std::vector<int>> 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)

View File

@ -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

View File

@ -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

View File

@ -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