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 *, ModeValueDef*, CharPtrLess> ModeValueMap;
typedef Map<TimingArcSet*, LatchEnable*> LatchEnableMap;
typedef Vector<LatchEnable*> LatchEnableSeq;
typedef Map<const char *, OcvDerate*, CharPtrLess> OcvDerateMap;
typedef Vector<InternalPowerAttrs*> InternalPowerAttrsSeq;
typedef Map<const char *, float, CharPtrLess> 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_;

View File

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

View File

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

View File

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

View File

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

View File

@ -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());