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