issue44 liberty pos/neg latches

Signed-off-by: James Cherry <cherry@parallaxsw.com>
This commit is contained in:
James Cherry 2024-07-07 17:56:55 -07:00
parent 2c4b81d0f4
commit 3ba6f0e527
6 changed files with 133 additions and 92 deletions

View File

@ -74,6 +74,7 @@ typedef Map<const OperatingConditions*, LibertyPort*> ScaledPortMap;
typedef Map<const char *, ModeDef*, CharPtrLess> ModeDefMap; typedef Map<const char *, ModeDef*, CharPtrLess> ModeDefMap;
typedef Map<const char *, ModeValueDef*, CharPtrLess> ModeValueMap; typedef Map<const char *, ModeValueDef*, CharPtrLess> ModeValueMap;
typedef Map<TimingArcSet*, LatchEnable*> LatchEnableMap; typedef Map<TimingArcSet*, LatchEnable*> LatchEnableMap;
typedef Vector<LatchEnable*> LatchEnableSeq;
typedef Map<const char *, OcvDerate*, CharPtrLess> OcvDerateMap; typedef Map<const char *, OcvDerate*, CharPtrLess> OcvDerateMap;
typedef Vector<InternalPowerAttrs*> InternalPowerAttrsSeq; typedef Vector<InternalPowerAttrs*> InternalPowerAttrsSeq;
typedef Map<const char *, float, CharPtrLess> SupplyVoltageMap; typedef Map<const char *, float, CharPtrLess> SupplyVoltageMap;
@ -476,8 +477,8 @@ public:
// Return values. // Return values.
LibertyPort *&enable_port, LibertyPort *&enable_port,
FuncExpr *&enable_func, FuncExpr *&enable_func,
RiseFall *&enable_rf) const; const RiseFall *&enable_rf) const;
RiseFall *latchCheckEnableEdge(TimingArcSet *check_set); const RiseFall *latchCheckEnableEdge(TimingArcSet *check_set);
bool isDisabledConstraint() const { return is_disabled_constraint_; } bool isDisabledConstraint() const { return is_disabled_constraint_; }
LibertyCell *cornerCell(const Corner *corner, LibertyCell *cornerCell(const Corner *corner,
const MinMax *min_max); const MinMax *min_max);
@ -539,18 +540,26 @@ protected:
void setLibertyLibrary(LibertyLibrary *library); void setLibertyLibrary(LibertyLibrary *library);
void makeLatchEnables(Report *report, void makeLatchEnables(Report *report,
Debug *debug); Debug *debug);
FuncExpr *findLatchEnableFunc(LibertyPort *data, FuncExpr *findLatchEnableFunc(const LibertyPort *d,
LibertyPort *enable) const; const LibertyPort *en,
const RiseFall *en_rf) const;
LatchEnable *makeLatchEnable(LibertyPort *d, LatchEnable *makeLatchEnable(LibertyPort *d,
LibertyPort *en, LibertyPort *en,
const RiseFall *en_rf,
LibertyPort *q, LibertyPort *q,
TimingArcSet *d_to_q, TimingArcSet *d_to_q,
TimingArcSet *en_to_q, TimingArcSet *en_to_q,
TimingArcSet *setup_check, TimingArcSet *setup_check,
Debug *debug); Debug *debug);
TimingArcSet *findLatchSetup(const LibertyPort *d,
const LibertyPort *en,
const RiseFall *en_rf,
const LibertyPort *q,
Report *report);
void findDefaultCondArcs(); void findDefaultCondArcs();
void translatePresetClrCheckRoles(); void translatePresetClrCheckRoles();
void inferLatchRoles(Debug *debug); void inferLatchRoles(Report *report,
Debug *debug);
void deleteInternalPowerAttrs(); void deleteInternalPowerAttrs();
void makeTimingArcMap(Report *report); void makeTimingArcMap(Report *report);
void makeTimingArcPortMaps(); void makeTimingArcPortMaps();
@ -596,6 +605,7 @@ protected:
LatchEnableMap latch_d_to_q_map_; LatchEnableMap latch_d_to_q_map_;
// Latch EN->D setup to LatchEnable. // Latch EN->D setup to LatchEnable.
LatchEnableMap latch_check_map_; LatchEnableMap latch_check_map_;
LatchEnableSeq latch_enables_;
// Ports that have latch D->Q timing arc sets from them. // Ports that have latch D->Q timing arc sets from them.
LibertyPortSet latch_data_ports_; LibertyPortSet latch_data_ports_;
float ocv_arc_depth_; float ocv_arc_depth_;

View File

