// OpenSTA, Static Timing Analyzer // Copyright (c) 2018, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include "Machine.hh" #include "DisallowCopyAssign.hh" #include "TimingRole.hh" #include "FuncExpr.hh" #include "TimingArc.hh" #include "Sequential.hh" #include "Liberty.hh" #include "Network.hh" #include "Graph.hh" #include "Sdc.hh" #include "SearchPred.hh" #include "Search.hh" #include "Clock.hh" #include "FindRegister.hh" namespace sta { static TimingSense pathSenseThru(TimingSense from_sense, TimingSense thru_sense); static bool hasMinPulseWidthCheck(LibertyPort *port); // Predicate used for searching from clocks to find registers. class FindRegClkPred : public SearchPred1 { public: FindRegClkPred(Clock *clk, const StaState *sta); virtual bool searchThru(Edge *edge); virtual bool searchFrom(const Vertex *from_vertex); private: DISALLOW_COPY_AND_ASSIGN(FindRegClkPred); Clock *clk_; }; FindRegClkPred::FindRegClkPred(Clock *clk, const StaState *sta) : SearchPred1(sta), clk_(clk) { } bool FindRegClkPred::searchFrom(const Vertex *from_vertex) { const Sdc *sdc = sta_->sdc(); const Pin *from_pin = from_vertex->pin(); return !sdc->clkStopPropagation(from_pin, clk_) && SearchPred1::searchFrom(from_vertex); } bool FindRegClkPred::searchThru(Edge *edge) { TimingRole *role = edge->role(); return (role->isWire() || role == TimingRole::combinational()) && SearchPred1::searchThru(edge); } // Helper for "all_registers". // Visit all register instances. class FindRegVisitor : public StaState { public: FindRegVisitor(StaState *sta); virtual ~FindRegVisitor() {} void visitRegs(ClockSet *clks, const TransRiseFallBoth *clk_tr, bool edge_triggered, bool latches); private: DISALLOW_COPY_AND_ASSIGN(FindRegVisitor); void visitRegs(const Pin *clk_pin, TimingSense clk_sense, const TransRiseFallBoth *clk_tr, bool edge_triggered, bool latches); virtual void visitReg(Instance *inst) = 0; virtual void visitSequential(Instance *inst, Sequential *seq) = 0; void visitFanoutRegs(Vertex *from_vertex, TimingSense from_sense, const TransRiseFallBoth *clk_tr, bool edge_triggered, bool latches, SearchPred &clk_pred, VertexSet &visited_vertices); void findSequential(const Pin *clk_pin, Instance *inst, LibertyCell *cell, TimingSense clk_sense, const TransRiseFallBoth *clk_tr, bool edge_triggered, bool latches, bool &has_seqs, bool &matches); bool findInferedSequential(LibertyCell *cell, TimingSense clk_sense, const TransRiseFallBoth *clk_tr, bool edge_triggered, bool latches); bool hasTimingCheck(LibertyCell *cell, LibertyPort *clk, LibertyPort *d); }; FindRegVisitor::FindRegVisitor(StaState *sta) : StaState(sta) { } void FindRegVisitor::visitRegs(ClockSet *clks, const TransRiseFallBoth *clk_tr, bool edge_triggered, bool latches) { if (clks && !clks->empty()) { // Use DFS search to find all registers downstream of the clocks. ClockSet::Iterator clk_iter(clks); while (clk_iter.hasNext()) { Clock *clk = clk_iter.next(); FindRegClkPred clk_pred(clk, this); VertexSet visited_vertices; ClockVertexPinIterator pin_iter(clk); while (pin_iter.hasNext()) { Pin *pin = pin_iter.next(); Vertex *vertex, *bidirect_drvr_vertex; graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); visitFanoutRegs(vertex, timing_sense_positive_unate, clk_tr, edge_triggered, latches, clk_pred, visited_vertices); // Clocks defined on bidirect pins blow it out both ends. if (bidirect_drvr_vertex) visitFanoutRegs(bidirect_drvr_vertex, timing_sense_positive_unate, clk_tr, edge_triggered, latches, clk_pred, visited_vertices); } } } else { VertexSet::ConstIterator reg_clk_iter(graph_->regClkVertices()); while (reg_clk_iter.hasNext()) { Vertex *vertex = reg_clk_iter.next(); visitRegs(vertex->pin(), timing_sense_positive_unate, TransRiseFallBoth::riseFall(), edge_triggered, latches); } } } void FindRegVisitor::visitFanoutRegs(Vertex *from_vertex, TimingSense from_sense, const TransRiseFallBoth *clk_tr, bool edge_triggered, bool latches, SearchPred &clk_pred, VertexSet &visited_vertices) { if (!visited_vertices.hasKey(from_vertex) && clk_pred.searchFrom(from_vertex)) { visited_vertices.insert(from_vertex); VertexOutEdgeIterator edge_iter(from_vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); Vertex *to_vertex = edge->to(graph_); const Pin *to_pin = to_vertex->pin(); TimingSense to_sense = pathSenseThru(from_sense, edge->sense()); if (to_vertex->isRegClk()) visitRegs(to_pin, to_sense, clk_tr, edge_triggered, latches); // Even register clock pins can have combinational fanout arcs. if (clk_pred.searchThru(edge) && clk_pred.searchTo(to_vertex)) visitFanoutRegs(to_vertex, to_sense, clk_tr, edge_triggered, latches, clk_pred, visited_vertices); } } } void FindRegVisitor::visitRegs(const Pin *clk_pin, TimingSense clk_sense, const TransRiseFallBoth *clk_tr, bool edge_triggered, bool latches) { Instance *inst = network_->instance(clk_pin); LibertyCell *cell = network_->libertyCell(inst); if (!edge_triggered || !latches || clk_tr != TransRiseFallBoth::riseFall()) { bool matches, has_seqs; findSequential(clk_pin, inst, cell, clk_sense, clk_tr, edge_triggered, latches, has_seqs, matches); if (!has_seqs) matches = findInferedSequential(cell, clk_sense, clk_tr, edge_triggered, latches); if (matches) visitReg(inst); } else // Do not require sequentials to match if the search is // not specific to edge triggered, latches, or clock edge. visitReg(inst); } void FindRegVisitor::findSequential(const Pin *clk_pin, Instance *inst, LibertyCell *cell, TimingSense clk_sense, const TransRiseFallBoth *clk_tr, bool edge_triggered, bool latches, bool &has_seqs, bool &matches) { has_seqs = false; matches = false; CellSequentialIterator *seq_iter = cell->sequentialIterator(); while (seq_iter->hasNext()) { has_seqs = true; Sequential *seq = seq_iter->next(); if ((seq->isRegister() && edge_triggered) || (seq->isLatch() && latches)) { if (clk_tr == TransRiseFallBoth::riseFall()) { visitSequential(inst, seq); matches = true; break; } else { FuncExpr *clk_func = seq->clock(); LibertyPort *port = network_->libertyPort(clk_pin); TimingSense port_sense = clk_func->portTimingSense(port); TimingSense path_sense = pathSenseThru(clk_sense, port_sense); if ((path_sense == timing_sense_positive_unate && clk_tr == TransRiseFallBoth::rise()) || (path_sense == timing_sense_negative_unate && clk_tr == TransRiseFallBoth::fall())) { visitSequential(inst, seq); matches = true; break; } } } } delete seq_iter; } bool FindRegVisitor::findInferedSequential(LibertyCell *cell, TimingSense clk_sense, const TransRiseFallBoth *clk_tr, bool edge_triggered, bool latches) { bool matches = false; CellTimingArcSetIterator *set_iter = cell->timingArcSetIterator(); const TransRiseFall *clk_tr1 = clk_tr->asRiseFall(); while (set_iter->hasNext()) { TimingArcSet *set = set_iter->next(); TimingArcSetArcIterator *arc_iter = set->timingArcIterator(); TimingArc *arc = arc_iter->next(); delete arc_iter; TransRiseFall *arc_clk_tr = arc->fromTrans()->asRiseFall(); bool tr_matches = (clk_tr == TransRiseFallBoth::riseFall() || (arc_clk_tr == clk_tr1 && clk_sense == timing_sense_positive_unate) || (arc_clk_tr == clk_tr1->opposite() && clk_sense == timing_sense_negative_unate)); TimingRole *role = set->role(); if (tr_matches && ((role == TimingRole::regClkToQ() && edge_triggered) || (role == TimingRole::latchEnToQ() && latches))) { matches = true; break; } } delete set_iter; return matches; } bool FindRegVisitor::hasTimingCheck(LibertyCell *cell, LibertyPort *clk, LibertyPort *d) { bool has_check = false; CellTimingArcSetIterator *set_iter = cell->timingArcSetIterator(clk, d); while (set_iter->hasNext()) { TimingArcSet *set = set_iter->next(); TimingRole *role = set->role(); if (role->isTimingCheck()) { has_check = true; break; } } delete set_iter; return has_check; } class FindRegInstances : public FindRegVisitor { public: explicit FindRegInstances(StaState *sta); InstanceSet *findRegs(ClockSet *clks, const TransRiseFallBoth *clk_tr, bool edge_triggered, bool latches); private: DISALLOW_COPY_AND_ASSIGN(FindRegInstances); virtual void visitReg(Instance *inst); virtual void visitSequential(Instance *inst, Sequential *seq); InstanceSet *regs_; }; FindRegInstances::FindRegInstances(StaState *sta) : FindRegVisitor(sta), regs_(NULL) { } InstanceSet * FindRegInstances::findRegs(ClockSet *clks, const TransRiseFallBoth *clk_tr, bool edge_triggered, bool latches) { regs_ = new InstanceSet; visitRegs(clks, clk_tr, edge_triggered, latches); return regs_; } void FindRegInstances::visitSequential(Instance *, Sequential *) { } void FindRegInstances::visitReg(Instance *inst) { regs_->insert(inst); } InstanceSet * findRegInstances(ClockSet *clks, const TransRiseFallBoth *clk_tr, bool edge_triggered, bool latches, StaState *sta) { FindRegInstances find_regs(sta); return find_regs.findRegs(clks, clk_tr, edge_triggered, latches); } //////////////////////////////////////////////////////////////// class FindRegPins : public FindRegVisitor { public: explicit FindRegPins(StaState *sta); PinSet *findPins(ClockSet *clks, const TransRiseFallBoth *clk_tr, bool edge_triggered, bool latches); protected: DISALLOW_COPY_AND_ASSIGN(FindRegPins); virtual void visitReg(Instance *inst); virtual void visitSequential(Instance *inst, Sequential *seq); virtual bool matchPin(Pin *pin); void visitExpr(FuncExpr *expr, Instance *inst, Sequential *seq); // Sequential expressions to find instance pins. virtual FuncExpr *seqExpr1(Sequential *seq) = 0; virtual FuncExpr *seqExpr2(Sequential *seq) = 0; PinSet *pins_; }; FindRegPins::FindRegPins(StaState *sta) : FindRegVisitor(sta), pins_(NULL) { } PinSet * FindRegPins::findPins(ClockSet *clks, const TransRiseFallBoth *clk_tr, bool edge_triggered, bool latches) { pins_ = new PinSet; visitRegs(clks, clk_tr, edge_triggered, latches); return pins_; } void FindRegPins::visitSequential(Instance *inst, Sequential *seq) { visitExpr(seqExpr1(seq), inst, seq); visitExpr(seqExpr2(seq), inst, seq); } void FindRegPins::visitExpr(FuncExpr *expr, Instance *inst, Sequential *) { if (expr) { FuncExprPortIterator port_iter(expr); while (port_iter.hasNext()) { LibertyPort *port = port_iter.next(); Pin *pin = network_->findPin(inst, port); if (pin) pins_->insert(pin); } } } void FindRegPins::visitReg(Instance *inst) { InstancePinIterator *pin_iter = network_->pinIterator(inst); while (pin_iter->hasNext()) { Pin *pin = pin_iter->next(); if (matchPin(pin)) pins_->insert(pin); } delete pin_iter; } bool FindRegPins::matchPin(Pin *) { return true; } class FindRegDataPins : public FindRegPins { public: explicit FindRegDataPins(StaState *sta); private: DISALLOW_COPY_AND_ASSIGN(FindRegDataPins); virtual bool matchPin(Pin *pin); virtual FuncExpr *seqExpr1(Sequential *seq); virtual FuncExpr *seqExpr2(Sequential *seq); }; FindRegDataPins::FindRegDataPins(StaState *sta) : FindRegPins(sta) { } FuncExpr * FindRegDataPins::seqExpr1(Sequential *seq) { return seq->data(); } FuncExpr * FindRegDataPins::seqExpr2(Sequential *) { return NULL; } bool FindRegDataPins::matchPin(Pin *pin) { LibertyPort *port = network_->libertyPort(pin); Vertex *vertex = graph_->pinLoadVertex(pin); float ignore; bool has_min_period; port->minPeriod(ignore, has_min_period); return vertex && vertex->hasChecks() && !has_min_period && !hasMinPulseWidthCheck(port); } static bool hasMinPulseWidthCheck(LibertyPort *port) { float ignore; bool exists; port->minPulseWidth(TransRiseFall::rise(), ignore, exists); if (exists) return true; port->minPulseWidth(TransRiseFall::fall(), ignore, exists); return exists; } PinSet * findRegDataPins(ClockSet *clks, const TransRiseFallBoth *clk_tr, bool edge_triggered, bool latches, StaState *sta) { FindRegDataPins find_regs(sta); return find_regs.findPins(clks, clk_tr, edge_triggered, latches); } //////////////////////////////////////////////////////////////// class FindRegClkPins : public FindRegPins { public: explicit FindRegClkPins(StaState *sta); private: DISALLOW_COPY_AND_ASSIGN(FindRegClkPins); virtual bool matchPin(Pin *pin); virtual FuncExpr *seqExpr1(Sequential *seq); virtual FuncExpr *seqExpr2(Sequential *seq); }; FindRegClkPins::FindRegClkPins(StaState *sta) : FindRegPins(sta) { } bool FindRegClkPins::matchPin(Pin *pin) { LibertyPort *port = network_->libertyPort(pin); LibertyCell *cell = port->libertyCell(); CellTimingArcSetIterator *set_iter = cell->timingArcSetFromIterator(port); bool match = false; while (set_iter->hasNext()) { TimingArcSet *set = set_iter->next(); TimingRole *role = set->role(); if (role->isTimingCheck()) { match = true; break; } } delete set_iter; return match; } FuncExpr * FindRegClkPins::seqExpr1(Sequential *seq) { return seq->clock(); } FuncExpr * FindRegClkPins::seqExpr2(Sequential *) { return NULL; } PinSet * findRegClkPins(ClockSet *clks, const TransRiseFallBoth *clk_tr, bool edge_triggered, bool latches, StaState *sta) { FindRegClkPins find_regs(sta); return find_regs.findPins(clks, clk_tr, edge_triggered, latches); } //////////////////////////////////////////////////////////////// class FindRegAsyncPins : public FindRegPins { public: explicit FindRegAsyncPins(StaState *sta); private: DISALLOW_COPY_AND_ASSIGN(FindRegAsyncPins); virtual bool matchPin(Pin *pin); virtual FuncExpr *seqExpr1(Sequential *seq) { return seq->clear(); } virtual FuncExpr *seqExpr2(Sequential *seq) { return seq->preset(); } }; FindRegAsyncPins::FindRegAsyncPins(StaState *sta) : FindRegPins(sta) { } bool FindRegAsyncPins::matchPin(Pin *pin) { LibertyPort *port = network_->libertyPort(pin); LibertyCell *cell = port->libertyCell(); CellTimingArcSetIterator *set_iter = cell->timingArcSetFromIterator(port); bool match = false; while (set_iter->hasNext()) { TimingArcSet *set = set_iter->next(); TimingRole *role = set->role(); if (role == TimingRole::regSetClr()) { match = true; break; } } delete set_iter; return match; } PinSet * findRegAsyncPins(ClockSet *clks, const TransRiseFallBoth *clk_tr, bool edge_triggered, bool latches, StaState *sta) { FindRegAsyncPins find_regs(sta); return find_regs.findPins(clks, clk_tr, edge_triggered, latches); } //////////////////////////////////////////////////////////////// class FindRegOutputPins : public FindRegPins { public: explicit FindRegOutputPins(StaState *sta); private: DISALLOW_COPY_AND_ASSIGN(FindRegOutputPins); virtual bool matchPin(Pin *pin); virtual void visitSequential(Instance *inst, Sequential *seq); void visitOutput(LibertyPort *port, Instance *inst); // Unused. virtual FuncExpr *seqExpr1(Sequential *seq); virtual FuncExpr *seqExpr2(Sequential *seq); }; FindRegOutputPins::FindRegOutputPins(StaState *sta) : FindRegPins(sta) { } bool FindRegOutputPins::matchPin(Pin *pin) { LibertyPort *port = network_->libertyPort(pin); LibertyCell *cell = port->libertyCell(); CellTimingArcSetIterator *set_iter = cell->timingArcSetToIterator(port); bool match = false; while (set_iter->hasNext()) { TimingArcSet *set = set_iter->next(); TimingRole *role = set->role(); if (role == TimingRole::regClkToQ() || role == TimingRole::latchEnToQ() || role == TimingRole::latchDtoQ()) { match = true; break; } } delete set_iter; return match; } void FindRegOutputPins::visitSequential(Instance *inst, Sequential *seq) { visitOutput(seq->output(), inst); visitOutput(seq->outputInv(), inst); } void FindRegOutputPins::visitOutput(LibertyPort *port, Instance *inst) { if (port) { // Sequential outputs are internal ports. // Find the output port that is connected to the internal port // by uses it as its function. InstancePinIterator *pin_iter = network_->pinIterator(inst); while (pin_iter->hasNext()) { Pin *pin = pin_iter->next(); LibertyPort *pin_port = network_->libertyPort(pin); FuncExpr *func = pin_port->function(); if (func && func->port() && func->port() == port) pins_->insert(pin); } delete pin_iter; } } FuncExpr * FindRegOutputPins::seqExpr1(Sequential *) { return NULL; } FuncExpr * FindRegOutputPins::seqExpr2(Sequential *) { return NULL; } PinSet * findRegOutputPins(ClockSet *clks, const TransRiseFallBoth *clk_tr, bool edge_triggered, bool latches, StaState *sta) { FindRegOutputPins find_regs(sta); return find_regs.findPins(clks, clk_tr, edge_triggered, latches); } //////////////////////////////////////////////////////////////// static TimingSense path_sense_thru[timing_sense_count][timing_sense_count]; void initPathSenseThru() { path_sense_thru[timing_sense_positive_unate][timing_sense_positive_unate] = timing_sense_positive_unate; path_sense_thru[timing_sense_positive_unate][timing_sense_negative_unate] = timing_sense_negative_unate; path_sense_thru[timing_sense_positive_unate][timing_sense_non_unate] = timing_sense_non_unate; path_sense_thru[timing_sense_positive_unate][timing_sense_none] = timing_sense_none; path_sense_thru[timing_sense_positive_unate][timing_sense_unknown] = timing_sense_unknown; path_sense_thru[timing_sense_negative_unate][timing_sense_positive_unate] = timing_sense_negative_unate; path_sense_thru[timing_sense_negative_unate][timing_sense_negative_unate] = timing_sense_positive_unate; path_sense_thru[timing_sense_negative_unate][timing_sense_non_unate] = timing_sense_non_unate; path_sense_thru[timing_sense_negative_unate][timing_sense_none] = timing_sense_none; path_sense_thru[timing_sense_negative_unate][timing_sense_unknown] = timing_sense_unknown; path_sense_thru[timing_sense_non_unate][timing_sense_positive_unate] = timing_sense_non_unate; path_sense_thru[timing_sense_non_unate][timing_sense_negative_unate] = timing_sense_non_unate; path_sense_thru[timing_sense_non_unate][timing_sense_non_unate] = timing_sense_non_unate; path_sense_thru[timing_sense_non_unate][timing_sense_none] = timing_sense_none; path_sense_thru[timing_sense_non_unate][timing_sense_unknown] = timing_sense_unknown; path_sense_thru[timing_sense_none][timing_sense_positive_unate] = timing_sense_none; path_sense_thru[timing_sense_none][timing_sense_negative_unate] = timing_sense_none; path_sense_thru[timing_sense_none][timing_sense_non_unate] = timing_sense_none; path_sense_thru[timing_sense_none][timing_sense_none] = timing_sense_none; path_sense_thru[timing_sense_none][timing_sense_unknown] = timing_sense_unknown; path_sense_thru[timing_sense_unknown][timing_sense_positive_unate] = timing_sense_unknown; path_sense_thru[timing_sense_unknown][timing_sense_negative_unate] = timing_sense_unknown; path_sense_thru[timing_sense_unknown][timing_sense_non_unate] = timing_sense_unknown; path_sense_thru[timing_sense_unknown][timing_sense_none] = timing_sense_unknown; path_sense_thru[timing_sense_unknown][timing_sense_unknown] = timing_sense_unknown; } static TimingSense pathSenseThru(TimingSense from_sense, TimingSense thru_sense) { return path_sense_thru[from_sense][thru_sense]; } } // namespace