From cf25c93c042c54f1801593e83c8e5099f9fcdde1 Mon Sep 17 00:00:00 2001 From: Arthur Koucher Date: Thu, 25 Jun 2026 21:08:07 -0300 Subject: [PATCH 1/3] odb: add dbCorner and add/removal APIs Signed-off-by: Arthur Koucher --- src/odb/README.md | 38 ++++++ src/odb/include/odb/db.h | 21 ++++ src/odb/include/odb/dbCompare.inc | 7 ++ src/odb/include/odb/dbObject.h | 1 + .../codeGenerator/schema/chip/dbCorner.json | 11 ++ src/odb/src/db/CMakeLists.txt | 1 + src/odb/src/db/dbBlock.cpp | 89 ++++++++++++++ src/odb/src/db/dbBlock.h | 3 + src/odb/src/db/dbCorner.cpp | 114 ++++++++++++++++++ src/odb/src/db/dbCorner.h | 54 +++++++++ src/odb/src/db/dbDatabase.h | 5 +- src/odb/src/db/dbObject.cpp | 3 + src/odb/src/swig/tcl/odb.tcl | 41 +++++++ src/odb/test/cpp/BUILD | 13 ++ src/odb/test/cpp/CMakeLists.txt | 2 +- src/odb/test/cpp/TestCorner.cpp | 81 +++++++++++++ 16 files changed, 482 insertions(+), 2 deletions(-) create mode 100644 src/odb/src/codeGenerator/schema/chip/dbCorner.json create mode 100644 src/odb/src/db/dbCorner.cpp create mode 100644 src/odb/src/db/dbCorner.h create mode 100644 src/odb/test/cpp/TestCorner.cpp diff --git a/src/odb/README.md b/src/odb/README.md index 47aa8dd635e..aa65949b0c0 100644 --- a/src/odb/README.md +++ b/src/odb/README.md @@ -78,6 +78,44 @@ database to have exactly the same layout across save/restores. The database distance units are **nanometers** and use the type `uint32_t`. +### Add Corner + +Specifies a process corner for the current block. Corner names must be unique +within a block, so the command fails if a corner with the same name already +exists. + +```tcl +add_corner corner_name +``` + +#### Options + +| Switch Name | Description | +| ----- | ----- | +| `corner_name` | Name of the process corner to add. | + +### Remove Corner + +Removes a process corner from the current block. + +```tcl +remove_corner corner_name +``` + +#### Options + +| Switch Name | Description | +| ----- | ----- | +| `corner_name` | Name of the process corner to remove. | + +### Remove Corners + +Removes all process corners from the current block. + +```tcl +remove_corners +``` + ### Create Physical Cluster Description TBC. diff --git a/src/odb/include/odb/db.h b/src/odb/include/odb/db.h index d20b0f5d995..aa4c304b95d 100644 --- a/src/odb/include/odb/db.h +++ b/src/odb/include/odb/db.h @@ -121,6 +121,7 @@ class dbChipNet; class dbChipPath; class dbChipRegion; class dbChipRegionInst; +class dbCorner; class dbDatabase; class dbDft; class dbGCellGrid; @@ -1026,6 +1027,14 @@ class dbBlock : public dbObject /// int getCornerCount(); + void addCorner(const std::string& corner_name); + + dbCorner* findCorner(const std::string& corner_name) const; + + void removeCorner(const std::string& corner_name); + + void removeCorners(); + /// /// Get the number of corners kept n this block /// @@ -7575,6 +7584,18 @@ class dbChipRegionInst : public dbObject // User Code End dbChipRegionInst }; +class dbCorner : public dbObject +{ + public: + const std::string& getName() const; + + // User Code Begin dbCorner + static dbCorner* create(dbBlock* block_, const std::string& corner_name); + + static void destroy(dbCorner* corner_); + // User Code End dbCorner +}; + class dbDatabase : public dbObject { public: diff --git a/src/odb/include/odb/dbCompare.inc b/src/odb/include/odb/dbCompare.inc index e0f4ea41a1d..d4cbae96fc1 100644 --- a/src/odb/include/odb/dbCompare.inc +++ b/src/odb/include/odb/dbCompare.inc @@ -392,6 +392,13 @@ struct less = delete; }; +template <> +struct less +{ + bool operator()(const odb::dbCorner* lhs, const odb::dbCorner* rhs) const + = delete; +}; + template <> struct less { diff --git a/src/odb/include/odb/dbObject.h b/src/odb/include/odb/dbObject.h index b2e3edf3b35..137984d1f00 100644 --- a/src/odb/include/odb/dbObject.h +++ b/src/odb/include/odb/dbObject.h @@ -70,6 +70,7 @@ enum dbObjectType dbChipPathObj, dbChipRegionObj, dbChipRegionInstObj, + dbCornerObj, dbDatabaseObj, dbDftObj, dbGCellGridObj, diff --git a/src/odb/src/codeGenerator/schema/chip/dbCorner.json b/src/odb/src/codeGenerator/schema/chip/dbCorner.json new file mode 100644 index 00000000000..7b82f8e4db0 --- /dev/null +++ b/src/odb/src/codeGenerator/schema/chip/dbCorner.json @@ -0,0 +1,11 @@ +{ + "name": "dbCorner", + "type": "dbObject", + "fields": [ + { + "name": "name_", + "type": "std::string", + "flags":["cmpgt", "no-set"] + } + ] +} diff --git a/src/odb/src/db/CMakeLists.txt b/src/odb/src/db/CMakeLists.txt index 55944919aad..55059781813 100644 --- a/src/odb/src/db/CMakeLists.txt +++ b/src/odb/src/db/CMakeLists.txt @@ -103,6 +103,7 @@ add_library(db dbChipPath.cpp dbChipRegion.cpp dbChipRegionInst.cpp + dbCorner.cpp dbDatabase.cpp dbDft.cpp dbGCellGrid.cpp diff --git a/src/odb/src/db/dbBlock.cpp b/src/odb/src/db/dbBlock.cpp index 59582f99bd2..e00f5a1be6d 100644 --- a/src/odb/src/db/dbBlock.cpp +++ b/src/odb/src/db/dbBlock.cpp @@ -47,6 +47,7 @@ #include "dbChip.h" #include "dbCommon.h" #include "dbCore.h" +#include "dbCorner.h" #include "dbDatabase.h" #include "dbDft.h" #include "dbFill.h" @@ -116,6 +117,7 @@ #include "dbTechLayerRule.h" #include "dbTechNonDefaultRule.h" #include "dbTrackGrid.h" +#include "dbVector.h" #include "dbVia.h" #include "dbWire.h" #include "odb/PtrSetMap.h" @@ -245,6 +247,9 @@ _dbBlock::_dbBlock(_dbDatabase* db) net_tracks_tbl_ = new dbTable<_dbNetTrack>( db, this, (GetObjTbl_t) &_dbBlock::getObjectTable, dbNetTrackObj); + corner_tbl_ = new dbTable<_dbCorner>( + db, this, (GetObjTbl_t) &_dbBlock::getObjectTable, dbCornerObj); + box_tbl_ = new dbTable<_dbBox, 1024>( db, this, (GetObjTbl_t) &_dbBlock::getObjectTable, dbBoxObj); @@ -437,6 +442,7 @@ _dbBlock::~_dbBlock() delete global_connect_tbl_; delete guide_tbl_; delete net_tracks_tbl_; + delete corner_tbl_; delete box_tbl_; delete via_tbl_; delete gcell_grid_tbl_; @@ -616,6 +622,9 @@ dbObjectTable* _dbBlock::getObjectTable(dbObjectType type) case dbNetTrackObj: return net_tracks_tbl_; + case dbCornerObj: + return corner_tbl_; + case dbNetObj: return net_tbl_; @@ -780,6 +789,7 @@ dbOStream& operator<<(dbOStream& stream, const _dbBlock& block) stream << block.max_cc_seg_id_; stream << block.children_; stream << block.component_mask_shift_; + stream << block.corners_; stream << block.currentCcAdjOrder_; stream << *block.bterm_tbl_; @@ -804,6 +814,7 @@ dbOStream& operator<<(dbOStream& stream, const _dbBlock& block) stream << *block.global_connect_tbl_; stream << *block.guide_tbl_; stream << *block.net_tracks_tbl_; + stream << *block.corner_tbl_; stream << *block.box_tbl_; stream << *block.via_tbl_; stream << *block.gcell_grid_tbl_; @@ -975,6 +986,9 @@ dbIStream& operator>>(dbIStream& stream, _dbBlock& block) if (db->isSchema(kSchemaBlockComponentMaskShift)) { stream >> block.component_mask_shift_; } + if (db->isSchema(kSchemaCorner)) { + stream >> block.corners_; + } stream >> block.currentCcAdjOrder_; stream >> *block.bterm_tbl_; stream >> *block.iterm_tbl_; @@ -1041,6 +1055,9 @@ dbIStream& operator>>(dbIStream& stream, _dbBlock& block) if (db->isSchema(kSchemaNetTracks)) { stream >> *block.net_tracks_tbl_; } + if (db->isSchema(kSchemaCorner)) { + stream >> *block.corner_tbl_; + } stream >> *block.box_tbl_; stream >> *block.via_tbl_; stream >> *block.gcell_grid_tbl_; @@ -1336,6 +1353,9 @@ bool _dbBlock::operator==(const _dbBlock& rhs) const if (children_ != rhs.children_) { return false; } + if (corners_ != rhs.corners_) { + return false; + } if (component_mask_shift_ != rhs.component_mask_shift_) { return false; @@ -1411,6 +1431,10 @@ bool _dbBlock::operator==(const _dbBlock& rhs) const return false; } + if (*corner_tbl_ != *rhs.corner_tbl_) { + return false; + } + if (*box_tbl_ != *rhs.box_tbl_) { return false; } @@ -2871,6 +2895,69 @@ void dbBlock::setCornerCount(int cornersStoredCnt, } } +dbCorner* dbBlock::findCorner(const std::string& corner_name) const +{ + _dbBlock* block = (_dbBlock*) this; + + for (const dbId<_dbCorner> corner_id : block->corners_) { + _dbCorner* corner = block->corner_tbl_->getPtr(corner_id); + + if (corner->name_ == corner_name) { + return (dbCorner*) corner; + } + } + + return nullptr; +} + +void dbBlock::addCorner(const std::string& corner_name) +{ + if (findCorner(corner_name)) { + utl::Logger* logger = getImpl()->getLogger(); + logger->error( + utl::ODB, + 5, + "Could not add corner {}. A corner with that name already exists.", + corner_name); + } + + dbCorner* corner_ = dbCorner::create(this, corner_name); + + _dbBlock* block = (_dbBlock*) this; + block->corners_.push_back(corner_->getId()); +} + +void dbBlock::removeCorner(const std::string& corner_name) +{ + dbCorner* corner_ = findCorner(corner_name); + + if (!corner_) { + utl::Logger* logger = getImpl()->getLogger(); + logger->error(utl::ODB, + 6, + "Could not remove corner {}. No corner with that name " + "exists.", + corner_name); + } + + _dbBlock* block = (_dbBlock*) this; + + // Note that std::ranges::find requires the search value + // to be the same type as the range's elements. + const auto corner_id = (dbId<_dbCorner>) corner_->getId(); + auto corner_position = std::ranges::find(block->corners_, corner_id); + block->corners_.erase(corner_position); + + dbCorner::destroy(corner_); +} + +void dbBlock::removeCorners() +{ + _dbBlock* block = (_dbBlock*) this; + block->corners_.clear(); + block->corner_tbl_->clear(); +} + char* dbBlock::getCornerNameList() { _dbBlock* block = (_dbBlock*) this; @@ -3722,6 +3809,7 @@ void _dbBlock::collectMemInfo(MemInfo& info) info.children["bterm_hash"].add(bterm_hash_); info.children["children"].add(children_); + info.children["corners"].add(corners_); info.children["component_mask_shift"].add(component_mask_shift_); bterm_tbl_->collectMemInfo(info.children["bterm"]); @@ -3758,6 +3846,7 @@ void _dbBlock::collectMemInfo(MemInfo& info) global_connect_tbl_->collectMemInfo(info.children["global_connect"]); guide_tbl_->collectMemInfo(info.children["guide"]); net_tracks_tbl_->collectMemInfo(info.children["net_tracks"]); + corner_tbl_->collectMemInfo(info.children["corner"]); dft_tbl_->collectMemInfo(info.children["dft"]); modbterm_tbl_->collectMemInfo(info.children["modbterm"]); moditerm_tbl_->collectMemInfo(info.children["moditerm"]); diff --git a/src/odb/src/db/dbBlock.h b/src/odb/src/db/dbBlock.h index 8c0eba09e05..06976b027d5 100644 --- a/src/odb/src/db/dbBlock.h +++ b/src/odb/src/db/dbBlock.h @@ -76,6 +76,7 @@ class _dbAccessPoint; class _dbGlobalConnect; class _dbGuide; class _dbNetTrack; +class _dbCorner; class _dbMarkerCategory; class dbJournal; @@ -219,6 +220,7 @@ class _dbBlock : public _dbObject uint32_t max_cc_seg_id_; dbVector> children_; dbVector> component_mask_shift_; + dbVector> corners_; uint32_t currentCcAdjOrder_; dbId<_dbDft> dft_; int min_routing_layer_; @@ -267,6 +269,7 @@ class _dbBlock : public _dbObject dbTable<_dbGlobalConnect>* global_connect_tbl_; dbTable<_dbGuide>* guide_tbl_; dbTable<_dbNetTrack>* net_tracks_tbl_; + dbTable<_dbCorner>* corner_tbl_; _dbNameCache* name_cache_; dbTable<_dbDft, 4096>* dft_tbl_; diff --git a/src/odb/src/db/dbCorner.cpp b/src/odb/src/db/dbCorner.cpp new file mode 100644 index 00000000000..567681b1a16 --- /dev/null +++ b/src/odb/src/db/dbCorner.cpp @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2019-2025, The OpenROAD Authors + +// Generator Code Begin Cpp +#include "dbCorner.h" + +#include + +#include "dbCore.h" +#include "dbDatabase.h" +#include "dbTable.h" +#include "dbVector.h" +#include "odb/db.h" +// User Code Begin Includes +#include "dbBlock.h" +#include "utl/Logger.h" +// User Code End Includes +namespace odb { +template class dbTable<_dbCorner>; +// User Code Begin Static +// User Code End Static + +bool _dbCorner::operator==(const _dbCorner& rhs) const +{ + // NOLINTBEGIN(readability-simplify-boolean-expr) + if (name_ != rhs.name_) { + return false; + } + + // User Code Begin == + // User Code End == + return true; + // NOLINTEND(readability-simplify-boolean-expr) +} + +bool _dbCorner::operator<(const _dbCorner& rhs) const +{ + if (name_ >= rhs.name_) { + return false; + } + + // User Code Begin < + // User Code End < + return true; +} + +_dbCorner::_dbCorner(_dbDatabase* db) +{ + // User Code Begin Constructor + // User Code End Constructor +} + +dbIStream& operator>>(dbIStream& stream, _dbCorner& obj) +{ + stream >> obj.name_; + // User Code Begin >> + // User Code End >> + return stream; +} + +dbOStream& operator<<(dbOStream& stream, const _dbCorner& obj) +{ + stream << obj.name_; + // User Code Begin << + // User Code End << + return stream; +} + +void _dbCorner::collectMemInfo(MemInfo& info) +{ + info.cnt++; + info.size += sizeof(*this); + + info.children["name"].add(name_); + + // User Code Begin collectMemInfo + // User Code End collectMemInfo +} + +// User Code Begin PrivateMethods +// User Code End PrivateMethods + +//////////////////////////////////////////////////////////////////// +// +// dbCorner - Methods +// +//////////////////////////////////////////////////////////////////// + +const std::string& dbCorner::getName() const +{ + _dbCorner* obj = (_dbCorner*) this; + return obj->name_; +} + +// User Code Begin dbCornerPublicMethods + +dbCorner* dbCorner::create(dbBlock* block_, const std::string& corner_name) +{ + _dbBlock* block = (_dbBlock*) block_; + _dbCorner* corner = block->corner_tbl_->create(); + corner->name_ = corner_name; + return (dbCorner*) corner; +} + +void dbCorner::destroy(dbCorner* corner_) +{ + _dbCorner* corner = (_dbCorner*) corner_; + _dbBlock* block = (_dbBlock*) corner->getOwner(); + block->corner_tbl_->destroy(corner); +} + +// User Code End dbCornerPublicMethods +} // namespace odb +// Generator Code End Cpp \ No newline at end of file diff --git a/src/odb/src/db/dbCorner.h b/src/odb/src/db/dbCorner.h new file mode 100644 index 00000000000..237b15f1c19 --- /dev/null +++ b/src/odb/src/db/dbCorner.h @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2019-2025, The OpenROAD Authors + +// Generator Code Begin Header +#pragma once + +#include +#include + +#include "dbCore.h" +// User Code Begin Includes +// User Code End Includes + +namespace odb { +// User Code Begin Consts +// User Code End Consts +class dbIStream; +class dbOStream; +class _dbDatabase; +// User Code Begin Classes +// User Code End Classes + +// User Code Begin Types +// User Code End Types + +// User Code Begin Structs +// User Code End Structs + +class _dbCorner : public _dbObject +{ + public: + // User Code Begin Enums + // User Code End Enums + + _dbCorner(_dbDatabase*); + + bool operator==(const _dbCorner& rhs) const; + bool operator!=(const _dbCorner& rhs) const { return !operator==(rhs); } + bool operator<(const _dbCorner& rhs) const; + void collectMemInfo(MemInfo& info); + // User Code Begin Methods + // User Code End Methods + + std::string name_; + + // User Code Begin Fields + // User Code End Fields +}; +dbIStream& operator>>(dbIStream& stream, _dbCorner& obj); +dbOStream& operator<<(dbOStream& stream, const _dbCorner& obj); +// User Code Begin General +// User Code End General +} // namespace odb +// Generator Code End Header \ No newline at end of file diff --git a/src/odb/src/db/dbDatabase.h b/src/odb/src/db/dbDatabase.h index 6efb166d53f..f3c8d2e0b37 100644 --- a/src/odb/src/db/dbDatabase.h +++ b/src/odb/src/db/dbDatabase.h @@ -50,7 +50,10 @@ namespace odb { inline constexpr uint32_t kSchemaMajor = 0; // Not used... inline constexpr uint32_t kSchemaInitial = 57; -inline constexpr uint32_t kSchemaMinor = 134; // Current revision number +inline constexpr uint32_t kSchemaMinor = 135; // Current revision number + +// Revision where dbCorner object was added +inline constexpr uint32_t kSchemaCorner = 135; // Revision where the per-corner child-block feature for parasitics was removed inline constexpr uint32_t kSchemaRemovePerCornerBlock = 134; diff --git a/src/odb/src/db/dbObject.cpp b/src/odb/src/db/dbObject.cpp index 8cb2e1a866a..562d744b626 100644 --- a/src/odb/src/db/dbObject.cpp +++ b/src/odb/src/db/dbObject.cpp @@ -68,6 +68,7 @@ static const char* name_tbl[] = {"dbGDSLib", "dbChipPath", "dbChipRegion", "dbChipRegionInst", + "dbCorner", "dbDatabase", "dbDft", "dbGCellGrid", @@ -195,6 +196,7 @@ static const std::unordered_map hash_to_object_type {0x359DE612, dbChipPathObj}, {0x0676E6F1, dbChipRegionObj}, {0x457A83E5, dbChipRegionInstObj}, + {0xFE85C102, dbCornerObj}, {0x00000000, dbDatabaseObj}, {0x7C713BD7, dbDftObj}, {0x645E6090, dbGCellGridObj}, @@ -323,6 +325,7 @@ static const std::unordered_map object_type_to_hash {dbChipPathObj, 0x359DE612}, {dbChipRegionObj, 0x0676E6F1}, {dbChipRegionInstObj, 0x457A83E5}, + {dbCornerObj, 0xFE85C102}, {dbDatabaseObj, 0x00000000}, {dbDftObj, 0x7C713BD7}, {dbGCellGridObj, 0x645E6090}, diff --git a/src/odb/src/swig/tcl/odb.tcl b/src/odb/src/swig/tcl/odb.tcl index ddcb8432466..32d24e3a501 100644 --- a/src/odb/src/swig/tcl/odb.tcl +++ b/src/odb/src/swig/tcl/odb.tcl @@ -1,6 +1,47 @@ # SPDX-License-Identifier: BSD-3-Clause # Copyright (c) 2019-2025, The OpenROAD Authors +sta::define_cmd_args "add_corner" {corner_name} + +proc add_corner { args } { + sta::parse_key_args "add_corner" args keys {} flags {} + + set corner_name $args + set block [odb::get_block] + if { $block == "NULL" } { + utl::error ODB 1 "Could not add corner $corner_name. No block found." + } + + $block addCorner $corner_name +} + +sta::define_cmd_args "remove_corner" {corner_name} + +proc remove_corner { args } { + sta::parse_key_args "remove_corner" args keys {} flags {} + + set corner_name $args + set block [odb::get_block] + if { $block == "NULL" } { + utl::error ODB 7 "Could not remove corner $corner_name. No block found." + } + + $block removeCorner $corner_name +} + +sta::define_cmd_args "remove_corners" {} + +proc remove_corners { args } { + sta::parse_key_args "remove_corners" args keys {} flags {} + + set block [odb::get_block] + if { $block == "NULL" } { + utl::error ODB 8 "Could not remove corners. No block found." + } + + $block removeCorners +} + sta::define_cmd_args "create_physical_cluster" {cluster_name} proc create_physical_cluster { args } { diff --git a/src/odb/test/cpp/BUILD b/src/odb/test/cpp/BUILD index 10d9d782281..17973156e4e 100644 --- a/src/odb/test/cpp/BUILD +++ b/src/odb/test/cpp/BUILD @@ -67,6 +67,19 @@ cc_test( ], ) +cc_test( + name = "TestCorner", + srcs = [ + "TestCorner.cpp", + ], + deps = [ + "//src/odb/src/db", + "//src/odb/test/cpp/helper", + "@googletest//:gtest", + "@googletest//:gtest_main", + ], +) + cc_test( name = "TestDbWire", srcs = ["TestDbWire.cc"], diff --git a/src/odb/test/cpp/CMakeLists.txt b/src/odb/test/cpp/CMakeLists.txt index f314f4973ff..288eaee171c 100644 --- a/src/odb/test/cpp/CMakeLists.txt +++ b/src/odb/test/cpp/CMakeLists.txt @@ -20,7 +20,7 @@ set(TEST_LIBS GTest::gmock ) -add_executable(OdbGTests TestDbWire.cc TestDbNet.cpp TestAbstractLef.cc TestPolygonalFloorplan.cc) +add_executable(OdbGTests TestDbWire.cc TestDbNet.cpp TestAbstractLef.cc TestPolygonalFloorplan.cc TestCorner.cpp) add_executable(TestCallBacks TestCallBacks.cpp) add_executable(TestGeom TestGeom.cpp) add_executable(TestModule TestModule.cpp) diff --git a/src/odb/test/cpp/TestCorner.cpp b/src/odb/test/cpp/TestCorner.cpp new file mode 100644 index 00000000000..e9e6337436a --- /dev/null +++ b/src/odb/test/cpp/TestCorner.cpp @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2024-2025, The OpenROAD Authors + +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "helper.h" +#include "odb/db.h" + +namespace odb { +namespace { + +TEST_F(SimpleDbFixture, test_add_find_remove) +{ + createSimpleDB(); + dbBlock* block = db_->getChip()->getBlock(); + + block->addCorner("corner1"); + block->addCorner("corner2"); + + EXPECT_EQ(block->findCorner("corner1")->getName(), "corner1"); + EXPECT_EQ(block->findCorner("corner2")->getName(), "corner2"); + EXPECT_EQ(block->findCorner("missing"), nullptr); + + // Removing one corner leaves the others in place. + block->removeCorner("corner1"); + EXPECT_EQ(block->findCorner("corner1"), nullptr); + EXPECT_NE(block->findCorner("corner2"), nullptr); + + // removeCorners clears everything that remains. + block->removeCorners(); + EXPECT_EQ(block->findCorner("corner2"), nullptr); +} + +TEST_F(SimpleDbFixture, test_add_duplicate_errors) +{ + createSimpleDB(); + dbBlock* block = db_->getChip()->getBlock(); + + block->addCorner("corner1"); + // Adding a second corner with the same name is a fatal error. + EXPECT_THROW(block->addCorner("corner1"), std::runtime_error); +} + +TEST_F(SimpleDbFixture, test_write_read_roundtrip) +{ + createSimpleDB(); + dbBlock* block = db_->getChip()->getBlock(); + block->addCorner("corner1"); + block->addCorner("corner2"); + + std::filesystem::create_directory("results"); + const std::string tmp_path = "results/TestCorner.odb"; + + std::ofstream write_stream; + write_stream.exceptions(std::ifstream::failbit | std::ifstream::badbit + | std::ios::eofbit); + write_stream.open(tmp_path, std::ios::binary); + db_->write(write_stream); + write_stream.close(); + + dbDatabase* read_db = dbDatabase::create(); + std::ifstream read_stream; + read_stream.exceptions(std::ifstream::failbit | std::ifstream::badbit + | std::ios::eofbit); + read_stream.open(tmp_path, std::ios::binary); + read_db->read(read_stream); + + dbBlock* read_block = read_db->getChip()->getBlock(); + EXPECT_EQ(read_block->findCorner("corner1")->getName(), "corner1"); + EXPECT_EQ(read_block->findCorner("corner2")->getName(), "corner2"); + + dbDatabase::destroy(read_db); +} + +} // namespace +} // namespace odb From 2bb8aef5dc0d9cbfca40dbddc700acd441733f9b Mon Sep 17 00:00:00 2001 From: Arthur Koucher Date: Fri, 26 Jun 2026 12:06:59 -0300 Subject: [PATCH 2/3] odb: address gemini review and fix CI failures 1) Check input strings in .tcl for correct number of arguments 2) Check for empty name and add test for coverage 3) Re-generate correctly without --keep_empty 4) Re-golden read me message count 5) Remove includes that were not used directly Signed-off-by: Arthur Koucher --- src/odb/src/db/dbBlock.cpp | 8 +++++++- src/odb/src/db/dbCorner.cpp | 20 -------------------- src/odb/src/db/dbCorner.h | 22 ---------------------- src/odb/src/swig/tcl/odb.tcl | 3 +++ src/odb/test/cpp/TestCorner.cpp | 9 +++++++++ src/odb/test/odb_readme_msgs_check.ok | 2 +- 6 files changed, 20 insertions(+), 44 deletions(-) diff --git a/src/odb/src/db/dbBlock.cpp b/src/odb/src/db/dbBlock.cpp index e00f5a1be6d..0e2ffcae432 100644 --- a/src/odb/src/db/dbBlock.cpp +++ b/src/odb/src/db/dbBlock.cpp @@ -2912,8 +2912,14 @@ dbCorner* dbBlock::findCorner(const std::string& corner_name) const void dbBlock::addCorner(const std::string& corner_name) { + utl::Logger* logger = getImpl()->getLogger(); + + if (corner_name.empty()) { + logger->error( + utl::ODB, 9, "Could not add corner. Empty name is not allowed."); + } + if (findCorner(corner_name)) { - utl::Logger* logger = getImpl()->getLogger(); logger->error( utl::ODB, 5, diff --git a/src/odb/src/db/dbCorner.cpp b/src/odb/src/db/dbCorner.cpp index 567681b1a16..708e23cb6ab 100644 --- a/src/odb/src/db/dbCorner.cpp +++ b/src/odb/src/db/dbCorner.cpp @@ -9,16 +9,12 @@ #include "dbCore.h" #include "dbDatabase.h" #include "dbTable.h" -#include "dbVector.h" #include "odb/db.h" // User Code Begin Includes #include "dbBlock.h" -#include "utl/Logger.h" // User Code End Includes namespace odb { template class dbTable<_dbCorner>; -// User Code Begin Static -// User Code End Static bool _dbCorner::operator==(const _dbCorner& rhs) const { @@ -27,8 +23,6 @@ bool _dbCorner::operator==(const _dbCorner& rhs) const return false; } - // User Code Begin == - // User Code End == return true; // NOLINTEND(readability-simplify-boolean-expr) } @@ -39,30 +33,22 @@ bool _dbCorner::operator<(const _dbCorner& rhs) const return false; } - // User Code Begin < - // User Code End < return true; } _dbCorner::_dbCorner(_dbDatabase* db) { - // User Code Begin Constructor - // User Code End Constructor } dbIStream& operator>>(dbIStream& stream, _dbCorner& obj) { stream >> obj.name_; - // User Code Begin >> - // User Code End >> return stream; } dbOStream& operator<<(dbOStream& stream, const _dbCorner& obj) { stream << obj.name_; - // User Code Begin << - // User Code End << return stream; } @@ -72,14 +58,8 @@ void _dbCorner::collectMemInfo(MemInfo& info) info.size += sizeof(*this); info.children["name"].add(name_); - - // User Code Begin collectMemInfo - // User Code End collectMemInfo } -// User Code Begin PrivateMethods -// User Code End PrivateMethods - //////////////////////////////////////////////////////////////////// // // dbCorner - Methods diff --git a/src/odb/src/db/dbCorner.h b/src/odb/src/db/dbCorner.h index 237b15f1c19..263f16f1e25 100644 --- a/src/odb/src/db/dbCorner.h +++ b/src/odb/src/db/dbCorner.h @@ -8,47 +8,25 @@ #include #include "dbCore.h" -// User Code Begin Includes -// User Code End Includes namespace odb { -// User Code Begin Consts -// User Code End Consts class dbIStream; class dbOStream; class _dbDatabase; -// User Code Begin Classes -// User Code End Classes - -// User Code Begin Types -// User Code End Types - -// User Code Begin Structs -// User Code End Structs class _dbCorner : public _dbObject { public: - // User Code Begin Enums - // User Code End Enums - _dbCorner(_dbDatabase*); bool operator==(const _dbCorner& rhs) const; bool operator!=(const _dbCorner& rhs) const { return !operator==(rhs); } bool operator<(const _dbCorner& rhs) const; void collectMemInfo(MemInfo& info); - // User Code Begin Methods - // User Code End Methods std::string name_; - - // User Code Begin Fields - // User Code End Fields }; dbIStream& operator>>(dbIStream& stream, _dbCorner& obj); dbOStream& operator<<(dbOStream& stream, const _dbCorner& obj); -// User Code Begin General -// User Code End General } // namespace odb // Generator Code End Header \ No newline at end of file diff --git a/src/odb/src/swig/tcl/odb.tcl b/src/odb/src/swig/tcl/odb.tcl index 32d24e3a501..fddc3c56761 100644 --- a/src/odb/src/swig/tcl/odb.tcl +++ b/src/odb/src/swig/tcl/odb.tcl @@ -5,6 +5,7 @@ sta::define_cmd_args "add_corner" {corner_name} proc add_corner { args } { sta::parse_key_args "add_corner" args keys {} flags {} + sta::check_argc_eq1 "add_corner" $args set corner_name $args set block [odb::get_block] @@ -19,6 +20,7 @@ sta::define_cmd_args "remove_corner" {corner_name} proc remove_corner { args } { sta::parse_key_args "remove_corner" args keys {} flags {} + sta::check_argc_eq1 "remove_corner" $args set corner_name $args set block [odb::get_block] @@ -33,6 +35,7 @@ sta::define_cmd_args "remove_corners" {} proc remove_corners { args } { sta::parse_key_args "remove_corners" args keys {} flags {} + sta::check_argc_eq0 "remove_corners" $args set block [odb::get_block] if { $block == "NULL" } { diff --git a/src/odb/test/cpp/TestCorner.cpp b/src/odb/test/cpp/TestCorner.cpp index e9e6337436a..409e152529e 100644 --- a/src/odb/test/cpp/TestCorner.cpp +++ b/src/odb/test/cpp/TestCorner.cpp @@ -46,6 +46,15 @@ TEST_F(SimpleDbFixture, test_add_duplicate_errors) EXPECT_THROW(block->addCorner("corner1"), std::runtime_error); } +TEST_F(SimpleDbFixture, test_add_empty_name_errors) +{ + createSimpleDB(); + dbBlock* block = db_->getChip()->getBlock(); + + // Empty name is not allowed. + EXPECT_THROW(block->addCorner(""), std::runtime_error); +} + TEST_F(SimpleDbFixture, test_write_read_roundtrip) { createSimpleDB(); diff --git a/src/odb/test/odb_readme_msgs_check.ok b/src/odb/test/odb_readme_msgs_check.ok index 390cecb66d4..8a88038a28a 100644 --- a/src/odb/test/odb_readme_msgs_check.ok +++ b/src/odb/test/odb_readme_msgs_check.ok @@ -1,5 +1,5 @@ README.md -Names: 22, Desc: 22, Syn: 22, Options: 22, Args: 22 +Names: 25, Desc: 25, Syn: 25, Options: 25, Args: 25 Global Examples: None Global See Also: None Man2 successfully compiled. From db6645c8efdb5635f52699b9310de8eb77fce93e Mon Sep 17 00:00:00 2001 From: Arthur Koucher Date: Fri, 26 Jun 2026 16:15:44 -0300 Subject: [PATCH 3/3] odb: address codex review 1) Move the add/remove corner logic into dbCorner::create/destroy and delete dbBlock::addCorner/removeCorner 2) Destroy attached properties before freeing the object in destroy and removeCorners 3) Update TestCorner to the factory API Signed-off-by: Arthur Koucher --- src/odb/include/odb/db.h | 4 --- src/odb/src/db/dbBlock.cpp | 49 +++------------------------------ src/odb/src/db/dbCorner.cpp | 28 +++++++++++++++++++ src/odb/src/swig/tcl/odb.tcl | 11 ++++++-- src/odb/test/cpp/TestCorner.cpp | 16 +++++------ 5 files changed, 49 insertions(+), 59 deletions(-) diff --git a/src/odb/include/odb/db.h b/src/odb/include/odb/db.h index aa4c304b95d..76323959c26 100644 --- a/src/odb/include/odb/db.h +++ b/src/odb/include/odb/db.h @@ -1027,12 +1027,8 @@ class dbBlock : public dbObject /// int getCornerCount(); - void addCorner(const std::string& corner_name); - dbCorner* findCorner(const std::string& corner_name) const; - void removeCorner(const std::string& corner_name); - void removeCorners(); /// diff --git a/src/odb/src/db/dbBlock.cpp b/src/odb/src/db/dbBlock.cpp index 0e2ffcae432..0ac7afd5777 100644 --- a/src/odb/src/db/dbBlock.cpp +++ b/src/odb/src/db/dbBlock.cpp @@ -2910,56 +2910,15 @@ dbCorner* dbBlock::findCorner(const std::string& corner_name) const return nullptr; } -void dbBlock::addCorner(const std::string& corner_name) +void dbBlock::removeCorners() { - utl::Logger* logger = getImpl()->getLogger(); - - if (corner_name.empty()) { - logger->error( - utl::ODB, 9, "Could not add corner. Empty name is not allowed."); - } - - if (findCorner(corner_name)) { - logger->error( - utl::ODB, - 5, - "Could not add corner {}. A corner with that name already exists.", - corner_name); - } - - dbCorner* corner_ = dbCorner::create(this, corner_name); - _dbBlock* block = (_dbBlock*) this; - block->corners_.push_back(corner_->getId()); -} - -void dbBlock::removeCorner(const std::string& corner_name) -{ - dbCorner* corner_ = findCorner(corner_name); - if (!corner_) { - utl::Logger* logger = getImpl()->getLogger(); - logger->error(utl::ODB, - 6, - "Could not remove corner {}. No corner with that name " - "exists.", - corner_name); + for (const dbId<_dbCorner> corner_id : block->corners_) { + _dbCorner* corner = block->corner_tbl_->getPtr(corner_id); + dbProperty::destroyProperties((dbCorner*) corner); } - _dbBlock* block = (_dbBlock*) this; - - // Note that std::ranges::find requires the search value - // to be the same type as the range's elements. - const auto corner_id = (dbId<_dbCorner>) corner_->getId(); - auto corner_position = std::ranges::find(block->corners_, corner_id); - block->corners_.erase(corner_position); - - dbCorner::destroy(corner_); -} - -void dbBlock::removeCorners() -{ - _dbBlock* block = (_dbBlock*) this; block->corners_.clear(); block->corner_tbl_->clear(); } diff --git a/src/odb/src/db/dbCorner.cpp b/src/odb/src/db/dbCorner.cpp index 708e23cb6ab..e02802e0b61 100644 --- a/src/odb/src/db/dbCorner.cpp +++ b/src/odb/src/db/dbCorner.cpp @@ -11,7 +11,10 @@ #include "dbTable.h" #include "odb/db.h" // User Code Begin Includes +#include + #include "dbBlock.h" +#include "utl/Logger.h" // User Code End Includes namespace odb { template class dbTable<_dbCorner>; @@ -77,8 +80,25 @@ const std::string& dbCorner::getName() const dbCorner* dbCorner::create(dbBlock* block_, const std::string& corner_name) { _dbBlock* block = (_dbBlock*) block_; + utl::Logger* logger = block->getImpl()->getLogger(); + + if (corner_name.empty()) { + logger->error( + utl::ODB, 9, "Could not add corner. Empty name is not allowed."); + } + + if (block_->findCorner(corner_name)) { + logger->error( + utl::ODB, + 5, + "Could not add corner {}. A corner with that name already exists.", + corner_name); + } + _dbCorner* corner = block->corner_tbl_->create(); corner->name_ = corner_name; + block->corners_.push_back(corner->getId()); + return (dbCorner*) corner; } @@ -86,6 +106,14 @@ void dbCorner::destroy(dbCorner* corner_) { _dbCorner* corner = (_dbCorner*) corner_; _dbBlock* block = (_dbBlock*) corner->getOwner(); + + // Note that std::ranges::find requires the search value + // to be the same type as the range's elements. + const dbId<_dbCorner> corner_id = corner->getId(); + auto corner_position = std::ranges::find(block->corners_, corner_id); + block->corners_.erase(corner_position); + + dbProperty::destroyProperties(corner_); block->corner_tbl_->destroy(corner); } diff --git a/src/odb/src/swig/tcl/odb.tcl b/src/odb/src/swig/tcl/odb.tcl index fddc3c56761..6ea93e1630d 100644 --- a/src/odb/src/swig/tcl/odb.tcl +++ b/src/odb/src/swig/tcl/odb.tcl @@ -13,7 +13,7 @@ proc add_corner { args } { utl::error ODB 1 "Could not add corner $corner_name. No block found." } - $block addCorner $corner_name + odb::dbCorner_create $block $corner_name } sta::define_cmd_args "remove_corner" {corner_name} @@ -28,7 +28,14 @@ proc remove_corner { args } { utl::error ODB 7 "Could not remove corner $corner_name. No block found." } - $block removeCorner $corner_name + set corner [$block findCorner $corner_name] + + if { $corner == "NULL" } { + utl::error ODB 6 \ + "Could not remove corner $corner_name. No corner with that name exists." + } + + odb::dbCorner_destroy $corner } sta::define_cmd_args "remove_corners" {} diff --git a/src/odb/test/cpp/TestCorner.cpp b/src/odb/test/cpp/TestCorner.cpp index 409e152529e..d05aba7d8ef 100644 --- a/src/odb/test/cpp/TestCorner.cpp +++ b/src/odb/test/cpp/TestCorner.cpp @@ -19,15 +19,15 @@ TEST_F(SimpleDbFixture, test_add_find_remove) createSimpleDB(); dbBlock* block = db_->getChip()->getBlock(); - block->addCorner("corner1"); - block->addCorner("corner2"); + dbCorner::create(block, "corner1"); + dbCorner::create(block, "corner2"); EXPECT_EQ(block->findCorner("corner1")->getName(), "corner1"); EXPECT_EQ(block->findCorner("corner2")->getName(), "corner2"); EXPECT_EQ(block->findCorner("missing"), nullptr); // Removing one corner leaves the others in place. - block->removeCorner("corner1"); + dbCorner::destroy(block->findCorner("corner1")); EXPECT_EQ(block->findCorner("corner1"), nullptr); EXPECT_NE(block->findCorner("corner2"), nullptr); @@ -41,9 +41,9 @@ TEST_F(SimpleDbFixture, test_add_duplicate_errors) createSimpleDB(); dbBlock* block = db_->getChip()->getBlock(); - block->addCorner("corner1"); + dbCorner::create(block, "corner1"); // Adding a second corner with the same name is a fatal error. - EXPECT_THROW(block->addCorner("corner1"), std::runtime_error); + EXPECT_THROW(dbCorner::create(block, "corner1"), std::runtime_error); } TEST_F(SimpleDbFixture, test_add_empty_name_errors) @@ -52,15 +52,15 @@ TEST_F(SimpleDbFixture, test_add_empty_name_errors) dbBlock* block = db_->getChip()->getBlock(); // Empty name is not allowed. - EXPECT_THROW(block->addCorner(""), std::runtime_error); + EXPECT_THROW(dbCorner::create(block, ""), std::runtime_error); } TEST_F(SimpleDbFixture, test_write_read_roundtrip) { createSimpleDB(); dbBlock* block = db_->getChip()->getBlock(); - block->addCorner("corner1"); - block->addCorner("corner2"); + dbCorner::create(block, "corner1"); + dbCorner::create(block, "corner2"); std::filesystem::create_directory("results"); const std::string tmp_path = "results/TestCorner.odb";