diff --git a/include/sta/Liberty.hh b/include/sta/Liberty.hh index fd8868ec..704cf8e5 100644 --- a/include/sta/Liberty.hh +++ b/include/sta/Liberty.hh @@ -74,6 +74,7 @@ typedef Map ScaledPortMap; typedef Map ModeDefMap; typedef Map ModeValueMap; typedef Map LatchEnableMap; +typedef Vector LatchEnableSeq; typedef Map OcvDerateMap; typedef Vector InternalPowerAttrsSeq; typedef Map SupplyVoltageMap; @@ -476,8 +477,8 @@ public: // Return values. LibertyPort *&enable_port, FuncExpr *&enable_func, - RiseFall *&enable_rf) const; - RiseFall *latchCheckEnableEdge(TimingArcSet *check_set); + const RiseFall *&enable_rf) const; + const RiseFall *latchCheckEnableEdge(TimingArcSet *check_set); bool isDisabledConstraint() const { return is_disabled_constraint_; } LibertyCell *cornerCell(const Corner *corner, const MinMax *min_max); @@ -539,18 +540,26 @@ protected: void setLibertyLibrary(LibertyLibrary *library); void makeLatchEnables(Report *report, Debug *debug); - FuncExpr *findLatchEnableFunc(LibertyPort *data, - LibertyPort *enable) const; + FuncExpr *findLatchEnableFunc(const LibertyPort *d, + const LibertyPort *en, + const RiseFall *en_rf) const; LatchEnable *makeLatchEnable(LibertyPort *d, LibertyPort *en, + const RiseFall *en_rf, LibertyPort *q, TimingArcSet *d_to_q, TimingArcSet *en_to_q, TimingArcSet *setup_check, Debug *debug); + TimingArcSet *findLatchSetup(const LibertyPort *d, + const LibertyPort *en, + const RiseFall *en_rf, + const LibertyPort *q, + Report *report); void findDefaultCondArcs(); void translatePresetClrCheckRoles(); - void inferLatchRoles(Debug *debug); + void inferLatchRoles(Report *report, + Debug *debug); void deleteInternalPowerAttrs(); void makeTimingArcMap(Report *report); void makeTimingArcPortMaps(); @@ -596,6 +605,7 @@ protected: LatchEnableMap latch_d_to_q_map_; // Latch EN->D setup to LatchEnable. LatchEnableMap latch_check_map_; + LatchEnableSeq latch_enables_; // Ports that have latch D->Q timing arc sets from them. LibertyPortSet latch_data_ports_; float ocv_arc_depth_; diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index 9a6d6264..5e0edffa 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -950,7 +950,7 @@ LibertyCell::LibertyCell(LibertyLibrary *library, LibertyCell::~LibertyCell() { mode_defs_.deleteContents(); - latch_d_to_q_map_.deleteContents(); + latch_enables_.deleteContents(); timing_arc_sets_.deleteContents(); port_timing_arc_set_map_.deleteContents(); @@ -1306,7 +1306,7 @@ LibertyCell::finish(bool infer_latches, findDefaultCondArcs(); makeLatchEnables(report, debug); if (infer_latches) - inferLatchRoles(debug); + inferLatchRoles(report, debug); } void @@ -1675,7 +1675,7 @@ class LatchEnable public: LatchEnable(LibertyPort *data, LibertyPort *enable, - RiseFall *enable_edge, + const RiseFall *enable_edge, FuncExpr *enable_func, LibertyPort *output, TimingArcSet *d_to_q, @@ -1685,7 +1685,7 @@ public: LibertyPort *output() const { return output_; } LibertyPort *enable() const { return enable_; } 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 *enToQ() const { return en_to_q_; } TimingArcSet *setupCheck() const { return setup_check_; } @@ -1693,7 +1693,7 @@ public: private: LibertyPort *data_; LibertyPort *enable_; - RiseFall *enable_edge_; + const RiseFall *enable_edge_; FuncExpr *enable_func_; LibertyPort *output_; TimingArcSet *d_to_q_; @@ -1703,7 +1703,7 @@ private: LatchEnable::LatchEnable(LibertyPort *data, LibertyPort *enable, - RiseFall *enable_edge, + const RiseFall *enable_edge, FuncExpr *enable_func, LibertyPort *output, TimingArcSet *d_to_q, @@ -1736,66 +1736,101 @@ LibertyCell::makeLatchEnables(Report *report, for (TimingArcSet *d_to_q : timingArcSets(nullptr, q)) { if (d_to_q->role() == TimingRole::latchDtoQ()) { LibertyPort *d = d_to_q->from(); - for (TimingArcSet *setup_check : timingArcSets(en, d)) { - if (setup_check->role() == TimingRole::setup()) { - LatchEnable *latch_enable = makeLatchEnable(d, en, q, d_to_q, - en_to_q, - setup_check, - debug); - for (TimingArc *check_arc : setup_check->arcs()) { - RiseFall *en_rf = latch_enable->enableEdge(); - RiseFall *check_rf = check_arc->fromEdge()->asRiseFall(); - if (check_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(), - check_rf==RiseFall::rise()?"rising":"falling"); - FuncExpr *en_func = latch_enable->enableFunc(); - if (en_func) { - TimingSense en_sense = en_func->portTimingSense(en); - if (en_sense == TimingSense::positive_unate - && en_rf != RiseFall::rise()) - report->warn(1114, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function positive sense.", - library_->name(), - 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"); - } - } - } - } - } + const RiseFall *en_rf = en_to_q->isRisingFallingEdge(); + if (en_rf) { + TimingArcSet *setup_check = findLatchSetup(d, en, en_rf, q, report); + LatchEnable *latch_enable = makeLatchEnable(d, en, en_rf, q, d_to_q, + en_to_q, + setup_check, + debug); + FuncExpr *en_func = latch_enable->enableFunc(); + if (en_func) { + TimingSense en_sense = en_func->portTimingSense(en); + if (en_sense == TimingSense::positive_unate + && en_rf != RiseFall::rise()) + report->warn(1114, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function positive sense.", + library_->name(), + 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 * -LibertyCell::findLatchEnableFunc(LibertyPort *data, - LibertyPort *enable) const +LibertyCell::findLatchEnableFunc(const LibertyPort *d, + const LibertyPort *en, + const RiseFall *en_rf) const { for (auto seq : sequentials_) { if (seq->isLatch() && seq->data() - && seq->data()->hasPort(data) + && seq->data()->hasPort(d) && seq->clock() - && seq->clock()->hasPort(enable)) - return seq->clock(); + && seq->clock()->hasPort(en)) { + 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; } @@ -1803,29 +1838,31 @@ LibertyCell::findLatchEnableFunc(LibertyPort *data, LatchEnable * LibertyCell::makeLatchEnable(LibertyPort *d, LibertyPort *en, + const RiseFall *en_rf, LibertyPort *q, TimingArcSet *d_to_q, TimingArcSet *en_to_q, TimingArcSet *setup_check, Debug *debug) { - RiseFall *en_rf = en_to_q->isRisingFallingEdge(); - FuncExpr *en_func = findLatchEnableFunc(d, en); + FuncExpr *en_func = findLatchEnableFunc(d, en, en_rf); LatchEnable *latch_enable = new LatchEnable(d, en, en_rf, en_func, q, d_to_q, en_to_q, setup_check); - // Multiple enables for D->Q pairs are not supported. - if (latch_d_to_q_map_[d_to_q]) - delete latch_d_to_q_map_[d_to_q]; + latch_enables_.push_back(latch_enable); latch_d_to_q_map_[d_to_q] = latch_enable; latch_check_map_[setup_check] = latch_enable; latch_data_ports_.insert(d); - debugPrint(debug, "liberty", 2, "latch d=%s en=%s q=%s", - d->name(), en->name(), q->name()); + debugPrint(debug, "liberty", 2, "latch d=%s en=%s %s q=%s", + d->name(), + en->name(), + en_rf->name(), + q->name()); return latch_enable; } void -LibertyCell::inferLatchRoles(Debug *debug) +LibertyCell::inferLatchRoles(Report *report, + Debug *debug) { if (hasInferedRegTimingArcs()) { // Hunt down potential latch D/EN/Q triples. @@ -1845,15 +1882,14 @@ LibertyCell::inferLatchRoles(Debug *debug) // Previously identified as D->Q arc. || d_to_q_role == TimingRole::latchDtoQ()) { LibertyPort *d = d_to_q->from(); - // Check for setup check from en -> d. - for (TimingArcSet *setup_check : timingArcSets(en, d)) { - if (setup_check->role() == TimingRole::setup()) { - makeLatchEnable(d, en, q, d_to_q, en_to_q, setup_check, debug); - d_to_q->setRole(TimingRole::latchDtoQ()); - en_to_q->setRole(TimingRole::latchEnToQ()); - } - } - } + const RiseFall *en_rf = en_to_q->isRisingFallingEdge(); + if (en_rf) { + TimingArcSet *setup_check = findLatchSetup(d, en, en_rf, q, report); + makeLatchEnable(d, en, en_rf, q, d_to_q, en_to_q, setup_check, debug); + d_to_q->setRole(TimingRole::latchDtoQ()); + en_to_q->setRole(TimingRole::latchEnToQ()); + } + } } } } @@ -1871,7 +1907,7 @@ LibertyCell::latchEnable(TimingArcSet *d_to_q_set, // Return values. LibertyPort *&enable_port, FuncExpr *&enable_func, - RiseFall *&enable_edge) const + const RiseFall *&enable_edge) const { enable_port = nullptr; 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) { LatchEnable *latch_enable = latch_check_map_.findKey(check_set); diff --git a/search/Latches.cc b/search/Latches.cc index bf1a8260..c625b855 100644 --- a/search/Latches.cc +++ b/search/Latches.cc @@ -269,7 +269,7 @@ Latches::latchEnablePath(Path *q_path, const PathAnalysisPt *tgt_clk_path_ap = path_ap->tgtClkAnalysisPt(); const Instance *latch = network_->instance(q_path->pin(this)); Vertex *en_vertex; - RiseFall *en_rf; + const RiseFall *en_rf; LatchEnableState state; latchDtoQEnable(d_q_edge, latch, en_vertex, en_rf, state); if (state == LatchEnableState::enabled) { @@ -303,7 +303,7 @@ Latches::latchOutArrival(Path *data_path, Vertex *data_vertex = d_q_edge->from(graph_); const Instance *inst = network_->instance(data_vertex->pin()); Vertex *enable_vertex; - RiseFall *enable_rf; + const RiseFall *enable_rf; LatchEnableState state; latchDtoQEnable(d_q_edge, inst, enable_vertex, enable_rf, state); // Latch enable may be missing if library is malformed. @@ -466,7 +466,7 @@ Latches::latchDtoQEnable(Edge *d_q_edge, const Instance *inst, // Return values. Vertex *&enable_vertex, - RiseFall *&enable_rf, + const RiseFall *&enable_rf, LatchEnableState &state) const { enable_vertex = nullptr; @@ -515,7 +515,7 @@ Latches::latchDtoQState(Edge *edge) const const Pin *from_pin = from_vertex->pin(); const Instance *inst = network_->instance(from_pin); Vertex *enable_vertex; - RiseFall *enable_rf; + const RiseFall *enable_rf; LatchEnableState state; latchDtoQEnable(edge, inst, enable_vertex, enable_rf, state); return state; diff --git a/search/Latches.hh b/search/Latches.hh index 69dacaa9..827b182f 100644 --- a/search/Latches.hh +++ b/search/Latches.hh @@ -77,7 +77,7 @@ public: const Instance *inst, // Return values. Vertex *&enable_vertex, - RiseFall *&enable_rf, + const RiseFall *&enable_rf, LatchEnableState &state) const; LatchEnableState latchDtoQState(Edge *d_q_edge) const; void latchEnableOtherPath(Path *path, diff --git a/search/ReportPath.cc b/search/ReportPath.cc index 89b31d28..9f521745 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -580,14 +580,9 @@ ReportPath::reportEndpoint(const PathEndLatchCheck *end) const char * 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(); - TimingArcSet *check_set = check_arc->set(); - LibertyCell *cell = check_set->from()->libertyCell(); - RiseFall *enable_rf = cell->latchCheckEnableEdge(check_set); - return latchDesc(enable_rf); + const RiseFall *en_rf = check_arc->fromEdge()->asRiseFall()->opposite(); + return latchDesc(en_rf); } void @@ -3257,7 +3252,7 @@ ReportPath::edgeRegLatchDesc(Edge *first_edge, if (cell) { LibertyPort *enable_port; FuncExpr *enable_func; - RiseFall *enable_rf; + const RiseFall *enable_rf; cell->latchEnable(first_edge->timingArcSet(), enable_port, enable_func, enable_rf); return latchDesc(enable_rf); diff --git a/tcl/StaTcl.i b/tcl/StaTcl.i index 26b9ac97..5523b543 100644 --- a/tcl/StaTcl.i +++ b/tcl/StaTcl.i @@ -4883,7 +4883,7 @@ latch_d_to_q_en() TimingArcSet *d_q_set = self->timingArcSet(); LibertyPort *enable_port; FuncExpr *enable_func; - RiseFall *enable_rf; + const RiseFall *enable_rf; lib_cell->latchEnable(d_q_set, enable_port, enable_func, enable_rf); const char *en_name = enable_port->name(); return stringPrintTmp("%s %s", en_name, enable_rf->asString());