mpl: pin-aware macro halos#10558
Conversation
Signed-off-by: João Mai <jmai@precisioninno.com>
Signed-off-by: João Mai <jmai@precisioninno.com>
Signed-off-by: João Mai <jmai@precisioninno.com>
Signed-off-by: João Mai <jmai@precisioninno.com>
Signed-off-by: João Mai <jmai@precisioninno.com>
Signed-off-by: João Mai <jmai@precisioninno.com>
Signed-off-by: João Mai <jmai@precisioninno.com>
Signed-off-by: João Mai <jmai@precisioninno.com>
Signed-off-by: João Mai <jmai@precisioninno.com>
Signed-off-by: João Mai <jmai@precisioninno.com>
Signed-off-by: João Mai <jmai@precisioninno.com>
Signed-off-by: João Mai <jmai@precisioninno.com>
Signed-off-by: João Mai <jmai@precisioninno.com>
Signed-off-by: João Mai <jmai@precisioninno.com>
Signed-off-by: João Mai <jmai@precisioninno.com>
Signed-off-by: João Mai <jmai@precisioninno.com>
Signed-off-by: João Mai <jmai@precisioninno.com>
Signed-off-by: João Mai <jmai@precisioninno.com>
Signed-off-by: João Mai <jmai@precisioninno.com>
There was a problem hiding this comment.
Code Review
This pull request introduces a new -use_full_halo option to the RTL macro placer, allowing users to disable pin-awareness during halo assignment and restore uniform halo behavior. The implementation adds pin-aware halo construction logic, cluster-based macro orientation correction, and updates corresponding tests. The review feedback identifies a high-severity risk of null pointer dereferences and undefined behavior when querying pin geometries in buildMacroHalo. Additionally, it suggests performance optimizations, such as caching the macro halo object and using a set to track visited masters in getMinimumSpacing to avoid redundant scans on large designs.
| auto direction | ||
| = (mpin->getGeometry().begin())->getTechLayer()->getDirection(); |
There was a problem hiding this comment.
Dereferencing mpin->getGeometry().begin() without checking if the geometry list is empty can lead to undefined behavior or a crash. Additionally, getTechLayer() can return nullptr, which would cause a null pointer dereference when calling getDirection().
It is safer to retrieve the tech layer and direction inside the loop over mpin->getGeometry(), checking for null pointers first. This also correctly handles pins with multiple geometries on different layers/directions.
for (odb::dbBox* box : mpin->getGeometry()) {
auto* layer = box->getTechLayer();
if (!layer) {
continue;
}
auto direction = layer->getDirection();
odb::Rect pin_rect = box->getBox();| if (inst->getHalo() != nullptr) { | ||
| odb::Rect inst_halo = inst->getHalo()->getBox(); | ||
| if (inst->getHalo()->isSoft()) { | ||
| full_halo = HardMacro::Halo(inst->getHalo()); |
There was a problem hiding this comment.
The method inst->getHalo() is called multiple times. Caching it in a local variable improves readability and avoids redundant function calls.
| if (inst->getHalo() != nullptr) { | |
| odb::Rect inst_halo = inst->getHalo()->getBox(); | |
| if (inst->getHalo()->isSoft()) { | |
| full_halo = HardMacro::Halo(inst->getHalo()); | |
| auto* inst_halo_obj = inst->getHalo(); | |
| if (inst_halo_obj != nullptr) { | |
| odb::Rect inst_halo = inst_halo_obj->getBox(); | |
| if (inst_halo_obj->isSoft()) { | |
| full_halo = HardMacro::Halo(inst_halo_obj); |
| int ClusteringEngine::getMinimumSpacing() const | ||
| { | ||
| int spacing = 0; | ||
|
|
||
| for (odb::dbInst* inst : block_->getInsts()) { | ||
| if (inst->isBlock()) { | ||
| odb::dbMaster* master = inst->getMaster(); | ||
|
|
||
| for (odb::dbBox* obs : master->getObstructions()) { | ||
| if (auto layer = obs->getTechLayer()) { | ||
| spacing = std::max(spacing, layer->getSpacing()); | ||
| } | ||
| } | ||
|
|
||
| for (odb::dbMTerm* mterm : master->getMTerms()) { | ||
| for (odb::dbMPin* mpin : mterm->getMPins()) { | ||
| for (odb::dbBox* geom : mpin->getGeometry()) { | ||
| if (auto layer = geom->getTechLayer()) { | ||
| spacing = std::max(spacing, layer->getSpacing()); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| return spacing; | ||
| } |
There was a problem hiding this comment.
The current implementation of getMinimumSpacing() has two performance bottlenecks:
- It iterates over all instances in the block (
block_->getInsts()), which can be extremely slow on large designs with millions of standard cells. SincecreateHardMacros()also iterates over all instances, this results in redundant full-scan passes. - It scans the obstructions and pins of the
dbMasterfor every macro instance. Since multiple macro instances often share the samedbMaster, this performs redundant scans of the same master geometries.
Using a std::unordered_set<odb::dbMaster*> to cache already visited masters avoids redundant geometry scans.
int ClusteringEngine::getMinimumSpacing() const
{
int spacing = 0;
std::unordered_set<odb::dbMaster*> visited_masters;
for (odb::dbInst* inst : block_->getInsts()) {
if (inst->isBlock()) {
odb::dbMaster* master = inst->getMaster();
if (!visited_masters.insert(master).second) {
continue;
}
for (odb::dbBox* obs : master->getObstructions()) {
if (auto layer = obs->getTechLayer()) {
spacing = std::max(spacing, layer->getSpacing());
}
}
for (odb::dbMTerm* mterm : master->getMTerms()) {
for (odb::dbMPin* mpin : mterm->getMPins()) {
for (odb::dbBox* geom : mpin->getGeometry()) {
if (auto layer = geom->getTechLayer()) {
spacing = std::max(spacing, layer->getSpacing());
}
}
}
}
}
}
return spacing;
}| for (odb::dbBox* box : mpin->getGeometry()) { | ||
| odb::Rect pin_rect = box->getBox(); | ||
|
|
||
| if (direction == odb::dbTechLayerDir::HORIZONTAL) { | ||
| int dist_left = pin_rect.xMin(); | ||
| int dist_right = master->getWidth() - pin_rect.xMax(); | ||
|
|
||
| if (dist_left < dist_right) { | ||
| halo.left = full_halo.left; | ||
| } else { | ||
| halo.right = full_halo.right; | ||
| } | ||
| } else { | ||
| int dist_bottom = pin_rect.yMin(); | ||
| int dist_top = master->getHeight() - pin_rect.yMax(); | ||
|
|
||
| if (dist_bottom < dist_top) { | ||
| halo.bottom = full_halo.bottom; | ||
| } else { | ||
| halo.top = full_halo.top; | ||
| } | ||
| } |
There was a problem hiding this comment.
how would this handle macros where the pins are inside the macro and therefore the access is actually from the layer above? in this case, if M3 has pins and is vertical, then you would be creating halos for the top and bottom even though it should be based on M4's orientation
There was a problem hiding this comment.
I don't recall seeing a macro with this kind of pin before, so the possibility didn't cross my mind. Are they closer to the boundary they should be accessed from or something like that?
|
🥳 |
Summary
-use_full_haloflag.Type of Change
Impact
MPL now assigns proper halos only on sides with signal pins, those without are assigned a smaller halo size based on minimum spacing rules. Old behavior can be restored using the
-use_full_haloflag.Verification
./etc/Build.sh).Misc
MPL will undergo nomenclature changes (using channel instead of halo and others) and some commands related to halos will be removed/revamped. Since this PR was deep in development when the changes were proposed, it was decided to finish this work first and then follow it up with necessary changes along with any other changes required in MPL.