@ -950,7 +950,7 @@ LibertyCell::LibertyCell(LibertyLibrary *library,
LibertyCell::~LibertyCell() LibertyCell::~LibertyCell()
{ {
mode_defs_.deleteContents(); mode_defs_.deleteContents();
latch_d_to_q_map_.deleteContents(); latch_enables_.deleteContents();
timing_arc_sets_.deleteContents(); timing_arc_sets_.deleteContents();
port_timing_arc_set_map_.deleteContents(); port_timing_arc_set_map_.deleteContents();
@ -1306,7 +1306,7 @@ LibertyCell::finish(bool infer_latches,
findDefaultCondArcs(); findDefaultCondArcs();
makeLatchEnables(report, debug); makeLatchEnables(report, debug);
if (infer_latches) if (infer_latches)
inferLatchRoles(debug); inferLatchRoles(report, debug);
} }
void void
@ -1675,7 +1675,7 @@ class LatchEnable
public: public:
LatchEnable(LibertyPort *data, LatchEnable(LibertyPort *data,
LibertyPort *enable, LibertyPort *enable,
RiseFall *enable_edge, const RiseFall *enable_edge,
FuncExpr *enable_func, FuncExpr *enable_func,
LibertyPort *output, LibertyPort *output,
TimingArcSet *d_to_q, TimingArcSet *d_to_q,
@ -1685,7 +1685,7 @@ public:
LibertyPort *output() const { return output_; } LibertyPort *output() const { return output_; }
LibertyPort *enable() const { return enable_; } LibertyPort *enable() const { return enable_; }
FuncExpr *enableFunc() const { return enable_func_; } FuncExpr *enableFunc() const { return enable_func_; }
RiseFall *enableEdge() const { return enable_edge_; } const RiseFall *enableEdge() const { return enable_edge_; }
TimingArcSet *dToQ() const { return d_to_q_; } TimingArcSet *dToQ() const { return d_to_q_; }
TimingArcSet *enToQ() const { return en_to_q_; } TimingArcSet *enToQ() const { return en_to_q_; }
TimingArcSet *setupCheck() const { return setup_check_; } TimingArcSet *setupCheck() const { return setup_check_; }
@ -1693,7 +1693,7 @@ public:
private: private:
LibertyPort *data_; LibertyPort *data_;
LibertyPort *enable_; LibertyPort *enable_;
RiseFall *enable_edge_; const RiseFall *enable_edge_;
FuncExpr *enable_func_; FuncExpr *enable_func_;
LibertyPort *output_; LibertyPort *output_;
TimingArcSet *d_to_q_; TimingArcSet *d_to_q_;
@ -1703,7 +1703,7 @@ private:
LatchEnable::LatchEnable(LibertyPort *data, LatchEnable::LatchEnable(LibertyPort *data,
LibertyPort *enable, LibertyPort *enable,
RiseFall *enable_edge, const RiseFall *enable_edge,
FuncExpr *enable_func, FuncExpr *enable_func,
LibertyPort *output, LibertyPort *output,
TimingArcSet *d_to_q, TimingArcSet *d_to_q,
@ -1736,66 +1736,101 @@ LibertyCell::makeLatchEnables(Report *report,
for (TimingArcSet *d_to_q : timingArcSets(nullptr, q)) { for (TimingArcSet *d_to_q : timingArcSets(nullptr, q)) {
if (d_to_q->role() == TimingRole::latchDtoQ()) { if (d_to_q->role() == TimingRole::latchDtoQ()) {
LibertyPort *d = d_to_q->from(); LibertyPort *d = d_to_q->from();
for (TimingArcSet *setup_check : timingArcSets(en, d)) { const RiseFall *en_rf = en_to_q->isRisingFallingEdge();
if (setup_check->role() == TimingRole::setup()) { if (en_rf) {
LatchEnable *latch_enable = makeLatchEnable(d, en, q, d_to_q, TimingArcSet *setup_check = findLatchSetup(d, en, en_rf, q, report);
en_to_q, LatchEnable *latch_enable = makeLatchEnable(d, en, en_rf, q, d_to_q,
setup_check, en_to_q,
debug); setup_check,
for (TimingArc *check_arc : setup_check->arcs()) { debug);
RiseFall *en_rf = latch_enable->enableEdge(); FuncExpr *en_func = latch_enable->enableFunc();
RiseFall *check_rf = check_arc->fromEdge()->asRiseFall(); if (en_func) {
if (check_rf == en_rf) TimingSense en_sense = en_func->portTimingSense(en);
report->warn(1113, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with %s -> %s setup_%s check.", if (en_sense == TimingSense::positive_unate
library_->name(), && en_rf != RiseFall::rise())
name_, report->warn(1114, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function positive sense.",
en->name(), library_->name(),
q->name(), name_,
en_rf == RiseFall::rise()?"rising":"falling", en->name(),
en->name(), q->name(),
d->name(), en_rf == RiseFall::rise()?"rising":"falling");
check_rf==RiseFall::rise()?"rising":"falling"); else if (en_sense == TimingSense::negative_unate
FuncExpr *en_func = latch_enable->enableFunc(); && en_rf != RiseFall::fall())
if (en_func) { report->warn(1115, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function negative sense.",
TimingSense en_sense = en_func->portTimingSense(en); library_->name(),
if (en_sense == TimingSense::positive_unate name_,
&& en_rf != RiseFall::rise()) en->name(),
report->warn(1114, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function positive sense.", q->name(),
library_->name(), en_rf == RiseFall::rise()?"rising":"falling");
name_, }
en->name(), }
q->name(), }
en_rf == RiseFall::rise()?"rising":"falling");
else if (en_sense == TimingSense::negative_unate
&& en_rf != RiseFall::fall())
report->warn(1115, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function negative sense.",
library_->name(),
name_,
en->name(),
q->name(),
en_rf == RiseFall::rise()?"rising":"falling");
}
}
}
}
}
} }
} }
} }
} }
} }
TimingArcSet *
LibertyCell::findLatchSetup(const LibertyPort *d,
const LibertyPort *en,
const RiseFall *en_rf,
const LibertyPort *q,
Report *report)
{
TimingArcSetSeq en_d_arcs = timingArcSets(en, d);
// First search for setup checks with the correct clock edge.
for (TimingArcSet *arc_set : en_d_arcs) {
if (arc_set->role() == TimingRole::setup()) {
for (TimingArc *arc : arc_set->arcs()) {
const RiseFall *from_rf = arc->fromEdge()->asRiseFall();
if (from_rf == en_rf->opposite())
return arc_set;
}
}
}
// Then search for setup checks with the opposite clock edge.
for (TimingArcSet *arc_set : en_d_arcs) {
if (arc_set->role() == TimingRole::setup()) {
for (TimingArc *arc : arc_set->arcs()) {
const RiseFall *from_rf = arc->fromEdge()->asRiseFall();
if (from_rf == en_rf) {
report->warn(1113, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with %s -> %s setup_%s check.",
library_->name(),
name_,
en->name(),
q->name(),
en_rf == RiseFall::rise() ? "rising" : "falling",
en->name(),
d->name(),
from_rf == RiseFall::rise() ? "rising" : "falling");
return arc_set;
}
}
}
}
return nullptr;
}
FuncExpr * FuncExpr *
LibertyCell::findLatchEnableFunc(LibertyPort *data, LibertyCell::findLatchEnableFunc(const LibertyPort *d,
LibertyPort *enable) const const LibertyPort *en,
const RiseFall *en_rf) const
{ {
for (auto seq : sequentials_) { for (auto seq : sequentials_) {
if (seq->isLatch() if (seq->isLatch()
&& seq->data() && seq->data()
&& seq->data()->hasPort(data) && seq->data()->hasPort(d)
&& seq->clock() && seq->clock()
&& seq->clock()->hasPort(enable)) && seq->clock()->hasPort(en)) {
return seq->clock(); FuncExpr *en_func = seq->clock();
TimingSense en_sense = en_func->portTimingSense(en);
if ((en_sense == TimingSense::positive_unate
&& en_rf == RiseFall::rise())
|| (en_sense == TimingSense::negative_unate
&& en_rf == RiseFall::fall()))
return seq->clock();
}
} }
return nullptr; return nullptr;
} }
@ -1803,29 +1838,31 @@ LibertyCell::findLatchEnableFunc(LibertyPort *data,
LatchEnable * LatchEnable *
LibertyCell::makeLatchEnable(LibertyPort *d, LibertyCell::makeLatchEnable(LibertyPort *d,
LibertyPort *en, LibertyPort *en,
const RiseFall *en_rf,
LibertyPort *q, LibertyPort *q,
TimingArcSet *d_to_q, TimingArcSet *d_to_q,
TimingArcSet *en_to_q, TimingArcSet *en_to_q,
TimingArcSet *setup_check, TimingArcSet *setup_check,
Debug *debug) Debug *debug)
{ {
RiseFall *en_rf = en_to_q->isRisingFallingEdge(); FuncExpr *en_func = findLatchEnableFunc(d, en, en_rf);
FuncExpr *en_func = findLatchEnableFunc(d, en);
LatchEnable *latch_enable = new LatchEnable(d, en, en_rf, en_func, q, LatchEnable *latch_enable = new LatchEnable(d, en, en_rf, en_func, q,
d_to_q, en_to_q, setup_check); d_to_q, en_to_q, setup_check);
// Multiple enables for D->Q pairs are not supported. latch_enables_.push_back(latch_enable);
if (latch_d_to_q_map_[d_to_q])
delete latch_d_to_q_map_[d_to_q];
latch_d_to_q_map_[d_to_q] = latch_enable; latch_d_to_q_map_[d_to_q] = latch_enable;
latch_check_map_[setup_check] = latch_enable; latch_check_map_[setup_check] = latch_enable;
latch_data_ports_.insert(d); latch_data_ports_.insert(d);
debugPrint(debug, "liberty", 2, "latch d=%s en=%s q=%s", debugPrint(debug, "liberty", 2, "latch d=%s en=%s %s q=%s",
d->name(), en->name(), q->name()); d->name(),
en->name(),
en_rf->name(),
q->name());
return latch_enable; return latch_enable;
} }
void void
LibertyCell::inferLatchRoles(Debug *debug) LibertyCell::inferLatchRoles(Report *report,
Debug *debug)
{ {
if (hasInferedRegTimingArcs()) { if (hasInferedRegTimingArcs()) {
// Hunt down potential latch D/EN/Q triples. // Hunt down potential latch D/EN/Q triples.
@ -1845,15 +1882,14 @@ LibertyCell::inferLatchRoles(Debug *debug)
// Previously identified as D->Q arc. // Previously identified as D->Q arc.
|| d_to_q_role == TimingRole::latchDtoQ()) { || d_to_q_role == TimingRole::latchDtoQ()) {
LibertyPort *d = d_to_q->from(); LibertyPort *d = d_to_q->from();
// Check for setup check from en -> d. const RiseFall *en_rf = en_to_q->isRisingFallingEdge();
for (TimingArcSet *setup_check : timingArcSets(en, d)) { if (en_rf) {
if (setup_check->role() == TimingRole::setup()) { TimingArcSet *setup_check = findLatchSetup(d, en, en_rf, q, report);
makeLatchEnable(d, en, q, d_to_q, en_to_q, setup_check, debug); makeLatchEnable(d, en, en_rf, q, d_to_q, en_to_q, setup_check, debug);
d_to_q->setRole(TimingRole::latchDtoQ()); d_to_q->setRole(TimingRole::latchDtoQ());
en_to_q->setRole(TimingRole::latchEnToQ()); en_to_q->setRole(TimingRole::latchEnToQ());
} }
} }
}
} }
} }
} }
@ -1871,7 +1907,7 @@ LibertyCell::latchEnable(TimingArcSet *d_to_q_set,
// Return values. // Return values.
LibertyPort *&enable_port, LibertyPort *&enable_port,
FuncExpr *&enable_func, FuncExpr *&enable_func,
RiseFall *&enable_edge) const const RiseFall *&enable_edge) const
{ {
enable_port = nullptr; enable_port = nullptr;
LatchEnable *latch_enable = latch_d_to_q_map_.findKey(d_to_q_set); LatchEnable *latch_enable = latch_d_to_q_map_.findKey(d_to_q_set);
@ -1882,7 +1918,7 @@ LibertyCell::latchEnable(TimingArcSet *d_to_q_set,
} }
} }
RiseFall * const RiseFall *
LibertyCell::latchCheckEnableEdge(TimingArcSet *check_set) LibertyCell::latchCheckEnableEdge(TimingArcSet *check_set)
{ {
LatchEnable *latch_enable = latch_check_map_.findKey(check_set); LatchEnable *latch_enable = latch_check_map_.findKey(check_set);

View File

@ -269,7 +269,7 @@ Latches::latchEnablePath(Path *q_path,
const PathAnalysisPt *tgt_clk_path_ap = path_ap->tgtClkAnalysisPt(); const PathAnalysisPt *tgt_clk_path_ap = path_ap->tgtClkAnalysisPt();
const Instance *latch = network_->instance(q_path->pin(this)); const Instance *latch = network_->instance(q_path->pin(this));
Vertex *en_vertex; Vertex *en_vertex;
RiseFall *en_rf; const RiseFall *en_rf;
LatchEnableState state; LatchEnableState state;
latchDtoQEnable(d_q_edge, latch, en_vertex, en_rf, state); latchDtoQEnable(d_q_edge, latch, en_vertex, en_rf, state);
if (state == LatchEnableState::enabled) { if (state == LatchEnableState::enabled) {
@ -303,7 +303,7 @@ Latches::latchOutArrival(Path *data_path,
Vertex *data_vertex = d_q_edge->from(graph_); Vertex *data_vertex = d_q_edge->from(graph_);
const Instance *inst = network_->instance(data_vertex->pin()); const Instance *inst = network_->instance(data_vertex->pin());
Vertex *enable_vertex; Vertex *enable_vertex;
RiseFall *enable_rf; const RiseFall *enable_rf;
LatchEnableState state; LatchEnableState state;
latchDtoQEnable(d_q_edge, inst, enable_vertex, enable_rf, state); latchDtoQEnable(d_q_edge, inst, enable_vertex, enable_rf, state);
// Latch enable may be missing if library is malformed. // Latch enable may be missing if library is malformed.
@ -466,7 +466,7 @@ Latches::latchDtoQEnable(Edge *d_q_edge,
const Instance *inst, const Instance *inst,
// Return values. // Return values.
Vertex *&enable_vertex, Vertex *&enable_vertex,
RiseFall *&enable_rf, const RiseFall *&enable_rf,
LatchEnableState &state) const LatchEnableState &state) const
{ {
enable_vertex = nullptr; enable_vertex = nullptr;
@ -515,7 +515,7 @@ Latches::latchDtoQState(Edge *edge) const
const Pin *from_pin = from_vertex->pin(); const Pin *from_pin = from_vertex->pin();
const Instance *inst = network_->instance(from_pin); const Instance *inst = network_->instance(from_pin);
Vertex *enable_vertex; Vertex *enable_vertex;
RiseFall *enable_rf; const RiseFall *enable_rf;
LatchEnableState state; LatchEnableState state;
latchDtoQEnable(edge, inst, enable_vertex, enable_rf, state); latchDtoQEnable(edge, inst, enable_vertex, enable_rf, state);
return state; return state;

View File

@ -77,7 +77,7 @@ public:
const Instance *inst, const Instance *inst,
// Return values. // Return values.
Vertex *&enable_vertex, Vertex *&enable_vertex,
RiseFall *&enable_rf, const RiseFall *&enable_rf,
LatchEnableState &state) const; LatchEnableState &state) const;
LatchEnableState latchDtoQState(Edge *d_q_edge) const; LatchEnableState latchDtoQState(Edge *d_q_edge) const;
void latchEnableOtherPath(Path *path, void latchEnableOtherPath(Path *path,

View File

@ -580,14 +580,9 @@ ReportPath::reportEndpoint(const PathEndLatchCheck *end)
const char * const char *
ReportPath::latchDesc(const PathEndLatchCheck *end) ReportPath::latchDesc(const PathEndLatchCheck *end)
{ {
// Liberty latch descriptions can have timing checks to the
// wrong edge of the enable, so look up the EN->Q arcs and use
// them to characterize the latch as positive/negative.
TimingArc *check_arc = end->checkArc(); TimingArc *check_arc = end->checkArc();
TimingArcSet *check_set = check_arc->set(); const RiseFall *en_rf = check_arc->fromEdge()->asRiseFall()->opposite();
LibertyCell *cell = check_set->from()->libertyCell(); return latchDesc(en_rf);
RiseFall *enable_rf = cell->latchCheckEnableEdge(check_set);
return latchDesc(enable_rf);
} }
void void
@ -3257,7 +3252,7 @@ ReportPath::edgeRegLatchDesc(Edge *first_edge,
if (cell) { if (cell) {
LibertyPort *enable_port; LibertyPort *enable_port;
FuncExpr *enable_func; FuncExpr *enable_func;
RiseFall *enable_rf; const RiseFall *enable_rf;
cell->latchEnable(first_edge->timingArcSet(), cell->latchEnable(first_edge->timingArcSet(),
enable_port, enable_func, enable_rf); enable_port, enable_func, enable_rf);
return latchDesc(enable_rf); return latchDesc(enable_rf);

View File

@ -4883,7 +4883,7 @@ latch_d_to_q_en()
TimingArcSet *d_q_set = self->timingArcSet(); TimingArcSet *d_q_set = self->timingArcSet();
LibertyPort *enable_port; LibertyPort *enable_port;
FuncExpr *enable_func; FuncExpr *enable_func;
RiseFall *enable_rf; const RiseFall *enable_rf;
lib_cell->latchEnable(d_q_set, enable_port, enable_func, enable_rf); lib_cell->latchEnable(d_q_set, enable_port, enable_func, enable_rf);
const char *en_name = enable_port->name(); const char *en_name = enable_port->name();
return stringPrintTmp("%s %s", en_name, enable_rf->asString()); return stringPrintTmp("%s %s", en_name, enable_rf->asString());