diff --git a/CMakeLists.txt b/CMakeLists.txt index f41fcac08..4bc6f4ed4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -114,6 +114,7 @@ set(STA_SOURCE liberty/TimingRole.cc liberty/Units.cc liberty/Wireload.cc + liberty/GeneratedClock.cc network/ConcreteLibrary.cc network/ConcreteNetwork.cc diff --git a/include/sta/Clock.hh b/include/sta/Clock.hh index d372aab2f..2d8e01a99 100644 --- a/include/sta/Clock.hh +++ b/include/sta/Clock.hh @@ -294,5 +294,7 @@ sortByName(ClockSet *set); int compare(const ClockSet *set1, const ClockSet *set2); +bool +isPowerOfTwo(int i); } // namespace diff --git a/include/sta/GeneratedClock.hh b/include/sta/GeneratedClock.hh new file mode 100644 index 000000000..e9fb03998 --- /dev/null +++ b/include/sta/GeneratedClock.hh @@ -0,0 +1,46 @@ +#pragma once + +#include "LibertyClass.hh" +#include "SdcClass.hh" + +namespace sta { + +class GeneratedClock +{ +public: + ~GeneratedClock(); + const char *name() const { return name_; } + const char *clockPin() const { return clock_pin_; } + const char *masterPin() const { return master_pin_; } + int dividedBy() const { return divided_by_; } + int multipliedBy() const { return multiplied_by_; } + float dutyCycle() const { return duty_cycle_; } + bool invert() const { return invert_; } + IntSeq *edges() const { return edges_; } + FloatSeq *edgeShifts() const { return edge_shifts_; } + +protected: + GeneratedClock(const char *name, + const char *clock_pin, + const char *master_pin, + int divided_by, + int multiplied_by, + float duty_cycle, + bool invert, + IntSeq *edges, + FloatSeq *edge_shifts); + + const char *name_; + const char *clock_pin_; + const char *master_pin_; + int divided_by_; + int multiplied_by_; + float duty_cycle_; + bool invert_; + IntSeq *edges_; + FloatSeq *edge_shifts_; + + friend class LibertyCell; +}; + +} // namespace diff --git a/include/sta/Liberty.hh b/include/sta/Liberty.hh index 09b4579a2..ec05648a4 100644 --- a/include/sta/Liberty.hh +++ b/include/sta/Liberty.hh @@ -36,6 +36,7 @@ #include "Transition.hh" #include "Delay.hh" #include "LibertyClass.hh" +#include "SdcClass.hh" namespace sta { @@ -483,6 +484,8 @@ public: const SequentialSeq &sequentials() const { return sequentials_; } // Find the sequential with the output connected to an (internal) port. Sequential *outputPortSequential(LibertyPort *port); + // Generated clocks. + const GeneratedClockSeq &generatedClocks() const { return generated_clocks_; } const Statetable *statetable() const { return statetable_; } // Find bus declaration local to this cell. @@ -509,6 +512,15 @@ public: OcvDerate *findOcvDerate(const char *derate_name); // Build helpers. + void makeGeneratedClock(const char *name, + const char *clock_pin, + const char *master_pin, + int divided_by, + int multiplied_by, + float duty_cycle, + bool invert, + IntSeq *edges, + FloatSeq *edge_shifts); void makeSequential(int size, bool is_register, FuncExpr *clk, @@ -621,6 +633,7 @@ protected: PortInternalPowerSeq port_internal_powers_; InternalPowerAttrsSeq internal_power_attrs_; LeakagePowerSeq leakage_powers_; + GeneratedClockSeq generated_clocks_; SequentialSeq sequentials_; PortToSequentialMap port_to_seq_map_; Statetable *statetable_; diff --git a/include/sta/LibertyClass.hh b/include/sta/LibertyClass.hh index 06f2729f9..c4e5bb562 100644 --- a/include/sta/LibertyClass.hh +++ b/include/sta/LibertyClass.hh @@ -56,6 +56,7 @@ class TimingArc; class TimingArcAttrs; class InternalPower; class LeakagePower; +class GeneratedClock; class Sequential; class FuncExpr; class TimingModel; @@ -70,6 +71,7 @@ class StatetableRow; typedef Vector LibertyLibrarySeq; typedef Vector LibertyCellSeq; typedef Vector SequentialSeq; +typedef Vector GeneratedClockSeq; typedef Map LibertyCellEquivMap; typedef Vector LibertyPortSeq; typedef Set LibertyPortSet; diff --git a/include/sta/Network.hh b/include/sta/Network.hh index 00566c10b..6bc7f859c 100644 --- a/include/sta/Network.hh +++ b/include/sta/Network.hh @@ -443,6 +443,12 @@ public: virtual char pathEscape() const { return escape_; } virtual void setPathEscape(char escape); + // Generated clocks related functions + void addGeneratedClockPinToCell(const char *pinName, LibertyCell *cell); + const Map &generatedClockPinsToCellMap() const { + return generated_clock_pins_to_cells_; + } + protected: Pin *findPinLinear(const Instance *instance, const char *port_name) const; @@ -497,6 +503,9 @@ protected: char divider_; char escape_; NetDrvrPinsMap net_drvr_pin_map_; + + // Map of generated clock pins to their corresponding liberty cell + Map generated_clock_pins_to_cells_; }; // Network API to support network edits. diff --git a/include/sta/Sdc.hh b/include/sta/Sdc.hh index b14e10cc8..a8747d909 100644 --- a/include/sta/Sdc.hh +++ b/include/sta/Sdc.hh @@ -376,6 +376,7 @@ public: float fanout); void setMaxArea(float area); float maxArea() const; + void createLibertyGeneratedClocks(Clock *clk); Clock *makeClock(const char *name, PinSet *pins, bool add_to_pins, diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index 4404eedfa..147a7be12 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -298,6 +298,8 @@ public: void removeClock(Clock *clk); // Update period/waveform for generated clocks from source pin clock. void updateGeneratedClks(); + // Mark that generated clocks need to be updated. + void setUpdateGenclks(); // True if pin is defined as a clock source (pin may be hierarchical). bool isClockSrc(const Pin *pin) const; // Propagated (non-ideal) clocks. diff --git a/include/sta/StringUtil.hh b/include/sta/StringUtil.hh index c858df5f4..b2cfa4b81 100644 --- a/include/sta/StringUtil.hh +++ b/include/sta/StringUtil.hh @@ -199,6 +199,13 @@ isTmpString(const char *str); void trimRight(std::string &str); +// Trim left spaces. +void +trimLeft(std::string &str); + +void +trim(std::string &str); + typedef Vector StringVector; void diff --git a/liberty/GeneratedClock.cc b/liberty/GeneratedClock.cc new file mode 100644 index 000000000..379c1aa44 --- /dev/null +++ b/liberty/GeneratedClock.cc @@ -0,0 +1,43 @@ +#include "GeneratedClock.hh" + +#include "Liberty.hh" +#include "StringUtil.hh" + +namespace sta { + +GeneratedClock::GeneratedClock(const char *name, + const char *clock_pin, + const char *master_pin, + int divided_by, + int multiplied_by, + float duty_cycle, + bool invert, + IntSeq *edges, + FloatSeq *edge_shifts) : + name_(name ? stringCopy(name) : nullptr), + clock_pin_(clock_pin ? stringCopy(clock_pin) : nullptr), + master_pin_(master_pin ? stringCopy(master_pin) : nullptr), + divided_by_(divided_by), + multiplied_by_(multiplied_by), + duty_cycle_(duty_cycle), + invert_(invert), + edges_(edges), + edge_shifts_(edge_shifts) +{ +} + +GeneratedClock::~GeneratedClock() +{ + if (name_) + stringDelete(name_); + if (clock_pin_) + stringDelete(clock_pin_); + if (master_pin_) + stringDelete(master_pin_); + if (edges_) + delete edges_; + if (edge_shifts_) + delete edge_shifts_; +} + +} // namespace diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc index ec922176f..415dd5c65 100644 --- a/liberty/Liberty.cc +++ b/liberty/Liberty.cc @@ -41,6 +41,7 @@ #include "InternalPower.hh" #include "LeakagePower.hh" #include "Sequential.hh" +#include "GeneratedClock.hh" #include "Wireload.hh" #include "EquivCells.hh" #include "Network.hh" @@ -969,6 +970,7 @@ LibertyCell::~LibertyCell() leakage_powers_.deleteContents(); sequentials_.deleteContents(); + generated_clocks_.deleteContents(); delete statetable_; bus_dcls_.deleteContents(); scaled_cells_.deleteContents(); @@ -1474,6 +1476,43 @@ LibertyCell::hasTimingArcs(LibertyPort *port) const || timing_arc_set_to_map_.findKey(port); } +void +LibertyCell::makeGeneratedClock(const char *name, + const char *clock_pin, + const char *master_pin, + int divided_by, + int multiplied_by, + float duty_cycle, + bool invert, + IntSeq *edges, + FloatSeq *edge_shifts) +{ + // Copy edges and edge_shifts if they exist + IntSeq *edges_copy = nullptr; + if (edges) { + edges_copy = new IntSeq(*edges); + } + + FloatSeq *edge_shifts_copy = nullptr; + if (edge_shifts) { + edge_shifts_copy = new FloatSeq(*edge_shifts); + } + + // Create the GeneratedClock object + GeneratedClock *generated_clock = new GeneratedClock( + name, + clock_pin, + master_pin, + divided_by, + multiplied_by, + duty_cycle, + invert, + edges_copy, + edge_shifts_copy + ); + generated_clocks_.push_back(generated_clock); +} + void LibertyCell::makeSequential(int size, bool is_register, diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc index 450c990a6..7974f1e9f 100644 --- a/liberty/LibertyReader.cc +++ b/liberty/LibertyReader.cc @@ -49,6 +49,7 @@ #include "PortDirection.hh" #include "ParseBus.hh" #include "Network.hh" +#include "Clock.hh" extern int LibertyParse_debug; @@ -316,6 +317,18 @@ LibertyReader::defineVisitors() defineAttrVisitor("user_function_class", &LibertyReader::visitCellUserFunctionClass); + // Generated clock + defineGroupVisitor("generated_clock", &LibertyReader::beginGeneratedClock, + &LibertyReader::endGeneratedClock); + defineAttrVisitor("clock_pin", &LibertyReader::visitClockPin); + defineAttrVisitor("master_pin", &LibertyReader::visitMasterPin); + defineAttrVisitor("divided_by", &LibertyReader::visitDividedBy); + defineAttrVisitor("multiplied_by", &LibertyReader::visitMultipliedBy); + defineAttrVisitor("duty_cycle", &LibertyReader::visitDutyCycle); + defineAttrVisitor("invert", &LibertyReader::visitInvert); + defineAttrVisitor("shifts", &LibertyReader::visitShifts); + defineAttrVisitor("edges", &LibertyReader::visitEdges); + // Pins defineGroupVisitor("pin", &LibertyReader::beginPin,&LibertyReader::endPin); defineGroupVisitor("bus", &LibertyReader::beginBus,&LibertyReader::endBus); @@ -1995,6 +2008,8 @@ LibertyReader::endCell(LibertyGroup *group) parseCellFuncs(); makeLeakagePowers(); finishPortGroups(); + // Make generated clocks if they exist + makeGeneratedClocks(); if (ocv_derate_name_) { OcvDerate *derate = cell_->findOcvDerate(ocv_derate_name_); @@ -2187,6 +2202,32 @@ LibertyReader::makeCellSequential(SequentialGroup *seq) preset_expr->deleteSubexprs(); } +void +LibertyReader::makeGeneratedClocks() +{ + for (GeneratedClockGroup *generated_clock : generated_clocks_) { + makeGeneratedClock(generated_clock); + delete generated_clock; + } + generated_clocks_.clear(); +} + +void +LibertyReader::makeGeneratedClock(GeneratedClockGroup *generated_clock) +{ + if (cell_) { + cell_->makeGeneratedClock(generated_clock->name(), + generated_clock->clockPin(), + generated_clock->masterPin(), + generated_clock->dividedBy(), + generated_clock->multipliedBy(), + generated_clock->dutyCycle(), + generated_clock->invert(), + generated_clock->edges(), + generated_clock->edgeShifts()); + } +} + void LibertyReader::checkLatchEnableSense(FuncExpr *enable_func, int line) @@ -3171,6 +3212,175 @@ LibertyReader::visitCellUserFunctionClass(LibertyAttr *attr) //////////////////////////////////////////////////////////////// +void +LibertyReader::beginGeneratedClock(LibertyGroup *group) +{ + if (cell_) { + const char *name = group->firstName(); + if (name) { + generated_clock_ = new GeneratedClockGroup(); + generated_clock_->setName(name); + generated_clocks_.push_back(generated_clock_); + } + } +} + +void +LibertyReader::endGeneratedClock(LibertyGroup *group) +{ + if (generated_clock_) { + if (!generated_clock_->clockPin()) { + libError(1234, group, "generated_clock missing clock_pin."); + } + } + generated_clock_ = nullptr; +} + +void +LibertyReader::visitClockPin(LibertyAttr *attr) +{ + if (generated_clock_) { + const char *clock_pin = getAttrString(attr); + if (clock_pin) { + string str(clock_pin); + trim(str); + generated_clock_->setClockPin(stringCopy(str.c_str())); + } + } +} + +void +LibertyReader::visitMasterPin(LibertyAttr *attr) +{ + if (generated_clock_) { + const char *master_pin = getAttrString(attr); + if (master_pin) { + string str(master_pin); + trim(str); + generated_clock_->setMasterPin(stringCopy(str.c_str())); + } + } +} + +void +LibertyReader::visitDividedBy(LibertyAttr *attr) +{ + bool exists; + int value; + getAttrInt(attr, value, exists); + if (exists) { + if (!isPowerOfTwo(value)) { + libError(1234, attr, "divided_by must be a power of two."); + } + generated_clock_->setDividedBy(value); + } +} + +void +LibertyReader::visitMultipliedBy(LibertyAttr *attr) +{ + bool exists; + int value; + getAttrInt(attr, value, exists); + if (exists) { + if (!isPowerOfTwo(value)) { + libError(1234, attr, "multiplied_by must be a power of two."); + } + generated_clock_->setMultipliedBy(value); + } +} + +void +LibertyReader::visitDutyCycle(LibertyAttr *attr) +{ + bool exists; + float dutyCycle; + getAttrFloat(attr, dutyCycle, exists); + if (exists) { + if (dutyCycle < 0.0 || dutyCycle > 100.0) { + libError( + 1234, + attr, + "duty_cycle must be between 0.0 and 100.0, inclusive. Duty cycle: %f", + dutyCycle); + } + generated_clock_->setDutyCycle(dutyCycle); + } +} + +void +LibertyReader::visitInvert(LibertyAttr *attr) +{ + bool exists, value; + getAttrBool(attr, value, exists); + if (exists) { + generated_clock_->setInvert(value); + } +} + +void +LibertyReader::visitShifts(LibertyAttr *attr) +{ + if (generated_clock_) { + if (!attr->isComplex()) { + libError(1234, attr, "'shifts' attribute is not a complex attribute."); + } + // Initialize edges sequence + FloatSeq *shifts = new FloatSeq; + LibertyAttrValueIterator value_iter(attr->values()); + while (value_iter.hasNext()) { + LibertyAttrValue *value = value_iter.next(); + if (!value->isFloat()) { + delete shifts; + libError(1234, attr, "shifts attribute must be a float."); + } + float float_value = value->floatValue(); + shifts->push_back(float_value); + } + + // Error checking, only size 3 is supported at the moment + if (shifts->size() != 3) { + delete shifts; + libError(1234, attr, "shifts attribute must have 3 values."); + } + libWarn( + 1234, + attr, + "shifts are not supported yet, may cause malformed waveforms."); + generated_clock_->setEdgeShifts(shifts); + } +} + +void +LibertyReader::visitEdges(LibertyAttr *attr) +{ + if (generated_clock_) { + if (!attr->isComplex()) { + libError(1234, attr, "'edges' attribute is not a complex attribute."); + } + // Initialize edges sequence + IntSeq *edges = new IntSeq; + LibertyAttrValueIterator value_iter(attr->values()); + while (value_iter.hasNext()) { + LibertyAttrValue *value = value_iter.next(); + if (!value->isFloat()) { + delete edges; + libError(1234, attr, "edges attribute must be a float."); + } + float float_value = value->floatValue(); + int int_value = static_cast(float_value); + edges->push_back(int_value); + } + + // Error checking, only size 3 is supported at the moment + if (edges->size() != 3) { + delete edges; + libError(1234, attr, "edges attribute must have 3 values."); + } + generated_clock_->setEdges(edges); + } +} + void LibertyReader::beginPin(LibertyGroup *group) { @@ -5873,6 +6083,59 @@ SequentialGroup::setClrPresetVar2(LogicValue var) //////////////////////////////////////////////////////////////// +GeneratedClockGroup::GeneratedClockGroup() : + name_(nullptr), + clock_pin_(nullptr), + master_pin_(nullptr), + divided_by_(0), + multiplied_by_(0), + duty_cycle_(50.0), + invert_(false), + edges_(nullptr), + edge_shifts_(nullptr) +{ +} + +GeneratedClockGroup::~GeneratedClockGroup() +{ + if (name_) + stringDelete(name_); + if (clock_pin_) + stringDelete(clock_pin_); + if (master_pin_) + stringDelete(master_pin_); + if (edges_) + delete edges_; + if (edge_shifts_) + delete edge_shifts_; +} + +void +GeneratedClockGroup::setName(const char *name) +{ + if (name_) + stringDelete(name_); + name_ = name ? stringCopy(name) : nullptr; +} + +void +GeneratedClockGroup::setClockPin(const char *clockPin) +{ + if (clock_pin_) + stringDelete(clock_pin_); + clock_pin_ = clockPin ? stringCopy(clockPin) : nullptr; +} + +void +GeneratedClockGroup::setMasterPin(const char *masterPin) +{ + if (master_pin_) + stringDelete(master_pin_); + master_pin_ = masterPin ? stringCopy(masterPin) : nullptr; +} + +//////////////////////////////////////////////////////////////// + StatetableGroup::StatetableGroup(StdStringSeq &input_ports, StdStringSeq &internal_ports, int line) : diff --git a/liberty/LibertyReaderPvt.hh b/liberty/LibertyReaderPvt.hh index 455c7b653..68979e792 100644 --- a/liberty/LibertyReaderPvt.hh +++ b/liberty/LibertyReaderPvt.hh @@ -42,6 +42,7 @@ #include "LibertyParser.hh" #include "LibertyReader.hh" #include "LibertyBuilder.hh" +#include "SdcClass.hh" namespace sta { @@ -50,6 +51,7 @@ class LibertyReader; class LibertyFunc; class PortGroup; class SequentialGroup; +class GeneratedClockGroup; class StatetableGroup; class RelatedPortGroup; class TimingGroup; @@ -70,6 +72,7 @@ typedef Vector LibertyFuncSeq; typedef Vector TimingGroupSeq; typedef Vector InternalPowerGroupSeq; typedef Vector LeakagePowerGroupSeq; +typedef Vector GeneratedClockGroupSeq; typedef void (LibertyPort::*LibertyPortBoolSetter)(bool value); typedef Vector OutputWaveformSeq; typedef std::vector StdStringSeq; @@ -186,6 +189,8 @@ public: virtual void makeCellSequential(SequentialGroup *seq); virtual void makeStatetable(); virtual void makeLeakagePowers(); + virtual void makeGeneratedClocks(); + virtual void makeGeneratedClock(GeneratedClockGroup *generated_clock); virtual void parseCellFuncs(); virtual void makeLibertyFunc(const char *expr, LibertySetFunc set_func, @@ -222,6 +227,17 @@ public: virtual void visitCellFootprint(LibertyAttr *attr); virtual void visitCellUserFunctionClass(LibertyAttr *attr); + virtual void beginGeneratedClock(LibertyGroup *group); + virtual void endGeneratedClock(LibertyGroup *group); + virtual void visitClockPin(LibertyAttr *attr); + virtual void visitMasterPin(LibertyAttr *attr); + virtual void visitDividedBy(LibertyAttr *attr); + virtual void visitMultipliedBy(LibertyAttr *attr); + virtual void visitDutyCycle(LibertyAttr *attr); + virtual void visitInvert(LibertyAttr *attr); + virtual void visitShifts(LibertyAttr *attr); + virtual void visitEdges(LibertyAttr *attr); + virtual void beginPin(LibertyGroup *group); virtual void endPin(LibertyGroup *group); virtual void beginBus(LibertyGroup *group); @@ -639,6 +655,10 @@ protected: bool type_bit_to_exists_; SequentialGroup *sequential_; SequentialGroupSeq cell_sequentials_; + + GeneratedClockGroup *generated_clock_; + GeneratedClockGroupSeq generated_clocks_; + StatetableGroup *statetable_; TimingGroup *timing_; InternalPowerGroup *internal_power_; @@ -803,6 +823,42 @@ protected: int line_; }; +class GeneratedClockGroup +{ +public: + GeneratedClockGroup(); + ~GeneratedClockGroup(); + const char *name() const { return name_; } + void setName(const char *name); + const char *clockPin() const { return clock_pin_; } + void setClockPin(const char *clockPin); + const char *masterPin() const { return master_pin_; } + void setMasterPin(const char *masterPin); + int dividedBy() const { return divided_by_; } + void setDividedBy(int dividedBy) { divided_by_ = dividedBy; } + int multipliedBy() const { return multiplied_by_; } + void setMultipliedBy(int multipliedBy) { multiplied_by_ = multipliedBy; } + float dutyCycle() const { return duty_cycle_; } + void setDutyCycle(float dutyCycle) { duty_cycle_ = dutyCycle; } + bool invert() const { return invert_; } + void setInvert(bool invert) { invert_ = invert; } + IntSeq *edges() const { return edges_; } + void setEdges(IntSeq *edges) { edges_ = edges; } + FloatSeq *edgeShifts() const { return edge_shifts_; } + void setEdgeShifts(FloatSeq *edgeShifts) { edge_shifts_ = edgeShifts; } + +protected: + const char *name_; + const char *clock_pin_; + const char *master_pin_; + int divided_by_; + int multiplied_by_; + float duty_cycle_; + bool invert_; + IntSeq *edges_; + FloatSeq *edge_shifts_; +}; + class StatetableGroup { public: diff --git a/network/Network.cc b/network/Network.cc index 578c06645..44b785be3 100644 --- a/network/Network.cc +++ b/network/Network.cc @@ -53,6 +53,7 @@ Network::clear() { default_liberty_ = nullptr; clearNetDrvrPinMap(); + generated_clock_pins_to_cells_.clear(); } bool @@ -1223,6 +1224,13 @@ Network::setPathEscape(char escape) escape_ = escape; } +void Network::addGeneratedClockPinToCell(const char *pinName, LibertyCell *cell) +{ + generated_clock_pins_to_cells_[pinName]=cell; +} + + + //////////////////////////////////////////////////////////////// typedef Vector InstanceChildIteratorSeq; diff --git a/sdc/Clock.cc b/sdc/Clock.cc index 8e7aadd14..d87f33043 100644 --- a/sdc/Clock.cc +++ b/sdc/Clock.cc @@ -37,8 +37,6 @@ namespace sta { -static bool -isPowerOfTwo(int i); Clock::Clock(const char *name, int index, @@ -482,7 +480,7 @@ Clock::generateEdgesClk(const Clock *src_clk) criticalError(244, "generated clock edges size is not three."); } -static bool +bool isPowerOfTwo(int i) { return (i & (i - 1)) == 0; diff --git a/sdc/Sdc.cc b/sdc/Sdc.cc index daca91869..7f1686e2a 100644 --- a/sdc/Sdc.cc +++ b/sdc/Sdc.cc @@ -43,10 +43,12 @@ #include "Clock.hh" #include "ClockLatency.hh" #include "ClockInsertion.hh" +#include "GeneratedClock.hh" #include "CycleAccting.hh" #include "PortDelay.hh" #include "ExceptionPath.hh" #include "PortExtCap.hh" +#include "Sta.hh" #include "DisabledPorts.hh" #include "InputDrive.hh" #include "DataCheck.hh" @@ -992,6 +994,105 @@ Sdc::maxArea() const //////////////////////////////////////////////////////////////// +void Sdc::createLibertyGeneratedClocks(Clock *clk) { + + // Validation to proceed with creating liberty-defined generated clocks + if (network_->defaultLibertyLibrary() == nullptr) + return; + + // sta object + Sta *sta = Sta::sta(); + + // Invalidate and rebuild the clock network to include the newly created clock + sta->clkPinsInvalid(); + sta->ensureClkNetwork(); + + if (sta->pins(clk)) { + + // All pins along the clock network + const PinSet clk_network_pins = *sta->pins(clk); + + // Get all generated clock pins from the network + const Map &generated_clock_pins_to_cells = + network_->generatedClockPinsToCellMap(); + + // The keys of generated_clock_pins_to_cells_ + // (master clock pins) will be searched in the current clock network + for (const auto &entry : generated_clock_pins_to_cells) { + //const char *pin_name; + //LibertyCell *cell; + auto [pin_name,cell]=entry; + + // Search the current clock network for the pin and validate + // that it is in the clock network + Pin *pin = network_->findPin(pin_name); + if (pin && clk_network_pins.hasKey(pin)) { + + debugPrint(debug_, "libgenclk", 1, "Found generated clock pin %s " + "in liberty cell %s at path %s", + pin_name, cell->name(), network_->pathName(pin)); + + // Search liberty cell for the corresponding generated clock + for (const GeneratedClock *generatedClock : cell->generatedClocks()) { + + // Use the pin to find the instance and the path to the instance + const Instance *inst = network_->instance(pin); + const char *instPath = network_->pathName(inst); + + // Compare the full {instance path/master pin} + // and the full path to pin to validate the correct + // liberty information is supplied + const char *comparePath = stringPrintTmp( + "%s/%s", instPath, + generatedClock->masterPin() + ); + if (strcmp(comparePath, network_->pathName(pin)) == 0) { + + // Hierarchical path of the generated clock pin + // (name is with respect to source clock) + const char *generatedClockName = stringCopy(stringPrintTmp( + "%s/%s", instPath, + generatedClock->clockPin() + )); + + debugPrint(debug_, "libgenclk", 1, "Creating generated clock %s " + "from clock %s in instance %s", + generatedClockName, clk->name(), instPath); + + // Find the output pin, for nested generated clocks + Pin *clkOutPin = network_->findPin( + inst, + generatedClock->clockPin() + ); + PinSet *clkPins = nullptr; + if (clkOutPin) { + clkPins = new PinSet(); + clkPins->insert(clkOutPin); + } + + // Create generated clock using the existing + // makeGeneratedClock function + makeGeneratedClock( + generatedClockName, + clkPins, + true, + const_cast(pin), + clk, + generatedClock->dividedBy(), + generatedClock->multipliedBy(), + generatedClock->dutyCycle(), + generatedClock->invert(), + false, + generatedClock->edges(), + generatedClock->edgeShifts(), + nullptr); + } + } + } + } + } +} + Clock * Sdc::makeClock(const char *name, PinSet *pins, @@ -1019,6 +1120,12 @@ Sdc::makeClock(const char *name, clearCycleAcctings(); invalidateGeneratedClks(); clkHpinDisablesInvalid(); + if (network_->generatedClockPinsToCellMap().size() > 0) { + debugPrint(debug_, "libgenclk", 1, "Creating liberty-defined generated clocks " + "for clock %s by searching %lu liberty-defined generated clock pins", + name, network_->generatedClockPinsToCellMap().size()); + createLibertyGeneratedClocks(clk); + } return clk; } @@ -1057,6 +1164,12 @@ Sdc::makeGeneratedClock(const char *name, clearCycleAcctings(); invalidateGeneratedClks(); clkHpinDisablesInvalid(); + + // Trigger update of generated clocks + Sta::sta()->setUpdateGenclks(); + Sta::sta()->updateGeneratedClks(); + createLibertyGeneratedClocks(clk); + return clk; } diff --git a/search/Sta.cc b/search/Sta.cc index 7b9a3123d..60d169b3c 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -3556,6 +3556,13 @@ Sta::updateGeneratedClks() update_genclks_ = false; } +void +Sta::setUpdateGenclks() +{ + update_genclks_ = true; +} + + Level Sta::vertexLevel(Vertex *vertex) { diff --git a/tcl/CmdArgs.tcl b/tcl/CmdArgs.tcl index c1a598892..f58d8b3d9 100644 --- a/tcl/CmdArgs.tcl +++ b/tcl/CmdArgs.tcl @@ -935,6 +935,8 @@ proc get_clocks_warn { arg_name arglist } { return $clks } + + proc get_net_arg { arg_name arg } { set net "NULL" if {[llength $arg] > 1} { diff --git a/tcl/CmdUtil.tcl b/tcl/CmdUtil.tcl index a2380ace1..1db046146 100644 --- a/tcl/CmdUtil.tcl +++ b/tcl/CmdUtil.tcl @@ -269,6 +269,7 @@ proc report_object_names { objects } { define_cmd_args "get_name" {object} define_cmd_args "get_full_name" {object} + ################################################################ proc get_name { object } { @@ -279,6 +280,7 @@ proc get_full_name { object } { return [get_object_property $object "full_name"] } + proc sort_by_name { objects } { return [lsort -command name_cmp $objects] } diff --git a/test/generated_clock.lib b/test/generated_clock.lib new file mode 100644 index 000000000..c9d93ddda --- /dev/null +++ b/test/generated_clock.lib @@ -0,0 +1,66 @@ +library (generated_clock) { + delay_model : "table_lookup"; + time_unit : "1ns"; + voltage_unit : "1v"; + input_threshold_pct_rise : 50; + input_threshold_pct_fall : 50; + output_threshold_pct_rise : 50; + output_threshold_pct_fall : 50; + slew_lower_threshold_pct_rise : 30; + slew_lower_threshold_pct_fall : 30; + slew_upper_threshold_pct_rise : 70; + slew_upper_threshold_pct_fall : 70; + cell (CLK_GEN) { + area : 10.0; + pin (CLK_IN) { + direction : input; + clock : true; + } + pin (CLK_OUT_DIV) { + direction : output; + } + pin (CLK_OUT_MUL) { + direction : output; + } + generated_clock (gen_clk_div2) { + clock_pin : " CLK_OUT_DIV "; + master_pin : CLK_IN ; + divided_by : 2; + } + generated_clock (gen_clk_mul2_inv_dut_80) { + clock_pin : " CLK_OUT_MUL "; + master_pin : CLK_IN ; + multiplied_by : 2; + invert : true; /* inverted duty cycle of 80, so 20 */ + duty_cycle : 80.0; + } + timing () { + related_pin : CLK_IN; + timing_type : rising_edge; + intrinsic_rise : 10; + intrinsic_fall : 10; + } + } + cell (CLK_EDGE_SHIFT) { + area : 10.0; + pin (CLK_IN) { + direction : input; + clock : true; + } + pin (CLK_OUT) { + direction : output; + } + generated_clock (shift) { + clock_pin : CLK_OUT ; + master_pin : CLK_IN; + edges (1, 3, 5); + shifts (0, 0, 0) + } + timing () { + related_pin : CLK_IN; + timing_type : rising_edge; + intrinsic_rise : 10; + intrinsic_fall : 10; + } + } +} \ No newline at end of file diff --git a/test/generated_clock.ok b/test/generated_clock.ok new file mode 100644 index 000000000..797d61046 --- /dev/null +++ b/test/generated_clock.ok @@ -0,0 +1,24 @@ +Warning: generated_clock.lib line 57, shifts are not supported yet, may cause malformed waveforms. +Number of clocks: 9 +clk period: 10.000000 +u_second_hierarchy/clk_gen/CLK_OUT_DIV period: 20.000000 +u_second_hierarchy/clk_gen2/CLK_OUT_DIV period: 40.000000 +u_second_hierarchy/clk_gen3/CLK_OUT_DIV period: 80.000000 +u_second_hierarchy/clk_gen3/CLK_OUT_MUL period: 20.000000 +u_second_hierarchy/clk_gen2/CLK_OUT_MUL period: 10.000000 +u_second_hierarchy/clk_gen/CLK_OUT_MUL period: 5.000000 +clk2 period: 100.000000 +clk_edge_shift/CLK_OUT period: 200.000000 +Number of clocks: 10 +Clock Period Waveform +---------------------------------------------------- +clk 10.00 0.00 5.00 +u_second_hierarchy/clk_gen/CLK_OUT_DIV 20.00 0.00 10.00 (generated) +u_second_hierarchy/clk_gen2/CLK_OUT_DIV 40.00 0.00 20.00 (generated) +u_second_hierarchy/clk_gen3/CLK_OUT_DIV 80.00 0.00 40.00 (generated) +u_second_hierarchy/clk_gen3/CLK_OUT_MUL 20.00 16.00 20.00 (generated) +u_second_hierarchy/clk_gen2/CLK_OUT_MUL 10.00 8.00 10.00 (generated) +u_second_hierarchy/clk_gen/CLK_OUT_MUL 5.00 4.00 5.00 (generated) +clk2 100.00 0.00 50.00 +clk_edge_shift/CLK_OUT 200.00 0.00 100.00 (generated) +clk_manual 400.00 0.00 200.00 (generated) diff --git a/test/generated_clock.tcl b/test/generated_clock.tcl new file mode 100644 index 000000000..07011f84a --- /dev/null +++ b/test/generated_clock.tcl @@ -0,0 +1,60 @@ +#Get Object name +interp alias {} get_object_name {} get_full_name + +# Get attribute +sta::define_cmd_args "get_attribute" {args} + +proc get_attribute {args} { + sta::parse_key_args "get_attribute" args keys {} flags {-quiet} + set quiet [info exists flags(-quiet)] + set arg1 [lindex $args 0] + set arg2 [lindex $args 1] + + # Suppress unknown property warning + if { $quiet } { + suppress_msg 9000 + } + if { [sta::is_object $arg1] } { + set result [get_property $arg1 $arg2] + } elseif { [sta::is_object $arg2] } { + set result [get_property $arg2 $arg1] + } else { + if { $quiet } { + unsuppress_msg 9000 + } + error "get_attribute: invalid object $arg1 or $arg2" + } + # Re-enable warning after the call + if { $quiet } { + unsuppress_msg 9000 + } + return $result +} + +read_liberty generated_clock.lib +read_verilog generated_clock.v +link_design generated_clock +create_clock -name clk -period 10 [get_ports CLK_IN_1] +create_clock -name clk2 -period 100 [get_ports CLK_IN_2] + +# Should see 9 clocks +puts "Number of clocks: [ llength [get_clocks]]" + +# Report all clock periods +foreach clk [get_clocks] { + puts "[get_object_name $clk] period: [get_attribute $clk period]" +} + +# Use TCL command to create a generated clock from generated clock +create_generated_clock \ + -name clk_manual \ + -source [get_pins clk_edge_shift/CLK_OUT] \ + -master_clock clk_edge_shift/CLK_OUT \ + -divide_by 2 \ + [get_ports CLK_OUT_2] + +# Should see 10 clocks +puts "Number of clocks: [ llength [get_clocks]]" + +# Use command to validate waveforms +report_clock_properties diff --git a/test/generated_clock.v b/test/generated_clock.v new file mode 100644 index 000000000..4b5a04387 --- /dev/null +++ b/test/generated_clock.v @@ -0,0 +1,65 @@ +module generated_clock ( + + // Test basic test cases + input wire CLK_IN_1, + output wire slow_clk_int, + output wire fast_clk_int, + output wire slow_clk_out2, + output wire fast_clk_out2, + + // Test liberty-defined nested clock output + output wire slow_clk_out3, + output wire fast_clk_out3, + + // Test edges/shifts (shifts not supported) + input wire CLK_IN_2, + output wire CLK_OUT_2 +); + + // Give one more level of hierarchy to test names + second_hierarchy u_second_hierarchy ( + .clk_in(CLK_IN_1), + .slow_clk_out(slow_clk_int), + .fast_clk_out(fast_clk_int), + .slow_clk_out2(slow_clk_out2), + .fast_clk_out2(fast_clk_out2), + .slow_clk_out3(slow_clk_out3), + .fast_clk_out3(fast_clk_out3) + ); + + CLK_EDGE_SHIFT clk_edge_shift ( + .CLK_IN(CLK_IN_2), + .CLK_OUT(CLK_OUT_2) + ); + +endmodule + +module second_hierarchy ( + input wire clk_in, + output wire slow_clk_out, + output wire fast_clk_out, + output wire slow_clk_out2, + output wire fast_clk_out2, + output wire slow_clk_out3, + output wire fast_clk_out3 +); + + CLK_GEN clk_gen ( + .CLK_IN(clk_in), + .CLK_OUT_DIV(slow_clk_out), + .CLK_OUT_MUL(fast_clk_out) + ); + + CLK_GEN clk_gen2 ( + .CLK_IN(slow_clk_out), + .CLK_OUT_DIV(slow_clk_out2), + .CLK_OUT_MUL(fast_clk_out2) + ); + + CLK_GEN clk_gen3 ( + .CLK_IN(slow_clk_out2), + .CLK_OUT_DIV(slow_clk_out3), + .CLK_OUT_MUL(fast_clk_out3) + ); + +endmodule diff --git a/test/regression_vars.tcl b/test/regression_vars.tcl index acf6906bc..cfe45bd4d 100644 --- a/test/regression_vars.tcl +++ b/test/regression_vars.tcl @@ -151,6 +151,7 @@ record_public_tests { get_lib_pins_of_objects get_noargs get_objrefs + generated_clock liberty_arcs_one2one_1 liberty_arcs_one2one_2 liberty_backslash_eol diff --git a/util/StringUtil.cc b/util/StringUtil.cc index e9b0d74ab..511bfe503 100644 --- a/util/StringUtil.cc +++ b/util/StringUtil.cc @@ -258,6 +258,18 @@ trimRight(string &str) str.erase(str.find_last_not_of(" ") + 1); } +void +trimLeft(string &str) +{ + str.erase(0, str.find_first_not_of(" ")); +} + +void +trim(string &str) +{ + trimLeft(str); + trimRight(str); +} void split(const string &text, const string &delims, diff --git a/verilog/VerilogReader.cc b/verilog/VerilogReader.cc index 26ed54d80..e4c954d8a 100644 --- a/verilog/VerilogReader.cc +++ b/verilog/VerilogReader.cc @@ -38,6 +38,7 @@ #include "StringUtil.hh" #include "verilog/VerilogReaderPvt.hh" #include "verilog/VerilogScanner.hh" +#include "GeneratedClock.hh" namespace sta { @@ -1792,6 +1793,36 @@ VerilogReader::makeModuleInstBody(VerilogModule *module, } } +// Maps a clock pin path to the liberty cell containing the generated clock definition +void +VerilogReader::makeGeneratedClocks(LibertyCell *lib_cell, Instance *inst) +{ + if (lib_cell->generatedClocks().size() > 0) { + for (GeneratedClock *generated_clock : lib_cell->generatedClocks()) { + + // Path to the instance containing the clock pin + const char *inst_path = network_->pathName(inst); + + // HACK: Strip top-level prefix to get instance path for later search + if (const char *slash = strchr(inst_path, network_->pathDivider())) { + inst_path = slash + 1; + } + + const char *masterPin = generated_clock->masterPin(); + const char *pinPath = stringCopy(stringPrintTmp("%s/%s", + inst_path, masterPin)); + + // Map the full pinpath of source clock to the liberty cell + // containing the generated clock definition + network_->addGeneratedClockPinToCell(pinPath, lib_cell); + + debugPrint(debug_, "libgenclk", 1, "Adding generated clock pin %s " + "to liberty cell %s for instance %s", + pinPath, lib_cell->name(), inst_path); + } + } +} + void VerilogReader::makeModuleInstNetwork(VerilogModuleInst *mod_inst, Instance *parent, @@ -1836,6 +1867,7 @@ VerilogReader::makeModuleInstNetwork(VerilogModuleInst *mod_inst, LibertyPort *port = port_iter.next(); network_->makePin(inst, reinterpret_cast(port), nullptr); } + makeGeneratedClocks(lib_cell, inst); } bool is_leaf = network_->isLeaf(cell); VerilogBindingTbl bindings(zero_net_name_, one_net_name_); @@ -2037,6 +2069,7 @@ VerilogReader::makeLibertyInst(VerilogLibertyInst *lib_inst, // Make unconnected pin. network_->makePin(inst, reinterpret_cast(port), nullptr); } + makeGeneratedClocks(lib_cell, inst); } //////////////////////////////////////////////////////////////// diff --git a/verilog/VerilogReader.hh b/verilog/VerilogReader.hh index aca56682d..69b2b727e 100644 --- a/verilog/VerilogReader.hh +++ b/verilog/VerilogReader.hh @@ -180,6 +180,7 @@ protected: Instance *inst, VerilogBindingTbl *bindings, bool make_black_boxes); + void makeGeneratedClocks(LibertyCell *lib_cell, Instance *inst); void makeModuleInstNetwork(VerilogModuleInst *mod_inst, Instance *parent, VerilogModule *parent_module,