// OpenSTA, Static Timing Analyzer // Copyright (c) 2022, 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 "FindRegister.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 "Clock.hh" #include "SearchPred.hh" #include "Search.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: 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 RiseFallBoth *clk_rf, bool edge_triggered, bool latches); private: void visitRegs(const Pin *clk_pin, TimingSense clk_sense, const RiseFallBoth *clk_rf, 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 RiseFallBoth *clk_rf, 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 RiseFallBoth *clk_rf, bool edge_triggered, bool latches, bool &has_seqs, bool &matches); bool findInferedSequential(LibertyCell *cell, TimingSense clk_sense, const RiseFallBoth *clk_rf, 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 RiseFallBoth *clk_rf, 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(graph_); for (Pin *pin : clk->leafPins()) { Vertex *vertex, *bidirect_drvr_vertex; graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); visitFanoutRegs(vertex, TimingSense::positive_unate, clk_rf, 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, TimingSense::positive_unate, clk_rf, edge_triggered, latches, clk_pred, visited_vertices); } } } else { for (Vertex *vertex : *graph_->regClkVertices()) { visitRegs(vertex->pin(), TimingSense::positive_unate, RiseFallBoth::riseFall(), edge_triggered, latches); } } } void FindRegVisitor::visitFanoutRegs(Vertex *from_vertex, TimingSense from_sense, const RiseFallBoth *clk_rf, 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_rf, 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_rf, edge_triggered, latches, clk_pred, visited_vertices); } } } void FindRegVisitor::visitRegs(const Pin *clk_pin, TimingSense clk_sense, const RiseFallBoth *clk_rf, bool edge_triggered, bool latches) { Instance *inst = network_->instance(clk_pin); LibertyCell *cell = network_->libertyCell(inst); if (!edge_triggered || !latches || clk_rf != RiseFallBoth::riseFall()) { bool matches, has_seqs; findSequential(clk_pin, inst, cell, clk_sense, clk_rf, edge_triggered, latches, has_seqs, matches); if (!has_seqs) matches = findInferedSequential(cell, clk_sense, clk_rf, 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 RiseFallBoth *clk_rf, bool edge_triggered, bool latches, bool &has_seqs, bool &matches) { has_seqs = false; matches = false; LibertyCellSequentialIterator seq_iter(cell); while (seq_iter.hasNext()) { has_seqs = true; Sequential *seq = seq_iter.next(); if ((seq->isRegister() && edge_triggered) || (seq->isLatch() && latches)) { if (clk_rf == RiseFallBoth::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 == TimingSense::positive_unate && clk_rf == RiseFallBoth::rise()) || (path_sense == TimingSense::negative_unate && clk_rf == RiseFallBoth::fall())) { visitSequential(inst, seq); matches = true; break; } } } } } bool FindRegVisitor::findInferedSequential(LibertyCell *cell, TimingSense clk_sense, const RiseFallBoth *clk_rf, bool edge_triggered, bool latches) { bool matches = false; const RiseFall *clk_rf1 = clk_rf->asRiseFall(); for (TimingArcSet *arc_set : cell->timingArcSets()) { TimingArcSetArcIterator arc_iter(arc_set); TimingArc *arc = arc_iter.next(); RiseFall *arc_clk_rf = arc->fromEdge()->asRiseFall(); bool tr_matches = (clk_rf == RiseFallBoth::riseFall() || (arc_clk_rf == clk_rf1 && clk_sense == TimingSense::positive_unate) || (arc_clk_rf == clk_rf1->opposite() && clk_sense == TimingSense::negative_unate)); TimingRole *role = arc_set->role(); if (tr_matches && ((role == TimingRole::regClkToQ() && edge_triggered) || (role == TimingRole::latchEnToQ() && latches))) { matches = true; break; } } return matches; } bool FindRegVisitor::hasTimingCheck(LibertyCell *cell, LibertyPort *clk, LibertyPort *d) { for (TimingArcSet *arc_set : cell->timingArcSets(clk, d)) { TimingRole *role = arc_set->role(); if (role->isTimingCheck()) return true; } return false; } class FindRegInstances : public FindRegVisitor { public: explicit FindRegInstances(StaState *sta); InstanceSet *findRegs(ClockSet *clks, const RiseFallBoth *clk_rf, bool edge_triggered, bool latches); private: virtual void visitReg(Instance *inst); virtual void visitSequential(Instance *inst, Sequential *seq); InstanceSet *regs_; }; FindRegInstances::FindRegInstances(StaState *sta) : FindRegVisitor(sta), regs_(nullptr) { } InstanceSet * FindRegInstances::findRegs(ClockSet *clks, const RiseFallBoth *clk_rf, bool edge_triggered, bool latches) { regs_ = new InstanceSet; visitRegs(clks, clk_rf, edge_triggered, latches); return regs_; } void FindRegInstances::visitSequential(Instance *, Sequential *) { } void FindRegInstances::visitReg(Instance *inst) { regs_->insert(inst); } InstanceSet * findRegInstances(ClockSet *clks, const RiseFallBoth *clk_rf, bool edge_triggered, bool latches, StaState *sta) { FindRegInstances find_regs(sta); return find_regs.findRegs(clks, clk_rf, edge_triggered, latches); } //////////////////////////////////////////////////////////////// class FindRegPins : public FindRegVisitor { public: explicit FindRegPins(StaState *sta); PinSet *findPins(ClockSet *clks, const RiseFallBoth *clk_rf, bool edge_triggered, bool latches); protected: 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_(nullptr) { } PinSet * FindRegPins::findPins(ClockSet *clks, const RiseFallBoth *clk_rf, bool edge_triggered, bool latches) { pins_ = new PinSet; visitRegs(clks, clk_rf, 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: 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 nullptr; } 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(RiseFall::rise(), ignore, exists); if (exists) return true; port->minPulseWidth(RiseFall::fall(), ignore, exists); return exists; } PinSet * findRegDataPins(ClockSet *clks, const RiseFallBoth *clk_rf, bool edge_triggered, bool latches, StaState *sta) { FindRegDataPins find_regs(sta); return find_regs.findPins(clks, clk_rf, edge_triggered, latches); } //////////////////////////////////////////////////////////////// class FindRegClkPins : public FindRegPins { public: explicit FindRegClkPins(StaState *sta); private: 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(); for (TimingArcSet *arc_set : cell->timingArcSets(port, nullptr)) { TimingRole *role = arc_set->role(); if (role->isTimingCheck()) return true; } return false; } FuncExpr * FindRegClkPins::seqExpr1(Sequential *seq) { return seq->clock(); } FuncExpr * FindRegClkPins::seqExpr2(Sequential *) { return nullptr; } PinSet * findRegClkPins(ClockSet *clks, const RiseFallBoth *clk_rf, bool edge_triggered, bool latches, StaState *sta) { FindRegClkPins find_regs(sta); return find_regs.findPins(clks, clk_rf, edge_triggered, latches); } //////////////////////////////////////////////////////////////// class FindRegAsyncPins : public FindRegPins { public: explicit FindRegAsyncPins(StaState *sta); private: 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(); for (TimingArcSet *arc_set : cell->timingArcSets(port, nullptr)) { TimingRole *role = arc_set->role(); if (role == TimingRole::regSetClr()) return true; } return false; } PinSet * findRegAsyncPins(ClockSet *clks, const RiseFallBoth *clk_rf, bool edge_triggered, bool latches, StaState *sta) { FindRegAsyncPins find_regs(sta); return find_regs.findPins(clks, clk_rf, edge_triggered, latches); } //////////////////////////////////////////////////////////////// class FindRegOutputPins : public FindRegPins { public: explicit FindRegOutputPins(StaState *sta); private: 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(); for (TimingArcSet *arc_set : cell->timingArcSets(nullptr, port)) { TimingRole *role = arc_set->role(); if (role == TimingRole::regClkToQ() || role == TimingRole::latchEnToQ() || role == TimingRole::latchDtoQ()) return true; } return false; } 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 nullptr; } FuncExpr * FindRegOutputPins::seqExpr2(Sequential *) { return nullptr; } PinSet * findRegOutputPins(ClockSet *clks, const RiseFallBoth *clk_rf, bool edge_triggered, bool latches, StaState *sta) { FindRegOutputPins find_regs(sta); return find_regs.findPins(clks, clk_rf, edge_triggered, latches); } //////////////////////////////////////////////////////////////// static TimingSense path_sense_thru[timing_sense_count][timing_sense_count]; static void initPathSenseThru1(TimingSense from, TimingSense thru, TimingSense to) { path_sense_thru[int(from)][int(thru)] = to; } void initPathSenseThru() { initPathSenseThru1(TimingSense::positive_unate, TimingSense::positive_unate, TimingSense::positive_unate); initPathSenseThru1(TimingSense::positive_unate, TimingSense::negative_unate, TimingSense::negative_unate); initPathSenseThru1(TimingSense::positive_unate, TimingSense::non_unate, TimingSense::non_unate); initPathSenseThru1(TimingSense::positive_unate, TimingSense::none, TimingSense::none); initPathSenseThru1(TimingSense::positive_unate, TimingSense::unknown, TimingSense::unknown); initPathSenseThru1(TimingSense::negative_unate, TimingSense::positive_unate, TimingSense::negative_unate); initPathSenseThru1(TimingSense::negative_unate, TimingSense::negative_unate, TimingSense::positive_unate); initPathSenseThru1(TimingSense::negative_unate, TimingSense::non_unate, TimingSense::non_unate); initPathSenseThru1(TimingSense::negative_unate, TimingSense::none, TimingSense::none); initPathSenseThru1(TimingSense::negative_unate, TimingSense::unknown, TimingSense::unknown); initPathSenseThru1(TimingSense::non_unate, TimingSense::positive_unate, TimingSense::non_unate); initPathSenseThru1(TimingSense::non_unate, TimingSense::negative_unate, TimingSense::non_unate); initPathSenseThru1(TimingSense::non_unate, TimingSense::non_unate, TimingSense::non_unate); initPathSenseThru1(TimingSense::non_unate, TimingSense::none, TimingSense::none); initPathSenseThru1(TimingSense::non_unate, TimingSense::unknown, TimingSense::unknown); initPathSenseThru1(TimingSense::none, TimingSense::positive_unate, TimingSense::none); initPathSenseThru1(TimingSense::none, TimingSense::negative_unate, TimingSense::none); initPathSenseThru1(TimingSense::none, TimingSense::non_unate, TimingSense::none); initPathSenseThru1(TimingSense::none, TimingSense::none, TimingSense::none); initPathSenseThru1(TimingSense::none, TimingSense::unknown, TimingSense::unknown); initPathSenseThru1(TimingSense::unknown, TimingSense::positive_unate, TimingSense::unknown); initPathSenseThru1(TimingSense::unknown, TimingSense::negative_unate, TimingSense::unknown); initPathSenseThru1(TimingSense::unknown, TimingSense::non_unate, TimingSense::unknown); initPathSenseThru1(TimingSense::unknown, TimingSense::none, TimingSense::unknown); initPathSenseThru1(TimingSense::unknown, TimingSense::unknown, TimingSense::unknown); } static TimingSense pathSenseThru(TimingSense from_sense, TimingSense thru_sense) { return path_sense_thru[int(from_sense)][int(thru_sense)]; } } // namespace