Skip to content

Commit a166700

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. defin::readChip now reports success so the chiplet's DEF is marked read only on success. - 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. - external spec: single-valued external fields (def_file/verilog_file/sdc_file) accept a scalar or a single-element list via BaseParser::extractSinglePathFromList and error (ODB-521) on more than one entry. Signed-off-by: Jorge Ferreira <jorge.ferreira@precisioninno.com>
1 parent d66535c commit a166700

17 files changed

Lines changed: 262 additions & 87 deletions

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/baseParser.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,35 @@ void BaseParser::resolvePaths(const std::string& path,
172172
}
173173
}
174174

175+
std::string BaseParser::extractSinglePathFromList(const YAML::Node& parent,
176+
const std::string& key,
177+
const std::string& context)
178+
{
179+
const YAML::Node node = parent[key];
180+
if (!node) {
181+
return "";
182+
}
183+
std::vector<std::string> values;
184+
try {
185+
if (node.IsSequence()) {
186+
values = node.as<std::vector<std::string>>();
187+
} else {
188+
values.push_back(node.as<std::string>());
189+
}
190+
} catch (const YAML::Exception& e) {
191+
logError("Error parsing " + key + " for " + context + ": "
192+
+ std::string(e.what()));
193+
}
194+
if (values.size() > 1) {
195+
logError("Multiple " + key + " entries for " + context
196+
+ " are currently unsupported.");
197+
}
198+
if (values.empty()) {
199+
return "";
200+
}
201+
return resolvePath(values[0]);
202+
}
203+
175204
void BaseParser::logError(const std::string& message)
176205
{
177206
logger_->error(

src/odb/src/3dblox/baseParser.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@ class BaseParser
3636
void parseDefines(std::string& content);
3737
std::string resolvePath(const std::string& path);
3838
void resolvePaths(const std::string& path, std::vector<std::string>& paths);
39+
// Extracts a single resolved path from `parent[key]`, accepting either a
40+
// scalar or a single-element list. Returns "" if the key is absent/empty and
41+
// emits a parser error (terminal) when more than one entry is given, since
42+
// these external fields are single-valued per the 3DBlox spec. `context`
43+
// identifies the owning element in the error message.
44+
std::string extractSinglePathFromList(const YAML::Node& parent,
45+
const std::string& key,
46+
const std::string& context);
3947

4048
// Utility methods
4149
void logError(const std::string& message);

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

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -179,18 +179,11 @@ void DbvParser::parseChiplet(ChipletDef& chiplet,
179179
}
180180
}
181181
}
182-
if (chiplet_node["external"]["DEF_file"]) {
183-
extractValue(
184-
chiplet_node["external"], "DEF_file", chiplet.external.def_file);
185-
chiplet.external.def_file = resolvePath(chiplet.external.def_file);
186-
}
187-
if (chiplet_node["external"]["verilog_file"]) {
188-
extractValue(chiplet_node["external"],
189-
"verilog_file",
190-
chiplet.external.verilog_file);
191-
chiplet.external.verilog_file
192-
= resolvePath(chiplet.external.verilog_file);
193-
}
182+
const std::string context = "ChipletDef '" + chiplet.name + "'";
183+
chiplet.external.def_file = extractSinglePathFromList(
184+
chiplet_node["external"], "DEF_file", context);
185+
chiplet.external.verilog_file = extractSinglePathFromList(
186+
chiplet_node["external"], "verilog_file", context);
194187
}
195188
}
196189

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/dbxParser.cpp

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,8 @@ void DbxParser::parseDesign(DesignDef& design, const YAML::Node& design_node)
8989
void DbxParser::parseDesignExternal(DesignExternal& external,
9090
const YAML::Node& external_node)
9191
{
92-
if (external_node["verilog_file"]) {
93-
extractValue(external_node, "verilog_file", external.verilog_file);
94-
external.verilog_file = resolvePath(external.verilog_file);
95-
}
92+
external.verilog_file
93+
= extractSinglePathFromList(external_node, "verilog_file", "Design");
9694
}
9795

9896
void DbxParser::parseChipletInsts(std::map<std::string, ChipletInst>& instances,
@@ -126,27 +124,22 @@ void DbxParser::parseChipletInst(ChipletInst& instance,
126124
extractValue(instance_node, "reference", instance.reference);
127125

128126
if (instance_node["external"]) {
129-
parseChipletInstExternal(instance.external, instance_node["external"]);
127+
parseChipletInstExternal(
128+
instance.external, instance_node["external"], instance.name);
130129
}
131130
}
132131

133132
void DbxParser::parseChipletInstExternal(ChipletInstExternal& external,
134-
const YAML::Node& external_node)
133+
const YAML::Node& external_node,
134+
const std::string& instance_name)
135135
{
136-
if (external_node["verilog_file"]) {
137-
extractValue(external_node, "verilog_file", external.verilog_file);
138-
external.verilog_file = resolvePath(external.verilog_file);
139-
}
140-
141-
if (external_node["sdc_file"]) {
142-
extractValue(external_node, "sdc_file", external.sdc_file);
143-
external.sdc_file = resolvePath(external.sdc_file);
144-
}
145-
146-
if (external_node["def_file"]) {
147-
extractValue(external_node, "def_file", external.def_file);
148-
external.def_file = resolvePath(external.def_file);
149-
}
136+
const std::string context = "ChipletInst '" + instance_name + "'";
137+
external.verilog_file
138+
= extractSinglePathFromList(external_node, "verilog_file", context);
139+
external.sdc_file
140+
= extractSinglePathFromList(external_node, "sdc_file", context);
141+
external.def_file
142+
= extractSinglePathFromList(external_node, "def_file", context);
150143
}
151144

152145
void DbxParser::parseStack(std::map<std::string, ChipletInst>& instances,

src/odb/src/3dblox/dbxParser.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ class DbxParser : public BaseParser
3030
const YAML::Node& instances_node);
3131
void parseChipletInst(ChipletInst& instance, const YAML::Node& instance_node);
3232
void parseChipletInstExternal(ChipletInstExternal& external,
33-
const YAML::Node& external_node);
33+
const YAML::Node& external_node,
34+
const std::string& instance_name);
3435
void parseStack(std::map<std::string, ChipletInst>& instances,
3536
const YAML::Node& stack_node);
3637
void parseStackInstance(ChipletInst& instance,

0 commit comments

Comments
 (0)