1 /////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (c) 2020, The Regents of the University of California
4 // All rights reserved.
5 //
6 // BSD 3-Clause License
7 //
8 // Redistribution and use in source and binary forms, with or without
9 // modification, are permitted provided that the following conditions are met:
10 //
11 // * Redistributions of source code must retain the above copyright notice, this
12 //   list of conditions and the following disclaimer.
13 //
14 // * Redistributions in binary form must reproduce the above copyright notice,
15 //   this list of conditions and the following disclaimer in the documentation
16 //   and/or other materials provided with the distribution.
17 //
18 // * Neither the name of the copyright holder nor the names of its
19 //   contributors may be used to endorse or promote products derived from
20 //   this software without specific prior written permission.
21 //
22 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
26 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 // POSSIBILITY OF SUCH DAMAGE.
33 //
34 ///////////////////////////////////////////////////////////////////////////////
35 
36 #include <QApplication>
37 #include <QDebug>
38 #include <fstream>
39 #include <iostream>
40 #include <string>
41 
42 #include "db.h"
43 #include "dbShape.h"
44 #include "db_sta/dbNetwork.hh"
45 #include "db_sta/dbSta.hh"
46 #include "ord/OpenRoad.hh"
47 #include "sta/ArcDelayCalc.hh"
48 #include "sta/Corner.hh"
49 #include "sta/DcalcAnalysisPt.hh"
50 #include "sta/ExceptionPath.hh"
51 #include "sta/Graph.hh"
52 #include "sta/GraphDelayCalc.hh"
53 #include "sta/Liberty.hh"
54 #include "sta/Network.hh"
55 #include "sta/PathAnalysisPt.hh"
56 #include "sta/PathEnd.hh"
57 #include "sta/PathExpanded.hh"
58 #include "sta/PathRef.hh"
59 #include "sta/PatternMatch.hh"
60 #include "sta/PortDirection.hh"
61 #include "sta/Sdc.hh"
62 #include "sta/Search.hh"
63 #include "sta/Sta.hh"
64 #include "sta/Units.hh"
65 #include "staGui.h"
66 
67 namespace gui {
68 
69 const char* TimingPathDetailModel::up_down_arrows = u8"\u21C5";
70 const char* TimingPathDetailModel::up_arrow = u8"\u2191";
71 const char* TimingPathDetailModel::down_arrow = u8"\u2193";
72 
73 // These two definitions need to stay in sync
74 const std::vector<std::string> TimingPathsModel::_path_columns
75     = {"Capture Clock", "Required", "Arrival", "Slack", "Start", "End"};
76 enum TimingPathsModel::Column : int
77 {
78   Clock,
79   Required,
80   Arrival,
81   Slack,
82   Start,
83   End
84 };
85 
86 // These two definitions need to stay in sync
87 const std::vector<std::string> TimingPathDetailModel::_path_details_columns
88     = {"Pin", up_down_arrows, "Time", "Delay", "Slew", "Load"};
89 enum TimingPathDetailModel::Column : int
90 {
91   Pin,
92   RiseFall,
93   Time,
94   Delay,
95   Slew,
96   Load
97 };
98 
99 gui::Painter::Color TimingPathRenderer::inst_highlight_color_
100     = gui::Painter::dark_cyan;
101 gui::Painter::Color TimingPathRenderer::path_inst_color_
102     = gui::Painter::magenta;
103 gui::Painter::Color TimingPathRenderer::term_color_ = gui::Painter::blue;
104 gui::Painter::Color TimingPathRenderer::signal_color_ = gui::Painter::red;
105 gui::Painter::Color TimingPathRenderer::clock_color_ = gui::Painter::yellow;
106 // helper functions
getRequiredTime(sta::dbSta * staRoot,sta::Pin * term,bool is_rise,sta::PathAnalysisPt * path_ap)107 float getRequiredTime(sta::dbSta* staRoot,
108                       sta::Pin* term,
109                       bool is_rise,
110                       sta::PathAnalysisPt* path_ap)
111 {
112   auto vert = staRoot->getDbNetwork()->graph()->pinLoadVertex(term);
113   auto req = staRoot->vertexRequired(
114       vert, is_rise ? sta::RiseFall::rise() : sta::RiseFall::fall(), path_ap);
115   if (sta::delayInf(req)) {
116     return 0;
117   }
118   return req;
119 }
120 
TimingPathsModel(bool get_max,int path_count)121 TimingPathsModel::TimingPathsModel(bool get_max, int path_count)
122     : openroad_(ord::OpenRoad::openRoad())
123 {
124   populateModel(get_max, path_count);
125 }
126 
~TimingPathsModel()127 TimingPathsModel::~TimingPathsModel()
128 {
129   // TBD
130 }
131 
rowCount(const QModelIndex & parent) const132 int TimingPathsModel::rowCount(const QModelIndex& parent) const
133 {
134   return timing_paths_.size();
135 }
136 
columnCount(const QModelIndex & parent) const137 int TimingPathsModel::columnCount(const QModelIndex& parent) const
138 {
139   return TimingPathsModel::_path_columns.size();
140 }
141 
data(const QModelIndex & index,int role) const142 QVariant TimingPathsModel::data(const QModelIndex& index, int role) const
143 {
144   const Column col_index = static_cast<Column>(index.column());
145   if (index.isValid() && role == Qt::TextAlignmentRole) {
146     switch (col_index) {
147       case Clock:
148       case Start:
149       case End:
150         return Qt::AlignLeft;
151       case Required:
152       case Arrival:
153       case Slack:
154         return Qt::AlignRight;
155     }
156   }
157 
158   if (!index.isValid() || role != Qt::DisplayRole) {
159     return QVariant();
160   }
161 
162   sta::dbSta* sta = openroad_->getSta();
163   auto time_units = sta->search()->units()->timeUnit();
164 
165   unsigned int row_index = index.row();
166   if (row_index > timing_paths_.size())
167     return QVariant();
168   auto timing_path = timing_paths_[row_index];
169   switch (col_index) {
170     case Clock:
171       return QString::fromStdString(timing_path->getStartClock());
172     case Required:
173       return QString(time_units->asString(timing_path->getPathRequiredTime()));
174     case Arrival:
175       return QString(time_units->asString(timing_path->getPathArrivalTime()));
176     case Slack:
177       return QString(time_units->asString(timing_path->getSlack()));
178     case Start:
179       return QString(timing_path->getStartStageName().c_str());
180     case End:
181       return QString::fromStdString(timing_path->getEndStageName());
182   }
183   return QVariant();
184 }
185 
headerData(int section,Qt::Orientation orientation,int role) const186 QVariant TimingPathsModel::headerData(int section,
187                                       Qt::Orientation orientation,
188                                       int role) const
189 {
190   if (role == Qt::DisplayRole && orientation == Qt::Horizontal)
191     return QString::fromStdString(TimingPathsModel::_path_columns[section]);
192   return QVariant();
193 }
194 
resetModel()195 void TimingPathsModel::resetModel()
196 {
197   beginResetModel();
198   for (auto timing_path : timing_paths_)
199     delete timing_path;
200   timing_paths_.clear();
201   endResetModel();
202 }
203 
sort(int col_index,Qt::SortOrder sort_order)204 void TimingPathsModel::sort(int col_index, Qt::SortOrder sort_order)
205 {
206   beginResetModel();
207   (void) col_index;
208   if (sort_order == Qt::AscendingOrder)
209     std::stable_sort(timing_paths_.begin(),
210                      timing_paths_.end(),
211                      [](const TimingPath* path1, TimingPath* path2) {
212                        return path1->getStartClock() < path2->getStartClock();
213                      });
214   else
215     std::stable_sort(timing_paths_.begin(),
216                      timing_paths_.end(),
217                      [](const TimingPath* path1, TimingPath* path2) {
218                        return path1->getStartClock() > path2->getStartClock();
219                      });
220   endResetModel();
221 }
222 
populateModel(bool setup_hold,int path_count)223 void TimingPathsModel::populateModel(bool setup_hold, int path_count)
224 {
225   beginResetModel();
226   for (auto timing_path : timing_paths_)
227     delete timing_path;
228   timing_paths_.clear();
229   populatePaths(setup_hold, path_count);
230   endResetModel();
231 }
232 
populatePaths(bool get_max,int path_count,bool clockExpanded)233 bool TimingPathsModel::populatePaths(bool get_max,
234                                      int path_count,
235                                      bool clockExpanded)
236 {
237   // On lines of DataBaseHandler
238   QApplication::setOverrideCursor(Qt::WaitCursor);
239   sta::dbSta* sta_ = openroad_->getSta();
240   sta_->ensureGraph();
241   sta_->searchPreamble();
242 
243   auto sta_state = sta_->search();
244 
245   sta::PathEndSeq* path_ends
246       = sta_state->findPathEnds(  // from, thrus, to, unconstrained
247           nullptr,
248           nullptr,
249           nullptr,
250           false,
251           // corner, min_max,
252           sta_->findCorner("default"),
253           get_max ? sta::MinMaxAll::max() : sta::MinMaxAll::min(),
254           // group_count, endpoint_count, unique_pins
255           path_count,
256           path_count,
257           true,
258           -sta::INF,
259           sta::INF,  // slack_min, slack_max,
260           true,      // sort_by_slack
261           nullptr,   // group_names
262           // setup, hold, recovery, removal,
263           get_max,
264           !get_max,
265           false,
266           false,
267           // clk_gating_setup, clk_gating_hold
268           false,
269           false);
270 
271   bool first_path = true;
272   int path_index = 0;
273   for (auto& path_end : *path_ends) {
274     sta::PathExpanded* expanded = new sta::PathExpanded(path_end->path(), sta_);
275 
276     TimingPath* path = new TimingPath(path_index++);
277     sta::DcalcAnalysisPt* dcalc_ap
278         = path_end->path()->pathAnalysisPt(sta_)->dcalcAnalysisPt();
279 
280     path->setStartClock(path_end->sourceClkEdge(sta_)->clock()->name());
281     path->setEndClock(path_end->targetClk(sta_)->name());
282     path->setPathDelay(path_end->pathDelay() ? path_end->pathDelay()->delay()
283                                              : 0);
284     path->setSlack(path_end->slack(sta_));
285     path->setPathArrivalTime(path_end->dataArrivalTime(sta_));
286     path->setPathRequiredTime(path_end->requiredTime(sta_));
287     bool clockPropagated
288         = path_end->sourceClkEdge(sta_)->clock()->isPropagated();
289     if (!clockPropagated)
290       clockExpanded = false;
291     else
292       clockExpanded = true;
293     float arrival_prev_stage = 0;
294     float arrival_cur_stage = 0;
295     for (size_t i = 0; i < expanded->size(); i++) {
296       auto ref = expanded->path(i);
297       auto pin = ref->vertex(sta_)->pin();
298       auto slew = ref->slew(sta_);
299       float cap = 0.0;
300       if (sta_->network()->isDriver(pin)
301           && !(!clockExpanded && (sta_->network()->isCheckClk(pin) || !i))) {
302         sta::Parasitic* parasitic = nullptr;
303         sta::ArcDelayCalc* arc_delay_calc = sta_->arcDelayCalc();
304         if (arc_delay_calc)
305           parasitic = arc_delay_calc->findParasitic(
306               pin, ref->transition(sta_), dcalc_ap);
307         sta::GraphDelayCalc* graph_delay_calc = sta_->graphDelayCalc();
308         cap = graph_delay_calc->loadCap(
309             pin, parasitic, ref->transition(sta_), dcalc_ap);
310       }
311 
312       auto is_rising = ref->transition(sta_) == sta::RiseFall::rise();
313       auto arrival = ref->arrival(sta_);
314       auto path_ap = ref->pathAnalysisPt(sta_);
315       auto path_required = !first_path ? 0 : ref->required(sta_);
316       if (!path_required || sta::delayInf(path_required)) {
317         path_required = getRequiredTime(sta_, pin, is_rising, path_ap);
318       }
319       auto slack = !first_path ? path_required - arrival : ref->slack(sta_);
320       odb::dbITerm* term;
321       odb::dbBTerm* port;
322       sta_->getDbNetwork()->staToDb(pin, term, port);
323       odb::dbObject* pin_object = term;
324       if (term == nullptr)
325         pin_object = port;
326       arrival_cur_stage = arrival;
327       if (i == 0)
328         path->appendNode(TimingPathNode(pin_object,
329                                         is_rising,
330                                         arrival,
331                                         path_required,
332                                         0,
333                                         slack,
334                                         slew,
335                                         cap));
336       else {
337         path->appendNode(TimingPathNode(pin_object,
338                                         is_rising,
339                                         arrival,
340                                         path_required,
341                                         arrival_cur_stage - arrival_prev_stage,
342                                         slack,
343                                         slew,
344                                         cap));
345         arrival_prev_stage = arrival_cur_stage;
346       }
347       first_path = false;
348     }
349     timing_paths_.push_back(path);
350   }
351   QApplication::restoreOverrideCursor();
352   delete path_ends;
353   return true;
354 }
355 
getStartStageName() const356 std::string TimingPath::getStartStageName() const
357 {
358   auto node = getNodeAt(1);
359   return node.getNodeName();
360 }
361 
getEndStageName() const362 std::string TimingPath::getEndStageName() const
363 {
364   auto node = path_nodes_.back();
365   return node.getNodeName();
366 }
367 
getNodeName(bool include_master) const368 std::string TimingPathNode::getNodeName(bool include_master) const
369 {
370   if (pin_->getObjectType() == odb::dbObjectType::dbITermObj) {
371     odb::dbITerm* db_iterm = static_cast<odb::dbITerm*>(pin_);
372     return db_iterm->getInst()->getName() + "/"
373            + db_iterm->getMTerm()->getName()
374            + (include_master
375                   ? " (" + db_iterm->getInst()->getMaster()->getName() + ")"
376                   : "");
377   }
378   odb::dbBTerm* db_bterm = static_cast<odb::dbBTerm*>(pin_);
379   return db_bterm->getName();
380 }
381 
getNetName() const382 std::string TimingPathNode::getNetName() const
383 {
384   if (pin_->getObjectType() == odb::dbObjectType::dbITermObj) {
385     odb::dbITerm* db_iterm = static_cast<odb::dbITerm*>(pin_);
386     return db_iterm->getNet()->getName();
387   }
388   odb::dbBTerm* db_bterm = static_cast<odb::dbBTerm*>(pin_);
389   return db_bterm->getNet()->getName();
390 }
391 
rowCount(const QModelIndex & parent) const392 int TimingPathDetailModel::rowCount(const QModelIndex& parent) const
393 {
394   if (!path_)
395     return 0;
396   return path_->levelsCount();
397 }
398 
columnCount(const QModelIndex & parent) const399 int TimingPathDetailModel::columnCount(const QModelIndex& parent) const
400 {
401   return TimingPathDetailModel::_path_details_columns.size();
402 }
403 
data(const QModelIndex & index,int role) const404 QVariant TimingPathDetailModel::data(const QModelIndex& index, int role) const
405 {
406   const Column col_index = static_cast<Column>(index.column());
407   if (index.isValid() && role == Qt::TextAlignmentRole) {
408     switch (col_index) {
409       case Pin:
410         return Qt::AlignLeft;
411       case Time:
412       case Delay:
413       case Slew:
414       case Load:
415         return Qt::AlignRight;
416       case RiseFall:
417         return Qt::AlignCenter;
418     }
419   }
420 
421   if (!index.isValid() || role != Qt::DisplayRole || !path_) {
422     return QVariant();
423   }
424 
425   sta::dbSta* sta = ord::OpenRoad::openRoad()->getSta();
426   const auto time_units = sta->search()->units()->timeUnit();
427 
428   const int row_index = index.row();
429   if (row_index > path_->levelsCount())
430     return QVariant();
431   const auto node = path_->getNodeAt(row_index);
432   switch (col_index) {
433     case Pin:
434       return QString(node.getNodeName(/* include_master */ true).c_str());
435     case RiseFall:
436       return node.is_rising_ ? QString(up_arrow) : QString(down_arrow);
437     case Time:
438       return time_units->asString(node.arrival_);
439     case Delay:
440       return time_units->asString(node.delay_);
441     case Slew:
442       return time_units->asString(node.slew_);
443     case Load: {
444       if (node.load_ == 0)
445         return "";
446       const auto cap_units = sta->search()->units()->capacitanceUnit();
447       return cap_units->asString(node.load_);
448     }
449   }
450   return QVariant();
451 }
452 
headerData(int section,Qt::Orientation orientation,int role) const453 QVariant TimingPathDetailModel::headerData(int section,
454                                            Qt::Orientation orientation,
455                                            int role) const
456 {
457   if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
458     return QString::fromStdString(
459         TimingPathDetailModel::_path_details_columns.at(section));
460   }
461   return QVariant();
462 }
463 
populateModel(TimingPath * path)464 void TimingPathDetailModel::populateModel(TimingPath* path)
465 {
466   beginResetModel();
467   path_ = path;
468   endResetModel();
469 }
470 
TimingPathRenderer()471 TimingPathRenderer::TimingPathRenderer() : path_(nullptr)
472 {
473   TimingPathRenderer::path_inst_color_.a = 100;
474 }
475 
~TimingPathRenderer()476 TimingPathRenderer::~TimingPathRenderer()
477 {
478 }
479 
highlight(TimingPath * path)480 void TimingPathRenderer::highlight(TimingPath* path)
481 {
482   path_ = path;
483   highlight_node_ = -1;
484 }
485 
highlightNode(int node_idx)486 void TimingPathRenderer::highlightNode(int node_idx)
487 {
488   if (path_ && node_idx >= 0 && node_idx < path_->levelsCount())
489     highlight_node_ = node_idx;
490 }
491 
drawObjects(gui::Painter & painter)492 void TimingPathRenderer::drawObjects(gui::Painter& painter)
493 {
494   if (!path_)
495     return;
496   odb::dbObject* sink_node = nullptr;
497   odb::dbNet* net = nullptr;
498   int node_count = path_->levelsCount();
499   for (int i = node_count - 1; i >= 0; --i) {
500     auto node = path_->getNodeAt(i);
501     if (node.pin_->getObjectType() == odb::dbObjectType::dbITermObj) {
502       odb::dbITerm* db_iterm = static_cast<odb::dbITerm*>(node.pin_);
503       odb::dbInst* db_inst = db_iterm->getInst();
504       highlightInst(db_inst, painter, TimingPathRenderer::path_inst_color_);
505       auto io_dir = db_iterm->getIoType();
506       if (!sink_node
507           && (io_dir == odb::dbIoType::INPUT
508               || io_dir == odb::dbIoType::INOUT)) {
509         sink_node = db_iterm;
510         net = db_iterm->getNet();
511         continue;
512       } else if (sink_node) {
513         highlightNet(net, db_iterm /*source*/, sink_node, painter);
514         sink_node = nullptr;
515         net = nullptr;
516       }
517     } else {
518       odb::dbBTerm* bterm = static_cast<odb::dbBTerm*>(node.pin_);
519       highlightTerm(bterm, painter);
520       if (!sink_node
521           && (bterm->getIoType() == odb::dbIoType::OUTPUT
522               || bterm->getIoType() == odb::dbIoType::INOUT)) {
523         sink_node = bterm;
524         net = bterm->getNet();
525         continue;
526       } else if (sink_node) {
527         highlightNet(net, bterm, sink_node, painter);
528         sink_node = nullptr;
529         net = nullptr;
530       }
531     }
532   }
533   if (highlight_node_ >= 0 && highlight_node_ < node_count)
534     highlightStage(painter);
535 }
536 
highlightStage(gui::Painter & painter)537 void TimingPathRenderer::highlightStage(gui::Painter& painter)
538 {
539   if (!path_)
540     return;
541   odb::dbObject* sink_node = nullptr;
542   int src_x, src_y;
543   int dst_x, dst_y;
544   auto getSegmentEnds = [&](int node_idx, int& x, int& y) {
545     auto node = path_->getNodeAt(node_idx);
546     if (node.pin_->getObjectType() == odb::dbObjectType::dbITermObj) {
547       odb::dbITerm* db_iterm = static_cast<odb::dbITerm*>(node.pin_);
548       odb::dbInst* db_inst = db_iterm->getInst();
549       highlightInst(
550           db_inst, painter, TimingPathRenderer::inst_highlight_color_);
551       db_iterm->getAvgXY(&x, &y);
552       auto io_dir = db_iterm->getIoType();
553       if (io_dir == odb::dbIoType::INPUT || io_dir == odb::dbIoType::INOUT)
554         sink_node = db_iterm;
555     } else {
556       odb::dbBTerm* bterm = static_cast<odb::dbBTerm*>(node.pin_);
557       bterm->getFirstPinLocation(x, y);
558       auto io_dir = bterm->getIoType();
559       if (io_dir == odb::dbIoType::OUTPUT || io_dir == odb::dbIoType::INOUT)
560         sink_node = bterm;
561     }
562   };
563   getSegmentEnds(highlight_node_, src_x, src_y);
564   int nxt_stage = -1;
565   if ((sink_node == nullptr && highlight_node_ < (path_->levelsCount() - 1))
566       || (sink_node != nullptr && highlight_node_ == 0))
567     nxt_stage = highlight_node_ + 1;
568   else if (highlight_node_ != 0)
569     nxt_stage = highlight_node_ - 1;
570   if (nxt_stage != -1)
571     getSegmentEnds(nxt_stage, dst_x, dst_y);
572   odb::Point pt1(src_x, src_y);
573   odb::Point pt2(dst_x, dst_y);
574 
575   painter.setPen(TimingPathRenderer::inst_highlight_color_, true);
576   painter.drawLine(pt1, pt2);
577 }
578 // Color in the instances to make them more visible.
highlightInst(odb::dbInst * db_inst,gui::Painter & painter,const gui::Painter::Color & inst_color)579 void TimingPathRenderer::highlightInst(odb::dbInst* db_inst,
580                                        gui::Painter& painter,
581                                        const gui::Painter::Color& inst_color)
582 {
583   if (!path_)
584     return;
585   odb::dbBox* bbox = db_inst->getBBox();
586   odb::Rect rect;
587   bbox->getBox(rect);
588   painter.setBrush(inst_color);
589   painter.drawRect(rect);
590 }
591 
highlightTerm(odb::dbBTerm * term,gui::Painter & painter)592 void TimingPathRenderer::highlightTerm(odb::dbBTerm* term,
593                                        gui::Painter& painter)
594 {
595   if (!path_)
596     return;
597   odb::dbShape port_shape;
598   if (term->getFirstPin(port_shape)) {
599     odb::Rect rect;
600     port_shape.getBox(rect);
601     painter.setBrush(TimingPathRenderer::term_color_);
602     painter.drawRect(rect);
603   }
604 }
605 
highlightNet(odb::dbNet * net,odb::dbObject * source_node,odb::dbObject * sink_node,gui::Painter & painter)606 void TimingPathRenderer::highlightNet(odb::dbNet* net,
607                                       odb::dbObject* source_node,
608                                       odb::dbObject* sink_node,
609                                       gui::Painter& painter)
610 {
611   if (!path_)
612     return;
613   int src_x, src_y;
614   int dst_x, dst_y;
615   auto getSegmentEnd = [](odb::dbObject* node, int& x, int& y, bool& clk_node) {
616     if (node->getObjectType() == odb::dbObjectType::dbITermObj) {
617       odb::dbITerm* db_iterm = static_cast<odb::dbITerm*>(node);
618       db_iterm->getAvgXY(&x, &y);
619     } else {
620       odb::dbBTerm* bterm = static_cast<odb::dbBTerm*>(node);
621       bterm->getFirstPinLocation(x, y);
622       sta::dbSta* sta = ord::OpenRoad::openRoad()->getSta();
623       auto sta_term = sta->getDbNetwork()->dbToSta(bterm);
624       clk_node = sta->isClock(sta_term);
625     }
626   };
627   // SigType is not populated properly in OpenDB
628   bool clk_node = false;
629 
630   getSegmentEnd(source_node, src_x, src_y, clk_node);
631   getSegmentEnd(sink_node, dst_x, dst_y, clk_node);
632 
633   odb::Point pt1(src_x, src_y);
634   odb::Point pt2(dst_x, dst_y);
635 
636   gui::Painter::Color wire_color = clk_node == true
637                                        ? TimingPathRenderer::clock_color_
638                                        : TimingPathRenderer::signal_color_;
639   painter.setPen(wire_color, true);
640   painter.drawLine(pt1, pt2);
641 }
642 
643 }  // namespace gui
644