Skip to content

Commit 3d0ea11

Browse files
authored
Merge pull request #8214 from osamahammad21/3dblox-parser
ODB: 3dBlox parsers
2 parents 6524d9a + bff58bc commit 3d0ea11

File tree

22 files changed

+1435
-3
lines changed

22 files changed

+1435
-3
lines changed

MODULE.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ bazel_dep(name = "or-tools", version = "9.12")
9090
bazel_dep(name = "spdlog", version = "1.15.1")
9191
bazel_dep(name = "tcmalloc", version = "0.0.0-20250331-43fcf6e")
9292
bazel_dep(name = "zlib", version = "1.3.1.bcr.5")
93+
bazel_dep(name = "yaml-cpp", version = "0.8.0")
9394

9495
# A from source build of QT that allows it to link into OpenROAD.
9596
# Building like any other bazel project. scripts in the docker folder

src/odb/BUILD

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ cc_library(
4242
"src/lefin/*.h",
4343
"src/lefout/*.cpp",
4444
"src/swig/common/swig_common.cpp",
45+
"src/3dblox/*.cpp",
46+
"src/3dblox/*.h",
4547
]),
4648
hdrs = glob(
4749
include = [
@@ -78,6 +80,7 @@ cc_library(
7880
"@boost.property_tree",
7981
"@boost.regex",
8082
"@boost.spirit",
83+
"@yaml-cpp",
8184
"@spdlog",
8285
"@tk_tcl//:tcl",
8386
"@zlib",

src/odb/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ add_subdirectory(src/lefout)
1616
add_subdirectory(src/def)
1717
add_subdirectory(src/zutil)
1818
add_subdirectory(src/cdl)
19+
add_subdirectory(src/3dblox)
1920
add_subdirectory(src/gdsin)
2021
add_subdirectory(src/gdsout)
2122

@@ -28,6 +29,7 @@ target_link_libraries(odb
2829
INTERFACE
2930
db
3031
cdl
32+
3dblox
3133
defin
3234
defout
3335
lefin

src/odb/include/odb/3dblox.h

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
// Copyright (c) 2019-2025, The OpenROAD Authors
3+
4+
#pragma once
5+
6+
#include <string>
7+
#include <vector>
8+
9+
namespace utl {
10+
class Logger;
11+
}
12+
13+
namespace odb {
14+
class dbDatabase;
15+
class ChipletDef;
16+
class ChipletRegion;
17+
class dbChip;
18+
class ChipletInst;
19+
class Connection;
20+
class DesignDef;
21+
class dbChipRegionInst;
22+
class dbChipInst;
23+
24+
class ThreeDBlox
25+
{
26+
public:
27+
ThreeDBlox(utl::Logger* logger, odb::dbDatabase* db);
28+
~ThreeDBlox() = default;
29+
void readDbv(const std::string& dbv_file);
30+
void readDbx(const std::string& dbx_file);
31+
32+
private:
33+
void createChiplet(const ChipletDef& chiplet);
34+
void createRegion(const ChipletRegion& region, dbChip* chip);
35+
void createDesignTopChiplet(const DesignDef& design);
36+
void createChipInst(const ChipletInst& chip_inst);
37+
void createConnection(const Connection& connection);
38+
dbChipRegionInst* resolvePath(const std::string& path,
39+
std::vector<dbChipInst*>& path_insts);
40+
41+
std::string resolveIncludePath(const std::string& include_path,
42+
const std::string& current_file_path);
43+
44+
utl::Logger* logger_ = nullptr;
45+
odb::dbDatabase* db_ = nullptr;
46+
};
47+
} // namespace odb

src/odb/include/odb/db.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7238,6 +7238,10 @@ class dbChipRegionInst : public dbObject
72387238
class dbDatabase : public dbObject
72397239
{
72407240
public:
7241+
void setDbuPerMicron(uint dbu_per_micron);
7242+
7243+
uint getDbuPerMicron() const;
7244+
72417245
dbSet<dbChip> getChips() const;
72427246

72437247
dbChip* findChip(const char* name) const;

src/odb/src/3dblox/3dblox.cpp

Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
// Copyright (c) 2019-2025, The OpenROAD Authors
3+
4+
#include "odb/3dblox.h"
5+
6+
#include <filesystem>
7+
8+
#include "dbvParser.h"
9+
#include "dbxParser.h"
10+
#include "objects.h"
11+
#include "odb/db.h"
12+
#include "utl/Logger.h"
13+
14+
namespace odb {
15+
16+
static std::map<std::string, std::string> dup_orient_map
17+
= {{"MY_R180", "MX"},
18+
{"MY_R270", "MX_R90"},
19+
{"MX_R180", "MY"},
20+
{"MX_R180", "MY_R90"},
21+
{"MZ_MY_R180", "MZ_MX"},
22+
{"MZ_MY_R270", "MZ_MX_R90"},
23+
{"MZ_MX_R180", "MZ_MY"},
24+
{"MZ_MX_R270", "MZ_MY_R90"}};
25+
26+
ThreeDBlox::ThreeDBlox(utl::Logger* logger, odb::dbDatabase* db)
27+
: logger_(logger), db_(db)
28+
{
29+
}
30+
31+
void ThreeDBlox::readDbv(const std::string& dbv_file)
32+
{
33+
DbvParser parser(logger_);
34+
DbvData data = parser.parseFile(dbv_file);
35+
if (db_->getDbuPerMicron() == 0) {
36+
db_->setDbuPerMicron(data.header.precision);
37+
} else {
38+
if (data.header.precision > db_->getDbuPerMicron()) {
39+
logger_->error(utl::ODB,
40+
526,
41+
"3DBV Parser Error: Precision is greater than dbu per "
42+
"micron already set for database");
43+
} else if (db_->getDbuPerMicron() % data.header.precision != 0) {
44+
logger_->error(utl::ODB,
45+
516,
46+
"3DBV Parser Error: Database DBU per micron ({}) must be "
47+
"a multiple of the precision ({}) in dbv file {}",
48+
db_->getDbuPerMicron(),
49+
data.header.precision,
50+
dbv_file);
51+
}
52+
}
53+
for (const auto& [_, chiplet] : data.chiplet_defs) {
54+
createChiplet(chiplet);
55+
}
56+
}
57+
58+
std::string ThreeDBlox::resolveIncludePath(const std::string& include_path,
59+
const std::string& current_file_path)
60+
{
61+
std::filesystem::path include_fs_path(include_path);
62+
if (include_fs_path.is_absolute()) {
63+
return include_fs_path.string();
64+
}
65+
std::filesystem::path current_fs_path(current_file_path);
66+
std::filesystem::path current_dir = current_fs_path.parent_path();
67+
std::filesystem::path resolved_path = current_dir / include_fs_path;
68+
return resolved_path.string();
69+
}
70+
71+
void ThreeDBlox::readDbx(const std::string& dbx_file)
72+
{
73+
DbxParser parser(logger_);
74+
DbxData data = parser.parseFile(dbx_file);
75+
for (const auto& include : data.header.includes) {
76+
std::string resolved_path = resolveIncludePath(include, dbx_file);
77+
if (include.find(".3dbv") != std::string::npos) {
78+
readDbv(resolved_path);
79+
} else if (include.find(".3dbx") != std::string::npos) {
80+
readDbx(resolved_path);
81+
}
82+
}
83+
createDesignTopChiplet(data.design);
84+
for (const auto& [_, chip_inst] : data.chiplet_instances) {
85+
createChipInst(chip_inst);
86+
}
87+
for (const auto& [_, connection] : data.connections) {
88+
createConnection(connection);
89+
}
90+
}
91+
92+
dbChip::ChipType getChipType(const std::string& type, utl::Logger* logger)
93+
{
94+
if (type == "die") {
95+
return dbChip::ChipType::DIE;
96+
}
97+
if (type == "rdl") {
98+
return dbChip::ChipType::RDL;
99+
}
100+
if (type == "ip") {
101+
return dbChip::ChipType::IP;
102+
}
103+
if (type == "substrate") {
104+
return dbChip::ChipType::SUBSTRATE;
105+
}
106+
if (type == "hier") {
107+
return dbChip::ChipType::HIER;
108+
}
109+
logger->error(
110+
utl::ODB, 527, "3DBV Parser Error: Invalid chip type: {}", type);
111+
}
112+
void ThreeDBlox::createChiplet(const ChipletDef& chiplet)
113+
{
114+
auto tech = db_->getTech(); // TODO: specify tech
115+
dbChip* chip = dbChip::create(
116+
db_, tech, chiplet.name, getChipType(chiplet.type, logger_));
117+
118+
chip->setWidth(chiplet.design_width * db_->getDbuPerMicron());
119+
chip->setHeight(chiplet.design_height * db_->getDbuPerMicron());
120+
chip->setThickness(chiplet.thickness * db_->getDbuPerMicron());
121+
chip->setShrink(chiplet.shrink);
122+
chip->setTsv(chiplet.tsv);
123+
124+
chip->setScribeLineEast(chiplet.scribe_line_right * db_->getDbuPerMicron());
125+
chip->setScribeLineWest(chiplet.scribe_line_left * db_->getDbuPerMicron());
126+
chip->setScribeLineNorth(chiplet.scribe_line_top * db_->getDbuPerMicron());
127+
chip->setScribeLineSouth(chiplet.scribe_line_bottom * db_->getDbuPerMicron());
128+
129+
chip->setSealRingEast(chiplet.seal_ring_right * db_->getDbuPerMicron());
130+
chip->setSealRingWest(chiplet.seal_ring_left * db_->getDbuPerMicron());
131+
chip->setSealRingNorth(chiplet.seal_ring_top * db_->getDbuPerMicron());
132+
chip->setSealRingSouth(chiplet.seal_ring_bottom * db_->getDbuPerMicron());
133+
134+
chip->setOffset(Point(chiplet.offset.x * db_->getDbuPerMicron(),
135+
chiplet.offset.y * db_->getDbuPerMicron()));
136+
for (const auto& [_, region] : chiplet.regions) {
137+
createRegion(region, chip);
138+
}
139+
}
140+
dbChipRegion::Side getChipRegionSide(const std::string& side,
141+
utl::Logger* logger)
142+
{
143+
if (side == "front") {
144+
return dbChipRegion::Side::FRONT;
145+
}
146+
if (side == "back") {
147+
return dbChipRegion::Side::BACK;
148+
}
149+
if (side == "internal") {
150+
return dbChipRegion::Side::INTERNAL;
151+
}
152+
if (side == "internal_ext") {
153+
return dbChipRegion::Side::INTERNAL_EXT;
154+
}
155+
logger->error(
156+
utl::ODB, 528, "3DBV Parser Error: Invalid chip region side: {}", side);
157+
}
158+
void ThreeDBlox::createRegion(const ChipletRegion& region, dbChip* chip)
159+
{
160+
dbTechLayer* layer = nullptr;
161+
if (!region.layer.empty()) {
162+
// TODO: add layer
163+
}
164+
dbChipRegion* chip_region = dbChipRegion::create(
165+
chip, region.name, getChipRegionSide(region.side, logger_), layer);
166+
Rect box;
167+
box.mergeInit();
168+
for (const auto& coord : region.coords) {
169+
box.merge(Point(coord.x * db_->getDbuPerMicron(),
170+
coord.y * db_->getDbuPerMicron()),
171+
box);
172+
}
173+
chip_region->setBox(box);
174+
}
175+
void ThreeDBlox::createDesignTopChiplet(const DesignDef& design)
176+
{
177+
dbChip* chip
178+
= dbChip::create(db_, nullptr, design.name, dbChip::ChipType::HIER);
179+
db_->setTopChip(chip);
180+
}
181+
void ThreeDBlox::createChipInst(const ChipletInst& chip_inst)
182+
{
183+
auto chip = db_->findChip(chip_inst.reference.c_str());
184+
if (chip == nullptr) {
185+
logger_->error(utl::ODB,
186+
519,
187+
"3DBX Parser Error: Chiplet instance reference {} not found "
188+
"for chip inst {}",
189+
chip_inst.reference,
190+
chip_inst.name);
191+
}
192+
dbChipInst* inst = dbChipInst::create(db_->getChip(), chip, chip_inst.name);
193+
inst->setLoc(Point3D(chip_inst.loc.x * db_->getDbuPerMicron(),
194+
chip_inst.loc.y * db_->getDbuPerMicron(),
195+
chip_inst.z * db_->getDbuPerMicron()));
196+
auto orient_str = chip_inst.orient;
197+
if (dup_orient_map.find(orient_str) != dup_orient_map.end()) {
198+
orient_str = dup_orient_map[orient_str];
199+
}
200+
auto orient = dbOrientType3D::fromString(orient_str);
201+
if (!orient.has_value()) {
202+
logger_->error(utl::ODB,
203+
525,
204+
"3DBX Parser Error: Invalid orient {} for chip inst {}",
205+
chip_inst.orient,
206+
chip_inst.name);
207+
}
208+
inst->setOrient(orient.value());
209+
}
210+
std::vector<std::string> splitPath(const std::string& path)
211+
{
212+
std::vector<std::string> parts;
213+
std::istringstream stream(path);
214+
std::string part;
215+
216+
while (std::getline(stream, part, '/')) {
217+
if (!part.empty()) {
218+
parts.push_back(part);
219+
}
220+
}
221+
222+
return parts;
223+
}
224+
225+
dbChipRegionInst* ThreeDBlox::resolvePath(const std::string& path,
226+
std::vector<dbChipInst*>& path_insts)
227+
{
228+
if (path == "~") {
229+
return nullptr;
230+
}
231+
// Split the path by '/'
232+
std::vector<std::string> path_parts = splitPath(path);
233+
234+
if (path_parts.empty()) {
235+
logger_->error(utl::ODB, 524, "3DBX Parser Error: Invalid path {}", path);
236+
}
237+
238+
// The last part should contain ".regions.regionName"
239+
std::string last_part = path_parts.back();
240+
size_t regions_pos = last_part.find(".regions.");
241+
if (regions_pos == std::string::npos) {
242+
return nullptr; // Invalid format
243+
}
244+
245+
// Extract chip instance name and region name from last part
246+
std::string last_chip_inst = last_part.substr(0, regions_pos);
247+
std::string region_name = last_part.substr(regions_pos + 9);
248+
249+
// Replace the last part with just the chip instance name
250+
path_parts.back() = last_chip_inst;
251+
252+
// TODO: Traverse hierarchy and find region
253+
path_insts.reserve(path_parts.size());
254+
dbChip* curr_chip = db_->getChip();
255+
dbChipInst* curr_chip_inst = nullptr;
256+
for (const auto& inst_name : path_parts) {
257+
curr_chip_inst = curr_chip->findChipInst(inst_name);
258+
if (curr_chip_inst == nullptr) {
259+
logger_->error(utl::ODB,
260+
522,
261+
"3DBX Parser Error: Chip instance {} not found in path {}",
262+
inst_name,
263+
path);
264+
}
265+
path_insts.push_back(curr_chip_inst);
266+
curr_chip = curr_chip_inst->getMasterChip();
267+
}
268+
auto region = curr_chip_inst->findChipRegionInst(region_name);
269+
if (region == nullptr) {
270+
logger_->error(utl::ODB,
271+
523,
272+
"3DBX Parser Error: Chip region {} not found in path {}",
273+
region_name,
274+
path);
275+
}
276+
return region;
277+
}
278+
void ThreeDBlox::createConnection(const Connection& connection)
279+
{
280+
auto top_path = connection.top;
281+
auto bottom_path = connection.bot;
282+
std::vector<dbChipInst*> top_region_path;
283+
std::vector<dbChipInst*> bottom_region_path;
284+
auto top_region = resolvePath(top_path, top_region_path);
285+
auto bottom_region = resolvePath(bottom_path, bottom_region_path);
286+
auto conn = odb::dbChipConn::create(connection.name,
287+
db_->getChip(),
288+
top_region_path,
289+
top_region,
290+
bottom_region_path,
291+
bottom_region);
292+
conn->setThickness(connection.thickness * db_->getDbuPerMicron());
293+
}
294+
} // namespace odb

0 commit comments

Comments
 (0)