diff --git a/src/OpenRoad.cc b/src/OpenRoad.cc index eef476375e0..3a2f924c32d 100644 --- a/src/OpenRoad.cc +++ b/src/OpenRoad.cc @@ -563,8 +563,8 @@ void OpenRoad::readDb(const char* filename, bool hierarchy) void OpenRoad::readDb(std::istream& stream) { if (db_->getChip() && db_->getChip()->getBlock()) { - logger_->error( - ORD, 47, "You can't load a new db file as the db is already populated"); + db_->triggerPreDbClear(); + db_->clear(); } stream.exceptions(std::ifstream::failbit | std::ifstream::badbit diff --git a/src/dbSta/include/db_sta/dbSta.hh b/src/dbSta/include/db_sta/dbSta.hh index 2c66a95a89c..43ef6b9502f 100644 --- a/src/dbSta/include/db_sta/dbSta.hh +++ b/src/dbSta/include/db_sta/dbSta.hh @@ -163,6 +163,7 @@ class dbSta : public Sta, public odb::dbDatabaseObserver void postReadDef(odb::dbBlock* block) override; void postReadDb(odb::dbDatabase* db) override; void postRead3Dbx(odb::dbChip* chip) override; + void preDbClear(odb::dbDatabase* db) override; // Find clock nets connected by combinational gates from the clock roots. std::set findClkNets(); diff --git a/src/dbSta/src/dbNetwork.cc b/src/dbSta/src/dbNetwork.cc index 763b4f1a956..b09b74189d0 100644 --- a/src/dbSta/src/dbNetwork.cc +++ b/src/dbSta/src/dbNetwork.cc @@ -704,6 +704,8 @@ void dbNetwork::clear() { ConcreteNetwork::clear(); db_ = nullptr; + block_ = nullptr; + top_cell_ = nullptr; } Instance* dbNetwork::topInstance() const diff --git a/src/dbSta/src/dbSta.cc b/src/dbSta/src/dbSta.cc index 6577b50111e..edfdbe0bf53 100644 --- a/src/dbSta/src/dbSta.cc +++ b/src/dbSta/src/dbSta.cc @@ -323,6 +323,27 @@ void dbSta::postReadDb(odb::dbDatabase* db) } } +void dbSta::preDbClear(odb::dbDatabase* db) +{ + db_cbk_->removeOwner(); + // Clear staCell pointers on all masters before freeing the network. + // The GUI render thread may still be accessing these concurrently. + for (odb::dbLib* lib : db->getLibs()) { + for (odb::dbMaster* master : lib->getMasters()) { + master->staSetCell(nullptr); + } + } + // Delete scenes before clearing the network, since scenes hold + // LibertyLibrary pointers that ConcreteNetwork::clear() will free. + // Recreate the default scene so cmd_scene_ remains valid. + deleteScenes(); + makeDefaultScene(); + clear(); + db_network_->clear(); + // Restore db_ since dbNetwork::clear() nulls it, but we need it for reload + db_network_->init(db_, logger_); +} + float dbSta::slack(const odb::dbNet* db_net, const MinMax* min_max) { const Net* net = db_network_->dbToSta(db_net); diff --git a/src/grt/include/grt/GlobalRouter.h b/src/grt/include/grt/GlobalRouter.h index 4a947c30fd1..3fa4140d669 100644 --- a/src/grt/include/grt/GlobalRouter.h +++ b/src/grt/include/grt/GlobalRouter.h @@ -332,6 +332,7 @@ class GlobalRouter odb::dbDatabase* db() const { return db_; } FastRouteCore* fastroute() const { return fastroute_; } Rudy* getRudy(); + void clearRudy(); void writePinLocations(const char* file_name); diff --git a/src/grt/src/GlobalRouter.cpp b/src/grt/src/GlobalRouter.cpp index 201b5e70142..de2ed3847c4 100644 --- a/src/grt/src/GlobalRouter.cpp +++ b/src/grt/src/GlobalRouter.cpp @@ -1138,6 +1138,12 @@ Rudy* GlobalRouter::getRudy() return rudy_; } +void GlobalRouter::clearRudy() +{ + delete rudy_; + rudy_ = nullptr; +} + bool GlobalRouter::findPinAccessPointPositions( const Pin& pin, std::map>& ap_positions, diff --git a/src/grt/src/heatMap.h b/src/grt/src/heatMap.h index 49bd81a7db2..3a0301e2f83 100644 --- a/src/grt/src/heatMap.h +++ b/src/grt/src/heatMap.h @@ -15,6 +15,12 @@ class RoutingCongestionDataSource : public gui::GlobalRoutingDataSource public: RoutingCongestionDataSource(utl::Logger* logger, odb::dbDatabase* db); + void setChip(odb::dbChip* chip) override + { + layer_ = nullptr; + HeatMapDataSource::setChip(chip); + } + protected: bool populateMap() override; void combineMapData(bool base_has_value, diff --git a/src/grt/src/heatMapRudy.cpp b/src/grt/src/heatMapRudy.cpp index 0902e272bdd..fac11300db7 100644 --- a/src/grt/src/heatMapRudy.cpp +++ b/src/grt/src/heatMapRudy.cpp @@ -156,6 +156,17 @@ bool RUDYDataSource::populateMap() return true; } +void RUDYDataSource::setChip(odb::dbChip* chip) +{ + grouter_->clearRudy(); + rudy_ = nullptr; + removeOwner(); + HeatMapDataSource::setChip(chip); + if (chip && chip->getBlock()) { + addOwner(chip->getBlock()); + } +} + void RUDYDataSource::onShow() { HeatMapDataSource::onShow(); diff --git a/src/grt/src/heatMapRudy.h b/src/grt/src/heatMapRudy.h index 3527d4a7aae..ca631a39fec 100644 --- a/src/grt/src/heatMapRudy.h +++ b/src/grt/src/heatMapRudy.h @@ -23,6 +23,7 @@ class RUDYDataSource : public gui::GlobalRoutingDataSource, grt::GlobalRouter* grouter, odb::dbDatabase* db); + void setChip(odb::dbChip* chip) override; void onShow() override; void onHide() override; diff --git a/src/gui/include/gui/heatMap.h b/src/gui/include/gui/heatMap.h index df182b01570..07c342964ad 100644 --- a/src/gui/include/gui/heatMap.h +++ b/src/gui/include/gui/heatMap.h @@ -53,7 +53,7 @@ class HeatMapDataSource std::string name; std::string label; std::function()> choices; - std::function getter; + std::function getter; std::function setter; }; @@ -92,7 +92,13 @@ class HeatMapDataSource void registerHeatMap(); - virtual void setChip(odb::dbChip* chip) { chip_ = chip; } + virtual void setChip(odb::dbChip* chip) + { + if (chip_ != chip) { + destroyMap(); + } + chip_ = chip; + } void setUseDBU(bool use_dbu) { use_dbu_ = use_dbu; } bool getUseDBU() const { return use_dbu_; } diff --git a/src/gui/src/displayControls.cpp b/src/gui/src/displayControls.cpp index b6931490935..10b4ee1ecbf 100644 --- a/src/gui/src/displayControls.cpp +++ b/src/gui/src/displayControls.cpp @@ -2163,6 +2163,36 @@ void DisplayControls::techInit(odb::dbTech* tech) techs_.insert(tech); } +void DisplayControls::clearTechData() +{ + // Remove layer leaf rows but preserve sub-group items (implant, other) + // by clearing children of each sub-group first, then removing only + // direct layer rows from the layers group. + layers_.implant.name->removeRows(0, layers_.implant.name->rowCount()); + layers_.other.name->removeRows(0, layers_.other.name->rowCount()); + + // Remove direct children (routing/cut layers) from layers_group_, but + // keep the implant and other sub-group rows. Walk in reverse to avoid + // index shifting. + for (int i = layers_group_.name->rowCount() - 1; i >= 0; --i) { + QStandardItem* child = layers_group_.name->child(i); + if (child != layers_.implant.name && child != layers_.other.name) { + layers_group_.name->removeRow(i); + } + } + + site_group_.name->removeRows(0, site_group_.name->rowCount()); + + // Clear ODB-keyed maps + layer_controls_.clear(); + site_controls_.clear(); + layer_color_.clear(); + layer_pattern_.clear(); + site_color_.clear(); + techs_.clear(); + layers_menu_layer_ = nullptr; +} + void DisplayControls::blockLoaded(odb::dbBlock* block) { addTech(block->getTech()); diff --git a/src/gui/src/displayControls.h b/src/gui/src/displayControls.h index 6f6f0bf6667..fe12e21ebac 100644 --- a/src/gui/src/displayControls.h +++ b/src/gui/src/displayControls.h @@ -272,6 +272,9 @@ class DisplayControls : public QDockWidget, // options displayed need to match void blockLoaded(odb::dbBlock* block); + // Clears all ODB-derived display state (layers, sites) for DB reload + void clearTechData(); + void setCurrentChip(odb::dbChip* chip); // This is called by the check boxes to update the state diff --git a/src/gui/src/heatMapCore.cpp b/src/gui/src/heatMapCore.cpp index 4cebbc04c7d..8ed9eb150d3 100644 --- a/src/gui/src/heatMapCore.cpp +++ b/src/gui/src/heatMapCore.cpp @@ -397,7 +397,6 @@ Renderer::Settings HeatMapDataSource::getSettings() const settings[set.name] = set.getter(); } } - return settings; } diff --git a/src/gui/src/layoutTabs.cpp b/src/gui/src/layoutTabs.cpp index d7649ef05e2..5e1513f8e13 100644 --- a/src/gui/src/layoutTabs.cpp +++ b/src/gui/src/layoutTabs.cpp @@ -130,8 +130,30 @@ void LayoutTabs::chipLoaded(odb::dbChip* chip) emit newViewer(viewer); } +void LayoutTabs::clearViewers() +{ + // Stop render threads before destroying viewers to avoid + // deleting a running QThread. + for (LayoutViewer* viewer : viewers_) { + viewer->exit(); + } + // QTabWidget::clear() removes all tabs and deletes the page widgets + // (LayoutScroll), which are parents of the LayoutViewer widgets. + QTabWidget::clear(); + viewers_.clear(); + current_viewer_ = nullptr; + modules_.clear(); + focus_nets_.clear(); + route_guides_.clear(); + net_tracks_.clear(); +} + void LayoutTabs::tabChange(int index) { + if (index < 0 || index >= static_cast(viewers_.size())) { + current_viewer_ = nullptr; + return; + } current_viewer_ = viewers_[index]; emit setCurrentChip(current_viewer_->getChip()); diff --git a/src/gui/src/layoutTabs.h b/src/gui/src/layoutTabs.h index a0422f29227..4db63e8f78d 100644 --- a/src/gui/src/layoutTabs.h +++ b/src/gui/src/layoutTabs.h @@ -91,6 +91,7 @@ class LayoutTabs : public QTabWidget void tabChange(int index); void updateBackgroundColors(); void updateBackgroundColor(LayoutViewer* viewer); + void clearViewers(); // These are just forwarding to the current LayoutViewer void zoomIn(); diff --git a/src/gui/src/mainWindow.cpp b/src/gui/src/mainWindow.cpp index fd93eadb6fc..8223fc9248a 100644 --- a/src/gui/src/mainWindow.cpp +++ b/src/gui/src/mainWindow.cpp @@ -1629,6 +1629,10 @@ void MainWindow::selectHighlightConnectedBufferTrees(bool select_flag, void MainWindow::saveSettings() { + // Stop render threads before saving settings to avoid races + // between the render thread and the main thread. + viewers_->exit(); + QSettings settings("OpenRoad Project", "openroad"); settings.beginGroup("main"); settings.setValue("geometry", saveGeometry()); @@ -1662,6 +1666,17 @@ void MainWindow::postRead3Dbx(odb::dbChip* chip) emit chipLoaded(chip); } +void MainWindow::preDbClear(odb::dbDatabase* db) +{ + selected_.clear(); + for (auto& highlighted_set : highlighted_) { + highlighted_set.clear(); + } + viewers_->clearViewers(); + controls_->clearTechData(); + setBlock(nullptr); +} + void MainWindow::postReadDb(odb::dbDatabase* db) { auto chip = db->getChip(); diff --git a/src/gui/src/mainWindow.h b/src/gui/src/mainWindow.h index 9df989b3dd3..ce122b09f54 100644 --- a/src/gui/src/mainWindow.h +++ b/src/gui/src/mainWindow.h @@ -79,6 +79,7 @@ class MainWindow : public QMainWindow, public odb::dbDatabaseObserver void postReadDef(odb::dbBlock* block) override; void postReadDb(odb::dbDatabase* db) override; void postRead3Dbx(odb::dbChip* chip) override; + void preDbClear(odb::dbDatabase* db) override; // Capture logger messages into the script widget output void setLogger(utl::Logger* logger); diff --git a/src/odb/include/odb/db.h b/src/odb/include/odb/db.h index cd80da4ef81..588e21692eb 100644 --- a/src/odb/include/odb/db.h +++ b/src/odb/include/odb/db.h @@ -7716,6 +7716,7 @@ class dbDatabase : public dbObject void triggerPostReadDef(dbBlock* block, bool floorplan); void triggerPostReadDb(); void triggerPostRead3Dbx(dbChip* chip); + void triggerPreDbClear(); /// /// Create an instance of a database diff --git a/src/odb/include/odb/dbDatabaseObserver.h b/src/odb/include/odb/dbDatabaseObserver.h index 05ff66327ec..99a9f69a409 100644 --- a/src/odb/include/odb/dbDatabaseObserver.h +++ b/src/odb/include/odb/dbDatabaseObserver.h @@ -31,6 +31,9 @@ class dbDatabaseObserver virtual void postReadDb(odb::dbDatabase* db) = 0; virtual void postRead3Dbx(odb::dbChip* chip) = 0; + // Called before the database is cleared for a reload + virtual void preDbClear(odb::dbDatabase* /*db*/) {} + void setUnregisterObserver(std::function unregister_observer) { unregister_observer_ = std::move(unregister_observer); diff --git a/src/odb/src/db/dbDatabase.cpp b/src/odb/src/db/dbDatabase.cpp index 83a5fec5d6e..f115f6ebca5 100644 --- a/src/odb/src/db/dbDatabase.cpp +++ b/src/odb/src/db/dbDatabase.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include "absl/synchronization/mutex.h" @@ -478,6 +479,24 @@ _dbDatabase::_dbDatabase(_dbDatabase* /* unused: db */, int id) chip_tbl_ = new dbTable<_dbChip, 2>( this, this, (GetObjTbl_t) &_dbDatabase::getObjectTable, dbChipObj); + chip_hash_.setTable(chip_tbl_); + + chip_inst_tbl_ = new dbTable<_dbChipInst>( + this, this, (GetObjTbl_t) &_dbDatabase::getObjectTable, dbChipInstObj); + chip_region_inst_tbl_ = new dbTable<_dbChipRegionInst>( + this, + this, + (GetObjTbl_t) &_dbDatabase::getObjectTable, + dbChipRegionInstObj); + chip_conn_tbl_ = new dbTable<_dbChipConn>( + this, this, (GetObjTbl_t) &_dbDatabase::getObjectTable, dbChipConnObj); + chip_bump_inst_tbl_ + = new dbTable<_dbChipBumpInst>(this, + this, + (GetObjTbl_t) &_dbDatabase::getObjectTable, + dbChipBumpInstObj); + chip_net_tbl_ = new dbTable<_dbChipNet>( + this, this, (GetObjTbl_t) &_dbDatabase::getObjectTable, dbChipNetObj); gds_lib_tbl_ = new dbTable<_dbGDSLib, 2>( this, this, (GetObjTbl_t) &_dbDatabase::getObjectTable, dbGdsLibObj); @@ -925,8 +944,14 @@ void dbDatabase::clear() { _dbDatabase* db = (_dbDatabase*) this; int id = db->unique_id_; + // Save session-level state that was set up during init and is not + // part of the database content being cleared. + auto observers = std::move(db->observers_); + utl::Logger* logger = db->logger_; db->~_dbDatabase(); new (db) _dbDatabase(db, id); + db->observers_ = std::move(observers); + db->logger_ = logger; } void dbDatabase::destroy(dbDatabase* db_) @@ -1029,6 +1054,14 @@ void dbDatabase::triggerPostReadDb() } } +void dbDatabase::triggerPreDbClear() +{ + _dbDatabase* db = (_dbDatabase*) this; + for (dbDatabaseObserver* observer : db->observers_) { + observer->preDbClear(this); + } +} + // User Code End dbDatabasePublicMethods } // namespace odb // Generator Code End Cpp diff --git a/src/psm/src/heatMap.cpp b/src/psm/src/heatMap.cpp index 347ceac0bb1..faed2592e03 100644 --- a/src/psm/src/heatMap.cpp +++ b/src/psm/src/heatMap.cpp @@ -96,6 +96,9 @@ IRDropDataSource::IRDropDataSource(PDNSim* psm, void IRDropDataSource::setChip(odb::dbChip* chip) { + layer_ = nullptr; + net_ = nullptr; + corner_ = nullptr; gui::HeatMapDataSource::setChip(chip); if (chip != nullptr) { odb::dbBlock* block = chip->getBlock(); diff --git a/src/web/src/search.cpp b/src/web/src/search.cpp index e82ef1a82b8..4907937ef5b 100644 --- a/src/web/src/search.cpp +++ b/src/web/src/search.cpp @@ -203,10 +203,13 @@ void Search::setTopChip(odb::dbChip* chip) return; } odb::dbBlock* block = chip->getBlock(); - if (top_chip_ != chip) { + // Re-initialize when the chip changed or when the previous block was + // destroyed (e.g. after a database reload via read_db). The block + // destructor calls removeOwner() which clears hasOwner(). + if (top_chip_ != chip || !hasOwner()) { clear(); - if (top_chip_ != nullptr) { + if (hasOwner()) { removeOwner(); }