mirror of https://github.com/YosysHQ/yosys.git
Merge 68df0be7d2 into 05805e8b93
This commit is contained in:
commit
c875f3223c
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue