Skip to content

Commit 99ead73

Browse files
ODB: read DEF file from ChipletInst in 3DBlox (#10077)
Per the 3DBlox standard (sec 4.3.1) the DEF file is associated with a ChipletInst (in the .3dbx) rather than the ChipletDef. Read it from either location (whichever exists) onto the master chiplet block. - Add defin::THREE_D_BLOX mode: reads a DEF onto an already-existing block, find-or-creating COMPONENTS/PINS/NETS and deduplicating data shared with the bump map (a bump may appear in both the bmap and the DEF) instead of erroring or duplicating. - 3dblox: create block+bumps eagerly at .3dbv time; read each chiplet's DEF once (chips_with_def_); error (ODB-547) when two instances of the same chiplet each carry a DEF. - Writers: emit def_file on a single ChipletInst in the .3dbx (no longer in the .3dbv), chosen deterministically by name; sort the 3dbv LEF_file output for determinism. Signed-off-by: Jorge Ferreira <jorge.ferreira@precisioninno.com>
1 parent d66535c commit 99ead73

12 files changed

Lines changed: 205 additions & 54 deletions

File tree

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ class ThreeDBlox
5353
void createRegion(const ChipletRegion& region, dbChip* chip);
5454
dbChip* createDesignTopChiplet(const DesignDef& design);
5555
void createChipInst(const ChipletInst& chip_inst);
56+
void readDefForChip(dbChip* chip, const std::string& def_file);
5657
void createConnection(const Connection& connection);
5758
void createBump(const BumpMapEntry& entry, dbChipRegion* chip_region);
5859
std::pair<dbInst*, dbBTerm*> createBump(const BumpMapEntry& entry,
@@ -69,5 +70,13 @@ class ThreeDBlox
6970
std::unordered_set<odb::dbTech*> written_techs_;
7071
std::unordered_set<odb::dbLib*> written_libs_;
7172
std::unordered_set<std::string> read_files_;
73+
// Master chips for which a chiplet instance has already supplied a DEF file.
74+
// Used to reject two instances of the same chiplet that each carry a DEF,
75+
// since they would write design data onto the same shared master block.
76+
std::unordered_set<odb::dbChip*> insts_with_def_;
77+
// Master chips whose design DEF has already been read. The DEF may be given
78+
// by the chiplet definition (3dbv) and/or a chiplet instance (3dbx); it is
79+
// read only once per chip so shared DEF data is not duplicated.
80+
std::unordered_set<odb::dbChip*> chips_with_def_;
7281
};
7382
} // namespace odb

src/odb/include/odb/defin.h

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,14 @@ class defin
2525
public:
2626
enum MODE
2727
{
28-
DEFAULT, // creates db from scratch (from def)
29-
FLOORPLAN, // update existing COMPONENTS PINS DIEAREA TRACKS ROWS NETS
30-
// SNETS
31-
INCREMENTAL // update existing COMPONENTS PINS
28+
DEFAULT, // creates db from scratch (from def)
29+
FLOORPLAN, // update existing COMPONENTS PINS DIEAREA TRACKS ROWS NETS
30+
// SNETS
31+
INCREMENTAL, // update existing COMPONENTS PINS
32+
THREE_D_BLOX // read DEF onto an existing 3DBlox block (created when the
33+
// chiplet definition was read); find-or-create
34+
// COMPONENTS/PINS/NETS and dedup data shared with the bump
35+
// map
3236
};
3337
defin(dbDatabase* db, utl::Logger* logger, MODE mode = DEFAULT);
3438
~defin();
@@ -42,8 +46,9 @@ class defin
4246
void continueOnErrors();
4347
void useBlockName(const char* name);
4448

