OpenSTA/sdc/test/cpp/TestSdcStaDesign.cc

2627 lines
92 KiB
C++
Raw Normal View History

#include <gtest/gtest.h>
#include <tcl.h>
#include <fstream>
#include <iterator>
#include <string>
#include "Transition.hh"
#include "MinMax.hh"
#include "ExceptionPath.hh"
#include "TimingRole.hh"
#include "Clock.hh"
#include "RiseFallMinMax.hh"
#include "CycleAccting.hh"
#include "SdcCmdComment.hh"
#include "Variables.hh"
#include "DeratingFactors.hh"
#include "ClockLatency.hh"
#include "ClockInsertion.hh"
#include "ClockGatingCheck.hh"
#include "PortExtCap.hh"
#include "DataCheck.hh"
#include "PinPair.hh"
#include "Sta.hh"
#include "Sdc.hh"
#include "ReportTcl.hh"
#include "Scene.hh"
#include "DisabledPorts.hh"
#include "InputDrive.hh"
#include "PatternMatch.hh"
#include "Network.hh"
#include "Liberty.hh"
#include "TimingArc.hh"
#include "Graph.hh"
#include "PortDelay.hh"
#include "PortDirection.hh"
namespace sta {
static std::string
readTextFile(const char *filename)
{
std::ifstream in(filename);
if (!in.is_open())
return "";
return std::string((std::istreambuf_iterator<char>(in)),
std::istreambuf_iterator<char>());
}
static size_t
countSubstring(const std::string &text,
const std::string &needle)
{
if (needle.empty())
return 0;
size_t count = 0;
size_t pos = 0;
while ((pos = text.find(needle, pos)) != std::string::npos) {
++count;
pos += needle.size();
}
return count;
}
// ============================================================
// R10_ tests: Additional SDC coverage
// ============================================================
// SdcInitTest fixture (needed by some R10_ tests below)
class SdcInitTest : public ::testing::Test {
protected:
void SetUp() override {
interp_ = Tcl_CreateInterp();
initSta();
sta_ = new Sta;
Sta::setSta(sta_);
sta_->makeComponents();
ReportTcl *report = dynamic_cast<ReportTcl*>(sta_->report());
if (report)
report->setTclInterp(interp_);
}
void TearDown() override {
deleteAllMemory();
sta_ = nullptr;
if (interp_)
Tcl_DeleteInterp(interp_);
interp_ = nullptr;
}
Sta *sta_;
Tcl_Interp *interp_;
};
// --- SdcDesignTest: loads nangate45 + example1.v for SDC tests needing a design ---
class SdcDesignTest : public ::testing::Test {
protected:
void SetUp() override {
interp_ = Tcl_CreateInterp();
initSta();
sta_ = new Sta;
Sta::setSta(sta_);
sta_->makeComponents();
ReportTcl *report = dynamic_cast<ReportTcl*>(sta_->report());
if (report)
report->setTclInterp(interp_);
Scene *corner = sta_->cmdScene();
const MinMaxAll *min_max = MinMaxAll::all();
LibertyLibrary *lib = sta_->readLiberty(
"test/nangate45/Nangate45_typ.lib", corner, min_max, false);
ASSERT_NE(lib, nullptr);
bool ok = sta_->readVerilog("examples/example1.v");
ASSERT_TRUE(ok);
ok = sta_->linkDesign("top", true);
ASSERT_TRUE(ok);
Network *network = sta_->network();
Instance *top = network->topInstance();
Pin *clk1 = network->findPin(top, "clk1");
Pin *clk2 = network->findPin(top, "clk2");
Pin *clk3 = network->findPin(top, "clk3");
ASSERT_NE(clk1, nullptr);
PinSet *clk_pins = new PinSet(network);
clk_pins->insert(clk1);
clk_pins->insert(clk2);
clk_pins->insert(clk3);
FloatSeq *waveform = new FloatSeq;
waveform->push_back(0.0f);
waveform->push_back(5.0f);
sta_->makeClock("clk", clk_pins, false, 10.0f, waveform, nullptr, sta_->cmdMode());
Pin *in1 = network->findPin(top, "in1");
Clock *clk = sta_->cmdSdc()->findClock("clk");
if (in1 && clk) {
sta_->setInputDelay(in1, RiseFallBoth::riseFall(),
clk, RiseFall::rise(), nullptr,
false, false, MinMaxAll::all(), true, 0.0f, sta_->cmdSdc());
}
sta_->updateTiming(true);
}
void TearDown() override {
deleteAllMemory();
sta_ = nullptr;
if (interp_)
Tcl_DeleteInterp(interp_);
interp_ = nullptr;
}
Pin *findPin(const char *path_name) {
Network *network = sta_->cmdNetwork();
return network->findPin(path_name);
}
Sta *sta_;
Tcl_Interp *interp_;
};
// --- CycleAccting: sourceCycle, targetCycle via timing update ---
TEST_F(SdcDesignTest, CycleAcctingSourceTargetCycle) {
// CycleAccting methods are called internally during timing
Sdc *sdc = sta_->cmdSdc();
Clock *clk = sdc->findClock("clk");
ASSERT_NE(clk, nullptr);
// Make a second clock for inter-clock cycle accounting
Network *network = sta_->network();
Instance *top = network->topInstance();
Pin *clk2 = network->findPin(top, "clk2");
if (clk2) {
PinSet *clk2_pins = new PinSet(network);
clk2_pins->insert(clk2);
FloatSeq *waveform2 = new FloatSeq;
waveform2->push_back(0.0f);
waveform2->push_back(2.5f);
sta_->makeClock("clk2", clk2_pins, false, 5.0f, waveform2, nullptr, sta_->cmdMode());
sta_->updateTiming(true);
// Forces CycleAccting to compute inter-clock accounting
}
}
// --- ExceptionThru: to_string ---
TEST_F(SdcInitTest, ExceptionThruAsString) {
ASSERT_NO_THROW(( [&](){
Sdc *sdc = sta_->cmdSdc();
Network *network = sta_->cmdNetwork();
// Create ExceptionThru with no objects
ExceptionThru *thru = new ExceptionThru(nullptr, nullptr, nullptr,
RiseFallBoth::riseFall(), true, network);
std::string str = thru->to_string(network);
// With all-nullptr objects, to_string returns empty
delete thru;
}() ));
}
// --- ExceptionTo: to_string, matches, cmdKeyword ---
TEST_F(SdcInitTest, ExceptionToAsString) {
ASSERT_NO_THROW(( [&](){
Network *network = sta_->cmdNetwork();
ExceptionTo *to = new ExceptionTo(nullptr, nullptr, nullptr,
RiseFallBoth::riseFall(),
RiseFallBoth::riseFall(),
true, network);
std::string str = to->to_string(network);
// matches with null pin and rf
to->matches(nullptr, RiseFall::rise());
delete to;
}() ));
}
// --- ExceptionFrom: findHash ---
TEST_F(SdcInitTest, ExceptionFromHash) {
ASSERT_NO_THROW(( [&](){
Network *network = sta_->cmdNetwork();
ExceptionFrom *from = new ExceptionFrom(nullptr, nullptr, nullptr,
RiseFallBoth::riseFall(), true, network);
size_t h = from->hash();
EXPECT_GE(h, 0);
delete from;
}() ));
}
// --- ExceptionPath: mergeable ---
TEST_F(SdcInitTest, ExceptionPathMergeable) {
Sdc *sdc = sta_->cmdSdc();
FalsePath *fp1 = new FalsePath(nullptr, nullptr, nullptr,
MinMaxAll::all(), true, nullptr);
FalsePath *fp2 = new FalsePath(nullptr, nullptr, nullptr,
MinMaxAll::all(), true, nullptr);
bool m = fp1->mergeable(fp2);
EXPECT_TRUE(m);
// Different type should not be mergeable
PathDelay *pd = new PathDelay(nullptr, nullptr, nullptr,
MinMax::max(), false, false, 5.0, true, nullptr);
bool m2 = fp1->mergeable(pd);
EXPECT_FALSE(m2);
delete fp1;
delete fp2;
delete pd;
}
// --- ExceptionPt constructor ---
TEST_F(SdcInitTest, ExceptionPtBasic) {
Network *network = sta_->cmdNetwork();
ExceptionFrom *from = new ExceptionFrom(nullptr, nullptr, nullptr,
RiseFallBoth::rise(), true, network);
EXPECT_TRUE(from->isFrom());
EXPECT_FALSE(from->isTo());
EXPECT_FALSE(from->isThru());
delete from;
}
// --- ExceptionFromTo destructor ---
TEST_F(SdcInitTest, ExceptionFromToDestructor) {
ASSERT_NO_THROW(( [&](){
Network *network = sta_->cmdNetwork();
ExceptionFrom *from = new ExceptionFrom(nullptr, nullptr, nullptr,
RiseFallBoth::riseFall(), true, network);
delete from;
// Destructor coverage for ExceptionFromTo
}() ));
}
// --- ExceptionPath destructor ---
TEST_F(SdcInitTest, ExceptionPathDestructor) {
ASSERT_NO_THROW(( [&](){
FalsePath *fp = new FalsePath(nullptr, nullptr, nullptr,
MinMaxAll::all(), true, nullptr);
delete fp;
}() ));
}
// --- DisabledCellPorts: construct and accessors ---
TEST_F(SdcInitTest, DisabledCellPortsConstruct2) {
LibertyLibrary *lib = sta_->readLiberty("test/nangate45/Nangate45_typ.lib",
sta_->cmdScene(),
MinMaxAll::min(), false);
if (lib) {
LibertyCell *buf = lib->findLibertyCell("BUF_X1");
if (buf) {
DisabledCellPorts dcp(buf);
EXPECT_EQ(dcp.cell(), buf);
EXPECT_FALSE(dcp.all());
dcp.setDisabledAll();
EXPECT_TRUE(dcp.all());
dcp.removeDisabledAll();
EXPECT_FALSE(dcp.all());
}
}
}
// --- PortDelay: refTransition ---
TEST_F(SdcDesignTest, PortDelayRefTransition) {
ASSERT_NO_THROW(( [&](){
Sdc *sdc = sta_->cmdSdc();
const InputDelaySet &delays = sdc->inputDelays();
for (InputDelay *delay : delays) {
const RiseFall *ref_rf = delay->refTransition();
EXPECT_NE(ref_rf, nullptr);
// Also exercise other PortDelay accessors
const Pin *pin = delay->pin();
EXPECT_NE(pin, nullptr);
const ClockEdge *ce = delay->clkEdge();
EXPECT_NE(ce, nullptr);
delay->sourceLatencyIncluded();
delay->networkLatencyIncluded();
// refPin is null when no reference pin is set for the port delay
delay->refPin();
int idx = delay->index();
EXPECT_GE(idx, 0);
}
}() ));
}
// --- ClockEdge: accessors (time, clock, transition) ---
TEST_F(SdcInitTest, ClockEdgeAccessors) {
Sdc *sdc = sta_->cmdSdc();
PinSet *clk_pins = new PinSet(sta_->cmdNetwork());
FloatSeq *waveform = new FloatSeq;
waveform->push_back(0.0f);
waveform->push_back(5.0f);
sta_->makeClock("test_clk_edge", clk_pins, false, 10.0f, waveform, nullptr, sta_->cmdMode());
Clock *clk = sdc->findClock("test_clk_edge");
ASSERT_NE(clk, nullptr);
ClockEdge *rise_edge = clk->edge(RiseFall::rise());
ClockEdge *fall_edge = clk->edge(RiseFall::fall());
ASSERT_NE(rise_edge, nullptr);
ASSERT_NE(fall_edge, nullptr);
// time()
EXPECT_FLOAT_EQ(rise_edge->time(), 0.0f);
EXPECT_FLOAT_EQ(fall_edge->time(), 5.0f);
// clock()
EXPECT_EQ(rise_edge->clock(), clk);
EXPECT_EQ(fall_edge->clock(), clk);
// transition()
EXPECT_EQ(rise_edge->transition(), RiseFall::rise());
EXPECT_EQ(fall_edge->transition(), RiseFall::fall());
// name()
EXPECT_FALSE(rise_edge->name().empty());
EXPECT_FALSE(fall_edge->name().empty());
// index()
int ri = rise_edge->index();
int fi = fall_edge->index();
EXPECT_NE(ri, fi);
}
// --- Sdc: removeDataCheck ---
TEST_F(SdcDesignTest, SdcRemoveDataCheck) {
ASSERT_NO_THROW(( [&](){
Sdc *sdc = sta_->cmdSdc();
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *from_pin = network->findPin(top, "r1/D");
Pin *to_pin = network->findPin(top, "r1/CK");
if (from_pin && to_pin) {
sdc->setDataCheck(from_pin, RiseFallBoth::riseFall(),
to_pin, RiseFallBoth::riseFall(),
nullptr, MinMaxAll::max(), 1.0);
sdc->removeDataCheck(from_pin, RiseFallBoth::riseFall(),
to_pin, RiseFallBoth::riseFall(),
nullptr, MinMaxAll::max());
}
}() ));
}
// --- Sdc: deleteInterClockUncertainty ---
TEST_F(SdcInitTest, SdcInterClockUncertainty) {
Sdc *sdc = sta_->cmdSdc();
PinSet *pins1 = new PinSet(sta_->cmdNetwork());
FloatSeq *waveform1 = new FloatSeq;
waveform1->push_back(0.0f);
waveform1->push_back(5.0f);
sta_->makeClock("clk_a", pins1, false, 10.0f, waveform1, nullptr, sta_->cmdMode());
PinSet *pins2 = new PinSet(sta_->cmdNetwork());
FloatSeq *waveform2 = new FloatSeq;
waveform2->push_back(0.0f);
waveform2->push_back(2.5f);
sta_->makeClock("clk_b", pins2, false, 5.0f, waveform2, nullptr, sta_->cmdMode());
Clock *clk_a = sdc->findClock("clk_a");
Clock *clk_b = sdc->findClock("clk_b");
ASSERT_NE(clk_a, nullptr);
ASSERT_NE(clk_b, nullptr);
sta_->setClockUncertainty(clk_a, RiseFallBoth::riseFall(),
clk_b, RiseFallBoth::riseFall(),
MinMaxAll::max(), 0.2f, sta_->cmdSdc());
// Remove it
sta_->removeClockUncertainty(clk_a, RiseFallBoth::riseFall(),
clk_b, RiseFallBoth::riseFall(),
MinMaxAll::max(), sta_->cmdSdc());
}
// --- Sdc: clearClkGroupExclusions (via removeClockGroupsLogicallyExclusive) ---
TEST_F(SdcInitTest, SdcClearClkGroupExclusions) {
ClockGroups *cg = sta_->makeClockGroups("grp_exc", true, false, false, false, nullptr, sta_->cmdSdc());
EXPECT_NE(cg, nullptr);
sta_->removeClockGroupsLogicallyExclusive("grp_exc", sta_->cmdSdc());
}
// --- Sdc: false path exercises pathDelayFrom/To indirectly ---
TEST_F(SdcDesignTest, SdcFalsePathExercise) {
// Creating a false path from/to exercises pathDelayFrom/To code paths
// through makeFalsePath and the SDC infrastructure
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *in1 = network->findPin(top, "in1");
Pin *out = network->findPin(top, "out");
if (in1 && out) {
PinSet *from_pins = new PinSet(network);
from_pins->insert(in1);
ExceptionFrom *from = sta_->makeExceptionFrom(from_pins, nullptr, nullptr,
RiseFallBoth::riseFall(), sta_->cmdSdc());
PinSet *to_pins = new PinSet(network);
to_pins->insert(out);
ExceptionTo *to = sta_->makeExceptionTo(to_pins, nullptr, nullptr,
RiseFallBoth::riseFall(),
RiseFallBoth::riseFall(), sta_->cmdSdc());
sta_->makeFalsePath(from, nullptr, to, MinMaxAll::all(), nullptr, sta_->cmdSdc());
// Write SDC to exercise the path delay annotation
const char *fn = "/tmp/test_sdc_r10_falsepath_exercise.sdc";
sta_->writeSdc(sta_->cmdSdc(), fn, false, false, 4, false, true);
FILE *f = fopen(fn, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
}
// --- WriteSdc via SdcDesignTest ---
TEST_F(SdcDesignTest, WriteSdcBasic) {
const char *filename = "/tmp/test_write_sdc_sdc_r10.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
TEST_F(SdcDesignTest, WriteSdcWithOutputDelay) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *out = network->findPin(top, "out");
Clock *clk = sta_->cmdSdc()->findClock("clk");
if (out && clk) {
sta_->setOutputDelay(out, RiseFallBoth::riseFall(),
clk, RiseFall::rise(), nullptr,
false, false, MinMaxAll::all(), true, 3.0f, sta_->cmdSdc());
}
const char *filename = "/tmp/test_write_sdc_sdc_r10_outdelay.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
TEST_F(SdcDesignTest, WriteSdcNative) {
const char *filename = "/tmp/test_write_sdc_sdc_r10_native.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, true, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
TEST_F(SdcDesignTest, WriteSdcWithFalsePath) {
sta_->makeFalsePath(nullptr, nullptr, nullptr, MinMaxAll::all(), nullptr, sta_->cmdSdc());
const char *filename = "/tmp/test_write_sdc_sdc_r10_fp.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
TEST_F(SdcDesignTest, WriteSdcWithDerating) {
sta_->setTimingDerate(TimingDerateType::cell_delay,
PathClkOrData::data,
RiseFallBoth::riseFall(),
EarlyLate::early(), 0.95,
sta_->cmdSdc());
const char *filename = "/tmp/test_write_sdc_sdc_r10_derate.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
TEST_F(SdcDesignTest, WriteSdcWithDisable) {
Graph *graph = sta_->graph();
Network *network = sta_->cmdNetwork();
Pin *pin = findPin("r1/D");
if (pin && graph) {
Vertex *v = graph->pinLoadVertex(pin);
if (v) {
VertexInEdgeIterator in_iter(v, graph);
if (in_iter.hasNext()) {
Edge *edge = in_iter.next();
sta_->disable(edge, sta_->cmdSdc());
}
}
}
const char *filename = "/tmp/test_write_sdc_sdc_r10_disable.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
TEST_F(SdcDesignTest, WriteSdcWithClockLatency) {
Clock *clk = sta_->cmdSdc()->findClock("clk");
if (clk) {
sta_->setClockLatency(clk, nullptr, RiseFallBoth::riseFall(),
MinMaxAll::all(), 0.5f, sta_->cmdSdc());
}
const char *filename = "/tmp/test_write_sdc_sdc_r10_clklat.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
TEST_F(SdcDesignTest, WriteSdcWithInterClkUncertainty) {
Clock *clk = sta_->cmdSdc()->findClock("clk");
if (clk) {
sta_->setClockUncertainty(clk, RiseFallBoth::riseFall(),
clk, RiseFallBoth::riseFall(),
MinMaxAll::max(), 0.1f, sta_->cmdSdc());
}
const char *filename = "/tmp/test_write_sdc_sdc_r10_interclk.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- Sdc: capacitanceLimit ---
TEST_F(SdcDesignTest, SdcCapacitanceLimit) {
Sdc *sdc = sta_->cmdSdc();
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *pin = network->findPin(top, "r1/D");
if (pin) {
float limit;
bool exists;
sdc->capacitanceLimit(pin, MinMax::max(), limit, exists);
// No limit set initially
EXPECT_FALSE(exists);
}
}
// --- Sdc: annotateGraphConstrained ---
TEST_F(SdcDesignTest, SdcAnnotateGraphConstrained) {
ASSERT_NO_THROW(( [&](){
// These are called during timing update; exercising indirectly
sta_->updateTiming(true);
}() ));
}
// --- DisabledInstancePorts: construct and accessors ---
TEST_F(SdcDesignTest, DisabledInstancePortsAccessors) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
InstanceChildIterator *iter = network->childIterator(top);
if (iter->hasNext()) {
Instance *inst = iter->next();
DisabledInstancePorts dip(inst);
EXPECT_EQ(dip.instance(), inst);
}
delete iter;
}
// --- PinClockPairLess: using public class ---
TEST_F(SdcDesignTest, PinClockPairLessDesign) {
ASSERT_NO_THROW(( [&](){
Network *network = sta_->cmdNetwork();
PinClockPairLess less(network);
}() ));
}
// --- Sdc: clockLatency for edge ---
TEST_F(SdcDesignTest, SdcClockLatencyEdge) {
ASSERT_NO_THROW(( [&](){
Sdc *sdc = sta_->cmdSdc();
Graph *graph = sta_->graph();
Network *network = sta_->cmdNetwork();
Pin *pin = findPin("r1/CK");
if (pin && graph) {
Vertex *v = graph->pinLoadVertex(pin);
if (v) {
VertexInEdgeIterator in_iter(v, graph);
if (in_iter.hasNext()) {
Edge *edge = in_iter.next();
// clockLatency may be null if no latency is set for this edge
sdc->clockLatency(edge);
}
}
}
}() ));
}
// --- Sdc: disable/removeDisable for pin pair ---
TEST_F(SdcDesignTest, SdcDisablePinPair) {
ASSERT_NO_THROW(( [&](){
Sdc *sdc = sta_->cmdSdc();
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
// Find a gate with input/output pin pair
InstanceChildIterator *inst_iter = network->childIterator(top);
while (inst_iter->hasNext()) {
Instance *inst = inst_iter->next();
LibertyCell *lib_cell = network->libertyCell(inst);
if (lib_cell) {
LibertyPort *in_port = nullptr;
LibertyPort *out_port = nullptr;
LibertyCellPortIterator port_iter(lib_cell);
while (port_iter.hasNext()) {
LibertyPort *port = port_iter.next();
if (port->direction()->isInput() && !in_port)
in_port = port;
else if (port->direction()->isOutput() && !out_port)
out_port = port;
}
if (in_port && out_port) {
Pin *in_pin = network->findPin(inst, in_port);
Pin *out_pin = network->findPin(inst, out_port);
if (in_pin && out_pin) {
sdc->disableWire(in_pin, out_pin);
sdc->removeDisableWire(in_pin, out_pin);
break;
}
}
}
}
delete inst_iter;
}() ));
}
// --- ExceptionThru: makePinEdges, makeNetEdges, makeInstEdges, deletePinEdges ---
TEST_F(SdcDesignTest, ExceptionThruEdges) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *pin = network->findPin(top, "in1");
if (pin) {
PinSet *pins = new PinSet(network);
pins->insert(pin);
ExceptionThru *thru = new ExceptionThru(pins, nullptr, nullptr,
RiseFallBoth::riseFall(), true, network);
std::string str = thru->to_string(network);
EXPECT_FALSE(str.empty());
delete thru;
}
}
TEST_F(SdcDesignTest, ExceptionThruWithNet) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
// Find a net
NetIterator *net_iter = network->netIterator(top);
if (net_iter->hasNext()) {
Net *net = net_iter->next();
NetSet *nets = new NetSet(network);
nets->insert(net);
ExceptionThru *thru = new ExceptionThru(nullptr, nets, nullptr,
RiseFallBoth::riseFall(), true, network);
std::string str = thru->to_string(network);
EXPECT_FALSE(str.empty());
delete thru;
}
delete net_iter;
}
TEST_F(SdcDesignTest, ExceptionThruWithInstance) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
InstanceChildIterator *inst_iter = network->childIterator(top);
if (inst_iter->hasNext()) {
Instance *inst = inst_iter->next();
InstanceSet *insts = new InstanceSet(network);
insts->insert(inst);
ExceptionThru *thru = new ExceptionThru(nullptr, nullptr, insts,
RiseFallBoth::riseFall(), true, network);
std::string str = thru->to_string(network);
EXPECT_FALSE(str.empty());
delete thru;
}
delete inst_iter;
}
// --- WriteSdc with leaf/map_hpins ---
TEST_F(SdcDesignTest, WriteSdcLeaf) {
const char *filename = "/tmp/test_write_sdc_sdc_r10_leaf.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, true, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with data check ---
TEST_F(SdcDesignTest, WriteSdcWithDataCheck) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *from_pin = network->findPin(top, "r1/D");
Pin *to_pin = network->findPin(top, "r1/CK");
if (from_pin && to_pin) {
sta_->setDataCheck(from_pin, RiseFallBoth::riseFall(),
to_pin, RiseFallBoth::riseFall(),
nullptr, MinMaxAll::max(), 1.0f, sta_->cmdSdc());
}
const char *filename = "/tmp/test_write_sdc_sdc_r10_datacheck.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with port loads ---
TEST_F(SdcDesignTest, WriteSdcWithPortLoad) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *out = network->findPin(top, "out");
if (out) {
Port *port = network->port(out);
if (port)
sta_->setPortExtPinCap(port, RiseFallBoth::riseFall(), MinMaxAll::all(), 0.5f, sta_->cmdSdc());
}
const char *filename = "/tmp/test_write_sdc_sdc_r10_portload.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with clock slew ---
TEST_F(SdcDesignTest, WriteSdcWithClockSlew) {
Clock *clk = sta_->cmdSdc()->findClock("clk");
if (clk) {
sta_->setClockSlew(clk, RiseFallBoth::riseFall(), MinMaxAll::all(), 0.1f, sta_->cmdSdc());
}
const char *filename = "/tmp/test_write_sdc_sdc_r10_clkslew.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with clock insertion ---
TEST_F(SdcDesignTest, WriteSdcWithClockInsertion) {
Clock *clk = sta_->cmdSdc()->findClock("clk");
if (clk) {
sta_->setClockInsertion(clk, nullptr, RiseFallBoth::rise(),
MinMaxAll::all(), EarlyLateAll::all(), 0.3f, sta_->cmdSdc());
}
const char *filename = "/tmp/test_write_sdc_sdc_r10_clkins.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with multicycle path ---
TEST_F(SdcDesignTest, WriteSdcWithMulticycle) {
sta_->makeMulticyclePath(nullptr, nullptr, nullptr,
MinMaxAll::max(), true, 2, nullptr, sta_->cmdSdc());
const char *filename = "/tmp/test_write_sdc_sdc_r10_mcp.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with max area ---
TEST_F(SdcDesignTest, WriteSdcWithMaxArea) {
sta_->cmdSdc()->setMaxArea(1000.0);
const char *filename = "/tmp/test_write_sdc_sdc_r10_maxarea.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with min pulse width ---
TEST_F(SdcDesignTest, WriteSdcWithMpw) {
sta_->cmdSdc()->setMinPulseWidth(RiseFallBoth::rise(), 0.5);
const char *filename = "/tmp/test_write_sdc_sdc_r10_mpw.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with voltage ---
TEST_F(SdcDesignTest, WriteSdcWithVoltage) {
sta_->cmdSdc()->setVoltage(MinMax::max(), 1.1);
sta_->cmdSdc()->setVoltage(MinMax::min(), 0.9);
const char *filename = "/tmp/test_write_sdc_sdc_r10_voltage.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- Sdc: deleteLatchBorrowLimitsReferencing (via clock removal) ---
TEST_F(SdcInitTest, SdcDeleteLatchBorrowLimits) {
Sdc *sdc = sta_->cmdSdc();
PinSet *clk_pins = new PinSet(sta_->cmdNetwork());
FloatSeq *waveform = new FloatSeq;
waveform->push_back(0.0f);
waveform->push_back(5.0f);
sta_->makeClock("clk_borrow", clk_pins, false, 10.0f, waveform, nullptr, sta_->cmdMode());
Clock *clk = sdc->findClock("clk_borrow");
ASSERT_NE(clk, nullptr);
// Set latch borrow limit on clock
sdc->setLatchBorrowLimit(clk, 0.5f);
// Remove the clock; this calls deleteLatchBorrowLimitsReferencing
sta_->removeClock(clk, sta_->cmdSdc());
}
// ============================================================
// R10_ Additional SDC Tests - Round 2
// ============================================================
// --- WriteSdc with drive resistance ---
TEST_F(SdcDesignTest, WriteSdcWithDriveResistance) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *in1 = network->findPin(top, "in1");
if (in1) {
Port *port = network->port(in1);
if (port) {
sta_->setDriveResistance(port, RiseFallBoth::riseFall(),
MinMaxAll::all(), 50.0f, sta_->cmdSdc());
}
}
const char *filename = "/tmp/test_write_sdc_sdc_r10_driveres.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with logic value / set_logic_one ---
TEST_F(SdcDesignTest, WriteSdcWithLogicValue) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *in1 = network->findPin(top, "in1");
if (in1) {
sta_->setLogicValue(in1, LogicValue::one, sta_->cmdMode());
}
const char *filename = "/tmp/test_write_sdc_sdc_r10_logicval.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with case analysis ---
TEST_F(SdcDesignTest, WriteSdcWithCaseAnalysis) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *in2 = network->findPin(top, "in2");
if (in2) {
sta_->setCaseAnalysis(in2, LogicValue::zero, sta_->cmdMode());
}
const char *filename = "/tmp/test_write_sdc_sdc_r10_caseanalysis.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with latch borrow limit on pin ---
TEST_F(SdcDesignTest, WriteSdcWithLatchBorrowLimitPin) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *pin = network->findPin(top, "r1/D");
if (pin) {
sta_->setLatchBorrowLimit(pin, 0.3f, sta_->cmdSdc());
}
const char *filename = "/tmp/test_write_sdc_sdc_r10_latchborrow.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with latch borrow limit on instance ---
TEST_F(SdcDesignTest, WriteSdcWithLatchBorrowLimitInst) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
InstanceChildIterator *iter = network->childIterator(top);
if (iter->hasNext()) {
Instance *inst = iter->next();
sta_->setLatchBorrowLimit(inst, 0.5f, sta_->cmdSdc());
}
delete iter;
const char *filename = "/tmp/test_write_sdc_sdc_r10_latchborrowinst.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with slew limits ---
TEST_F(SdcDesignTest, WriteSdcWithSlewLimits) {
Sdc *sdc = sta_->cmdSdc();
Clock *clk = sdc->findClock("clk");
if (clk) {
sta_->setSlewLimit(clk, RiseFallBoth::riseFall(),
PathClkOrData::data, MinMax::max(), 2.0f, sta_->cmdSdc());
}
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *out = network->findPin(top, "out");
if (out) {
Port *port = network->port(out);
if (port) {
sta_->setSlewLimit(port, MinMax::max(), 3.0f, sta_->cmdSdc());
}
}
const char *filename = "/tmp/test_write_sdc_sdc_r10_slewlimit.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with cap limits ---
TEST_F(SdcDesignTest, WriteSdcWithCapLimits) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *out = network->findPin(top, "out");
if (out) {
Port *port = network->port(out);
if (port) {
sta_->setCapacitanceLimit(port, MinMax::max(), 0.5f, sta_->cmdSdc());
}
sta_->setCapacitanceLimit(out, MinMax::max(), 0.3f, sta_->cmdSdc());
}
const char *filename = "/tmp/test_write_sdc_sdc_r10_caplimit.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with fanout limits ---
TEST_F(SdcDesignTest, WriteSdcWithFanoutLimits) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *out = network->findPin(top, "out");
if (out) {
Port *port = network->port(out);
if (port) {
sta_->setFanoutLimit(port, MinMax::max(), 10.0f, sta_->cmdSdc());
}
}
const char *filename = "/tmp/test_write_sdc_sdc_r10_fanoutlimit.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with min pulse width on pin ---
TEST_F(SdcDesignTest, WriteSdcWithMpwOnPin) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *clk_pin = network->findPin(top, "r1/CK");
if (clk_pin) {
sta_->setMinPulseWidth(clk_pin, RiseFallBoth::rise(), 0.2f, sta_->cmdSdc());
}
const char *filename = "/tmp/test_write_sdc_sdc_r10_mpwpin.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with min pulse width on instance ---
TEST_F(SdcDesignTest, WriteSdcWithMpwOnInst) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
InstanceChildIterator *iter = network->childIterator(top);
if (iter->hasNext()) {
Instance *inst = iter->next();
sta_->setMinPulseWidth(inst, RiseFallBoth::rise(), 0.25f, sta_->cmdSdc());
}
delete iter;
const char *filename = "/tmp/test_write_sdc_sdc_r10_mpwinst.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with disable on instance ---
TEST_F(SdcDesignTest, WriteSdcWithDisableInstance) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
InstanceChildIterator *iter = network->childIterator(top);
if (iter->hasNext()) {
Instance *inst = iter->next();
LibertyCell *lib_cell = network->libertyCell(inst);
if (lib_cell) {
LibertyPort *in_port = nullptr;
LibertyPort *out_port = nullptr;
LibertyCellPortIterator port_iter(lib_cell);
while (port_iter.hasNext()) {
LibertyPort *port = port_iter.next();
if (port->direction()->isInput() && !in_port)
in_port = port;
else if (port->direction()->isOutput() && !out_port)
out_port = port;
}
if (in_port && out_port)
sta_->disable(inst, in_port, out_port, sta_->cmdSdc());
}
}
delete iter;
const char *filename = "/tmp/test_write_sdc_sdc_r10_disableinst.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with disable on liberty port ---
TEST_F(SdcDesignTest, WriteSdcWithDisableLibPort) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
InstanceChildIterator *iter = network->childIterator(top);
if (iter->hasNext()) {
Instance *inst = iter->next();
LibertyCell *lib_cell = network->libertyCell(inst);
if (lib_cell) {
LibertyCellPortIterator port_iter(lib_cell);
if (port_iter.hasNext()) {
LibertyPort *port = port_iter.next();
sta_->disable(port, sta_->cmdSdc());
}
}
}
delete iter;
const char *filename = "/tmp/test_write_sdc_sdc_r10_disablelibport.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with disable on cell ---
TEST_F(SdcDesignTest, WriteSdcWithDisableCell) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
InstanceChildIterator *iter = network->childIterator(top);
if (iter->hasNext()) {
Instance *inst = iter->next();
LibertyCell *lib_cell = network->libertyCell(inst);
if (lib_cell) {
sta_->disable(lib_cell, nullptr, nullptr, sta_->cmdSdc());
}
}
delete iter;
const char *filename = "/tmp/test_write_sdc_sdc_r10_disablecell.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with output delay ---
TEST_F(SdcDesignTest, WriteSdcWithOutputDelayDetailed) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *out = network->findPin(top, "out");
Sdc *sdc = sta_->cmdSdc();
Clock *clk = sdc->findClock("clk");
if (out && clk) {
sta_->setOutputDelay(out, RiseFallBoth::rise(),
clk, RiseFall::rise(), nullptr,
false, false, MinMaxAll::max(), true, 2.5f, sta_->cmdSdc());
sta_->setOutputDelay(out, RiseFallBoth::fall(),
clk, RiseFall::fall(), nullptr,
false, false, MinMaxAll::min(), true, 1.0f, sta_->cmdSdc());
}
const char *filename = "/tmp/test_write_sdc_sdc_r10_outdelay_detail.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- Sdc: outputDelays iterator ---
TEST_F(SdcDesignTest, SdcOutputDelays) {
ASSERT_NO_THROW(( [&](){
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *out = network->findPin(top, "out");
Sdc *sdc = sta_->cmdSdc();
Clock *clk = sdc->findClock("clk");
if (out && clk) {
sta_->setOutputDelay(out, RiseFallBoth::riseFall(),
clk, RiseFall::rise(), nullptr,
false, false, MinMaxAll::all(), true, 1.0f, sta_->cmdSdc());
}
const OutputDelaySet &out_delays = sdc->outputDelays();
for (OutputDelay *delay : out_delays) {
const Pin *pin = delay->pin();
EXPECT_NE(pin, nullptr);
const ClockEdge *ce = delay->clkEdge();
EXPECT_NE(ce, nullptr);
delay->sourceLatencyIncluded();
}
}() ));
}
// --- Sdc: Variables class accessors ---
TEST_F(SdcDesignTest, VariablesAccessors) {
// Test Variables accessors that modify search behavior
bool crpr_orig = sta_->crprEnabled();
sta_->setCrprEnabled(!crpr_orig);
EXPECT_NE(sta_->crprEnabled(), crpr_orig);
sta_->setCrprEnabled(crpr_orig);
bool prop_gate = sta_->propagateGatedClockEnable();
sta_->setPropagateGatedClockEnable(!prop_gate);
EXPECT_NE(sta_->propagateGatedClockEnable(), prop_gate);
sta_->setPropagateGatedClockEnable(prop_gate);
}
// --- Clock: name, period, waveform ---
TEST_F(SdcDesignTest, ClockAccessors) {
Sdc *sdc = sta_->cmdSdc();
Clock *clk = sdc->findClock("clk");
ASSERT_NE(clk, nullptr);
EXPECT_STREQ(clk->name(), "clk");
EXPECT_FLOAT_EQ(clk->period(), 10.0f);
const FloatSeq *wave = clk->waveform();
ASSERT_NE(wave, nullptr);
EXPECT_GE(wave->size(), 2u);
EXPECT_FLOAT_EQ((*wave)[0], 0.0f);
EXPECT_FLOAT_EQ((*wave)[1], 5.0f);
EXPECT_FALSE(clk->isGenerated());
EXPECT_FALSE(clk->isVirtual());
int idx = clk->index();
EXPECT_GE(idx, 0);
}
// --- ExceptionFrom: hasPins, hasClocks, hasInstances ---
TEST_F(SdcDesignTest, ExceptionFromHasPins) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *in1 = network->findPin(top, "in1");
if (in1) {
PinSet *pins = new PinSet(network);
pins->insert(in1);
ExceptionFrom *from = sta_->makeExceptionFrom(pins, nullptr, nullptr,
RiseFallBoth::riseFall(), sta_->cmdSdc());
ASSERT_NE(from, nullptr);
EXPECT_TRUE(from->hasPins());
EXPECT_FALSE(from->hasClocks());
EXPECT_FALSE(from->hasInstances());
EXPECT_TRUE(from->hasObjects());
delete from;
}
}
// --- ExceptionTo: hasPins, endRf ---
TEST_F(SdcDesignTest, ExceptionToHasPins) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *out = network->findPin(top, "out");
if (out) {
PinSet *pins = new PinSet(network);
pins->insert(out);
ExceptionTo *to = sta_->makeExceptionTo(pins, nullptr, nullptr,
RiseFallBoth::rise(),
RiseFallBoth::riseFall(), sta_->cmdSdc());
ASSERT_NE(to, nullptr);
EXPECT_TRUE(to->hasPins());
const RiseFallBoth *end_rf = to->endTransition();
EXPECT_NE(end_rf, nullptr);
delete to;
}
}
// --- Sdc: removeClockLatency ---
TEST_F(SdcDesignTest, SdcRemoveClockLatency) {
ASSERT_NO_THROW(( [&](){
Sdc *sdc = sta_->cmdSdc();
Clock *clk = sdc->findClock("clk");
if (clk) {
sta_->setClockLatency(clk, nullptr,
RiseFallBoth::riseFall(),
MinMaxAll::all(), 0.3f, sta_->cmdSdc());
sta_->removeClockLatency(clk, nullptr, sta_->cmdSdc());
}
}() ));
}
// --- Sdc: removeCaseAnalysis ---
TEST_F(SdcDesignTest, SdcRemoveCaseAnalysis) {
ASSERT_NO_THROW(( [&](){
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *in1 = network->findPin(top, "in1");
if (in1) {
sta_->setCaseAnalysis(in1, LogicValue::one, sta_->cmdMode());
sta_->removeCaseAnalysis(in1, sta_->cmdMode());
}
}() ));
}
// --- Sdc: removeDerating ---
TEST_F(SdcDesignTest, SdcRemoveDerating) {
ASSERT_NO_THROW(( [&](){
sta_->setTimingDerate(TimingDerateType::cell_delay,
PathClkOrData::data,
RiseFallBoth::riseFall(),
EarlyLate::early(), 0.95,
sta_->cmdSdc());
sta_->unsetTimingDerate(sta_->cmdSdc());
}() ));
}
// --- WriteSdc comprehensive: multiple constraints ---
TEST_F(SdcDesignTest, WriteSdcComprehensive) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Sdc *sdc = sta_->cmdSdc();
Clock *clk = sdc->findClock("clk");
// Add various constraints
Pin *in1 = network->findPin(top, "in1");
Pin *in2 = network->findPin(top, "in2");
Pin *out = network->findPin(top, "out");
if (in1) {
Port *port = network->port(in1);
if (port)
sta_->setDriveResistance(port, RiseFallBoth::riseFall(),
MinMaxAll::all(), 100.0f, sta_->cmdSdc());
}
if (in2) {
sta_->setCaseAnalysis(in2, LogicValue::zero, sta_->cmdMode());
}
if (out) {
Port *port = network->port(out);
if (port) {
sta_->setPortExtPinCap(port, RiseFallBoth::riseFall(),
MinMaxAll::all(), 0.1f, sta_->cmdSdc());
sta_->setFanoutLimit(port, MinMax::max(), 5.0f, sta_->cmdSdc());
}
}
if (clk) {
sta_->setClockLatency(clk, nullptr, RiseFallBoth::riseFall(),
MinMaxAll::all(), 0.5f, sta_->cmdSdc());
sta_->setClockInsertion(clk, nullptr, RiseFallBoth::riseFall(),
MinMaxAll::all(), EarlyLateAll::all(), 0.2f, sta_->cmdSdc());
}
sdc->setMaxArea(2000.0);
sdc->setMinPulseWidth(RiseFallBoth::rise(), 0.3);
sdc->setVoltage(MinMax::max(), 1.2);
sdc->setVoltage(MinMax::min(), 0.8);
sta_->setTimingDerate(TimingDerateType::cell_delay,
PathClkOrData::data,
RiseFallBoth::riseFall(),
EarlyLate::early(), 0.95,
sta_->cmdSdc());
// Write SDC with all constraints
const char *filename = "/tmp/test_write_sdc_sdc_r10_comprehensive.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
// Also write native format
const char *filename2 = "/tmp/test_write_sdc_sdc_r10_comprehensive_native.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename2, false, true, 4, false, true);
FILE *f2 = fopen(filename2, "r");
EXPECT_NE(f2, nullptr);
if (f2) fclose(f2);
}
// --- Clock: isPropagated, edges, edgeCount ---
TEST_F(SdcDesignTest, ClockEdgeDetails) {
Sdc *sdc = sta_->cmdSdc();
Clock *clk = sdc->findClock("clk");
ASSERT_NE(clk, nullptr);
clk->isPropagated();
// Each clock has 2 edges: rise and fall
ClockEdge *rise = clk->edge(RiseFall::rise());
ClockEdge *fall = clk->edge(RiseFall::fall());
ASSERT_NE(rise, nullptr);
ASSERT_NE(fall, nullptr);
// Opposite edges
const ClockEdge *rise_opp = rise->opposite();
EXPECT_EQ(rise_opp, fall);
const ClockEdge *fall_opp = fall->opposite();
EXPECT_EQ(fall_opp, rise);
}
// --- Sdc: clocks() - get all clocks ---
TEST_F(SdcDesignTest, SdcClocksList) {
Sdc *sdc = sta_->cmdSdc();
const ClockSeq &clks = sdc->clocks();
EXPECT_GT(clks.size(), 0u);
for (Clock *c : clks) {
EXPECT_NE(c->name(), nullptr);
}
}
// --- InputDrive: accessors ---
TEST_F(SdcDesignTest, InputDriveAccessors) {
ASSERT_NO_THROW(( [&](){
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *in1 = network->findPin(top, "in1");
if (in1) {
Port *port = network->port(in1);
if (port) {
// Set a drive resistance
sta_->setDriveResistance(port, RiseFallBoth::riseFall(),
MinMaxAll::all(), 75.0f, sta_->cmdSdc());
// Now check the input drive on the port via Sdc
Sdc *sdc = sta_->cmdSdc();
InputDrive *drive = sdc->findInputDrive(port);
if (drive) {
drive->hasDriveCell(RiseFall::rise(), MinMax::max());
// driveCell may be null if no drive cell is set
InputDriveCell *dc = drive->driveCell(RiseFall::rise(), MinMax::max());
if (dc) {
EXPECT_NE(dc->cell(), nullptr);
}
}
}
}
}() ));
}
// ============================================================
// R11_ SDC Tests - WriteSdc coverage and Sdc method coverage
// ============================================================
// --- WriteSdc with net wire cap (triggers writeNetLoads, writeNetLoad,
// writeGetNet, WriteGetNet, scaleCapacitance, writeFloat, writeCapacitance,
// writeCommentSeparator, closeFile, ~WriteSdc) ---
TEST_F(SdcDesignTest, WriteSdcWithNetWireCap) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
NetIterator *net_iter = network->netIterator(top);
if (net_iter->hasNext()) {
Net *net = net_iter->next();
sta_->setNetWireCap(net, false, MinMaxAll::all(), 0.05f, sta_->cmdSdc());
}
delete net_iter;
const char *filename = "/tmp/test_sdc_r11_netwire.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with net resistance (triggers writeNetResistances,
// writeNetResistance, writeGetNet, scaleResistance, writeResistance) ---
TEST_F(SdcDesignTest, WriteSdcWithNetResistance) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
NetIterator *net_iter = network->netIterator(top);
if (net_iter->hasNext()) {
Net *net = net_iter->next();
sta_->setResistance(net, MinMaxAll::all(), 100.0f, sta_->cmdSdc());
}
delete net_iter;
const char *filename = "/tmp/test_sdc_r11_netres.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with input slew (triggers writeInputTransitions,
// writeRiseFallMinMaxTimeCmd, WriteGetPort, scaleTime) ---
TEST_F(SdcDesignTest, WriteSdcWithInputSlew) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *in1 = network->findPin(top, "in1");
if (in1) {
Port *port = network->port(in1);
if (port) {
sta_->setInputSlew(port, RiseFallBoth::riseFall(),
MinMaxAll::all(), 0.1f, sta_->cmdSdc());
}
}
const char *filename = "/tmp/test_sdc_r11_inputslew.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with driving cell (triggers writeDrivingCells, writeDrivingCell,
// WriteGetLibCell, WriteGetPort) ---
TEST_F(SdcDesignTest, WriteSdcWithDrivingCell) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *in1 = network->findPin(top, "in1");
if (in1) {
Port *port = network->port(in1);
if (port) {
// Find a buffer cell to use as driving cell
LibertyLibrary *lib = nullptr;
LibertyLibraryIterator *lib_iter = network->libertyLibraryIterator();
if (lib_iter->hasNext())
lib = lib_iter->next();
delete lib_iter;
if (lib) {
LibertyCell *buf_cell = nullptr;
LibertyCellIterator cell_iter(lib);
while (cell_iter.hasNext()) {
LibertyCell *cell = cell_iter.next();
if (cell->portCount() >= 2) {
buf_cell = cell;
break;
}
}
if (buf_cell) {
// Find input and output ports on the cell
LibertyPort *from_port = nullptr;
LibertyPort *to_port = nullptr;
LibertyCellPortIterator port_iter(buf_cell);
while (port_iter.hasNext()) {
LibertyPort *lp = port_iter.next();
if (lp->direction()->isInput() && !from_port)
from_port = lp;
else if (lp->direction()->isOutput() && !to_port)
to_port = lp;
}
if (from_port && to_port) {
float from_slews[2] = {0.05f, 0.05f};
sta_->setDriveCell(lib, buf_cell, port,
from_port, from_slews, to_port,
RiseFallBoth::riseFall(), MinMaxAll::all(), sta_->cmdSdc());
}
}
}
}
}
const char *filename = "/tmp/test_sdc_r11_drivecell.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with clock groups that have actual clock members
// (triggers writeClockGroups, WriteGetClock, writeGetClock) ---
TEST_F(SdcDesignTest, WriteSdcWithClockGroupsMembers) {
Sdc *sdc = sta_->cmdSdc();
Clock *clk = sdc->findClock("clk");
if (clk) {
// Create a second clock
Network *network = sta_->network();
Instance *top = network->topInstance();
Pin *clk2_pin = network->findPin(top, "clk2");
if (clk2_pin) {
PinSet *clk2_pins = new PinSet(network);
clk2_pins->insert(clk2_pin);
FloatSeq *waveform2 = new FloatSeq;
waveform2->push_back(0.0f);
waveform2->push_back(2.5f);
sta_->makeClock("clk2", clk2_pins, false, 5.0f, waveform2, nullptr, sta_->cmdMode());
Clock *clk2 = sdc->findClock("clk2");
if (clk2) {
ClockGroups *cg = sta_->makeClockGroups("grp1", true, false, false,
false, nullptr, sta_->cmdSdc());
ClockSet *group1 = new ClockSet;
group1->insert(clk);
sta_->makeClockGroup(cg, group1, sta_->cmdSdc());
ClockSet *group2 = new ClockSet;
group2->insert(clk2);
sta_->makeClockGroup(cg, group2, sta_->cmdSdc());
}
}
}
const char *filename = "/tmp/test_sdc_r11_clkgrp_members.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with false path having -from pins and -through pins and -to pins
// (triggers writeExceptionFrom, WriteGetPin, writeExceptionThru,
// writeExceptionTo) ---
TEST_F(SdcDesignTest, WriteSdcFalsePathFromThruTo) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *in1 = network->findPin(top, "in1");
Pin *out = network->findPin(top, "out");
if (in1 && out) {
// -from
PinSet *from_pins = new PinSet(network);
from_pins->insert(in1);
ExceptionFrom *from = sta_->makeExceptionFrom(from_pins, nullptr, nullptr,
RiseFallBoth::riseFall(), sta_->cmdSdc());
// -through: use an instance
InstanceChildIterator *inst_iter = network->childIterator(top);
ExceptionThruSeq *thrus = new ExceptionThruSeq;
if (inst_iter->hasNext()) {
Instance *inst = inst_iter->next();
InstanceSet *insts = new InstanceSet(network);
insts->insert(inst);
ExceptionThru *thru = sta_->makeExceptionThru(nullptr, nullptr, insts,
RiseFallBoth::riseFall(), sta_->cmdSdc());
thrus->push_back(thru);
}
delete inst_iter;
// -to
PinSet *to_pins = new PinSet(network);
to_pins->insert(out);
ExceptionTo *to = sta_->makeExceptionTo(to_pins, nullptr, nullptr,
RiseFallBoth::riseFall(),
RiseFallBoth::riseFall(), sta_->cmdSdc());
sta_->makeFalsePath(from, thrus, to, MinMaxAll::all(), nullptr, sta_->cmdSdc());
}
const char *filename = "/tmp/test_sdc_r11_fp_fromthru.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with false path -through net
// (triggers writeExceptionThru with nets, writeGetNet) ---
TEST_F(SdcDesignTest, WriteSdcFalsePathThruNet) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
NetIterator *net_iter = network->netIterator(top);
if (net_iter->hasNext()) {
Net *net = net_iter->next();
NetSet *nets = new NetSet(network);
nets->insert(net);
ExceptionThruSeq *thrus = new ExceptionThruSeq;
ExceptionThru *thru = sta_->makeExceptionThru(nullptr, nets, nullptr,
RiseFallBoth::riseFall(), sta_->cmdSdc());
thrus->push_back(thru);
sta_->makeFalsePath(nullptr, thrus, nullptr, MinMaxAll::all(), nullptr, sta_->cmdSdc());
}
delete net_iter;
const char *filename = "/tmp/test_sdc_r11_fp_thrunet.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with false path -from clock (triggers writeGetClock in from) ---
TEST_F(SdcDesignTest, WriteSdcFalsePathFromClock) {
Sdc *sdc = sta_->cmdSdc();
Clock *clk = sdc->findClock("clk");
if (clk) {
ClockSet *from_clks = new ClockSet;
from_clks->insert(clk);
ExceptionFrom *from = sta_->makeExceptionFrom(nullptr, from_clks, nullptr,
RiseFallBoth::riseFall(), sta_->cmdSdc());
sta_->makeFalsePath(from, nullptr, nullptr, MinMaxAll::all(), nullptr, sta_->cmdSdc());
}
const char *filename = "/tmp/test_sdc_r11_fp_fromclk.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with false path -from instance (triggers writeGetInstance,
// WriteGetInstance) ---
TEST_F(SdcDesignTest, WriteSdcFalsePathFromInstance) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
InstanceChildIterator *iter = network->childIterator(top);
if (iter->hasNext()) {
Instance *inst = iter->next();
InstanceSet *from_insts = new InstanceSet(network);
from_insts->insert(inst);
ExceptionFrom *from = sta_->makeExceptionFrom(nullptr, nullptr, from_insts,
RiseFallBoth::riseFall(), sta_->cmdSdc());
sta_->makeFalsePath(from, nullptr, nullptr, MinMaxAll::all(), nullptr, sta_->cmdSdc());
}
delete iter;
const char *filename = "/tmp/test_sdc_r11_fp_frominst.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with multicycle path with -from pin
// (triggers writeExceptionCmd for multicycle, writeExceptionFrom) ---
TEST_F(SdcDesignTest, WriteSdcMulticycleWithFrom) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *in1 = network->findPin(top, "in1");
if (in1) {
PinSet *from_pins = new PinSet(network);
from_pins->insert(in1);
ExceptionFrom *from = sta_->makeExceptionFrom(from_pins, nullptr, nullptr,
RiseFallBoth::riseFall(), sta_->cmdSdc());
sta_->makeMulticyclePath(from, nullptr, nullptr,
MinMaxAll::max(), true, 3, nullptr, sta_->cmdSdc());
}
const char *filename = "/tmp/test_sdc_r11_mcp_from.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with path delay (max_delay/min_delay)
// (triggers writeExceptionCmd for path delay, writeExceptionValue) ---
TEST_F(SdcDesignTest, WriteSdcPathDelay) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *in1 = network->findPin(top, "in1");
Pin *out = network->findPin(top, "out");
if (in1 && out) {
PinSet *from_pins = new PinSet(network);
from_pins->insert(in1);
ExceptionFrom *from = sta_->makeExceptionFrom(from_pins, nullptr, nullptr,
RiseFallBoth::riseFall(), sta_->cmdSdc());
PinSet *to_pins = new PinSet(network);
to_pins->insert(out);
ExceptionTo *to = sta_->makeExceptionTo(to_pins, nullptr, nullptr,
RiseFallBoth::riseFall(),
RiseFallBoth::riseFall(), sta_->cmdSdc());
sta_->makePathDelay(from, nullptr, to, MinMax::max(), false, false,
5.0f, nullptr, sta_->cmdSdc());
}
const char *filename = "/tmp/test_sdc_r11_pathdelay.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with group path
// (triggers writeExceptionCmd for group path) ---
TEST_F(SdcDesignTest, WriteSdcGroupPath) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *in1 = network->findPin(top, "in1");
if (in1) {
PinSet *from_pins = new PinSet(network);
from_pins->insert(in1);
ExceptionFrom *from = sta_->makeExceptionFrom(from_pins, nullptr, nullptr,
RiseFallBoth::riseFall(), sta_->cmdSdc());
sta_->makeGroupPath("mygroup", false, from, nullptr, nullptr, nullptr, sta_->cmdSdc());
}
const char *filename = "/tmp/test_sdc_r11_grouppath.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with clock sense
// (triggers writeClockSenses, PinClockPairNameLess) ---
TEST_F(SdcDesignTest, WriteSdcWithClockSense) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *clk1 = network->findPin(top, "clk1");
Sdc *sdc = sta_->cmdSdc();
Clock *clk = sdc->findClock("clk");
if (clk1 && clk) {
PinSet *pins = new PinSet(network);
pins->insert(clk1);
ClockSet *clks = new ClockSet;
clks->insert(clk);
sta_->setClockSense(pins, clks, ClockSense::positive, sta_->cmdSdc());
}
const char *filename = "/tmp/test_sdc_r11_clksense.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with port ext wire cap and fanout
// (triggers writePortLoads with wire cap, writeMinMaxIntValuesCmd) ---
TEST_F(SdcDesignTest, WriteSdcWithPortExtWireCap) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *out = network->findPin(top, "out");
if (out) {
Port *port = network->port(out);
if (port) {
sta_->setPortExtWireCap(port, RiseFallBoth::riseFall(),
MinMaxAll::all(), 0.02f, sta_->cmdSdc());
sta_->setPortExtFanout(port, 3, MinMaxAll::all(), sta_->cmdSdc());
}
}
const char *filename = "/tmp/test_sdc_r11_portwire.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with clock gating check
// (triggers writeClockGatingChecks) ---
TEST_F(SdcDesignTest, WriteSdcWithClockGatingCheck) {
sta_->setClockGatingCheck(RiseFallBoth::riseFall(),
MinMax::max(), 0.1f, sta_->cmdSdc());
Sdc *sdc = sta_->cmdSdc();
Clock *clk = sdc->findClock("clk");
if (clk)
sta_->setClockGatingCheck(clk, RiseFallBoth::riseFall(),
MinMax::min(), 0.05f, sta_->cmdSdc());
const char *filename = "/tmp/test_sdc_r11_clkgate.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- Sdc: connectedCap via Sta API ---
TEST_F(SdcDesignTest, SdcConnectedCap) {
ASSERT_NO_THROW(( [&](){
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *out = network->findPin(top, "out");
if (out) {
Scene *corner = sta_->cmdScene();
float pin_cap, wire_cap;
sta_->connectedCap(out, RiseFall::rise(), corner, MinMax::max(),
pin_cap, wire_cap);
}
}() ));
}
// --- Sdc: connectedCap on net via Sta API ---
TEST_F(SdcDesignTest, SdcConnectedCapNet) {
ASSERT_NO_THROW(( [&](){
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
NetIterator *net_iter = network->netIterator(top);
if (net_iter->hasNext()) {
Net *net = net_iter->next();
Scene *corner = sta_->cmdScene();
float pin_cap, wire_cap;
sta_->connectedCap(net, corner, MinMax::max(), pin_cap, wire_cap);
}
delete net_iter;
}() ));
}
// --- ExceptionPath::mergeable ---
TEST_F(SdcDesignTest, ExceptionPathMergeable) {
// Create two false paths and check mergeability
sta_->makeFalsePath(nullptr, nullptr, nullptr, MinMaxAll::all(), nullptr, sta_->cmdSdc());
Sdc *sdc = sta_->cmdSdc();
const ExceptionPathSet &exceptions = sdc->exceptions();
ExceptionPath *first = nullptr;
for (ExceptionPath *ep : exceptions) {
if (ep->isFalse()) {
if (!first) {
first = ep;
} else {
first->mergeable(ep);
break;
}
}
}
}
// --- WriteSdc with propagated clock on pin
// (triggers writePropagatedClkPins) ---
TEST_F(SdcDesignTest, WriteSdcWithPropagatedClk) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *clk1 = network->findPin(top, "clk1");
if (clk1) {
sta_->setPropagatedClock(clk1, sta_->cmdMode());
}
const char *filename = "/tmp/test_sdc_r11_propagated.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with min_delay path delay
// (triggers min_delay branch in writeExceptionCmd) ---
TEST_F(SdcDesignTest, WriteSdcMinDelay) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *in1 = network->findPin(top, "in1");
Pin *out = network->findPin(top, "out");
if (in1 && out) {
PinSet *from_pins = new PinSet(network);
from_pins->insert(in1);
ExceptionFrom *from = sta_->makeExceptionFrom(from_pins, nullptr, nullptr,
RiseFallBoth::riseFall(), sta_->cmdSdc());
PinSet *to_pins = new PinSet(network);
to_pins->insert(out);
ExceptionTo *to = sta_->makeExceptionTo(to_pins, nullptr, nullptr,
RiseFallBoth::riseFall(),
RiseFallBoth::riseFall(), sta_->cmdSdc());
sta_->makePathDelay(from, nullptr, to, MinMax::min(), false, false,
1.0f, nullptr, sta_->cmdSdc());
}
const char *filename = "/tmp/test_sdc_r11_mindelay.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with multicycle -hold (min) with -end
// (triggers the hold branch in writeExceptionCmd) ---
TEST_F(SdcDesignTest, WriteSdcMulticycleHold) {
sta_->makeMulticyclePath(nullptr, nullptr, nullptr,
MinMaxAll::min(), true, 0, nullptr, sta_->cmdSdc());
const char *filename = "/tmp/test_sdc_r11_mcp_hold.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with multicycle -setup with -start
// (triggers the start branch in writeExceptionCmd) ---
TEST_F(SdcDesignTest, WriteSdcMulticycleStart) {
sta_->makeMulticyclePath(nullptr, nullptr, nullptr,
MinMaxAll::max(), false, 2, nullptr, sta_->cmdSdc());
const char *filename = "/tmp/test_sdc_r11_mcp_start.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with group path default
// (triggers isDefault branch in writeExceptionCmd) ---
TEST_F(SdcDesignTest, WriteSdcGroupPathDefault) {
sta_->makeGroupPath("", true, nullptr, nullptr, nullptr, nullptr, sta_->cmdSdc());
const char *filename = "/tmp/test_sdc_r11_grppath_default.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with false path -from with rise_from
// (triggers rf_prefix = "-rise_" branch) ---
TEST_F(SdcDesignTest, WriteSdcFalsePathRiseFrom) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *in1 = network->findPin(top, "in1");
if (in1) {
PinSet *from_pins = new PinSet(network);
from_pins->insert(in1);
ExceptionFrom *from = sta_->makeExceptionFrom(from_pins, nullptr, nullptr,
RiseFallBoth::rise(), sta_->cmdSdc());
sta_->makeFalsePath(from, nullptr, nullptr, MinMaxAll::all(), nullptr, sta_->cmdSdc());
}
const char *filename = "/tmp/test_sdc_r11_fp_risefrom.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with false path -from with fall_from
// (triggers rf_prefix = "-fall_" branch) ---
TEST_F(SdcDesignTest, WriteSdcFalsePathFallFrom) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *in1 = network->findPin(top, "in1");
if (in1) {
PinSet *from_pins = new PinSet(network);
from_pins->insert(in1);
ExceptionFrom *from = sta_->makeExceptionFrom(from_pins, nullptr, nullptr,
RiseFallBoth::fall(), sta_->cmdSdc());
sta_->makeFalsePath(from, nullptr, nullptr, MinMaxAll::all(), nullptr, sta_->cmdSdc());
}
const char *filename = "/tmp/test_sdc_r11_fp_fallfrom.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with path delay -ignore_clock_latency
// (triggers the ignoreClkLatency branch) ---
TEST_F(SdcDesignTest, WriteSdcPathDelayIgnoreClkLat) {
sta_->makePathDelay(nullptr, nullptr, nullptr, MinMax::max(), true, false,
8.0f, nullptr, sta_->cmdSdc());
const char *filename = "/tmp/test_sdc_r11_pathdelay_ignoreclk.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with false path -to with end_rf rise
// (triggers the end_rf != riseFall branch in writeExceptionTo) ---
TEST_F(SdcDesignTest, WriteSdcFalsePathToRise) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *out = network->findPin(top, "out");
if (out) {
PinSet *to_pins = new PinSet(network);
to_pins->insert(out);
ExceptionTo *to = sta_->makeExceptionTo(to_pins, nullptr, nullptr,
RiseFallBoth::riseFall(),
RiseFallBoth::rise(), sta_->cmdSdc());
sta_->makeFalsePath(nullptr, nullptr, to, MinMaxAll::all(), nullptr, sta_->cmdSdc());
}
const char *filename = "/tmp/test_sdc_r11_fp_torise.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with multiple from objects (triggers multi_objs branch with [list ]) ---
TEST_F(SdcDesignTest, WriteSdcFalsePathMultiFrom) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *in1 = network->findPin(top, "in1");
Pin *in2 = network->findPin(top, "in2");
if (in1 && in2) {
PinSet *from_pins = new PinSet(network);
from_pins->insert(in1);
from_pins->insert(in2);
ExceptionFrom *from = sta_->makeExceptionFrom(from_pins, nullptr, nullptr,
RiseFallBoth::riseFall(), sta_->cmdSdc());
sta_->makeFalsePath(from, nullptr, nullptr, MinMaxAll::all(), nullptr, sta_->cmdSdc());
}
const char *filename = "/tmp/test_sdc_r11_fp_multifrom.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with data check that has a clock ref
// (triggers writeDataChecks, WriteGetPinAndClkKey) ---
TEST_F(SdcDesignTest, WriteSdcDataCheckWithClock) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *from_pin = network->findPin(top, "r1/D");
Pin *to_pin = network->findPin(top, "r1/CK");
Sdc *sdc = sta_->cmdSdc();
Clock *clk = sdc->findClock("clk");
if (from_pin && to_pin && clk) {
sta_->setDataCheck(from_pin, RiseFallBoth::riseFall(),
to_pin, RiseFallBoth::riseFall(),
clk, MinMaxAll::max(), 0.5f, sta_->cmdSdc());
}
const char *filename = "/tmp/test_sdc_r11_datacheck_clk.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- Sdc::removeDataCheck ---
TEST_F(SdcDesignTest, SdcRemoveDataCheck2) {
ASSERT_NO_THROW(( [&](){
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *from_pin = network->findPin(top, "r1/D");
Pin *to_pin = network->findPin(top, "r1/CK");
if (from_pin && to_pin) {
sta_->setDataCheck(from_pin, RiseFallBoth::riseFall(),
to_pin, RiseFallBoth::riseFall(),
nullptr, MinMaxAll::max(), 1.0f, sta_->cmdSdc());
sta_->removeDataCheck(from_pin, RiseFallBoth::riseFall(),
to_pin, RiseFallBoth::riseFall(),
nullptr, MinMaxAll::max(), sta_->cmdSdc());
}
}() ));
}
// --- WriteSdc with clock uncertainty on pin
// (triggers writeClockUncertaintyPins, writeClockUncertaintyPin) ---
TEST_F(SdcDesignTest, WriteSdcClockUncertaintyPin) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *clk1 = network->findPin(top, "clk1");
if (clk1) {
sta_->setClockUncertainty(clk1, MinMaxAll::max(), 0.2f, sta_->cmdSdc());
}
const char *filename = "/tmp/test_sdc_r11_clkuncpin.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with voltage on net
// (triggers writeVoltages with net voltage) ---
TEST_F(SdcDesignTest, WriteSdcVoltageNet) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
NetIterator *net_iter = network->netIterator(top);
if (net_iter->hasNext()) {
Net *net = net_iter->next();
sta_->setVoltage(net, MinMax::max(), 1.0f, sta_->cmdSdc());
sta_->setVoltage(net, MinMax::min(), 0.9f, sta_->cmdSdc());
}
delete net_iter;
const char *filename = "/tmp/test_sdc_r11_voltnet.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with disable on timing arcs of cell
// (triggers writeGetTimingArcsOfOjbects, writeGetTimingArcs,
// getTimingArcsCmd) ---
TEST_F(SdcDesignTest, WriteSdcDisableTimingArcs) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
InstanceChildIterator *iter = network->childIterator(top);
if (iter->hasNext()) {
Instance *inst = iter->next();
LibertyCell *lib_cell = network->libertyCell(inst);
if (lib_cell) {
LibertyPort *in_port = nullptr;
LibertyPort *out_port = nullptr;
LibertyCellPortIterator port_iter(lib_cell);
while (port_iter.hasNext()) {
LibertyPort *port = port_iter.next();
if (port->direction()->isInput() && !in_port)
in_port = port;
else if (port->direction()->isOutput() && !out_port)
out_port = port;
}
if (in_port && out_port) {
// Disable specific from->to arc on cell
sta_->disable(lib_cell, in_port, out_port, sta_->cmdSdc());
}
}
}
delete iter;
const char *filename = "/tmp/test_sdc_r11_disablearcs.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with min pulse width on clock
// (triggers writeMinPulseWidths clock branch) ---
TEST_F(SdcDesignTest, WriteSdcMpwClock) {
Sdc *sdc = sta_->cmdSdc();
Clock *clk = sdc->findClock("clk");
if (clk) {
sta_->setMinPulseWidth(clk, RiseFallBoth::rise(), 0.4f, sta_->cmdSdc());
sta_->setMinPulseWidth(clk, RiseFallBoth::fall(), 0.3f, sta_->cmdSdc());
}
const char *filename = "/tmp/test_sdc_r11_mpwclk.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with slew limit on clock data
// (triggers writeClkSlewLimits, writeClkSlewLimit) ---
TEST_F(SdcDesignTest, WriteSdcSlewLimitClkData) {
Sdc *sdc = sta_->cmdSdc();
Clock *clk = sdc->findClock("clk");
if (clk) {
sta_->setSlewLimit(clk, RiseFallBoth::riseFall(),
PathClkOrData::clk, MinMax::max(), 1.5f, sta_->cmdSdc());
sta_->setSlewLimit(clk, RiseFallBoth::riseFall(),
PathClkOrData::data, MinMax::max(), 2.5f, sta_->cmdSdc());
}
const char *filename = "/tmp/test_sdc_r11_slewclkdata.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with cell-level cap limit
// (triggers writeCapLimits cell branch) ---
TEST_F(SdcDesignTest, WriteSdcCapLimitCell) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
InstanceChildIterator *iter = network->childIterator(top);
if (iter->hasNext()) {
Instance *inst = iter->next();
Cell *cell = network->cell(inst);
if (cell) {
sta_->setCapacitanceLimit(cell, MinMax::max(), 2.0f, sta_->cmdSdc());
}
}
delete iter;
const char *filename = "/tmp/test_sdc_r11_caplimitcell.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with cell-level fanout limit
// (triggers writeFanoutLimits cell branch) ---
TEST_F(SdcDesignTest, WriteSdcFanoutLimitCell) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
InstanceChildIterator *iter = network->childIterator(top);
if (iter->hasNext()) {
Instance *inst = iter->next();
Cell *cell = network->cell(inst);
if (cell) {
sta_->setFanoutLimit(cell, MinMax::max(), 15.0f, sta_->cmdSdc());
}
}
delete iter;
const char *filename = "/tmp/test_sdc_r11_fanoutcell.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc with cell-level slew limit
// (triggers writeSlewLimits cell branch) ---
TEST_F(SdcDesignTest, WriteSdcSlewLimitCell) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
InstanceChildIterator *iter = network->childIterator(top);
if (iter->hasNext()) {
Instance *inst = iter->next();
Cell *cell = network->cell(inst);
if (cell) {
sta_->setSlewLimit(cell, MinMax::max(), 5.0f, sta_->cmdSdc());
}
}
delete iter;
const char *filename = "/tmp/test_sdc_r11_slewcell.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
}
// --- WriteSdc comprehensive: trigger as many writer paths as possible ---
TEST_F(SdcDesignTest, WriteSdcMegaComprehensive) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Sdc *sdc = sta_->cmdSdc();
Clock *clk = sdc->findClock("clk");
Pin *in1 = network->findPin(top, "in1");
Pin *in2 = network->findPin(top, "in2");
Pin *out = network->findPin(top, "out");
// Net wire cap and resistance
NetIterator *net_iter = network->netIterator(top);
if (net_iter->hasNext()) {
Net *net = net_iter->next();
sta_->setNetWireCap(net, false, MinMaxAll::all(), 0.03f, sta_->cmdSdc());
sta_->setResistance(net, MinMaxAll::all(), 50.0f, sta_->cmdSdc());
sta_->setVoltage(net, MinMax::max(), 1.1f, sta_->cmdSdc());
}
delete net_iter;
// Input slew
if (in1) {
Port *port = network->port(in1);
if (port)
sta_->setInputSlew(port, RiseFallBoth::riseFall(), MinMaxAll::all(), 0.08f, sta_->cmdSdc());
}
// Port ext wire cap + fanout
if (out) {
Port *port = network->port(out);
if (port) {
sta_->setPortExtPinCap(port, RiseFallBoth::riseFall(),
MinMaxAll::all(), 0.1f, sta_->cmdSdc());
sta_->setPortExtWireCap(port, RiseFallBoth::riseFall(),
MinMaxAll::all(), 0.015f, sta_->cmdSdc());
sta_->setPortExtFanout(port, 2, MinMaxAll::all(), sta_->cmdSdc());
}
}
// Clock groups
if (clk) {
ClockGroups *cg = sta_->makeClockGroups("mega_grp", false, true, false,
false, nullptr, sta_->cmdSdc());
ClockSet *g1 = new ClockSet;
g1->insert(clk);
sta_->makeClockGroup(cg, g1, sta_->cmdSdc());
}
// False path with -from pin, -through instance, -to pin
if (in1 && out) {
PinSet *from_pins = new PinSet(network);
from_pins->insert(in1);
ExceptionFrom *from = sta_->makeExceptionFrom(from_pins, nullptr, nullptr,
RiseFallBoth::rise(), sta_->cmdSdc());
InstanceChildIterator *inst_iter = network->childIterator(top);
ExceptionThruSeq *thrus = new ExceptionThruSeq;
if (inst_iter->hasNext()) {
Instance *inst = inst_iter->next();
InstanceSet *insts = new InstanceSet(network);
insts->insert(inst);
ExceptionThru *thru = sta_->makeExceptionThru(nullptr, nullptr, insts,
RiseFallBoth::riseFall(), sta_->cmdSdc());
thrus->push_back(thru);
}
delete inst_iter;
PinSet *to_pins = new PinSet(network);
to_pins->insert(out);
ExceptionTo *to = sta_->makeExceptionTo(to_pins, nullptr, nullptr,
RiseFallBoth::riseFall(),
RiseFallBoth::rise(), sta_->cmdSdc());
sta_->makeFalsePath(from, thrus, to, MinMaxAll::all(), nullptr, sta_->cmdSdc());
}
// Max/min delay
if (in2 && out) {
PinSet *from_pins = new PinSet(network);
from_pins->insert(in2);
ExceptionFrom *from = sta_->makeExceptionFrom(from_pins, nullptr, nullptr,
RiseFallBoth::riseFall(), sta_->cmdSdc());
PinSet *to_pins = new PinSet(network);
to_pins->insert(out);
ExceptionTo *to = sta_->makeExceptionTo(to_pins, nullptr, nullptr,
RiseFallBoth::riseFall(),
RiseFallBoth::riseFall(), sta_->cmdSdc());
sta_->makePathDelay(from, nullptr, to, MinMax::max(), true, false,
6.0f, nullptr, sta_->cmdSdc());
}
// Multicycle
sta_->makeMulticyclePath(nullptr, nullptr, nullptr,
MinMaxAll::max(), false, 4, nullptr, sta_->cmdSdc());
// Group path
sta_->makeGroupPath("mega", false, nullptr, nullptr, nullptr, nullptr, sta_->cmdSdc());
// Clock gating check
sta_->setClockGatingCheck(RiseFallBoth::riseFall(), MinMax::max(), 0.15f, sta_->cmdSdc());
// Logic value
if (in2) {
sta_->setLogicValue(in2, LogicValue::zero, sta_->cmdMode());
}
// Voltage
sdc->setVoltage(MinMax::max(), 1.2);
sdc->setVoltage(MinMax::min(), 0.8);
// Min pulse width
sdc->setMinPulseWidth(RiseFallBoth::rise(), 0.35);
sdc->setMinPulseWidth(RiseFallBoth::fall(), 0.25);
// Max area
sdc->setMaxArea(3000.0);
// Write SDC
const char *filename = "/tmp/test_sdc_r11_mega.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
FILE *f = fopen(filename, "r");
EXPECT_NE(f, nullptr);
if (f) fclose(f);
// Also write in native mode
const char *filename2 = "/tmp/test_sdc_r11_mega_native.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename2, false, true, 4, false, true);
FILE *f2 = fopen(filename2, "r");
EXPECT_NE(f2, nullptr);
if (f2) fclose(f2);
// Also write in leaf mode
const char *filename3 = "/tmp/test_sdc_r11_mega_leaf.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename3, true, false, 4, false, true);
FILE *f3 = fopen(filename3, "r");
EXPECT_NE(f3, nullptr);
if (f3) fclose(f3);
}
// --- Sdc: remove clock groups ---
TEST_F(SdcDesignTest, SdcRemoveClockGroups) {
ASSERT_NO_THROW(( [&](){
Sdc *sdc = sta_->cmdSdc();
Clock *clk = sdc->findClock("clk");
if (clk) {
ClockGroups *cg = sta_->makeClockGroups("rm_grp", true, false, false,
false, nullptr, sta_->cmdSdc());
ClockSet *g1 = new ClockSet;
g1->insert(clk);
sta_->makeClockGroup(cg, g1, sta_->cmdSdc());
// Remove by name
sta_->removeClockGroupsLogicallyExclusive("rm_grp", sta_->cmdSdc());
}
}() ));
}
// --- Sdc: remove physically exclusive clock groups ---
TEST_F(SdcDesignTest, SdcRemovePhysExclClkGroups) {
ASSERT_NO_THROW(( [&](){
Sdc *sdc = sta_->cmdSdc();
Clock *clk = sdc->findClock("clk");
if (clk) {
ClockGroups *cg = sta_->makeClockGroups("phys_grp", false, true, false,
false, nullptr, sta_->cmdSdc());
ClockSet *g1 = new ClockSet;
g1->insert(clk);
sta_->makeClockGroup(cg, g1, sta_->cmdSdc());
sta_->removeClockGroupsPhysicallyExclusive("phys_grp", sta_->cmdSdc());
}
}() ));
}
// --- Sdc: remove async clock groups ---
TEST_F(SdcDesignTest, SdcRemoveAsyncClkGroups) {
ASSERT_NO_THROW(( [&](){
Sdc *sdc = sta_->cmdSdc();
Clock *clk = sdc->findClock("clk");
if (clk) {
ClockGroups *cg = sta_->makeClockGroups("async_grp", false, false, true,
false, nullptr, sta_->cmdSdc());
ClockSet *g1 = new ClockSet;
g1->insert(clk);
sta_->makeClockGroup(cg, g1, sta_->cmdSdc());
sta_->removeClockGroupsAsynchronous("async_grp", sta_->cmdSdc());
}
}() ));
}
// --- Sdc: clear via removeConstraints (covers initVariables, clearCycleAcctings) ---
TEST_F(SdcDesignTest, SdcRemoveConstraintsCover) {
ASSERT_NO_THROW(( [&](){
Sdc *sdc = sta_->cmdSdc();
// Set various constraints first
sdc->setMaxArea(500.0);
sdc->setMinPulseWidth(RiseFallBoth::rise(), 0.3);
sdc->setVoltage(MinMax::max(), 1.1);
// removeConstraints calls initVariables and clearCycleAcctings internally
sdc->clear();
}() ));
}
// --- ExceptionFrom: hash via exception creation and matching ---
TEST_F(SdcDesignTest, ExceptionFromMatching) {
ASSERT_NO_THROW(( [&](){
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *in1 = network->findPin(top, "in1");
Pin *in2 = network->findPin(top, "in2");
if (in1 && in2) {
PinSet *pins1 = new PinSet(network);
pins1->insert(in1);
ExceptionFrom *from1 = sta_->makeExceptionFrom(pins1, nullptr, nullptr,
RiseFallBoth::riseFall(), sta_->cmdSdc());
PinSet *pins2 = new PinSet(network);
pins2->insert(in2);
ExceptionFrom *from2 = sta_->makeExceptionFrom(pins2, nullptr, nullptr,
RiseFallBoth::riseFall(), sta_->cmdSdc());
// Make false paths - internally triggers findHash
sta_->makeFalsePath(from1, nullptr, nullptr, MinMaxAll::all(), nullptr, sta_->cmdSdc());
sta_->makeFalsePath(from2, nullptr, nullptr, MinMaxAll::all(), nullptr, sta_->cmdSdc());
}
}() ));
}
// --- DisabledCellPorts accessors ---
TEST_F(SdcDesignTest, DisabledCellPortsAccessors) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
InstanceChildIterator *iter = network->childIterator(top);
if (iter->hasNext()) {
Instance *inst = iter->next();
LibertyCell *lib_cell = network->libertyCell(inst);
if (lib_cell) {
DisabledCellPorts *dcp = new DisabledCellPorts(lib_cell);
EXPECT_EQ(dcp->cell(), lib_cell);
EXPECT_FALSE(dcp->all());
delete dcp;
}
}
delete iter;
}
// --- DisabledInstancePorts with disable ---
TEST_F(SdcDesignTest, DisabledInstancePortsDisable) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
InstanceChildIterator *iter = network->childIterator(top);
ASSERT_TRUE(iter->hasNext());
Instance *inst = iter->next();
ASSERT_NE(inst, nullptr);
LibertyCell *lib_cell = network->libertyCell(inst);
ASSERT_NE(lib_cell, nullptr);
LibertyPort *in_port = nullptr;
LibertyPort *out_port = nullptr;
LibertyCellPortIterator port_iter(lib_cell);
while (port_iter.hasNext()) {
LibertyPort *port = port_iter.next();
if (port->direction()->isInput() && !in_port)
in_port = port;
else if (port->direction()->isOutput() && !out_port)
out_port = port;
}
ASSERT_NE(in_port, nullptr);
ASSERT_NE(out_port, nullptr);
// Compare emitted SDC before/after disabling this specific arc.
const char *filename = "/tmp/test_sdc_r11_disinstports.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
std::string before = readTextFile(filename);
ASSERT_FALSE(before.empty());
size_t before_disable_cnt = countSubstring(before, "set_disable_timing");
sta_->disable(inst, in_port, out_port, sta_->cmdSdc());
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
std::string after_disable = readTextFile(filename);
ASSERT_FALSE(after_disable.empty());
size_t after_disable_cnt = countSubstring(after_disable, "set_disable_timing");
EXPECT_GT(after_disable_cnt, before_disable_cnt);
EXPECT_NE(after_disable.find("-from"), std::string::npos);
EXPECT_NE(after_disable.find("-to"), std::string::npos);
sta_->removeDisable(inst, in_port, out_port, sta_->cmdSdc());
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
std::string after_remove = readTextFile(filename);
ASSERT_FALSE(after_remove.empty());
size_t after_remove_cnt = countSubstring(after_remove, "set_disable_timing");
EXPECT_EQ(after_remove_cnt, before_disable_cnt);
delete iter;
}
// --- WriteSdc with latch borrow limit on clock
// (triggers writeLatchBorrowLimits clock branch) ---
TEST_F(SdcDesignTest, WriteSdcLatchBorrowClock) {
Sdc *sdc = sta_->cmdSdc();
Clock *clk = sdc->findClock("clk");
if (clk) {
sta_->setLatchBorrowLimit(clk, 0.6f, sta_->cmdSdc());
}
const char *filename = "/tmp/test_sdc_r11_latchborrowclk.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
std::string text = readTextFile(filename);
ASSERT_FALSE(text.empty());
EXPECT_NE(text.find("set_max_time_borrow"), std::string::npos);
EXPECT_NE(text.find("[get_clocks {clk}]"), std::string::npos);
}
// --- WriteSdc with derating on cell, instance, net ---
TEST_F(SdcDesignTest, WriteSdcDeratingCellInstNet) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
// Cell-level derating
InstanceChildIterator *iter = network->childIterator(top);
if (iter->hasNext()) {
Instance *inst = iter->next();
LibertyCell *lib_cell = network->libertyCell(inst);
if (lib_cell) {
sta_->setTimingDerate(lib_cell,
TimingDerateCellType::cell_delay,
PathClkOrData::data,
RiseFallBoth::riseFall(),
EarlyLate::early(), 0.93,
sta_->cmdSdc());
}
// Instance-level derating
sta_->setTimingDerate(inst,
TimingDerateCellType::cell_delay,
PathClkOrData::data,
RiseFallBoth::riseFall(),
EarlyLate::late(), 1.07,
sta_->cmdSdc());
}
delete iter;
// Net-level derating
NetIterator *net_iter = network->netIterator(top);
if (net_iter->hasNext()) {
Net *net = net_iter->next();
sta_->setTimingDerate(net,
PathClkOrData::data,
RiseFallBoth::riseFall(),
EarlyLate::early(), 0.92,
sta_->cmdSdc());
}
delete net_iter;
const char *filename = "/tmp/test_sdc_r11_derate_all.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
std::string text = readTextFile(filename);
ASSERT_FALSE(text.empty());
EXPECT_NE(text.find("set_timing_derate -net_delay -early -data"),
std::string::npos);
EXPECT_NE(text.find("set_timing_derate -cell_delay -late -data"),
std::string::npos);
EXPECT_NE(text.find("set_timing_derate -cell_delay -early -data"),
std::string::npos);
}
// --- Sdc: capacitanceLimit on pin ---
TEST_F(SdcDesignTest, SdcCapLimitPin) {
Sdc *sdc = sta_->cmdSdc();
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *out = network->findPin(top, "out");
if (out) {
sta_->setCapacitanceLimit(out, MinMax::max(), 0.5f, sta_->cmdSdc());
float limit;
bool exists;
sdc->capacitanceLimit(out, MinMax::max(), limit, exists);
EXPECT_TRUE(exists);
EXPECT_FLOAT_EQ(limit, 0.5f);
}
}
// --- WriteSdc with set_false_path -hold only
// (triggers writeSetupHoldFlag for hold) ---
TEST_F(SdcDesignTest, WriteSdcFalsePathHold) {
sta_->makeFalsePath(nullptr, nullptr, nullptr, MinMaxAll::min(), nullptr, sta_->cmdSdc());
const char *filename = "/tmp/test_sdc_r11_fp_hold.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
std::string text = readTextFile(filename);
ASSERT_FALSE(text.empty());
EXPECT_NE(text.find("set_false_path -hold"), std::string::npos);
}
// --- WriteSdc with set_false_path -setup only
// (triggers writeSetupHoldFlag for setup) ---
TEST_F(SdcDesignTest, WriteSdcFalsePathSetup) {
sta_->makeFalsePath(nullptr, nullptr, nullptr, MinMaxAll::max(), nullptr, sta_->cmdSdc());
const char *filename = "/tmp/test_sdc_r11_fp_setup.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
std::string text = readTextFile(filename);
ASSERT_FALSE(text.empty());
EXPECT_NE(text.find("set_false_path -setup"), std::string::npos);
}
// --- WriteSdc with exception -through with rise_through
// (triggers rf_prefix branches in writeExceptionThru) ---
TEST_F(SdcDesignTest, WriteSdcFalsePathRiseThru) {
Network *network = sta_->cmdNetwork();
Instance *top = network->topInstance();
Pin *in1 = network->findPin(top, "in1");
if (in1) {
PinSet *thru_pins = new PinSet(network);
thru_pins->insert(in1);
ExceptionThruSeq *thrus = new ExceptionThruSeq;
ExceptionThru *thru = sta_->makeExceptionThru(thru_pins, nullptr, nullptr,
RiseFallBoth::rise(), sta_->cmdSdc());
thrus->push_back(thru);
sta_->makeFalsePath(nullptr, thrus, nullptr, MinMaxAll::all(), nullptr, sta_->cmdSdc());
}
const char *filename = "/tmp/test_sdc_r11_fp_risethru.sdc";
sta_->writeSdc(sta_->cmdSdc(), filename, false, false, 4, false, true);
std::string text = readTextFile(filename);
ASSERT_FALSE(text.empty());
EXPECT_NE(text.find("set_false_path"), std::string::npos);
EXPECT_NE(text.find("-rise_through [get_ports {in1}]"), std::string::npos);
}
} // namespace sta