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 <QDesktopWidget>
34 #include <QMenu>
35 #include <QMenuBar>
36 #include <QSettings>
37 #include <QStatusBar>
38 #include <QToolButton>
39 #include <QWidgetAction>
40 #include <map>
41 #include <vector>
42
43 #include "displayControls.h"
44 #include "inspector.h"
45 #include "layoutViewer.h"
46 #include "mainWindow.h"
47 #include "ord/OpenRoad.hh"
48 #include "scriptWidget.h"
49 #include "selectHighlightWindow.h"
50 #include "staGui.h"
51
52 namespace gui {
53
MainWindow(QWidget * parent)54 MainWindow::MainWindow(QWidget* parent)
55 : QMainWindow(parent),
56 db_(nullptr),
57 controls_(new DisplayControls(this)),
58 inspector_(new Inspector(selected_, this)),
59 viewer_(new LayoutViewer(
60 controls_,
61 selected_,
62 highlighted_,
63 rulers_,
64 [this](const std::any& object) { return makeSelected(object); },
65 this)),
66 selection_browser_(
67 new SelectHighlightWindow(selected_, highlighted_, this)),
68 scroll_(new LayoutScroll(viewer_, this)),
69 script_(new ScriptWidget(this))
70 {
71 // Size and position the window
72 QSize size = QDesktopWidget().availableGeometry(this).size();
73 resize(size * 0.8);
74 move(size.width() * 0.1, size.height() * 0.1);
75
76 find_dialog_ = new FindObjectDialog(this);
77 timing_dialog_ = new TimingDebugDialog(this);
78
79 QFont font("Monospace");
80 font.setStyleHint(QFont::Monospace);
81 script_->setFont(font);
82
83 setCentralWidget(scroll_);
84 addDockWidget(Qt::BottomDockWidgetArea, script_);
85 addDockWidget(Qt::LeftDockWidgetArea, controls_);
86 addDockWidget(Qt::RightDockWidgetArea, inspector_);
87 addDockWidget(Qt::BottomDockWidgetArea, selection_browser_);
88
89 tabifyDockWidget(selection_browser_, script_);
90 selection_browser_->hide();
91
92 // Hook up all the signals/slots
93 connect(script_, SIGNAL(tclExiting()), this, SIGNAL(exit()));
94 connect(script_, SIGNAL(commandExecuted(int)), viewer_, SLOT(update()));
95 connect(this,
96 SIGNAL(designLoaded(odb::dbBlock*)),
97 viewer_,
98 SLOT(designLoaded(odb::dbBlock*)));
99 connect(this, SIGNAL(redraw()), viewer_, SLOT(repaint()));
100 connect(this,
101 SIGNAL(designLoaded(odb::dbBlock*)),
102 controls_,
103 SLOT(designLoaded(odb::dbBlock*)));
104
105 connect(this, SIGNAL(pause()), script_, SLOT(pause()));
106 connect(controls_, SIGNAL(changed()), viewer_, SLOT(update()));
107 connect(viewer_,
108 SIGNAL(location(qreal, qreal)),
109 this,
110 SLOT(setLocation(qreal, qreal)));
111 connect(viewer_,
112 SIGNAL(selected(const Selected&, bool)),
113 this,
114 SLOT(setSelected(const Selected&, bool)));
115 connect(viewer_,
116 SIGNAL(addSelected(const Selected&)),
117 this,
118 SLOT(addSelected(const Selected&)));
119
120 connect(viewer_,
121 SIGNAL(addRuler(int, int, int, int)),
122 this,
123 SLOT(addRuler(int, int, int, int)));
124
125 connect(this, SIGNAL(selectionChanged()), viewer_, SLOT(update()));
126 connect(this, SIGNAL(highlightChanged()), viewer_, SLOT(update()));
127 connect(this, SIGNAL(rulersChanged()), viewer_, SLOT(update()));
128
129 connect(inspector_,
130 SIGNAL(selected(const Selected&, bool)),
131 this,
132 SLOT(setSelected(const Selected&, bool)));
133 connect(this, SIGNAL(selectionChanged()), inspector_, SLOT(update()));
134
135 connect(this,
136 SIGNAL(selectionChanged()),
137 selection_browser_,
138 SLOT(updateSelectionModel()));
139 connect(this,
140 SIGNAL(highlightChanged()),
141 selection_browser_,
142 SLOT(updateHighlightModel()));
143
144 connect(selection_browser_,
145 &SelectHighlightWindow::clearAllSelections,
146 this,
__anon1fc8aabc0202() 147 [this]() { this->setSelected(Selected(), false); });
148 connect(selection_browser_,
149 &SelectHighlightWindow::clearAllHighlights,
150 this,
__anon1fc8aabc0302() 151 [this]() { this->clearHighlighted(); });
152 connect(selection_browser_,
153 SIGNAL(clearSelectedItems(const QList<const Selected*>&)),
154 this,
155 SLOT(removeFromSelected(const QList<const Selected*>&)));
156
157 connect(selection_browser_,
158 SIGNAL(zoomInToItems(const QList<const Selected*>&)),
159 this,
160 SLOT(zoomInToItems(const QList<const Selected*>&)));
161
162 connect(selection_browser_,
163 SIGNAL(clearHighlightedItems(const QList<const Selected*>&)),
164 this,
165 SLOT(removeFromHighlighted(const QList<const Selected*>&)));
166
167 connect(selection_browser_,
168 SIGNAL(highlightSelectedItemsSig(const QList<const Selected*>&, int)),
169 this,
170 SLOT(updateHighlightedSet(const QList<const Selected*>&, int)));
171
172 connect(timing_dialog_,
173 SIGNAL(highlightTimingPath(TimingPath*)),
174 viewer_,
175 SLOT(update()));
176
177 createActions();
178 createToolbars();
179 createMenus();
180 createStatusBar();
181
182 // Restore the settings (if none this is a no-op)
183 QSettings settings("OpenRoad Project", "openroad");
184 settings.beginGroup("main");
185 restoreGeometry(settings.value("geometry").toByteArray());
186 restoreState(settings.value("state").toByteArray());
187 script_->readSettings(&settings);
188 controls_->readSettings(&settings);
189 settings.endGroup();
190 }
191
createStatusBar()192 void MainWindow::createStatusBar()
193 {
194 location_ = new QLabel();
195 statusBar()->addPermanentWidget(location_);
196 }
197
getBlock()198 odb::dbBlock* MainWindow::getBlock()
199 {
200 if (!db_) {
201 return nullptr;
202 }
203
204 auto chip = db_->getChip();
205 if (!chip) {
206 return nullptr;
207 }
208
209 auto top_block = chip->getBlock();
210 return top_block;
211 }
212
createActions()213 void MainWindow::createActions()
214 {
215 exit_ = new QAction("Exit", this);
216
217 fit_ = new QAction("Fit", this);
218 fit_->setShortcut(QString("F"));
219
220 find_ = new QAction("Find", this);
221 find_->setShortcut(QString("Ctrl+F"));
222 zoom_in_ = new QAction("Zoom in", this);
223 zoom_in_->setShortcut(QString("Z"));
224
225 zoom_out_ = new QAction("Zoom out", this);
226 zoom_out_->setShortcut(QString("Shift+Z"));
227
228 inspect_ = new QAction("Inspect", this);
229 inspect_->setShortcut(QString("q"));
230
231 timing_debug_ = new QAction("Timing ...", this);
232 timing_debug_->setShortcut(QString("Ctrl+T"));
233
234 congestion_setup_ = new QAction("Congestion Setup...", this);
235
236 connect(congestion_setup_,
237 SIGNAL(triggered()),
238 controls_,
239 SLOT(showCongestionSetup()));
240
241 connect(exit_, SIGNAL(triggered()), this, SIGNAL(exit()));
242 connect(fit_, SIGNAL(triggered()), viewer_, SLOT(fit()));
243 connect(zoom_in_, SIGNAL(triggered()), scroll_, SLOT(zoomIn()));
244 connect(zoom_out_, SIGNAL(triggered()), scroll_, SLOT(zoomOut()));
245 connect(find_, SIGNAL(triggered()), this, SLOT(showFindDialog()));
246 connect(timing_debug_, SIGNAL(triggered()), this, SLOT(showTimingDialog()));
247 connect(inspect_, SIGNAL(triggered()), inspector_, SLOT(show()));
248 }
249
createMenus()250 void MainWindow::createMenus()
251 {
252 file_menu_ = menuBar()->addMenu("&File");
253 file_menu_->addAction(exit_);
254
255 view_menu_ = menuBar()->addMenu("&View");
256 view_menu_->addAction(fit_);
257 view_menu_->addAction(find_);
258 view_menu_->addAction(zoom_in_);
259 view_menu_->addAction(zoom_out_);
260 view_menu_->addAction(inspect_);
261 view_menu_->addAction(timing_debug_);
262
263 tools_menu_ = menuBar()->addMenu("&Tools");
264 tools_menu_->addAction(congestion_setup_);
265 tools_menu_->addAction(timing_debug_);
266
267 windows_menu_ = menuBar()->addMenu("&Windows");
268 windows_menu_->addAction(controls_->toggleViewAction());
269 windows_menu_->addAction(inspector_->toggleViewAction());
270 windows_menu_->addAction(script_->toggleViewAction());
271 windows_menu_->addAction(selection_browser_->toggleViewAction());
272 windows_menu_->addAction(view_tool_bar_->toggleViewAction());
273 selection_browser_->setVisible(false);
274 }
275
createToolbars()276 void MainWindow::createToolbars()
277 {
278 view_tool_bar_ = addToolBar("Toolbar");
279 view_tool_bar_->addAction(fit_);
280 view_tool_bar_->addAction(find_);
281 view_tool_bar_->addAction(congestion_setup_);
282 view_tool_bar_->addAction(inspect_);
283 view_tool_bar_->addAction(timing_debug_);
284
285 view_tool_bar_->setObjectName("view_toolbar"); // for settings
286 }
287
setDb(odb::dbDatabase * db)288 void MainWindow::setDb(odb::dbDatabase* db)
289 {
290 db_ = db;
291 controls_->setDb(db);
292 viewer_->setDb(db);
293 selection_browser_->setDb(db);
294 }
295
setLocation(qreal x,qreal y)296 void MainWindow::setLocation(qreal x, qreal y)
297 {
298 location_->setText(QString("%1, %2").arg(x, 0, 'f', 5).arg(y, 0, 'f', 5));
299 }
300
addSelected(const Selected & selection)301 void MainWindow::addSelected(const Selected& selection)
302 {
303 if (selection) {
304 selected_.emplace(selection);
305 }
306 status(selection ? selection.getName() : "");
307 emit selectionChanged();
308 }
309
addSelected(const SelectionSet & selections)310 void MainWindow::addSelected(const SelectionSet& selections)
311 {
312 selected_.insert(selections.begin(), selections.end());
313 status(std::string("Added ") + std::to_string(selections.size()));
314 emit selectionChanged();
315 }
316
setSelected(const Selected & selection,bool show_connectivity)317 void MainWindow::setSelected(const Selected& selection, bool show_connectivity)
318 {
319 selected_.clear();
320 addSelected(selection);
321 if (show_connectivity)
322 selectHighlightConnectedNets(true, true, true, false);
323 }
324
addHighlighted(const SelectionSet & highlights,int highlight_group)325 void MainWindow::addHighlighted(const SelectionSet& highlights,
326 int highlight_group)
327 {
328 if (highlight_group >= 7)
329 return;
330 highlighted_[highlight_group].insert(highlights.begin(), highlights.end());
331 emit highlightChanged();
332 }
333
addRuler(int x0,int y0,int x1,int y1)334 void MainWindow::addRuler(int x0, int y0, int x1, int y1)
335 {
336 QLine ruler(QPoint(x0, y0), QPoint(x1, y1));
337 rulers_.push_back(ruler);
338 emit rulersChanged();
339 }
340
updateHighlightedSet(const QList<const Selected * > & items,int highlight_group)341 void MainWindow::updateHighlightedSet(const QList<const Selected*>& items,
342 int highlight_group)
343 {
344 if (highlight_group >= 7)
345 return;
346 for (auto item : items) {
347 highlighted_[highlight_group].insert(*item);
348 }
349 emit highlightChanged();
350 }
351
clearHighlighted(int highlight_group)352 void MainWindow::clearHighlighted(int highlight_group)
353 {
354 if (highlighted_.empty())
355 return;
356 int num_items_cleared = 0;
357 if (highlight_group < 0) {
358 for (auto& highlighted_set : highlighted_) {
359 num_items_cleared += highlighted_set.size();
360 highlighted_set.clear();
361 }
362 } else if (highlight_group < 7) {
363 num_items_cleared += highlighted_[highlight_group].size();
364 highlighted_[highlight_group].clear();
365 }
366 if (num_items_cleared > 0)
367 emit highlightChanged();
368 }
369
clearRulers()370 void MainWindow::clearRulers()
371 {
372 if (rulers_.empty())
373 return;
374 rulers_.clear();
375 emit rulersChanged();
376 }
377
removeFromSelected(const QList<const Selected * > & items)378 void MainWindow::removeFromSelected(const QList<const Selected*>& items)
379 {
380 if (items.empty())
381 return;
382 for (auto& item : items) {
383 selected_.erase(*item);
384 }
385 emit selectionChanged();
386 }
387
removeFromHighlighted(const QList<const Selected * > & items,int highlight_group)388 void MainWindow::removeFromHighlighted(const QList<const Selected*>& items,
389 int highlight_group)
390 {
391 if (items.empty())
392 return;
393 if (highlight_group < 0) {
394 for (auto& item : items) {
395 for (auto& highlighted_set : highlighted_)
396 highlighted_set.erase(*item);
397 }
398 } else if (highlight_group < 7) {
399 for (auto& item : items) {
400 highlighted_[highlight_group].erase(*item);
401 }
402 }
403
404 emit highlightChanged();
405 }
406
zoomTo(const odb::Rect & rect_dbu)407 void MainWindow::zoomTo(const odb::Rect& rect_dbu)
408 {
409 viewer_->zoomTo(rect_dbu);
410 }
411
zoomInToItems(const QList<const Selected * > & items)412 void MainWindow::zoomInToItems(const QList<const Selected*>& items)
413 {
414 if (items.empty())
415 return;
416 odb::Rect items_bbox;
417 items_bbox.mergeInit();
418 int merge_cnt = 0;
419 for (auto& item : items) {
420 odb::Rect item_bbox;
421 if (item->getBBox(item_bbox)) {
422 merge_cnt++;
423 items_bbox.merge(item_bbox);
424 }
425 }
426 if (merge_cnt == 0)
427 return;
428 zoomTo(items_bbox);
429 }
430
status(const std::string & message)431 void MainWindow::status(const std::string& message)
432 {
433 statusBar()->showMessage(QString::fromStdString(message));
434 }
435
showFindDialog()436 void MainWindow::showFindDialog()
437 {
438 if (getBlock() == nullptr)
439 return;
440 find_dialog_->exec();
441 }
442
showTimingDialog()443 void MainWindow::showTimingDialog()
444 {
445 if (timing_dialog_->populateTimingPaths(nullptr)) {
446 timing_dialog_->show();
447 Gui::get()->registerRenderer(timing_dialog_->getTimingRenderer());
448 }
449 }
450
anyObjectInSet(bool selection_set,odb::dbObjectType obj_type)451 bool MainWindow::anyObjectInSet(bool selection_set, odb::dbObjectType obj_type)
452 {
453 if (selection_set) {
454 for (auto& selected_obj : selected_) {
455 if ((selected_obj.isInst() && obj_type == odb::dbInstObj)
456 || (selected_obj.isNet() && obj_type == odb::dbNetObj))
457 return true;
458 }
459 return false;
460 } else {
461 for (auto& highlight_set : highlighted_) {
462 for (auto& selected_obj : highlight_set) {
463 if (selected_obj.isInst() && obj_type == odb::dbInstObj)
464 return true;
465 if (selected_obj.isNet() && obj_type == odb::dbNetObj)
466 return true;
467 }
468 }
469 }
470 return false;
471 }
472
selectHighlightConnectedInsts(bool select_flag,int highlight_group)473 void MainWindow::selectHighlightConnectedInsts(bool select_flag,
474 int highlight_group)
475 {
476 SelectionSet connected_insts;
477 for (auto& sel_obj : selected_) {
478 if (sel_obj.isNet()) {
479 auto net_obj = std::any_cast<odb::dbNet*>(sel_obj.getObject());
480 for (auto inst_term : net_obj->getITerms()) {
481 connected_insts.insert(makeSelected(inst_term));
482 }
483 }
484 }
485 if (connected_insts.empty())
486 return;
487 if (select_flag)
488 addSelected(connected_insts);
489 else
490 addHighlighted(connected_insts, highlight_group);
491 }
492
selectHighlightConnectedNets(bool select_flag,bool output,bool input,int highlight_group)493 void MainWindow::selectHighlightConnectedNets(bool select_flag,
494 bool output,
495 bool input,
496 int highlight_group)
497 {
498 SelectionSet connected_nets;
499 for (auto sel_obj : selected_) {
500 if (sel_obj.isInst()) {
501 auto inst_obj = std::any_cast<odb::dbInst*>(sel_obj.getObject());
502 for (auto inst_term : inst_obj->getITerms()) {
503 if (inst_term->getNet() == nullptr
504 || inst_term->getNet()->getSigType() != odb::dbSigType::SIGNAL)
505 continue;
506 auto inst_term_dir = inst_term->getIoType();
507
508 if (output
509 && (inst_term_dir == odb::dbIoType::OUTPUT
510 || inst_term_dir == odb::dbIoType::INOUT))
511 connected_nets.insert(makeSelected(inst_term->getNet()));
512 if (input
513 && (inst_term_dir == odb::dbIoType::INPUT
514 || inst_term_dir == odb::dbIoType::INOUT))
515 connected_nets.insert(makeSelected(inst_term->getNet(), inst_term));
516 }
517 }
518 }
519 if (connected_nets.empty())
520 return;
521 if (select_flag)
522 addSelected(connected_nets);
523 else
524 addHighlighted(connected_nets, highlight_group);
525 }
526
saveSettings()527 void MainWindow::saveSettings()
528 {
529 QSettings settings("OpenRoad Project", "openroad");
530 settings.beginGroup("main");
531 settings.setValue("geometry", saveGeometry());
532 settings.setValue("state", saveState());
533 script_->writeSettings(&settings);
534 controls_->writeSettings(&settings);
535 settings.endGroup();
536 }
537
postReadLef(odb::dbTech * tech,odb::dbLib * library)538 void MainWindow::postReadLef(odb::dbTech* tech, odb::dbLib* library)
539 {
540 // We don't process this until we have a design to show
541 }
542
postReadDef(odb::dbBlock * block)543 void MainWindow::postReadDef(odb::dbBlock* block)
544 {
545 congestion_setup_->setEnabled(true);
546 emit designLoaded(block);
547 }
548
postReadDb(odb::dbDatabase * db)549 void MainWindow::postReadDb(odb::dbDatabase* db)
550 {
551 auto chip = db->getChip();
552 if (chip == nullptr) {
553 return;
554 }
555 auto block = chip->getBlock();
556 if (block == nullptr) {
557 return;
558 }
559
560 congestion_setup_->setEnabled(true);
561 emit designLoaded(block);
562 }
563
setLogger(utl::Logger * logger)564 void MainWindow::setLogger(utl::Logger* logger)
565 {
566 script_->setLogger(logger);
567 viewer_->setLogger(logger);
568 }
569
fit()570 void MainWindow::fit()
571 {
572 fit_->trigger();
573 }
574
makeSelected(std::any object,void * additional_data)575 Selected MainWindow::makeSelected(std::any object, void* additional_data)
576 {
577 if (!object.has_value()) {
578 return Selected();
579 }
580
581 auto it = descriptors_.find(object.type());
582 if (it != descriptors_.end()) {
583 return it->second->makeSelected(object, additional_data);
584 } else {
585 return Selected(); // FIXME: null descriptor
586 }
587 }
588
registerDescriptor(const std::type_info & type,const Descriptor * descriptor)589 void MainWindow::registerDescriptor(const std::type_info& type,
590 const Descriptor* descriptor)
591 {
592 descriptors_[type] = descriptor;
593 }
594
595 } // namespace gui
596