45-
/// Create a new chip
46-
void readChip(std::vector<dbLib*>& search_libs,
49+
/// Read a DEF onto a chip. Returns true on success, false if the DEF file
50+
/// could not be read.
51+
bool readChip(std::vector<dbLib*>& search_libs,
4752
const char* def_file,
4853
dbChip* chip,
4954
bool issue_callback = true);

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

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -453,19 +453,6 @@ void ThreeDBlox::createChiplet(const ChipletDef& chiplet)
453453
db_, tech, chiplet.name, getChipType(chiplet.type, logger_));
454454
}
455455

456-
// Read DEF file
457-
if (!chiplet.external.def_file.empty()) {
458-
odb::defin def_reader(db_, logger_, odb::defin::DEFAULT);
459-
std::vector<odb::dbLib*> search_libs;
460-
for (odb::dbLib* lib : db_->getLibs()) {
461-
search_libs.push_back(lib);
462-
}
463-
// No callbacks here as we are going to give one postRead3Dbx later
464-
def_reader.readChip(search_libs,
465-
chiplet.external.def_file.c_str(),
466-
chip,
467-
/*issue_callback*/ false);
468-
}
469456
const int dbu_per_micron = db_->getDbuPerMicron();
470457
if (chiplet.design_width != -1.0) {
471458
chip->setWidth(std::round(chiplet.design_width * dbu_per_micron));
@@ -515,6 +502,35 @@ void ThreeDBlox::createChiplet(const ChipletDef& chiplet)
515502
for (const auto& [_, region] : chiplet.regions) {
516503
createRegion(region, chip);
517504
}
505+
506+
// Read the DEF file (if any) onto the block created above. The block and its
507+
// bumps already exist, so the read uses the 3DBlox defin mode, which
508+
// find-or-creates and dedups data shared with the bump map instead of
509+
// erroring because a block is already present.
510+
if (!chiplet.external.def_file.empty()) {
511+
readDefForChip(chip, chiplet.external.def_file);
512+
}
513+
}
514+
515+
void ThreeDBlox::readDefForChip(dbChip* chip, const std::string& def_file)
516+
{
517+
// A chiplet's design DEF only needs to be read once. The DEF may be supplied
518+
// by the chiplet definition (3dbv) and/or by a chiplet instance (3dbx); if it
519+
// was already read onto this chip, skip it so that DEF data not shared with
520+
// the bump map (rows, tracks, ...) is not duplicated.
521+
if (chips_with_def_.contains(chip)) {
522+
return;
523+
}
524+
odb::defin def_reader(db_, logger_, odb::defin::THREE_D_BLOX);
525+
std::vector<odb::dbLib*> search_libs;
526+
search_libs.assign(db_->getLibs().begin(), db_->getLibs().end());
527+
// No callbacks here as we are going to give one postRead3Dbx later. Mark the
528+
// chip only on success so a failed read (e.g. bad path) does not suppress a
529+
// later valid DEF for the same chip.
530+
if (def_reader.readChip(
531+
search_libs, def_file.c_str(), chip, /*issue_callback*/ false)) {
532+
chips_with_def_.insert(chip);
533+
}
518534
}
519535

