397 lines
10 KiB
C++
397 lines
10 KiB
C++
// OpenSTA, Static Timing Analyzer
|
|
// Copyright (c) 2025, 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/>.
|
|
//
|
|
// The origin of this software must not be misrepresented; you must not
|
|
// claim that you wrote the original software.
|
|
//
|
|
// Altered source versions must be plainly marked as such, and must not be
|
|
// misrepresented as being the original software.
|
|
//
|
|
// This notice may not be removed or altered from any source distribution.
|
|
|
|
#include "Stats.hh"
|
|
#include "PortDirection.hh"
|
|
#include "Network.hh"
|
|
#include "Graph.hh"
|
|
#include "DisabledPorts.hh"
|
|
#include "PortDelay.hh"
|
|
#include "ClockLatency.hh"
|
|
#include "Sdc.hh"
|
|
|
|
namespace sta {
|
|
|
|
static void
|
|
annotateGraphDisabledWireEdge(const Pin *from_pin,
|
|
const Pin *to_pin,
|
|
Graph *graph);
|
|
|
|
// Annotate constraints to the timing graph.
|
|
void
|
|
Sdc::annotateGraph()
|
|
{
|
|
Stats stats(debug_, report_);
|
|
// All output pins are considered constrained because
|
|
// they may be downstream from a set_min/max_delay -from that
|
|
// does not have a set_output_delay.
|
|
annotateGraphConstrainOutputs();
|
|
annotateDisables();
|
|
annotateGraphOutputDelays();
|
|
annotateGraphDataChecks();
|
|
annotateHierClkLatency();
|
|
stats.report("Annotate constraints to graph");
|
|
}
|
|
|
|
void
|
|
Sdc::annotateGraphConstrainOutputs()
|
|
{
|
|
Instance *top_inst = network_->topInstance();
|
|
InstancePinIterator *pin_iter = network_->pinIterator(top_inst);
|
|
while (pin_iter->hasNext()) {
|
|
Pin *pin = pin_iter->next();
|
|
if (network_->direction(pin)->isAnyOutput())
|
|
annotateGraphConstrained(pin);
|
|
}
|
|
delete pin_iter;
|
|
}
|
|
|
|
void
|
|
Sdc::annotateDisables()
|
|
{
|
|
PinSet::Iterator pin_iter(disabled_pins_);
|
|
while (pin_iter.hasNext()) {
|
|
const Pin *pin = pin_iter.next();
|
|
annotateGraphDisabled(pin);
|
|
}
|
|
|
|
if (!disabled_lib_ports_.empty()) {
|
|
VertexIterator vertex_iter(graph_);
|
|
while (vertex_iter.hasNext()) {
|
|
Vertex *vertex = vertex_iter.next();
|
|
Pin *pin = vertex->pin();
|
|
LibertyPort *port = network_->libertyPort(pin);
|
|
if (disabled_lib_ports_.hasKey(port))
|
|
annotateGraphDisabled(pin);
|
|
}
|
|
}
|
|
|
|
Instance *top_inst = network_->topInstance();
|
|
PortSet::Iterator port_iter(disabled_ports_);
|
|
while (port_iter.hasNext()) {
|
|
const Port *port = port_iter.next();
|
|
Pin *pin = network_->findPin(top_inst, port);
|
|
annotateGraphDisabled(pin);
|
|
}
|
|
|
|
for (const PinPair &pair : disabled_wire_edges_)
|
|
annotateGraphDisabledWireEdge(pair.first, pair.second, graph_);
|
|
|
|
for (Edge *edge : disabled_edges_)
|
|
edge->setIsDisabledConstraint(true);
|
|
|
|
DisabledInstancePortsMap::Iterator disable_inst_iter(disabled_inst_ports_);
|
|
while (disable_inst_iter.hasNext()) {
|
|
DisabledInstancePorts *disabled_inst = disable_inst_iter.next();
|
|
setEdgeDisabledInstPorts(disabled_inst);
|
|
}
|
|
}
|
|
|
|
class DisableHpinEdgeVisitor : public HierPinThruVisitor
|
|
{
|
|
public:
|
|
DisableHpinEdgeVisitor(Graph *graph);
|
|
virtual void visit(const Pin *from_pin,
|
|
const Pin *to_pin);
|
|
|
|
protected:
|
|
bool annotate_;
|
|
Graph *graph_;
|
|
};
|
|
|
|
DisableHpinEdgeVisitor::DisableHpinEdgeVisitor(Graph *graph) :
|
|
HierPinThruVisitor(),
|
|
graph_(graph)
|
|
{
|
|
}
|
|
|
|
void
|
|
DisableHpinEdgeVisitor::visit(const Pin *from_pin,
|
|
const Pin *to_pin)
|
|
{
|
|
annotateGraphDisabledWireEdge(from_pin, to_pin, graph_);
|
|
}
|
|
|
|
static void
|
|
annotateGraphDisabledWireEdge(const Pin *from_pin,
|
|
const Pin *to_pin,
|
|
Graph *graph)
|
|
{
|
|
Vertex *from_vertex = graph->pinDrvrVertex(from_pin);
|
|
Vertex *to_vertex = graph->pinLoadVertex(to_pin);
|
|
if (from_vertex && to_vertex) {
|
|
VertexOutEdgeIterator edge_iter(from_vertex, graph);
|
|
while (edge_iter.hasNext()) {
|
|
Edge *edge = edge_iter.next();
|
|
if (edge->isWire()
|
|
&& edge->to(graph) == to_vertex)
|
|
edge->setIsDisabledConstraint(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
Sdc::annotateGraphDisabled(const Pin *pin)
|
|
{
|
|
Vertex *vertex, *bidirect_drvr_vertex;
|
|
graph_->pinVertices(pin, vertex, bidirect_drvr_vertex);
|
|
vertex->setIsDisabledConstraint(true);
|
|
if (bidirect_drvr_vertex)
|
|
bidirect_drvr_vertex->setIsDisabledConstraint(true);
|
|
}
|
|
|
|
void
|
|
Sdc::setEdgeDisabledInstPorts(DisabledInstancePorts *disabled_inst)
|
|
{
|
|
setEdgeDisabledInstPorts(disabled_inst, disabled_inst->instance());
|
|
}
|
|
|
|
void
|
|
Sdc::setEdgeDisabledInstPorts(DisabledPorts *disabled_port,
|
|
Instance *inst)
|
|
{
|
|
if (disabled_port->all()) {
|
|
InstancePinIterator *pin_iter = network_->pinIterator(inst);
|
|
while (pin_iter->hasNext()) {
|
|
Pin *pin = pin_iter->next();
|
|
// set_disable_timing instance does not disable timing checks.
|
|
setEdgeDisabledInstFrom(pin, false);
|
|
}
|
|
delete pin_iter;
|
|
}
|
|
|
|
// Disable from pins.
|
|
LibertyPortSet::Iterator from_iter(disabled_port->from());
|
|
while (from_iter.hasNext()) {
|
|
LibertyPort *from_port = from_iter.next();
|
|
Pin *from_pin = network_->findPin(inst, from_port);
|
|
if (from_pin)
|
|
setEdgeDisabledInstFrom(from_pin, true);
|
|
}
|
|
|
|
// Disable to pins.
|
|
LibertyPortSet::Iterator to_iter(disabled_port->to());
|
|
while (to_iter.hasNext()) {
|
|
LibertyPort *to_port = to_iter.next();
|
|
Pin *to_pin = network_->findPin(inst, to_port);
|
|
if (to_pin) {
|
|
if (network_->direction(to_pin)->isAnyOutput()) {
|
|
Vertex *vertex = graph_->pinDrvrVertex(to_pin);
|
|
if (vertex) {
|
|
VertexInEdgeIterator edge_iter(vertex, graph_);
|
|
while (edge_iter.hasNext()) {
|
|
Edge *edge = edge_iter.next();
|
|
edge->setIsDisabledConstraint(true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Disable from/to pins.
|
|
if (disabled_port->fromTo()) {
|
|
for (const LibertyPortPair &from_to : *disabled_port->fromTo()) {
|
|
const LibertyPort *from_port = from_to.first;
|
|
const LibertyPort *to_port = from_to.second;
|
|
Pin *from_pin = network_->findPin(inst, from_port);
|
|
Pin *to_pin = network_->findPin(inst, to_port);
|
|
if (from_pin && network_->direction(from_pin)->isAnyInput()
|
|
&& to_pin) {
|
|
Vertex *from_vertex = graph_->pinLoadVertex(from_pin);
|
|
Vertex *to_vertex = graph_->pinDrvrVertex(to_pin);
|
|
if (from_vertex && to_vertex) {
|
|
VertexOutEdgeIterator edge_iter(from_vertex, graph_);
|
|
while (edge_iter.hasNext()) {
|
|
Edge *edge = edge_iter.next();
|
|
if (edge->to(graph_) == to_vertex)
|
|
edge->setIsDisabledConstraint(true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
Sdc::setEdgeDisabledInstFrom(Pin *from_pin,
|
|
bool disable_checks)
|
|
{
|
|
if (network_->direction(from_pin)->isAnyInput()) {
|
|
Vertex *from_vertex = graph_->pinLoadVertex(from_pin);
|
|
if (from_vertex) {
|
|
VertexOutEdgeIterator edge_iter(from_vertex, graph_);
|
|
while (edge_iter.hasNext()) {
|
|
Edge *edge = edge_iter.next();
|
|
if (disable_checks
|
|
|| !edge->role()->isTimingCheck())
|
|
edge->setIsDisabledConstraint(true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
Sdc::annotateGraphOutputDelays()
|
|
{
|
|
for (OutputDelay *output_delay : output_delays_) {
|
|
for (const Pin *lpin : output_delay->leafPins())
|
|
annotateGraphConstrained(lpin);
|
|
}
|
|
}
|
|
|
|
void
|
|
Sdc::annotateGraphDataChecks()
|
|
{
|
|
DataChecksMap::Iterator data_checks_iter(data_checks_to_map_);
|
|
while (data_checks_iter.hasNext()) {
|
|
DataCheckSet *checks = data_checks_iter.next();
|
|
DataCheckSet::Iterator check_iter(checks);
|
|
// There may be multiple data checks on a single pin,
|
|
// but we only need to mark it as constrained once.
|
|
if (check_iter.hasNext()) {
|
|
DataCheck *check = check_iter.next();
|
|
annotateGraphConstrained(check->to());
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
Sdc::annotateGraphConstrained(const PinSet *pins)
|
|
{
|
|
PinSet::ConstIterator pin_iter(pins);
|
|
while (pin_iter.hasNext()) {
|
|
const Pin *pin = pin_iter.next();
|
|
annotateGraphConstrained(pin);
|
|
}
|
|
}
|
|
|
|
void
|
|
Sdc::annotateGraphConstrained(const InstanceSet *insts)
|
|
{
|
|
InstanceSet::ConstIterator inst_iter(insts);
|
|
while (inst_iter.hasNext()) {
|
|
const Instance *inst = inst_iter.next();
|
|
annotateGraphConstrained(inst);
|
|
}
|
|
}
|
|
|
|
void
|
|
Sdc::annotateGraphConstrained(const Instance *inst)
|
|
{
|
|
InstancePinIterator *pin_iter = network_->pinIterator(inst);
|
|
while (pin_iter->hasNext()) {
|
|
Pin *pin = pin_iter->next();
|
|
if (network_->direction(pin)->isAnyInput())
|
|
annotateGraphConstrained(pin);
|
|
}
|
|
delete pin_iter;
|
|
}
|
|
|
|
void
|
|
Sdc::annotateGraphConstrained(const Pin *pin)
|
|
{
|
|
Vertex *vertex, *bidirect_drvr_vertex;
|
|
graph_->pinVertices(pin, vertex, bidirect_drvr_vertex);
|
|
// Pin may be hierarchical and have no vertex.
|
|
if (vertex)
|
|
vertex->setIsConstrained(true);
|
|
if (bidirect_drvr_vertex)
|
|
bidirect_drvr_vertex->setIsConstrained(true);
|
|
}
|
|
|
|
void
|
|
Sdc::annotateHierClkLatency()
|
|
{
|
|
ClockLatencies::Iterator latency_iter(clk_latencies_);
|
|
while (latency_iter.hasNext()) {
|
|
ClockLatency *latency = latency_iter.next();
|
|
const Pin *pin = latency->pin();
|
|
if (pin && network_->isHierarchical(pin))
|
|
annotateHierClkLatency(pin, latency);
|
|
}
|
|
}
|
|
|
|
void
|
|
Sdc::annotateHierClkLatency(const Pin *hpin,
|
|
ClockLatency *latency)
|
|
{
|
|
EdgesThruHierPinIterator edge_iter(hpin, network_, graph_);
|
|
while (edge_iter.hasNext()) {
|
|
Edge *edge = edge_iter.next();
|
|
edge_clk_latency_[edge] = latency;
|
|
}
|
|
}
|
|
|
|
ClockLatency *
|
|
Sdc::clockLatency(Edge *edge) const
|
|
{
|
|
return edge_clk_latency_.findKey(edge);
|
|
}
|
|
|
|
void
|
|
Sdc::clockLatency(Edge *edge,
|
|
const RiseFall *rf,
|
|
const MinMax *min_max,
|
|
// Return values.
|
|
float &latency,
|
|
bool &exists) const
|
|
{
|
|
ClockLatency *latencies = edge_clk_latency_.findKey(edge);
|
|
if (latencies)
|
|
latencies->delay(rf, min_max, latency, exists);
|
|
else {
|
|
latency = 0.0;
|
|
exists = false;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
Sdc::removeGraphAnnotations()
|
|
{
|
|
VertexIterator vertex_iter(graph_);
|
|
while (vertex_iter.hasNext()) {
|
|
Vertex *vertex = vertex_iter.next();
|
|
vertex->setIsDisabledConstraint(false);
|
|
vertex->setIsConstrained(false);
|
|
|
|
VertexOutEdgeIterator edge_iter(vertex, graph_);
|
|
while (edge_iter.hasNext()) {
|
|
Edge *edge = edge_iter.next();
|
|
edge->setIsDisabledConstraint(false);
|
|
}
|
|
}
|
|
edge_clk_latency_.clear();
|
|
}
|
|
|
|
void
|
|
Sdc::searchPreamble()
|
|
{
|
|
ensureClkHpinDisables();
|
|
ensureClkGroupExclusions();
|
|
}
|
|
|
|
} // namespace
|