Files
OpenSTA/search/CheckTiming.cc
T

395 lines
11 KiB
C++
Raw Normal View History

2018-09-28 08:54:21 -07:00
// OpenSTA, Static Timing Analyzer
2026-03-10 13:21:17 -07:00
// Copyright (c) 2026, Parallax Software, Inc.
2018-09-28 08:54:21 -07:00
//
// 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
2022-01-04 10:17:08 -07:00
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2018-09-28 08:54:21 -07:00
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
2022-01-04 10:17:08 -07:00
// along with this program. If not, see <https://www.gnu.org/licenses/>.
2025-01-21 18:54:33 -07:00
//
// 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.
2018-09-28 08:54:21 -07:00
2020-04-05 14:53:44 -07:00
#include "CheckTiming.hh"
2020-04-05 11:35:51 -07:00
2026-04-13 14:58:16 -07:00
#include "ClkNetwork.hh"
#include "ExceptionPath.hh"
#include "Format.hh"
#include "Genclks.hh"
#include "Graph.hh"
#include "GraphClass.hh"
#include "Levelize.hh"
#include "Mode.hh"
2020-04-05 14:53:44 -07:00
#include "Network.hh"
2026-04-13 14:58:16 -07:00
#include "NetworkClass.hh"
2020-04-05 14:53:44 -07:00
#include "NetworkCmp.hh"
2026-04-13 14:58:16 -07:00
#include "Path.hh"
2020-04-05 14:53:44 -07:00
#include "PortDelay.hh"
2026-04-13 14:58:16 -07:00
#include "PortDirection.hh"
2020-04-05 14:53:44 -07:00
#include "Sdc.hh"
2026-04-13 14:58:16 -07:00
#include "SdcClass.hh"
#include "Sim.hh"
2026-04-13 14:58:16 -07:00
#include "StaState.hh"
#include "TimingRole.hh"
2018-09-28 08:54:21 -07:00
namespace sta {
CheckTiming::CheckTiming(StaState *sta) :
2026-04-13 14:58:16 -07:00
StaState(sta)
2018-09-28 08:54:21 -07:00
{
}
CheckTiming::~CheckTiming()
{
deleteErrors();
}
void
CheckTiming::deleteErrors()
{
2026-01-03 16:59:35 -08:00
for (CheckError *error : errors_) {
2018-09-28 08:54:21 -07:00
delete error;
}
}
void
CheckTiming::clear()
{
deleteErrors();
errors_.clear();
}
CheckErrorSeq &
2026-01-03 16:59:35 -08:00
CheckTiming::check(const Mode *mode,
bool no_input_delay,
bool no_output_delay,
bool reg_multiple_clks,
bool reg_no_clks,
bool unconstrained_endpoints,
bool loops,
bool generated_clks)
2018-09-28 08:54:21 -07:00
{
clear();
2026-01-03 16:59:35 -08:00
mode_ = mode;
sdc_ = mode->sdc();
sim_ = mode->sim();
clk_network_ = mode->clkNetwork();
2018-09-28 08:54:21 -07:00
if (no_input_delay)
checkNoInputDelay();
if (no_output_delay)
checkNoOutputDelay();
if (reg_multiple_clks || reg_no_clks)
checkRegClks(reg_multiple_clks, reg_no_clks);
if (unconstrained_endpoints)
checkUnconstrainedEndpoints();
if (loops)
checkLoops();
if (generated_clks)
checkGeneratedClocks();
return errors_;
}
// Make sure there is a set_input_delay for each input/bidirect.
void
CheckTiming::checkNoInputDelay()
{
2023-01-19 11:23:45 -07:00
PinSet no_arrival(network_);
2018-09-28 08:54:21 -07:00
Instance *top_inst = network_->topInstance();
InstancePinIterator *pin_iter = network_->pinIterator(top_inst);
while (pin_iter->hasNext()) {
2023-01-19 11:23:45 -07:00
const Pin *pin = pin_iter->next();
2018-09-28 08:54:21 -07:00
if (!sdc_->isClock(pin)) {
PortDirection *dir = network_->direction(pin);
if (dir->isAnyInput()
2026-01-03 16:59:35 -08:00
&& !sdc_->hasInputDelay(pin)
&& !sim_->isConstant(pin))
no_arrival.insert(pin);
2018-09-28 08:54:21 -07:00
}
}
delete pin_iter;
2026-03-15 14:35:24 -07:00
pushPinErrors("Warning: There {} {} input port{} missing set_input_delay.",no_arrival);
2018-09-28 08:54:21 -07:00
}
void
CheckTiming::checkNoOutputDelay()
{
2023-01-19 11:23:45 -07:00
PinSet no_departure(network_);
2018-09-28 08:54:21 -07:00
checkNoOutputDelay(no_departure);
2026-03-15 14:35:24 -07:00
pushPinErrors("Warning: There {} {} output port{} missing set_output_delay.",
2026-01-03 16:59:35 -08:00
no_departure);
2018-09-28 08:54:21 -07:00
}
void
CheckTiming::checkNoOutputDelay(PinSet &no_departure)
{
Instance *top_inst = network_->topInstance();
InstancePinIterator *pin_iter = network_->pinIterator(top_inst);
while (pin_iter->hasNext()) {
2023-01-19 11:23:45 -07:00
const Pin *pin = pin_iter->next();
2018-09-28 08:54:21 -07:00
PortDirection *dir = network_->direction(pin);
if (dir->isAnyOutput()
2026-01-03 16:59:35 -08:00
&& !sdc_->hasOutputDelay(pin)
&& !sim_->isConstant(pin))
2018-09-28 08:54:21 -07:00
no_departure.insert(pin);
}
delete pin_iter;
}
bool
CheckTiming::hasClkedCheck(Vertex *vertex)
{
VertexInEdgeIterator edge_iter(vertex, graph_);
while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next();
if (edge->role() == TimingRole::setup()
2026-01-03 16:59:35 -08:00
&& clk_network_->isClock(edge->from(graph_)))
2018-09-28 08:54:21 -07:00
return true;
}
return false;
}
void
CheckTiming::checkRegClks(bool reg_multiple_clks,
2026-01-03 16:59:35 -08:00
bool reg_no_clks)
2018-09-28 08:54:21 -07:00
{
2023-01-19 11:23:45 -07:00
PinSet no_clk_pins(network_);
PinSet multiple_clk_pins(network_);
2026-01-03 16:59:35 -08:00
for (Vertex *vertex : graph_->regClkVertices()) {
2023-01-19 11:23:45 -07:00
const Pin *pin = vertex->pin();
2026-01-03 16:59:35 -08:00
const ClockSet *clks = clk_network_->clocks(pin);
if (reg_no_clks && clks == nullptr)
2018-09-28 08:54:21 -07:00
no_clk_pins.insert(pin);
2026-01-03 16:59:35 -08:00
if (reg_multiple_clks && clks && clks->size() > 1)
2018-09-28 08:54:21 -07:00
multiple_clk_pins.insert(pin);
}
2026-03-15 14:35:24 -07:00
pushPinErrors("Warning: There {} {} unclocked register/latch pin{}.",
2026-01-03 16:59:35 -08:00
no_clk_pins);
2026-03-15 14:35:24 -07:00
pushPinErrors("Warning: There {} {} register/latch pin{} with multiple clocks.",
2026-01-03 16:59:35 -08:00
multiple_clk_pins);
2018-09-28 08:54:21 -07:00
}
2026-03-15 14:35:24 -07:00
static const char *
plurality(int n)
{
return n == 1 ? "is" : "are";
}
static const char *
pluralSuffix(int n)
{
return n == 1 ? "" : "s";
}
2018-09-28 08:54:21 -07:00
void
CheckTiming::checkLoops()
{
// These may not need to be sorted because the graph roots are
// sorted during levelization so the discovery should be consistent.
2025-04-17 16:53:55 -07:00
GraphLoopSeq &loops = levelize_->loops();
2018-09-28 08:54:21 -07:00
// Count the combinational loops.
int loop_count = 0;
2025-04-17 16:53:55 -07:00
for (GraphLoop *loop : loops) {
2018-09-28 08:54:21 -07:00
if (loop->isCombinational())
loop_count++;
}
if (loop_count > 0) {
CheckError *error = new CheckError;
2026-03-15 14:35:24 -07:00
error->push_back(sta::format("Warning: There {} {} combinational loop{} in the design.",
plurality(loop_count),
loop_count,
pluralSuffix(loop_count)));
2018-09-28 08:54:21 -07:00
2026-01-03 16:59:35 -08:00
for (GraphLoop *loop : loops) {
2018-09-28 08:54:21 -07:00
if (loop->isCombinational()) {
2026-01-03 16:59:35 -08:00
Edge *last_edge = nullptr;
for (Edge *edge : *loop->edges()) {
Pin *pin = edge->from(graph_)->pin();
2026-03-08 15:02:14 -07:00
error->push_back(sdc_network_->pathName(pin));
2026-01-03 16:59:35 -08:00
last_edge = edge;
}
2022-01-15 12:51:05 -07:00
if (last_edge) {
2026-04-13 14:58:16 -07:00
error->emplace_back("| loop cut point");
2022-01-15 12:51:05 -07:00
const Pin *pin = last_edge->to(graph_)->pin();
2026-03-08 15:02:14 -07:00
error->push_back(sdc_network_->pathName(pin));
2022-01-15 12:51:05 -07:00
// Separator between loops.
2026-04-13 14:58:16 -07:00
error->emplace_back("--------------------------------");
2022-01-15 12:51:05 -07:00
}
2018-09-28 08:54:21 -07:00
}
}
errors_.push_back(error);
}
}
void
CheckTiming::checkUnconstrainedEndpoints()
{
2023-01-19 11:23:45 -07:00
PinSet unconstrained_ends(network_);
2025-07-22 19:32:55 -07:00
checkUnconstrainedOutputs(unconstrained_ends);
2018-09-28 08:54:21 -07:00
checkUnconstrainedSetups(unconstrained_ends);
2026-03-15 14:35:24 -07:00
pushPinErrors("Warning: There {} {} unconstrained endpoint{}.",
2026-01-03 16:59:35 -08:00
unconstrained_ends);
2018-09-28 08:54:21 -07:00
}
void
2025-07-22 19:32:55 -07:00
CheckTiming::checkUnconstrainedOutputs(PinSet &unconstrained_ends)
2018-09-28 08:54:21 -07:00
{
Instance *top_inst = network_->topInstance();
InstancePinIterator *pin_iter = network_->pinIterator(top_inst);
while (pin_iter->hasNext()) {
Pin *pin = pin_iter->next();
PortDirection *dir = network_->direction(pin);
Vertex *vertex = graph_->pinLoadVertex(pin);
2018-09-28 08:54:21 -07:00
if (dir->isAnyOutput()
2026-01-03 16:59:35 -08:00
&& !sim_->isConstant(pin)
&& !((hasClkedDepature(pin)
2026-01-03 16:59:35 -08:00
&& hasClkedArrival(vertex))
|| hasMaxDelay(pin)))
2018-09-28 08:54:21 -07:00
unconstrained_ends.insert(pin);
}
delete pin_iter;
}
bool
CheckTiming::hasClkedDepature(Pin *pin)
{
OutputDelaySet *output_delays = sdc_->outputDelaysLeafPin(pin);
if (output_delays) {
for (OutputDelay *output_delay : *output_delays) {
2020-09-18 09:05:41 -07:00
if (output_delay->clkEdge() != nullptr
2026-01-03 16:59:35 -08:00
|| output_delay->refPin() != nullptr)
return true;
}
2018-09-28 08:54:21 -07:00
}
return false;
}
// Check for max delay exception that ends at pin.
bool
CheckTiming::hasMaxDelay(Pin *pin)
{
2026-01-03 16:59:35 -08:00
for (const ExceptionPath *exception : sdc_->exceptions()) {
2018-09-28 08:54:21 -07:00
ExceptionTo *to = exception->to();
if (exception->isPathDelay()
2026-01-03 16:59:35 -08:00
&& exception->minMax() == MinMaxAll::max()
&& to
&& to->hasPins()
&& to->pins()->contains(pin))
2018-09-28 08:54:21 -07:00
return true;
}
return false;
}
void
CheckTiming::checkUnconstrainedSetups(PinSet &unconstrained_ends)
{
VertexIterator vertex_iter(graph_);
while (vertex_iter.hasNext()) {
Vertex *vertex = vertex_iter.next();
2026-01-03 16:59:35 -08:00
if (!sim_->isConstant(vertex)) {
VertexInEdgeIterator edge_iter(vertex, graph_);
while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next();
if (edge->role() == TimingRole::setup()
2026-01-03 16:59:35 -08:00
&& (!clk_network_->isClock(edge->from(graph_))
|| !hasClkedArrival(edge->to(graph_)))) {
unconstrained_ends.insert(vertex->pin());
break;
}
2018-09-28 08:54:21 -07:00
}
}
}
}
bool
CheckTiming::hasClkedArrival(Vertex *vertex)
{
VertexPathIterator path_iter(vertex, this);
while (path_iter.hasNext()) {
2025-03-26 18:21:03 -07:00
Path *path = path_iter.next();
2018-09-28 08:54:21 -07:00
if (path->clock(this))
return true;
}
return false;
}
void
CheckTiming::checkGeneratedClocks()
{
ClockSet gen_clk_errors;
2026-01-03 16:59:35 -08:00
for (auto clk : sdc_->clocks()) {
2018-09-28 08:54:21 -07:00
if (clk->isGenerated()) {
2026-01-03 16:59:35 -08:00
mode_->genclks()->checkMaster(clk, sdc_);
2018-09-28 08:54:21 -07:00
bool found_clk = false;
2026-01-03 16:59:35 -08:00
VertexSet src_vertices = makeVertexSet(this);
2018-09-28 08:54:21 -07:00
clk->srcPinVertices(src_vertices, network_, graph_);
2026-01-03 16:59:35 -08:00
for (Vertex *vertex : src_vertices) {
if (clk_network_->isClock(vertex)) {
found_clk = true;
break;
}
2018-09-28 08:54:21 -07:00
}
if (!found_clk)
2026-01-03 16:59:35 -08:00
gen_clk_errors.insert(clk);
2018-09-28 08:54:21 -07:00
}
}
2026-03-15 14:35:24 -07:00
pushClkErrors("Warning: There {} {} generated clock{} not connected to a clock source.",
2026-01-03 16:59:35 -08:00
gen_clk_errors);
2018-09-28 08:54:21 -07:00
}
// Report the "msg" error for each pin in "pins".
void
2026-03-15 14:35:24 -07:00
CheckTiming::pushPinErrors(std::string_view msg,
2026-01-03 16:59:35 -08:00
PinSet &pins)
2018-09-28 08:54:21 -07:00
{
if (!pins.empty()) {
CheckError *error = new CheckError;
2026-03-15 14:35:24 -07:00
error->push_back(sta::formatRuntime(msg,
plurality(pins.size()),
pins.size(),
pluralSuffix(pins.size())));
2018-09-28 08:54:21 -07:00
// Sort the error pins so the output is independent of the order
// the the errors are discovered.
2023-01-19 11:23:45 -07:00
PinSeq pins1 = sortByPathName(&pins, network_);
for (const Pin *pin : pins1) {
2026-03-08 15:02:14 -07:00
error->push_back(sdc_network_->pathName(pin));
2018-09-28 08:54:21 -07:00
}
errors_.push_back(error);
}
}
void
CheckTiming::pushClkErrors(const char *msg,
2026-01-03 16:59:35 -08:00
ClockSet &clks)
2018-09-28 08:54:21 -07:00
{
if (!clks.empty()) {
CheckError *error = new CheckError;
2026-03-15 14:35:24 -07:00
error->push_back(sta::formatRuntime(msg,
plurality(clks.size()),
clks.size(),
pluralSuffix(clks.size())));
2018-09-28 08:54:21 -07:00
// Sort the error clks so the output is independent of the order
// the the errors are discovered.
2023-01-19 11:23:45 -07:00
ClockSeq clks1 = sortByName(&clks);
for (const Clock *clk : clks1) {
2026-03-08 15:02:14 -07:00
error->push_back(clk->name());
2018-09-28 08:54:21 -07:00
}
errors_.push_back(error);
}
}
2026-04-13 14:58:16 -07:00
} // namespace sta