520536
static dbChipRegion::Side getChipRegionSide(const std::string& side,
@@ -698,6 +714,21 @@ void ThreeDBlox::createChipInst(const ChipletInst& chip_inst)
698714
static_cast<int>(std::round(chip_inst.loc.y * dbu_per_micron)),
699715
static_cast<int>(std::round(chip_inst.z * dbu_per_micron)),
700716
});
717+
718+
// Per the 3DBlox standard, the DEF file may be associated with the chiplet
719+
// instance (ChipletInst.external) rather than the chiplet definition. Read it
720+
// onto the referenced master chip's block. The DEF is shared design data, so
721+
// two instances of the same chiplet may not each carry their own DEF.
722+
if (!chip_inst.external.def_file.empty()) {
723+
if (!insts_with_def_.insert(chip).second) {
724+
logger_->error(utl::ODB,
725+
547,
726+
"3DBX Parser Error: There can't be 2 instances of the "
727+
"same chiplet {} with a def file each",
728+
chip_inst.reference);
729+
}
730+
readDefForChip(chip, chip_inst.external.def_file);
731+
}
701732
}
702733
static std::vector<std::string> splitPath(const std::string& path)
703734
{

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

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@
33

44
#include "dbvWriter.h"
55

6+
#include <algorithm>
67
#include <filesystem>
78
#include <string>
89
#include <unordered_set>
10+
#include <vector>
911

1012
#include "baseWriter.h"
1113
#include "odb/db.h"
12-
#include "odb/defout.h"
1314
#include "utl/Logger.h"
1415
#include "yaml-cpp/yaml.h"
1516
namespace odb {
@@ -177,9 +178,8 @@ void DbvWriter::writeExternal(YAML::Node& external_node, odb::dbChip* chiplet)
177178
{
178179
if (chiplet->getChipType() != odb::dbChip::ChipType::HIER) {
179180
writeLef(external_node, chiplet);
180-
if (chiplet->getBlock() != nullptr) {
181-
writeDef(external_node, chiplet);
182-
}
181+
// Per the 3DBlox standard the DEF file is attached to a chiplet instance,
182+
// so it is emitted in the .3dbx (by the DbxWriter), not here in the .3dbv.
183183
if (auto prop = odb::dbStringProperty::find(chiplet, "verilog_file")) {
184184
external_node["verilog_file"] = prop->getValue();
185185
}
@@ -223,21 +223,18 @@ void DbvWriter::writeLef(YAML::Node& external_node, odb::dbChip* chiplet)
223223
YAML::Node list_node;
224224
list_node.SetStyle(YAML::EmitterStyle::Flow);
225225
external_node["LEF_file"] = list_node;
226+
// Sort so the output is deterministic regardless of the (unordered) set
227+
// iteration order.
228+
std::vector<std::string> lef_files;
229+
lef_files.reserve(libs.size());
226230
for (auto lib : libs) {
227-
std::string lef_file = std::string(lib->getName()) + "_lib.lef";
231+
lef_files.push_back(std::string(lib->getName()) + "_lib.lef");
232+
}
233+
std::ranges::sort(lef_files);
234+
for (const auto& lef_file : lef_files) {
228235
external_node["LEF_file"].push_back(lef_file);
229236
}
230237
}
231238
}
232239

233-
void DbvWriter::writeDef(YAML::Node& external_node, odb::dbChip* chiplet)
234-
{
235-
std::string def_file = std::string(chiplet->getName()) + ".def";
236-
std::string def_file_path = current_dir_path_ + def_file;
237-
odb::DefOut def_writer(logger_);
238-
auto block = chiplet->getBlock();
239-
def_writer.writeBlock(block, def_file_path.c_str());
240-
external_node["DEF_file"] = def_file;
241-
}
242-
243240
} // namespace odb

src/odb/src/3dblox/dbvWriter.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ class DbvWriter : public BaseWriter
3737
void writeRegion(YAML::Node& region_node, odb::dbChipRegion* region);
3838
void writeExternal(YAML::Node& external_node, odb::dbChip* chiplet);
3939
void writeLef(YAML::Node& external_node, odb::dbChip* chiplet);
40-
void writeDef(YAML::Node& external_node, odb::dbChip* chiplet);
4140
};
4241

4342
} // namespace odb

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

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@
33

44
#include "dbxWriter.h"
55

6+
#include <filesystem>
67
#include <string>
8+
#include <unordered_map>
79
#include <vector>
810

911
#include "baseWriter.h"
1012
#include "odb/db.h"
13+
#include "odb/defout.h"
1114
#include "odb/geom.h"
1215
#include "utl/Logger.h"
1316
#include "yaml-cpp/yaml.h"
@@ -21,6 +24,10 @@ DbxWriter::DbxWriter(utl::Logger* logger, odb::dbDatabase* db)
2124

