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