1 //////////////////////////////////////////////////////////////////////////////
2 // BSD 3-Clause License
3 //
4 // Copyright (c) 2019, The Regents of the University of California
5 // All rights reserved.
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions are met:
9 //
10 // * Redistributions of source code must retain the above copyright notice, this
11 //   list of conditions and the following disclaimer.
12 //
13 // * Redistributions in binary form must reproduce the above copyright notice,
14 //   this list of conditions and the following disclaimer in the documentation
15 //   and/or other materials provided with the distribution.
16 //
17 // * Neither the name of the copyright holder nor the names of its
18 //   contributors may be used to endorse or promote products derived from
19 //   this software without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 // POSSIBILITY OF SUCH DAMAGE.
32 
33 #include "timingDebugDialog.h"
34 
35 #include <QApplication>
36 #include <QClipboard>
37 #include <QDebug>
38 #include <QGuiApplication>
39 #include <QMessageBox>
40 #include <QSortFilterProxyModel>
41 
42 #include "db_sta/dbSta.hh"
43 #include "staGui.h"
44 
45 namespace gui {
TimingDebugDialog(QWidget * parent)46 TimingDebugDialog::TimingDebugDialog(QWidget* parent)
47     : QDialog(parent),
48       timing_paths_model_(nullptr),
49       path_details_model_(new TimingPathDetailModel()),
50       path_renderer_(new TimingPathRenderer()),
51       dbchange_listener_(new GuiDBChangeListener),
52       timing_report_dlg_(new TimingReportDialog()),
53       focus_view_(timingPathTableView)
54 {
55   setupUi(this);
56   timingPathTableView->horizontalHeader()->setSectionResizeMode(
57       QHeaderView::Interactive);
58   pathDetailsTableView->horizontalHeader()->setSectionResizeMode(
59       QHeaderView::Interactive);
60 
61   connect(timingPathTableView->horizontalHeader(),
62           SIGNAL(sectionClicked(int)),
63           this,
64           SLOT(timingPathsViewCustomSort(int)));
65   connect(pathDetailsTableView,
66           SIGNAL(clicked(const QModelIndex&)),
67           this,
68           SLOT(highlightPathStage(const QModelIndex&)));
69 
70   connect(findObjectEdit,
71           SIGNAL(returnPressed()),
72           this,
73           SLOT(findNodeInPathDetails()));
74 
75   connect(
76       pathIdSpinBox, SIGNAL(valueChanged(int)), this, SLOT(showPathIndex(int)));
77 
78   connect(dbchange_listener_,
79           SIGNAL(dbUpdated(QString, std::vector<odb::dbObject*>)),
80           this,
81           SLOT(handleDbChange(QString, std::vector<odb::dbObject*>)));
82   connect(
83       timingReportBtn, SIGNAL(clicked()), this, SLOT(showTimingReportDialog()));
84   pathIdSpinBox->setMaximum(0);
85   pathIdSpinBox->setMinimum(0);
86 
87   auto close_btn = buttonBox->button(QDialogButtonBox::Close);
88   if (close_btn)
89     close_btn->setAutoDefault(false);
90 }
91 
~TimingDebugDialog()92 TimingDebugDialog::~TimingDebugDialog()
93 {
94   // TBD
95 }
96 
accept()97 void TimingDebugDialog::accept()
98 {
99   QDialog::accept();
100 }
101 
reject()102 void TimingDebugDialog::reject()
103 {
104   QDialog::reject();
105   path_renderer_->highlight(nullptr);
106   Gui::get()->unregisterRenderer(path_renderer_);
107 }
108 
populateTimingPaths(odb::dbBlock * block)109 bool TimingDebugDialog::populateTimingPaths(odb::dbBlock* block)
110 {
111   if (timing_paths_model_ != nullptr)
112     return true;
113   bool setup_hold = true;
114   int path_count;
115   timing_report_dlg_->setupRadioButton->setChecked(setup_hold);
116   if (timing_report_dlg_->exec() == QDialog::Accepted) {
117     setup_hold = timing_report_dlg_->isSetupAnalysis();
118     path_count = timing_report_dlg_->getPathCount();
119   } else {
120     return false;
121   }
122   timing_paths_model_ = new TimingPathsModel(setup_hold, path_count);
123   timingPathTableView->setModel(timing_paths_model_);
124   timingPathTableView->resizeColumnsToContents();
125   timingPathTableView->horizontalHeader()->setSectionResizeMode(
126       timing_paths_model_->columnCount() - 1, QHeaderView::Stretch);
127 
128   auto selection_model = timingPathTableView->selectionModel();
129   connect(
130       selection_model,
131       SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
132       this,
133       SLOT(selectedRowChanged(const QItemSelection&, const QItemSelection&)));
134   pathIdSpinBox->setMaximum(timing_paths_model_->rowCount() - 1);
135   pathIdSpinBox->setMinimum(0);
136 
137   return true;
138 }
139 
keyPressEvent(QKeyEvent * key_event)140 void TimingDebugDialog::keyPressEvent(QKeyEvent* key_event)
141 {
142   if (key_event->matches(QKeySequence::Copy)) {
143     copy();
144     key_event->accept();
145   }
146 }
147 
showPathDetails(const QModelIndex & index)148 void TimingDebugDialog::showPathDetails(const QModelIndex& index)
149 {
150   if (!index.isValid() || timing_paths_model_ == nullptr)
151     return;
152   auto path = timing_paths_model_->getPathAt(index.row());
153   path_details_model_->populateModel(path);
154   pathDetailsTableView->setModel(path_details_model_);
155   pathDetailsTableView->resizeColumnsToContents();
156   pathDetailsTableView->horizontalHeader()->setSectionResizeMode(
157       0, QHeaderView::Stretch);
158 
159   path_renderer_->highlight(path);
160   emit highlightTimingPath(path);
161 
162   auto selection_model = pathDetailsTableView->selectionModel();
163   connect(
164       selection_model,
165       SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
166       this,
167       SLOT(selectedDetailRowChanged(const QItemSelection&,
168                                     const QItemSelection&)));
169 
170   pathIdSpinBox->setValue(index.row());
171 }
172 
highlightPathStage(const QModelIndex & index)173 void TimingDebugDialog::highlightPathStage(const QModelIndex& index)
174 {
175   if (!index.isValid() || timing_paths_model_ == nullptr) {
176     return;
177   }
178   path_renderer_->highlightNode(index.row());
179   emit highlightTimingPath(path_renderer_->getPathToRender());
180 }
181 
timingPathsViewCustomSort(int col_index)182 void TimingDebugDialog::timingPathsViewCustomSort(int col_index)
183 {
184   if (col_index != 1)
185     return;
186   auto sort_order
187       = timingPathTableView->horizontalHeader()->sortIndicatorOrder()
188                 == Qt::AscendingOrder
189             ? Qt::DescendingOrder
190             : Qt::AscendingOrder;
191   timing_paths_model_->sort(col_index, sort_order);
192 }
193 
findNodeInPathDetails()194 void TimingDebugDialog::findNodeInPathDetails()
195 {
196   auto search_node = findObjectEdit->text();
197   QSortFilterProxyModel proxy;
198   proxy.setSourceModel(path_details_model_);
199   proxy.setFilterKeyColumn(0);
200   proxy.setFilterFixedString(search_node);
201   QModelIndex match_index = proxy.mapToSource(proxy.index(0, 0));
202   if (match_index.isValid()) {
203     pathDetailsTableView->selectRow(match_index.row());
204     highlightPathStage(match_index);
205   } else {
206     QMessageBox::information(
207         this, "Node Search : ", search_node + " Match not found!");
208   }
209 }
210 
showPathIndex(int path_idx)211 void TimingDebugDialog::showPathIndex(int path_idx)
212 {
213   if (timingPathTableView->model() == nullptr)
214     return;
215   QModelIndex new_index = timingPathTableView->model()->index(path_idx, 0);
216   timingPathTableView->selectRow(new_index.row());
217   showPathDetails(new_index);
218 }
219 
showTimingReportDialog()220 void TimingDebugDialog::showTimingReportDialog()
221 {
222   bool setup_hold = true;
223   int path_count = 100;
224   if (timing_report_dlg_->exec() == QDialog::Accepted) {
225     setup_hold = timing_report_dlg_->isSetupAnalysis();
226     path_count = timing_report_dlg_->getPathCount();
227     path_details_model_->populateModel(nullptr);
228     path_renderer_->highlight(nullptr);
229     Gui::get()->unregisterRenderer(path_renderer_);
230     timing_paths_model_->populateModel(setup_hold, path_count);
231     Gui::get()->registerRenderer(path_renderer_);
232     pathIdSpinBox->setMaximum(timing_paths_model_->rowCount() - 1);
233     pathIdSpinBox->setMinimum(0);
234     pathIdSpinBox->setValue(0);
235 
236     auto selection_model = pathDetailsTableView->selectionModel();
237     if (selection_model) {
238       connect(selection_model,
239               SIGNAL(selectionChanged(const QItemSelection&,
240                                       const QItemSelection&)),
241               this,
242               SLOT(selectedDetailRowChanged(const QItemSelection&,
243                                             const QItemSelection&)));
244     }
245     timingPathTableView->resizeColumnsToContents();
246   }
247 }
248 
selectedRowChanged(const QItemSelection & selected_row,const QItemSelection & deselected_row)249 void TimingDebugDialog::selectedRowChanged(const QItemSelection& selected_row,
250                                            const QItemSelection& deselected_row)
251 {
252   auto sel_indices = selected_row.indexes();
253   if (sel_indices.isEmpty())
254     return;
255   auto top_sel_index = sel_indices.first();
256   showPathDetails(top_sel_index);
257   focus_view_ = timingPathTableView;
258 }
259 
selectedDetailRowChanged(const QItemSelection & selected_row,const QItemSelection & deselected_row)260 void TimingDebugDialog::selectedDetailRowChanged(
261     const QItemSelection& selected_row,
262     const QItemSelection& deselected_row)
263 {
264   auto sel_indices = selected_row.indexes();
265   if (sel_indices.isEmpty())
266     return;
267   auto top_sel_index = sel_indices.first();
268   highlightPathStage(top_sel_index);
269   focus_view_ = pathDetailsTableView;
270 }
271 
copy()272 void TimingDebugDialog::copy()
273 {
274   QItemSelectionModel* selection = focus_view_->selectionModel();
275   QModelIndexList indexes = selection->selectedIndexes();
276 
277   if (indexes.size() < 1)
278     return;
279   auto sel_index = indexes.first();
280   if (focus_view_ == timingPathTableView) {
281     auto src_index = sel_index.sibling(sel_index.row(), 5);
282     auto sink_index = sel_index.sibling(sel_index.row(), 6);
283     auto src_node
284         = selection->model()->data(src_index, Qt::DisplayRole).toString();
285     auto sink_node
286         = selection->model()->data(sink_index, Qt::DisplayRole).toString();
287     QString sel_text = src_node + " -> " + sink_node;
288     QGuiApplication::clipboard()->setText(sel_text);
289   } else {
290     auto sibling_index = sel_index.sibling(sel_index.row(), 0);
291     auto sel_text
292         = selection->model()->data(sibling_index, Qt::DisplayRole).toString();
293     QGuiApplication::clipboard()->setText(sel_text);
294   }
295 }
296 
handleDbChange(QString change_type,std::vector<odb::dbObject * > objects)297 void TimingDebugDialog::handleDbChange(QString change_type,
298                                        std::vector<odb::dbObject*> objects)
299 {
300   path_details_model_->populateModel(nullptr);
301   timing_paths_model_->resetModel();
302   timingPathTableView->resizeColumnsToContents();
303   timingPathTableView->horizontalHeader()->setSectionResizeMode(
304       timing_paths_model_->columnCount() - 1, QHeaderView::Stretch);
305   timingPathTableView->resizeColumnsToContents();
306 }
307 
308 }  // namespace gui
309