2225
void DbxWriter::writeChiplet(const std::string& filename, odb::dbChip* chiplet)
2326
{
27+
auto path = std::filesystem::path(filename);
28+
if (path.has_parent_path()) {
29+
current_dir_path_ = path.parent_path().string() + "/";
30+
}
2431
YAML::Node root;
2532
writeYamlContent(root, chiplet);
2633
writeYamlToFile(filename, root);
@@ -56,17 +63,49 @@ void DbxWriter::writeDesign(YAML::Node& design_node, odb::dbChip* chiplet)
5663
void DbxWriter::writeChipletInsts(YAML::Node& instances_node,
5764
odb::dbChip* chiplet)
5865
{
66+
// The DEF is master (design) data shared by all instances of a chiplet, so it
67+
// is emitted for exactly one instance per master chiplet. Choose that
68+
// instance deterministically as the one with the smallest name.
69+
std::unordered_map<odb::dbChip*, std::string> def_inst_by_master;
70+
for (auto inst : chiplet->getChipInsts()) {
71+
odb::dbChip* master = inst->getMasterChip();
72+
if (master->getBlock() == nullptr) {
73+
continue;
74+
}
75+
const std::string& name = inst->getName();
76+
auto it = def_inst_by_master.find(master);
77+
if (it == def_inst_by_master.end() || name < it->second) {
78+
def_inst_by_master[master] = name;
79+
}
80+
}
81+
5982
for (auto inst : chiplet->getChipInsts()) {
6083
YAML::Node instance_node = instances_node[std::string(inst->getName())];
61-
writeChipletInst(instance_node, inst);
84+
auto it = def_inst_by_master.find(inst->getMasterChip());
85+
const bool write_def
86+
= it != def_inst_by_master.end() && it->second == inst->getName();
87+
writeChipletInst(instance_node, inst, write_def);
6288
}
6389
}
6490

6591
void DbxWriter::writeChipletInst(YAML::Node& instance_node,
66-
odb::dbChipInst* inst)
92+
odb::dbChipInst* inst,
93+
bool write_def)
6794
{
68-
auto master_name = inst->getMasterChip()->getName();
69-
instance_node["reference"] = master_name;
95+
odb::dbChip* master = inst->getMasterChip();
96+
instance_node["reference"] = master->getName();
97+
98+
// Per the 3DBlox standard the DEF file is attached to a chiplet instance.
99+
// Dump the master block to a DEF and reference it from a single instance of
100+
// each master chiplet.
101+
if (write_def) {
102+
std::string def_file = std::string(master->getName()) + ".def";
103+
odb::DefOut def_writer(logger_);
104+
def_writer.writeBlock(master->getBlock(),
105+
(current_dir_path_ + def_file).c_str());
106+
YAML::Node external_node = instance_node["external"];
107+
external_node["def_file"] = def_file;
108+
}
70109
}
71110

72111
void DbxWriter::writeStack(YAML::Node& stack_node, odb::dbChip* chiplet)

src/odb/src/3dblox/dbxWriter.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ class DbxWriter : public BaseWriter
3434
void writeDesign(YAML::Node& design_node, odb::dbChip* chiplet);
3535
void writeDesignExternal(YAML::Node& external_node, odb::dbChip* chiplet);
3636
void writeChipletInsts(YAML::Node& instances_node, odb::dbChip* chiplet);
37-
void writeChipletInst(YAML::Node& instance_node, odb::dbChipInst* inst);
37+
void writeChipletInst(YAML::Node& instance_node,
38+
odb::dbChipInst* inst,
39+
bool write_def);
3840
void writeStack(YAML::Node& stack_node, odb::dbChip* chiplet);
3941
void writeStackInstance(YAML::Node& stack_instance_node,
4042
odb::dbChipInst* inst);

src/odb/src/defin/defin.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,13 @@ void defin::useBlockName(const char* name)
6666
reader_->useBlockName(name);
6767
}
6868

