diff --git a/include/ord/OpenRoad.hh b/include/ord/OpenRoad.hh index b635327bb6..b138b8133f 100644 --- a/include/ord/OpenRoad.hh +++ b/include/ord/OpenRoad.hh @@ -224,6 +224,7 @@ class OpenRoad void read3Dbv(const std::string& filename); void read3Dbx(const std::string& filename); + void write3Dbv(const std::string& filename); void read3DBloxBMap(const std::string& filename); void readDb(std::istream& stream); diff --git a/src/OpenRoad.cc b/src/OpenRoad.cc index 7302b34f73..48228fe256 100644 --- a/src/OpenRoad.cc +++ b/src/OpenRoad.cc @@ -502,7 +502,11 @@ void OpenRoad::read3DBloxBMap(const std::string& filename) odb::ThreeDBlox parser(logger_, db_); parser.readBMap(filename); } - +void OpenRoad::write3Dbv(const std::string& filename) +{ + odb::ThreeDBlox writer(logger_, db_, sta_); + writer.writeDbv(filename); +} void OpenRoad::readDb(const char* filename, bool hierarchy) { try { diff --git a/src/OpenRoad.i b/src/OpenRoad.i index a897f6adaa..83644405d0 100644 --- a/src/OpenRoad.i +++ b/src/OpenRoad.i @@ -376,6 +376,13 @@ read_3dblox_bmap_cmd(const char *filename) ord->read3DBloxBMap(filename); } +void +write_3dbv_cmd(const char *filename) +{ + OpenRoad *ord = getOpenRoad(); + ord->write3Dbv(filename); +} + void read_db_cmd(const char *filename, bool hierarchy) { diff --git a/src/OpenRoad.tcl b/src/OpenRoad.tcl index 6499461b41..7d7528c738 100644 --- a/src/OpenRoad.tcl +++ b/src/OpenRoad.tcl @@ -182,6 +182,15 @@ proc read_3dbv { args } { ord::read_3dbv_cmd $filename } +sta::define_cmd_args "write_3dbv" {filename} + +proc write_3dbv { args } { + sta::parse_key_args "write_3dbv" args keys {} flags {} + sta::check_argc_eq1 "write_3dbv" $args + set filename [file nativename [lindex $args 0]] + ord::write_3dbv_cmd $filename +} + sta::define_cmd_args "read_3dbx" {filename} proc read_3dbx { args } { diff --git a/src/odb/include/odb/3dblox.h b/src/odb/include/odb/3dblox.h index 38d8a75039..f558d60893 100644 --- a/src/odb/include/odb/3dblox.h +++ b/src/odb/include/odb/3dblox.h @@ -36,6 +36,7 @@ class ThreeDBlox void readDbx(const std::string& dbx_file); void readBMap(const std::string& bmap_file); void check(); + void writeDbv(const std::string& dbv_file); private: void createChiplet(const ChipletDef& chiplet); diff --git a/src/odb/src/3dblox/3dblox.cpp b/src/odb/src/3dblox/3dblox.cpp index 3e0f71d343..a5958d136e 100644 --- a/src/odb/src/3dblox/3dblox.cpp +++ b/src/odb/src/3dblox/3dblox.cpp @@ -14,6 +14,7 @@ #include "bmapParser.h" #include "checker.h" #include "dbvParser.h" +#include "dbvWriter.h" #include "dbxParser.h" #include "objects.h" #include "odb/db.h" @@ -93,6 +94,12 @@ void ThreeDBlox::check() checker.check(db_->getChip()); } +void ThreeDBlox::writeDbv(const std::string& dbv_file) +{ + DbvWriter writer(logger_); + writer.writeFile(dbv_file, db_); +} + void ThreeDBlox::calculateSize(dbChip* chip) { Rect box; diff --git a/src/odb/src/3dblox/CMakeLists.txt b/src/odb/src/3dblox/CMakeLists.txt index 019796b057..1ec109d9f8 100644 --- a/src/odb/src/3dblox/CMakeLists.txt +++ b/src/odb/src/3dblox/CMakeLists.txt @@ -8,6 +8,9 @@ add_library(3dblox bmapParser.cpp dbvParser.cpp dbxParser.cpp + baseWriter.cpp + dbvWriter.cpp + chipletHierarchy.cpp 3dblox.cpp checker.cpp ) diff --git a/src/odb/src/3dblox/baseWriter.cpp b/src/odb/src/3dblox/baseWriter.cpp new file mode 100644 index 0000000000..fa7664ad79 --- /dev/null +++ b/src/odb/src/3dblox/baseWriter.cpp @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2019-2025, The OpenROAD Authors + +#include "baseWriter.h" + +#include + +#include +#include +#include + +#include "odb/db.h" +#include "odb/defout.h" +#include "odb/lefout.h" +#include "utl/Logger.h" +#include "utl/ScopedTemporaryFile.h" + +namespace odb { + +BaseWriter::BaseWriter(utl::Logger* logger) : logger_(logger) +{ +} + +void BaseWriter::writeHeader(YAML::Node& header_node, odb::dbDatabase* db) +{ + header_node["version"] = "3"; + header_node["unit"] = "micron"; + header_node["precision"] = db->getDbuPerMicron(); +} + +void BaseWriter::writeYamlToFile(const std::string& filename, + const YAML::Node& root) +{ + std::ofstream out(filename); + if (!out) { + if (logger_ != nullptr) { + logError("cannot open " + filename); + } + return; + } + + out << root; +} + +void BaseWriter::writeLef(YAML::Node& external_node, + odb::dbDatabase* db, + odb::dbChip* chiplet) +{ + auto libs = db->getLibs(); + int num_libs = libs.size(); + if (num_libs > 0) { + if (num_libs > 1) { + logger_->info( + utl::ODB, + 541, + "More than one lib exists, multiple files will be written."); + } + int cnt = 0; + for (auto lib : libs) { + std::string name(std::string(lib->getName()) + ".lef"); + if (cnt > 0) { + auto pos = name.rfind('.'); + if (pos != std::string::npos) { + name.insert(pos, "_" + std::to_string(cnt)); + } else { + name += "_" + std::to_string(cnt); + } + utl::OutStreamHandler stream_handler(name.c_str()); + odb::lefout lef_writer(logger_, stream_handler.getStream()); + lef_writer.writeLib(lib); + } else { + utl::OutStreamHandler stream_handler(name.c_str()); + odb::lefout lef_writer(logger_, stream_handler.getStream()); + lef_writer.writeTechAndLib(lib); + } + YAML::Node list_node; + list_node.SetStyle(YAML::EmitterStyle::Flow); + list_node.push_back(name.c_str()); + if ((name.find("_tech") != std::string::npos) || (libs.size() == 1)) { + external_node["APR_tech_file"] = list_node; + } else { + external_node["LEF_file"] = list_node; + } + ++cnt; + } + } else if (db->getTech()) { + utl::OutStreamHandler stream_handler( + (std::string(chiplet->getName()) + ".lef").c_str()); + odb::lefout lef_writer(logger_, stream_handler.getStream()); + lef_writer.writeTech(db->getTech()); + external_node["APR_tech_file"] = (std::string(chiplet->getName()) + ".lef"); + } +} + +void BaseWriter::writeDef(YAML::Node& external_node, + odb::dbDatabase* db, + odb::dbChip* chiplet) +{ + odb::DefOut def_writer(logger_); + auto block = chiplet->getBlock(); + def_writer.writeBlock(block, + (std::string(chiplet->getName()) + ".def").c_str()); + external_node["DEF_file"] = std::string(chiplet->getName()) + ".def"; +} + +void BaseWriter::logError(const std::string& message) +{ + if (logger_ != nullptr) { + logger_->error(utl::ODB, 540, "Writer Error: {}", message); + } +} + +std::string BaseWriter::trim(const std::string& str) +{ + std::size_t first = str.find_first_not_of(' '); + if (first == std::string::npos) { + return ""; + } + std::size_t last = str.find_last_not_of(' '); + return str.substr(first, (last - first + 1)); +} + +} // namespace odb diff --git a/src/odb/src/3dblox/baseWriter.h b/src/odb/src/3dblox/baseWriter.h new file mode 100644 index 0000000000..79754c1871 --- /dev/null +++ b/src/odb/src/3dblox/baseWriter.h @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2019-2025, The OpenROAD Authors + +#pragma once + +#include + +#include + +#include "odb/db.h" +#include "odb/dbObject.h" +#include "odb/dbShape.h" +namespace utl { +class Logger; +} + +namespace odb { + +class BaseWriter +{ + public: + BaseWriter(utl::Logger* logger); + virtual ~BaseWriter() = default; + virtual void writeFile(const std::string& filename, odb::dbDatabase* db) = 0; + + protected: + // Common YAML content writing + void writeHeader(YAML::Node& header_node, odb::dbDatabase* db); + void writeLef(YAML::Node& external_node, + odb::dbDatabase* db, + odb::dbChip* chiplet); + void writeDef(YAML::Node& external_node, + odb::dbDatabase* db, + odb::dbChip* chiplet); + void logError(const std::string& message); + std::string trim(const std::string& str); + void writeYamlToFile(const std::string& filename, const YAML::Node& root); + + // Member variables + utl::Logger* logger_ = nullptr; + std::string current_file_path_; +}; + +} // namespace odb \ No newline at end of file diff --git a/src/odb/src/3dblox/chipletHierarchy.cpp b/src/odb/src/3dblox/chipletHierarchy.cpp new file mode 100644 index 0000000000..b7131df9b4 --- /dev/null +++ b/src/odb/src/3dblox/chipletHierarchy.cpp @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2019-2025, The OpenROAD Authors + +#include "chipletHierarchy.h" + +#include +#include +#include +#include + +#include "odb/db.h" + +namespace odb { + +ChipletNode* ChipletHierarchy::addChip(dbChip* chip) +{ + if (!chip) { + return nullptr; + } + + auto it = nodes_.find(chip); + if (it == nodes_.end()) { + auto node = std::make_unique(chip); + ChipletNode* ptr = node.get(); + nodes_[chip] = std::move(node); + return ptr; + } + return it->second.get(); +} + +void ChipletHierarchy::addDependency(dbChip* parent, dbChip* child) +{ + if (!parent || !child) { + return; + } + + ChipletNode* parent_node = addChip(parent); + ChipletNode* child_node = addChip(child); + + parent_node->children.push_back(child_node); + child_node->parents.push_back(parent_node); +} + +void ChipletHierarchy::buildHierarchy(const std::vector& all_chips) +{ + nodes_.clear(); + + for (odb::dbChip* parent : all_chips) { + ChipletNode* parent_node = addChip(parent); + if (!parent_node) { + continue; + } + + // Iterate instances to find dependencies + for (dbChipInst* inst : parent->getChipInsts()) { + // The instance master points to the child chiplets + odb::dbChip* child = inst->getMasterChip(); + if (child) { + addDependency(parent, child); + } + } + } +} + +std::vector ChipletHierarchy::getRoots() const +{ + std::vector roots; + for (auto& [chip, node] : nodes_) { + if (node->parents.empty()) { + roots.push_back(node.get()); + } + } + return roots; +} + +ChipletNode* ChipletHierarchy::findNodeForChip(dbChip* chip) const +{ + if (!chip) { + return nullptr; + } + + auto it = nodes_.find(chip); + if (it == nodes_.end()) { + return nullptr; + } + + return it->second.get(); +} + +} // namespace odb diff --git a/src/odb/src/3dblox/chipletHierarchy.h b/src/odb/src/3dblox/chipletHierarchy.h new file mode 100644 index 0000000000..2108fac6be --- /dev/null +++ b/src/odb/src/3dblox/chipletHierarchy.h @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2019-2025, The OpenROAD Authors + +#pragma once + +#include +#include +#include + +#include "odb/db.h" + +namespace odb { + +struct ChipletNode +{ + dbChip* chip; + std::vector children; + std::vector parents; + explicit ChipletNode(dbChip* c = nullptr) : chip(c) {} +}; + +class ChipletHierarchy +{ + public: + ChipletHierarchy() = default; + void buildHierarchy(const std::vector& all_chips); + ChipletNode* findNodeForChip(odb::dbChip* chip) const; + + private: + ChipletNode* addChip(odb::dbChip* chip); + void addDependency(odb::dbChip* parent, odb::dbChip* child); + std::vector getRoots() const; + std::unordered_map> nodes_; +}; + +} // namespace odb diff --git a/src/odb/src/3dblox/dbvWriter.cpp b/src/odb/src/3dblox/dbvWriter.cpp new file mode 100644 index 0000000000..0fe94bd7d1 --- /dev/null +++ b/src/odb/src/3dblox/dbvWriter.cpp @@ -0,0 +1,249 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2019-2025, The OpenROAD Authors + +#include "dbvWriter.h" + +#include + +#include +#include +#include + +#include "baseWriter.h" +#include "chipletHierarchy.h" +#include "odb/db.h" +#include "odb/geom.h" +#include "utl/Logger.h" + +namespace odb { + +DbvWriter::DbvWriter(utl::Logger* logger) : BaseWriter(logger) +{ +} + +void DbvWriter::writeFile(const std::string& filename, odb::dbDatabase* db) +{ + YAML::Node root; + writeYamlContent(root, db); + writeYamlToFile(filename, root); +} + +void DbvWriter::writeYamlContent(YAML::Node& root, odb::dbDatabase* db) +{ + YAML::Node header_node = root["Header"]; + writeHeader(header_node, db); + YAML::Node chiplets_node = root["ChipletDef"]; + writeChipletDefs(chiplets_node, db); +} + +void DbvWriter::writeChipletDefs(YAML::Node& chiplets_node, odb::dbDatabase* db) +{ + for (auto chiplet : db->getChips()) { + YAML::Node chiplet_node = chiplets_node[chiplet->getName()]; + writeChipletInternal(chiplet_node, chiplet, db); + } +} + +void DbvWriter::writeChipletInternal(YAML::Node& chiplet_node, + odb::dbChip* chiplet, + odb::dbDatabase* db) +{ + // Chiplet basic information + const auto chip_type = chiplet->getChipType(); + switch (chip_type) { + case (odb::dbChip::ChipType::DIE): + chiplet_node["type"] = "die"; + break; + case (odb::dbChip::ChipType::RDL): + chiplet_node["type"] = "rdl"; + break; + case (odb::dbChip::ChipType::IP): + chiplet_node["type"] = "ip"; + break; + case (odb::dbChip::ChipType::SUBSTRATE): + chiplet_node["type"] = "substrate"; + break; + case (odb::dbChip::ChipType::HIER): + chiplet_node["type"] = "hier"; + break; + default: + break; + } + const double u = db->getDbuPerMicron(); + auto width = chiplet->getWidth(); + auto height = chiplet->getHeight(); + YAML::Node dim_out; + dim_out.SetStyle(YAML::EmitterStyle::Flow); + dim_out.push_back(width / u); + dim_out.push_back(height / u); + chiplet_node["design_area"] = dim_out; + chiplet_node["thickness"] = chiplet->getThickness() / u; + chiplet_node["shrink"] = chiplet->getShrink(); + chiplet_node["tsv"] = chiplet->isTsv(); + + // Offset + auto offset_x = chiplet->getOffset().getX(); + auto offset_y = chiplet->getOffset().getY(); + YAML::Node off_out; + off_out.SetStyle(YAML::EmitterStyle::Flow); + off_out.push_back(offset_x / u); + off_out.push_back(offset_y / u); + chiplet_node["offset"] = off_out; + + // Seal Ring + YAML::Node sr_out; + sr_out.SetStyle(YAML::EmitterStyle::Flow); + sr_out.push_back(chiplet->getSealRingWest() / u); + sr_out.push_back(chiplet->getSealRingSouth() / u); + sr_out.push_back(chiplet->getSealRingEast() / u); + sr_out.push_back(chiplet->getSealRingNorth() / u); + chiplet_node["seal_ring_width"] = sr_out; + + // Scribe Line + YAML::Node sl_out; + sl_out.SetStyle(YAML::EmitterStyle::Flow); + sl_out.push_back(chiplet->getScribeLineWest() / u); + sl_out.push_back(chiplet->getScribeLineSouth() / u); + sl_out.push_back(chiplet->getScribeLineEast() / u); + sl_out.push_back(chiplet->getScribeLineNorth() / u); + chiplet_node["scribe_line_remaining_width"] = sl_out; + + // External files + YAML::Node external_node = chiplet_node["external"]; + writeExternal(external_node, chiplet, db); + + // Regions + YAML::Node regions_node = chiplet_node["regions"]; + writeRegions(regions_node, chiplet, db); +} + +void DbvWriter::writeRegions(YAML::Node& regions_node, + odb::dbChip* chiplet, + odb::dbDatabase* db) +{ + for (auto region : chiplet->getChipRegions()) { + YAML::Node region_node = regions_node[region->getName()]; + writeRegion(region_node, region, db); + } +} + +void DbvWriter::writeRegion(YAML::Node& region_node, + odb::dbChipRegion* region, + odb::dbDatabase* db) +{ + const auto side = region->getSide(); + switch (side) { + case odb::dbChipRegion::Side::FRONT: + region_node["side"] = "front"; + break; + case odb::dbChipRegion::Side::BACK: + region_node["side"] = "back"; + break; + case odb::dbChipRegion::Side::INTERNAL: + region_node["side"] = "internal"; + break; + case odb::dbChipRegion::Side::INTERNAL_EXT: + region_node["side"] = "internal_ext"; + break; + } + if (auto layer = region->getLayer(); layer != nullptr) { + region_node["layer"] = layer->getName(); + } + YAML::Node coords_node = region_node["coords"]; + writeCoordinates(coords_node, region->getBox(), db); +} + +void DbvWriter::writeExternal(YAML::Node& external_node, + odb::dbChip* chiplet, + odb::dbDatabase* db) +{ + if (chiplet->getChipType() != odb::dbChip::ChipType::HIER) { + BaseWriter::writeLef(external_node, db, chiplet); + if (db->getChip()->getBlock() != nullptr) { + BaseWriter::writeDef(external_node, db, chiplet); + } + } +} + +void DbvWriter::writeCoordinates(YAML::Node& coords_node, + const odb::Rect& rect, + odb::dbDatabase* db) +{ + const double u = db->getDbuPerMicron(); + YAML::Node c0, c1, c2, c3; + c0.SetStyle(YAML::EmitterStyle::Flow); + c1.SetStyle(YAML::EmitterStyle::Flow); + c2.SetStyle(YAML::EmitterStyle::Flow); + c3.SetStyle(YAML::EmitterStyle::Flow); + c0.push_back(rect.xMin() / u); + c0.push_back(rect.yMin() / u); + c1.push_back(rect.xMax() / u); + c1.push_back(rect.yMin() / u); + c2.push_back(rect.xMax() / u); + c2.push_back(rect.yMax() / u); + c3.push_back(rect.xMin() / u); + c3.push_back(rect.yMax() / u); + coords_node.push_back(c0); + coords_node.push_back(c1); + coords_node.push_back(c2); + coords_node.push_back(c3); +} + +void DbvWriter::writeChipDependencies(YAML::Node& header_node, + const ChipletNode* node) +{ + std::unordered_set included; + + for (auto child : node->children) { + auto* child_chip = child->chip; + if (child_chip->getChipType() == odb::dbChip::ChipType::HIER) { + // Add the child's .3dbx file include + std::string child_3dbx = std::string(child_chip->getName()) + ".3dbx"; + included.insert(child_3dbx); + // TODO: Call dbxWriter::writeChiplet(child_chip) + } + } + if (!included.empty()) { + YAML::Node includes_node = header_node["include"]; + for (const auto& include : included) { + includes_node.push_back(include); + } + } +} + +void DbvWriter::writeChipletToFile(const std::string& filename, + odb::dbChip* chiplet, + ChipletNode* node) +{ + YAML::Node root; + YAML::Node header_node = root["Header"]; + writeHeader(header_node, chiplet->getDb()); + + writeChipDependencies(header_node, node); + + YAML::Node chiplets_node = root["ChipletDef"]; + for (auto dependecy : node->children) { + YAML::Node chiplet_node = chiplets_node[dependecy->chip->getName()]; + writeChipletInternal(chiplet_node, dependecy->chip, chiplet->getDb()); + } + + writeYamlToFile(filename, root); +} + +void DbvWriter::writeChiplet(const std::string& base_filename, + odb::dbChip* top_chip) +{ + std::vector all_chips; + for (auto chiplet : top_chip->getDb()->getChips()) { + all_chips.push_back(chiplet); + } + + ChipletHierarchy hierarchy; + hierarchy.buildHierarchy(all_chips); + + auto chiplet_node = hierarchy.findNodeForChip(top_chip); + + writeChipletToFile(base_filename, top_chip, chiplet_node); +} + +} // namespace odb \ No newline at end of file diff --git a/src/odb/src/3dblox/dbvWriter.h b/src/odb/src/3dblox/dbvWriter.h new file mode 100644 index 0000000000..fccda038f3 --- /dev/null +++ b/src/odb/src/3dblox/dbvWriter.h @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2019-2025, The OpenROAD Authors + +#pragma once + +#include + +#include + +#include "baseWriter.h" +#include "chipletHierarchy.h" +#include "odb/db.h" +#include "odb/geom.h" + +namespace utl { +class Logger; +} + +namespace odb { + +class DbvWriter : public BaseWriter +{ + public: + DbvWriter(utl::Logger* logger); + + void writeFile(const std::string& filename, odb::dbDatabase* db) override; + + void writeChiplet(const std::string& base_filename, odb::dbChip* top_chip); + + private: + void writeYamlContent(YAML::Node& root, odb::dbDatabase* db); + void writeChipletToFile(const std::string& filename, + odb::dbChip* chiplet, + ChipletNode* node); + void writeChipletDefs(YAML::Node& chiplets_node, odb::dbDatabase* db); + void writeChipletInternal(YAML::Node& chiplet_node, + odb::dbChip* chiplet, + odb::dbDatabase* db); + void writeRegions(YAML::Node& regions_node, + odb::dbChip* chiplet, + odb::dbDatabase* db); + void writeRegion(YAML::Node& region_node, + odb::dbChipRegion* region, + odb::dbDatabase* db); + void writeExternal(YAML::Node& external_node, + odb::dbChip* chiplet, + odb::dbDatabase* db); + void writeCoordinates(YAML::Node& coords_node, + const odb::Rect& rect, + odb::dbDatabase* db); + void writeChipDependencies(YAML::Node& header_node, const ChipletNode* node); +}; + +} // namespace odb diff --git a/src/odb/test/BUILD b/src/odb/test/BUILD index 781fd105a8..cb22834c2c 100644 --- a/src/odb/test/BUILD +++ b/src/odb/test/BUILD @@ -198,6 +198,7 @@ COMPULSORY_TESTS = [ "transform", "wire_encoder", "write_cdl", + "write_3dbv", "write_def58", "write_def58_gzip", "write_lef_and_def", diff --git a/src/odb/test/CMakeLists.txt b/src/odb/test/CMakeLists.txt index 58fedef6a1..8ebbf9bb9f 100644 --- a/src/odb/test/CMakeLists.txt +++ b/src/odb/test/CMakeLists.txt @@ -56,6 +56,7 @@ or_integration_tests( transform wire_encoder write_cdl + write_3dbv write_def58 write_def58_gzip write_lef_and_def diff --git a/src/odb/test/write_3dbv.3dbvok b/src/odb/test/write_3dbv.3dbvok new file mode 100644 index 0000000000..9927058ba5 --- /dev/null +++ b/src/odb/test/write_3dbv.3dbvok @@ -0,0 +1,26 @@ +Header: + version: 3 + unit: micron + precision: 2000 +ChipletDef: + SoC: + type: die + design_area: [955, 1082] + thickness: 300 + shrink: 1 + tsv: false + offset: [0, 0] + seal_ring_width: [-1, -1, -1, -1] + scribe_line_remaining_width: [-1, -1, -1, -1] + external: + APR_tech_file: [Nangate45_tech.lef] + LEF_file: [fake_bumps_2.lef] + DEF_file: SoC.def + regions: + r1: + side: front + coords: + - [0, 0] + - [955, 0] + - [955, 1082] + - [0, 1082] \ No newline at end of file diff --git a/src/odb/test/write_3dbv.ok b/src/odb/test/write_3dbv.ok new file mode 100644 index 0000000000..47a912fa7f --- /dev/null +++ b/src/odb/test/write_3dbv.ok @@ -0,0 +1,10 @@ +[INFO ODB-0227] LEF file: data/../Nangate45/Nangate45_tech.lef, created 22 layers, 27 vias +[INFO ODB-0227] LEF file: data/../Nangate45/fake_macros.lef, created 10 library cells +[INFO ODB-0227] LEF file: data/../Nangate45/fake_bumps.lef, created 1 library cells +[WARNING STA-1171] data/../Nangate45/fake_macros.lib line 32, default_max_transition is 0.0. +[INFO ODB-0128] Design: fake_macros +[INFO ODB-0131] Created 10 components and 32 component-terminals. +[INFO ODB-0133] Created 12 nets and 24 connections. +[INFO ODB-0539] More than one lib exists, multiple files will be written. +No differences found. +pass diff --git a/src/odb/test/write_3dbv.tcl b/src/odb/test/write_3dbv.tcl new file mode 100644 index 0000000000..46383a7fa3 --- /dev/null +++ b/src/odb/test/write_3dbv.tcl @@ -0,0 +1,13 @@ +source "helpers.tcl" + +set db [ord::get_db] +set tech [odb::dbTech_create $db "tech"] + +read_3dbv "data/example.3dbv" +set out_3dbv [make_result_file "write_3dbv.3dbv"] +set 3dbv_write_result [write_3dbv $out_3dbv] + +diff_files $out_3dbv "write_3dbv.3dbvok" + +puts "pass" +exit 0