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