issue44 liberty pos/neg latches
Signed-off-by: James Cherry <cherry@parallaxsw.com>
This commit is contained in:
parent
2c4b81d0f4
commit
3ba6f0e527
|
|
@ -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_;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
Loading…
Reference in New Issue