mirror of
https://github.com/YosysHQ/yosys
synced 2026-07-04 14:36:10 +00:00
Recognise asynchronous set/reset.
This commit is contained in:
parent
8869ce61dc
commit
a5bdb29d7f
2 changed files with 177 additions and 18 deletions
|
|
@ -201,6 +201,77 @@ struct proc_dlatch_db_t
|
||||||
sig[index] = State::Sx;
|
sig[index] = State::Sx;
|
||||||
cell->setPort(ID::A, sig);
|
cell->setPort(ID::A, sig);
|
||||||
}
|
}
|
||||||
|
bool sibling_undef = true;
|
||||||
|
for (int i = 0; i < (is_bwmux ? 1 : GetSize(sig_s)); i++)
|
||||||
|
if (sig_b[i*width + index] != State::Sx)
|
||||||
|
sibling_undef = false;
|
||||||
|
if (!sibling_undef) {
|
||||||
|
if (!is_bwmux) {
|
||||||
|
for (int i = 0; i < GetSize(sig_s); i++)
|
||||||
|
n = make_inner(sig_s[i], State::S0, n);
|
||||||
|
} else {
|
||||||
|
n = make_inner(sig_s[index], State::S0, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
children.insert(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < (is_bwmux ? 1 : GetSize(sig_s)); i++) {
|
||||||
|
n = find_mux_feedback(sig_b[i*width + index], needle, set_undef);
|
||||||
|
if (n != false_node) {
|
||||||
|
if (set_undef && sig_b[i*width + index] == needle) {
|
||||||
|
SigSpec sig = cell->getPort(ID::B);
|
||||||
|
sig[i*width + index] = State::Sx;
|
||||||
|
cell->setPort(ID::B, sig);
|
||||||
|
}
|
||||||
|
bool sibling_undef = (sig_a[index] == State::Sx);
|
||||||
|
if (!is_bwmux)
|
||||||
|
for (int j = 0; j < GetSize(sig_s); j++)
|
||||||
|
if (j != i && sig_b[j*width + index] != State::Sx)
|
||||||
|
sibling_undef = false;
|
||||||
|
if (!sibling_undef)
|
||||||
|
n = make_inner(sig_s[is_bwmux ? index : i], State::S1, n);
|
||||||
|
children.insert(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (children.empty())
|
||||||
|
return false_node;
|
||||||
|
|
||||||
|
return make_inner(children);
|
||||||
|
}
|
||||||
|
|
||||||
|
int find_mux_constant(SigBit haystack, State needle, bool set_undef)
|
||||||
|
{
|
||||||
|
if (sigusers[haystack] > 1)
|
||||||
|
set_undef = false;
|
||||||
|
|
||||||
|
if (haystack == SigBit(needle))
|
||||||
|
return true_node;
|
||||||
|
|
||||||
|
auto it = mux_drivers.find(haystack);
|
||||||
|
if (it == mux_drivers.end())
|
||||||
|
return false_node;
|
||||||
|
|
||||||
|
Cell *cell = it->second.first;
|
||||||
|
int index = it->second.second;
|
||||||
|
|
||||||
|
log_assert(cell->type.in(ID($mux), ID($pmux), ID($bwmux)));
|
||||||
|
bool is_bwmux = (cell->type == ID($bwmux));
|
||||||
|
SigSpec sig_a = sigmap(cell->getPort(ID::A));
|
||||||
|
SigSpec sig_b = sigmap(cell->getPort(ID::B));
|
||||||
|
SigSpec sig_s = sigmap(cell->getPort(ID::S));
|
||||||
|
int width = GetSize(sig_a);
|
||||||
|
|
||||||
|
pool<int> children;
|
||||||
|
|
||||||
|
int n = find_mux_constant(sig_a[index], needle, set_undef);
|
||||||
|
if (n != false_node) {
|
||||||
|
if (set_undef && sig_a[index] == SigBit(needle)) {
|
||||||
|
SigSpec sig = cell->getPort(ID::A);
|
||||||
|
sig[index] = State::Sx;
|
||||||
|
cell->setPort(ID::A, sig);
|
||||||
|
}
|
||||||
if (!is_bwmux) {
|
if (!is_bwmux) {
|
||||||
for (int i = 0; i < GetSize(sig_s); i++)
|
for (int i = 0; i < GetSize(sig_s); i++)
|
||||||
n = make_inner(sig_s[i], State::S0, n);
|
n = make_inner(sig_s[i], State::S0, n);
|
||||||
|
|
@ -211,9 +282,9 @@ struct proc_dlatch_db_t
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < (is_bwmux ? 1 : GetSize(sig_s)); i++) {
|
for (int i = 0; i < (is_bwmux ? 1 : GetSize(sig_s)); i++) {
|
||||||
n = find_mux_feedback(sig_b[i*width + index], needle, set_undef);
|
n = find_mux_constant(sig_b[i*width + index], needle, set_undef);
|
||||||
if (n != false_node) {
|
if (n != false_node) {
|
||||||
if (set_undef && sig_b[i*width + index] == needle) {
|
if (set_undef && sig_b[i*width + index] == SigBit(needle)) {
|
||||||
SigSpec sig = cell->getPort(ID::B);
|
SigSpec sig = cell->getPort(ID::B);
|
||||||
sig[i*width + index] = State::Sx;
|
sig[i*width + index] = State::Sx;
|
||||||
cell->setPort(ID::B, sig);
|
cell->setPort(ID::B, sig);
|
||||||
|
|
@ -349,7 +420,7 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc)
|
||||||
{
|
{
|
||||||
RTLIL::SigSig latches_bits, nolatches_bits;
|
RTLIL::SigSig latches_bits, nolatches_bits;
|
||||||
dict<SigBit, SigBit> latches_out_in;
|
dict<SigBit, SigBit> latches_out_in;
|
||||||
dict<SigBit, int> latches_hold;
|
dict<SigBit, int> latches_hold, latches_rst, latches_set;
|
||||||
std::string src = proc->get_src_attribute();
|
std::string src = proc->get_src_attribute();
|
||||||
|
|
||||||
for (auto sr : proc->syncs)
|
for (auto sr : proc->syncs)
|
||||||
|
|
@ -381,15 +452,31 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc)
|
||||||
|
|
||||||
latches_out_in.sort();
|
latches_out_in.sort();
|
||||||
for (auto &it : latches_out_in) {
|
for (auto &it : latches_out_in) {
|
||||||
int n = db.find_mux_feedback(it.second, it.first, true);
|
if (db.find_mux_feedback(it.second, it.first, false) == db.false_node) {
|
||||||
if (n == db.false_node) {
|
|
||||||
nolatches_bits.first.append(it.first);
|
nolatches_bits.first.append(it.first);
|
||||||
nolatches_bits.second.append(it.second);
|
nolatches_bits.second.append(it.second);
|
||||||
} else {
|
continue;
|
||||||
latches_bits.first.append(it.first);
|
|
||||||
latches_bits.second.append(it.second);
|
|
||||||
latches_hold[it.first] = n;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
latches_bits.first.append(it.first);
|
||||||
|
latches_bits.second.append(it.second);
|
||||||
|
int nrst = db.find_mux_constant(it.second, State::S0, false);
|
||||||
|
int nset = db.find_mux_constant(it.second, State::S1, false);
|
||||||
|
bool has_rst = (nrst != db.false_node);
|
||||||
|
bool has_set = (nset != db.false_node);
|
||||||
|
|
||||||
|
if (has_rst && !has_set)
|
||||||
|
nrst = db.find_mux_constant(it.second, State::S0, true);
|
||||||
|
else if (has_set && !has_rst)
|
||||||
|
nset = db.find_mux_constant(it.second, State::S1, true);
|
||||||
|
else
|
||||||
|
nrst = nset = db.false_node;
|
||||||
|
|
||||||
|
int n = db.find_mux_feedback(it.second, it.first, true);
|
||||||
|
log_assert(n != db.false_node);
|
||||||
|
latches_hold[it.first] = n;
|
||||||
|
latches_rst[it.first] = nrst;
|
||||||
|
latches_set[it.first] = nset;
|
||||||
}
|
}
|
||||||
|
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
|
|
@ -423,20 +510,35 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc)
|
||||||
while (offset < GetSize(latches_bits.first))
|
while (offset < GetSize(latches_bits.first))
|
||||||
{
|
{
|
||||||
int width = 1;
|
int width = 1;
|
||||||
int n = latches_hold[latches_bits.first[offset]];
|
SigBit obit = latches_bits.first[offset];
|
||||||
Wire *w = latches_bits.first[offset].wire;
|
int n = latches_hold[obit];
|
||||||
|
int nrst = latches_rst[obit];
|
||||||
|
int nset = latches_set[obit];
|
||||||
|
Wire *w = obit.wire;
|
||||||
|
|
||||||
if (w != nullptr)
|
if (w != nullptr)
|
||||||
{
|
{
|
||||||
while (offset+width < GetSize(latches_bits.first) &&
|
while (offset+width < GetSize(latches_bits.first) &&
|
||||||
n == latches_hold[latches_bits.first[offset+width]] &&
|
n == latches_hold[latches_bits.first[offset+width]] &&
|
||||||
|
nrst == latches_rst[latches_bits.first[offset+width]] &&
|
||||||
|
nset == latches_set[latches_bits.first[offset+width]] &&
|
||||||
w == latches_bits.first[offset+width].wire)
|
w == latches_bits.first[offset+width].wire)
|
||||||
width++;
|
width++;
|
||||||
|
|
||||||
SigSpec lhs = latches_bits.first.extract(offset, width);
|
SigSpec lhs = latches_bits.first.extract(offset, width);
|
||||||
SigSpec rhs = latches_bits.second.extract(offset, width);
|
SigSpec rhs = latches_bits.second.extract(offset, width);
|
||||||
|
|
||||||
Cell *cell = db.module->addDlatch(NEW_ID, db.module->Not(NEW_ID, db.make_hold(n, src)), rhs, lhs);
|
SigBit en = db.module->Not(NEW_ID, db.make_hold(n, src));
|
||||||
|
bool has_rst = (nrst != db.false_node);
|
||||||
|
bool has_set = (nset != db.false_node);
|
||||||
|
|
||||||
|
Cell *cell;
|
||||||
|
if (has_rst)
|
||||||
|
cell = db.module->addAdlatch(NEW_ID, en, db.make_hold(nrst, src), rhs, lhs, RTLIL::Const(State::S0, width));
|
||||||
|
else if (has_set)
|
||||||
|
cell = db.module->addAdlatch(NEW_ID, en, db.make_hold(nset, src), rhs, lhs, RTLIL::Const(State::S1, width));
|
||||||
|
else
|
||||||
|
cell = db.module->addDlatch(NEW_ID, en, rhs, lhs);
|
||||||
cell->set_src_attribute(src);
|
cell->set_src_attribute(src);
|
||||||
db.generated_dlatches.insert(cell);
|
db.generated_dlatches.insert(cell);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -370,10 +370,16 @@ struct DffLegalizePass : public Pass {
|
||||||
else
|
else
|
||||||
fail_ff(ff, "initialized dffs with async set and reset are not supported");
|
fail_ff(ff, "initialized dffs with async set and reset are not supported");
|
||||||
} else {
|
} else {
|
||||||
if (!supported_cells[FF_DLATCHSR])
|
if (!supported_dlatch) {
|
||||||
fail_ff(ff, "dlatch with async set and reset are not supported");
|
if (!supported_cells[FF_DLATCHSR])
|
||||||
else
|
fail_ff(ff, "dlatch with async set and reset are not supported");
|
||||||
fail_ff(ff, "initialized dlatch with async set and reset are not supported");
|
else
|
||||||
|
fail_ff(ff, "initialized dlatch with async set and reset are not supported");
|
||||||
|
}
|
||||||
|
if (ff.cell)
|
||||||
|
log_warning("Emulating async set + reset latch with a plain D latch and logic for %s.%s\n", ff.module->name.unescape(), ff.cell->name.unescape());
|
||||||
|
emulate_dlatch(ff);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -449,6 +455,51 @@ struct DffLegalizePass : public Pass {
|
||||||
legalize_ff(ff_sel);
|
legalize_ff(ff_sel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void emulate_dlatch(FfData &ff) {
|
||||||
|
// emulate adlatch or dlatchsr
|
||||||
|
log_assert(!ff.has_clk);
|
||||||
|
log_assert(ff.has_aload);
|
||||||
|
log_assert(ff.width == 1);
|
||||||
|
|
||||||
|
auto active_high = [&](SigBit sig, bool pol) -> SigBit {
|
||||||
|
if (pol)
|
||||||
|
return sig;
|
||||||
|
return ff.is_fine ? ff.module->NotGate(NEW_ID, sig) : ff.module->Not(NEW_ID, sig)[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
auto do_mux = [&](SigBit a, SigBit b, SigBit s) -> SigBit {
|
||||||
|
return ff.is_fine ? ff.module->MuxGate(NEW_ID, a, b, s) : ff.module->Mux(NEW_ID, a, b, s)[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
auto do_or = [&](SigBit a, SigBit b) -> SigBit {
|
||||||
|
return ff.is_fine ? ff.module->OrGate(NEW_ID, a, b) : ff.module->Or(NEW_ID, a, b)[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
SigBit en = active_high(ff.sig_aload, ff.pol_aload);
|
||||||
|
SigBit d = ff.sig_ad;
|
||||||
|
|
||||||
|
if (ff.has_sr) {
|
||||||
|
SigBit set = active_high(ff.sig_set[0], ff.pol_set);
|
||||||
|
SigBit clr = active_high(ff.sig_clr[0], ff.pol_clr);
|
||||||
|
// clr > set > load > hold
|
||||||
|
d = do_mux(d, State::S1, set);
|
||||||
|
d = do_mux(d, State::S0, clr);
|
||||||
|
en = do_or(en, do_or(set, clr));
|
||||||
|
ff.has_sr = false;
|
||||||
|
}
|
||||||
|
if (ff.has_arst) {
|
||||||
|
SigBit arst = active_high(ff.sig_arst[0], ff.pol_arst);
|
||||||
|
d = do_mux(d, ff.val_arst[0], arst);
|
||||||
|
en = do_or(en, arst);
|
||||||
|
ff.has_arst = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ff.sig_ad = d;
|
||||||
|
ff.sig_aload = en;
|
||||||
|
ff.pol_aload = true;
|
||||||
|
legalize_dlatch(ff);
|
||||||
|
}
|
||||||
|
|
||||||
void legalize_dff(FfData &ff) {
|
void legalize_dff(FfData &ff) {
|
||||||
if (!try_flip(ff, supported_dff)) {
|
if (!try_flip(ff, supported_dff)) {
|
||||||
if (!supported_dff)
|
if (!supported_dff)
|
||||||
|
|
@ -742,8 +793,14 @@ struct DffLegalizePass : public Pass {
|
||||||
|
|
||||||
void legalize_adlatch(FfData &ff) {
|
void legalize_adlatch(FfData &ff) {
|
||||||
if (!try_flip(ff, supported_adlatch)) {
|
if (!try_flip(ff, supported_adlatch)) {
|
||||||
if (!supported_adlatch)
|
if (!supported_adlatch) {
|
||||||
fail_ff(ff, "D latches with async set or reset are not supported");
|
if (!supported_dlatch)
|
||||||
|
fail_ff(ff, "D latches with async set or reset are not supported");
|
||||||
|
if (ff.cell)
|
||||||
|
log_warning("Emulating async reset latch with a plain D latch and logic for %s.%s\n", ff.module->name.unescape(), ff.cell->name.unescape());
|
||||||
|
emulate_dlatch(ff);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!(supported_dlatch & (INIT_0 | INIT_1)))
|
if (!(supported_dlatch & (INIT_0 | INIT_1)))
|
||||||
fail_ff(ff, "initialized D latches are not supported");
|
fail_ff(ff, "initialized D latches are not supported");
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue