783 lines
20 KiB
C++
783 lines
20 KiB
C++
// 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 <https://www.gnu.org/licenses/>.
|
|
|
|
#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
|