Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/ord/OpenRoad.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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 readDb(std::istream& stream);
void readDb(const char* filename, bool hierarchy = false);
Expand Down
5 changes: 5 additions & 0 deletions src/OpenRoad.cc
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,11 @@ void OpenRoad::read3Dbx(const std::string& filename)
parser.readDbx(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 {
Expand Down
7 changes: 7 additions & 0 deletions src/OpenRoad.i
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,13 @@ read_3dbx_cmd(const char *filename)
ord->read3Dbx(filename);
}

void
write_3dbv_cmd(const char *filename)
{
OpenRoad *ord = getOpenRoad();
ord->write3Dbv(filename);
}

void
read_db_cmd(const char *filename, bool hierarchy)
{
Expand Down
9 changes: 9 additions & 0 deletions src/OpenRoad.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -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 } {
Expand Down
1 change: 1 addition & 0 deletions src/odb/include/odb/3dblox.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class ThreeDBlox
~ThreeDBlox() = default;
void readDbv(const std::string& dbv_file);
void readDbx(const std::string& dbx_file);
void writeDbv(const std::string& dbv_file);

private:
void createChiplet(const ChipletDef& chiplet);
Expand Down
7 changes: 7 additions & 0 deletions src/odb/src/3dblox/3dblox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include "bmapParser.h"
#include "dbvParser.h"
#include "dbvWriter.h"
#include "dbxParser.h"
#include "objects.h"
#include "odb/db.h"
Expand Down Expand Up @@ -83,6 +84,12 @@ void ThreeDBlox::readDbx(const std::string& dbx_file)
db_->triggerPostRead3Dbx(chip);
}

void ThreeDBlox::writeDbv(const std::string& dbv_file)
{
DbvWriter writer(logger_);
writer.writeFile(dbv_file, db_);
}

void ThreeDBlox::calculateSize(dbChip* chip)
{
Rect box;
Expand Down
3 changes: 3 additions & 0 deletions src/odb/src/3dblox/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ add_library(3dblox
bmapParser.cpp
dbvParser.cpp
dbxParser.cpp
baseWriter.cpp
dbvWriter.cpp
chipletHierarchy.cpp
3dblox.cpp
)

Expand Down
128 changes: 128 additions & 0 deletions src/odb/src/3dblox/baseWriter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2019-2025, The OpenROAD Authors

#include "baseWriter.h"

#include <yaml-cpp/emitter.h>
#include <yaml-cpp/emitterstyle.h>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: included header emitter.h is not used directly [misc-include-cleaner]

Suggested change
#include <yaml-cpp/emitterstyle.h>
#include <yaml-cpp/emitterstyle.h>

#include <yaml-cpp/node/convert.h>
#include <yaml-cpp/node/detail/impl.h>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: included header convert.h is not used directly [misc-include-cleaner]

Suggested change
#include <yaml-cpp/node/detail/impl.h>
#include <yaml-cpp/node/detail/impl.h>

#include <yaml-cpp/node/emit.h>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: included header impl.h is not used directly [misc-include-cleaner]

Suggested change
#include <yaml-cpp/node/emit.h>
#include <yaml-cpp/node/emit.h>

#include <yaml-cpp/node/node.h>

#include <cstddef>
#include <fstream>
#include <string>

#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,
539,
"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");
}
}
Comment on lines +50 to +98
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple of comments on this:

  • It's better and cleaner to separate the tech lef from the lib when we write, even though we don't enforce this when we read. So we shouldn't use writeTechAndLib.
  • We should infer the tech used by the current chip instead of using db->getTech(). we should use chip->getTech(). This would not work correctly on a design that has multiple technologies
  • We should resolve which libs are being used in the current chip instead of linking it to all libs in the current database. This could be done by going through the insts of the chip's block and collecting the set of libs used.


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, 538, "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
44 changes: 44 additions & 0 deletions src/odb/src/3dblox/baseWriter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2019-2025, The OpenROAD Authors

#pragma once

#include <yaml-cpp/node/node.h>

#include <string>

#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);
Comment on lines +29 to +34
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be moved to dbvWriter as dbxWriter should never handle lef and def.

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
90 changes: 90 additions & 0 deletions src/odb/src/3dblox/chipletHierarchy.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2019-2025, The OpenROAD Authors

#include "chipletHierarchy.h"

#include <memory>
#include <unordered_map>
#include <utility>
#include <vector>

#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<ChipletNode>(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<dbChip*>& 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<ChipletNode*> ChipletHierarchy::getRoots() const
{
std::vector<ChipletNode*> 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
36 changes: 36 additions & 0 deletions src/odb/src/3dblox/chipletHierarchy.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2019-2025, The OpenROAD Authors

#pragma once

#include <memory>
#include <unordered_map>
#include <vector>

#include "odb/db.h"

namespace odb {

struct ChipletNode
{
dbChip* chip;
std::vector<ChipletNode*> children;
std::vector<ChipletNode*> parents;
explicit ChipletNode(dbChip* c = nullptr) : chip(c) {}
};

class ChipletHierarchy
{
public:
ChipletHierarchy() = default;
void buildHierarchy(const std::vector<dbChip*>& all_chips);
ChipletNode* findNodeForChip(odb::dbChip* chip) const;

private:
ChipletNode* addChip(odb::dbChip* chip);
void addDependency(odb::dbChip* parent, odb::dbChip* child);
std::vector<ChipletNode*> getRoots() const;
std::unordered_map<odb::dbChip*, std::unique_ptr<ChipletNode>> nodes_;
};

} // namespace odb
Loading
Loading