69-
void defin::readChip(std::vector<dbLib*>& libs,
69+
bool defin::readChip(std::vector<dbLib*>& libs,
7070
const char* def_file,
7171
dbChip* chip,
7272
const bool issue_callback)
7373
{
7474
absl::MutexLock lock(&def_mutex_);
75-
reader_->readChip(libs, def_file, chip, issue_callback);
75+
return reader_->readChip(libs, def_file, chip, issue_callback);
7676
}
7777

7878
} // namespace odb

src/odb/src/defin/definComponent.cpp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,14 +136,33 @@ void definComponent::begin(const char* iname, const char* mname)
136136
_errors++;
137137
return;
138138
}
139-
if (_mode != defin::DEFAULT) {
139+
if (_mode == defin::FLOORPLAN || _mode == defin::INCREMENTAL) {
140140
_cur_inst = _block->findInst(iname);
141141
if (_cur_inst == nullptr) {
142142
_errors++;
143143
return;
144144
}
145145
_update_cnt++;
146146
} else {
147+
// DEFAULT and THREE_D_BLOX create the instance. THREE_D_BLOX first looks
148+
// for an existing instance (e.g. a bump already created from the bump map)
149+
// and reuses it instead of erroring on a duplicate definition.
150+
if (_mode == defin::THREE_D_BLOX) {
151+
_cur_inst = _block->findInst(iname);
152+
if (_cur_inst != nullptr) {
153+
if (_cur_inst->getMaster() != master) {
154+
_logger->warn(utl::ODB,
155+
549,
156+
"3DBlox DEF component {} reuses an existing instance "
157+
"with master {} but the DEF specifies master {}.",
158+
iname,
159+
_cur_inst->getMaster()->getName(),
160+
master->getName());
161+
}
162+
_update_cnt++;
163+
return;
164+
}
165+
}
147166
_cur_inst = dbInst::create(_block, master, iname);
148167
if (_cur_inst != nullptr) {
149168
_inst_cnt++;

src/odb/src/defin/definPin.cpp

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,40 @@ void definPin::pinBegin(const char* name, const char* net_name)
4343
const char* s = strstr(name, ".extra");
4444

4545
if (s == nullptr) {
46-
if (_mode != defin::DEFAULT) {
46+
if (_mode == defin::FLOORPLAN || _mode == defin::INCREMENTAL) {
4747
_cur_bterm = _block->findBTerm(name);
4848
if (_cur_bterm != nullptr) {
4949
_update_cnt++;
5050
}
51+
} else if (_mode == defin::THREE_D_BLOX) {
52+
// Find-or-create: a bump may have already created this bterm (and its
53+
// net) from the bump map, so reuse it instead of duplicating it.
54+
_cur_bterm = _block->findBTerm(name);
55+
if (_cur_bterm != nullptr) {
56+
_update_cnt++;
57+
// Make the DEF authoritative for the reused pin: connect it to the
58+
// net named by this PIN (idempotent if already the same net) and drop
59+
// any shapes created earlier (e.g. from the bump map) so the DEF's
60+
// port geometry is not duplicated.
61+
dbNet* existing_net = _cur_bterm->getNet();
62+
if (existing_net != nullptr && existing_net != net) {
63+
_logger->warn(utl::ODB,
64+
548,
65+
"3DBlox DEF reconnects pin {} from net {} to net {}.",
66+
name,
67+
existing_net->getName(),
68+
net->getName());
69+
}
70+
_cur_bterm->connect(net);
71+
std::vector<dbBPin*> old_bpins(_cur_bterm->getBPins().begin(),
72+
_cur_bterm->getBPins().end());
73+
for (dbBPin* bpin : old_bpins) {
74+
dbBPin::destroy(bpin);
75+
}
76+
} else {
77+
_cur_bterm = dbBTerm::create(net, name);
78+
_bterm_cnt++;
79+
}
5180
} else {
5281
_cur_bterm = dbBTerm::create(net, name);
5382
_bterm_cnt++;

0 commit comments

Comments
 (0)