diff --git a/src/pdn/include/pdn/PdnGen.hh b/src/pdn/include/pdn/PdnGen.hh index 3daa952a7b5..0989dc24298 100644 --- a/src/pdn/include/pdn/PdnGen.hh +++ b/src/pdn/include/pdn/PdnGen.hh @@ -72,15 +72,18 @@ class PdnGen void setCoreDomain(odb::dbNet* power, odb::dbNet* switched_power, odb::dbNet* ground, - const std::vector& secondary); + const std::vector& secondary_power, + const std::vector& secondary_ground); void makeRegionVoltageDomain(const std::string& name, odb::dbNet* power, odb::dbNet* switched_power, odb::dbNet* ground, - const std::vector& secondary_nets, + const std::vector& secondary_power, + const std::vector& secondary_ground, odb::dbRegion* region); // Grids + void run(bool trim, bool add_pins, const std::string& report_file); void buildGrids(bool trim); std::vector findGrid(const std::string& name) const; void makeCoreGrid(VoltageDomain* domain, diff --git a/src/pdn/src/PdnGen.cc b/src/pdn/src/PdnGen.cc index 6466e95a42b..351c07f3b2d 100644 --- a/src/pdn/src/PdnGen.cc +++ b/src/pdn/src/PdnGen.cc @@ -29,6 +29,7 @@ #include "straps.h" #include "techlayer.h" #include "utl/Logger.h" +#include "utl/timer.h" #include "via.h" #include "via_repair.h" @@ -58,6 +59,14 @@ void PdnGen::resetShapes() updateRenderer(); } +void PdnGen::run(bool trim, bool add_pins, const std::string& report_file) +{ + utl::Timer timer; + buildGrids(trim); + writeToDb(add_pins, report_file); + logger_->info(utl::PDN, 500, "Runtime: {:.2f}s", timer.elapsed()); +} + void PdnGen::buildGrids(bool trim) { debugPrint(logger_, utl::PDN, "Make", 1, "Build - begin"); @@ -254,7 +263,7 @@ VoltageDomain* PdnGen::getCoreDomain() const void PdnGen::ensureCoreDomain() { if (core_domain_ == nullptr) { - setCoreDomain(nullptr, nullptr, nullptr, {}); + setCoreDomain(nullptr, nullptr, nullptr, {}, {}); } } @@ -291,14 +300,15 @@ VoltageDomain* PdnGen::findDomain(const std::string& name) void PdnGen::setCoreDomain(odb::dbNet* power, odb::dbNet* switched_power, odb::dbNet* ground, - const std::vector& secondary) + const std::vector& secondary_power, + const std::vector& secondary_ground) { auto* block = db_->getChip()->getBlock(); if (core_domain_ != nullptr) { logger_->warn(utl::PDN, 183, "Replacing existing core voltage domain."); } core_domain_ = std::make_unique( - this, block, power, ground, secondary, logger_); + this, block, power, ground, secondary_power, secondary_ground, logger_); if (importUPF(core_domain_.get())) { if (switched_power) { @@ -315,7 +325,8 @@ void PdnGen::makeRegionVoltageDomain( odb::dbNet* power, odb::dbNet* switched_power, odb::dbNet* ground, - const std::vector& secondary_nets, + const std::vector& secondary_power, + const std::vector& secondary_ground, odb::dbRegion* region) { if (region == nullptr) { @@ -331,8 +342,15 @@ void PdnGen::makeRegionVoltageDomain( } } auto* block = db_->getChip()->getBlock(); - auto domain = std::make_unique( - this, name, block, power, ground, secondary_nets, region, logger_); + auto domain = std::make_unique(this, + name, + block, + power, + ground, + secondary_power, + secondary_ground, + region, + logger_); if (importUPF(domain.get())) { if (switched_power) { diff --git a/src/pdn/src/PdnGen.i b/src/pdn/src/PdnGen.i index 4cefcd2f84f..a63f5d2dbe2 100644 --- a/src/pdn/src/PdnGen.i +++ b/src/pdn/src/PdnGen.i @@ -44,16 +44,29 @@ using utl::PDN; namespace pdn { -void set_core_domain(odb::dbNet* power, odb::dbNet* switched_power, odb::dbNet* ground, const std::vector& secondary_nets) +void set_core_domain(odb::dbNet* power, + odb::dbNet* switched_power, + odb::dbNet* ground, + const std::vector& secondary_power, + const std::vector& secondary_ground) { PdnGen* pdngen = ord::getPdnGen(); - pdngen->setCoreDomain(power, switched_power, ground, secondary_nets); + pdngen->setCoreDomain( + power, switched_power, ground, secondary_power, secondary_ground); } -void make_region_domain(const char* name, odb::dbNet* power, odb::dbNet* switched_power, odb::dbNet* ground, const std::vector& secondary_nets, odb::dbRegion* region) +void make_region_domain(const char* name, + odb::dbNet* power, + odb::dbNet* switched_power, + odb::dbNet* ground, + const std::vector& secondary_power, + const std::vector& secondary_ground, + odb::dbRegion* region) { PdnGen* pdngen = ord::getPdnGen(); - pdngen->makeRegionVoltageDomain(name, power, switched_power, ground, secondary_nets, region); + pdngen->makeRegionVoltageDomain( + name, power, switched_power, ground, secondary_power, secondary_ground, + region); } void reset() @@ -68,6 +81,12 @@ void reset_shapes() pdngen->resetShapes(); } +void run_pdngen(bool trim, bool add_pins, const char* report_file) +{ + PdnGen* pdngen = ord::getPdnGen(); + pdngen->run(trim, add_pins, report_file); +} + void build_grids(bool trim = true) { PdnGen* pdngen = ord::getPdnGen(); diff --git a/src/pdn/src/domain.cpp b/src/pdn/src/domain.cpp index 17bbfa693c6..21a0f5467d0 100644 --- a/src/pdn/src/domain.cpp +++ b/src/pdn/src/domain.cpp @@ -18,11 +18,28 @@ namespace pdn { +namespace { +void reportSecondaryNets(utl::Logger* logger, + const char* label, + const std::vector& nets) +{ + if (nets.empty()) { + return; + } + std::string names; + for (auto* net : nets) { + names += net->getName() + " "; + } + logger->report(" Secondary {} nets: {}", label, names); +} +} // namespace + VoltageDomain::VoltageDomain(PdnGen* pdngen, odb::dbBlock* block, odb::dbNet* power, odb::dbNet* ground, - const std::vector& secondary_nets, + const std::vector& secondary_power, + const std::vector& secondary_ground, utl::Logger* logger) : name_("Core"), pdngen_(pdngen), @@ -30,7 +47,8 @@ VoltageDomain::VoltageDomain(PdnGen* pdngen, power_(power), switched_power_(nullptr), ground_(ground), - secondary_(secondary_nets), + secondary_power_(secondary_power), + secondary_ground_(secondary_ground), region_(nullptr), logger_(logger) { @@ -42,7 +60,8 @@ VoltageDomain::VoltageDomain(PdnGen* pdngen, odb::dbBlock* block, odb::dbNet* power, odb::dbNet* ground, - const std::vector& secondary_nets, + const std::vector& secondary_power, + const std::vector& secondary_ground, odb::dbRegion* region, utl::Logger* logger) : name_(name), @@ -51,7 +70,8 @@ VoltageDomain::VoltageDomain(PdnGen* pdngen, power_(power), switched_power_(nullptr), ground_(ground), - secondary_(secondary_nets), + secondary_power_(secondary_power), + secondary_ground_(secondary_ground), region_(region), logger_(logger) { @@ -75,21 +95,31 @@ std::vector VoltageDomain::getNets(bool start_with_power) const std::vector nets; if (start_with_power) { + // Order: primary power, switched power, secondary power nets, + // secondary ground nets, primary ground. + // This groups all supply rails together before the ground rails, + // fixing the ordering issue for multi-rail designs (e.g., VDDA, VDD, VSS). nets.push_back(power_); if (switched_power_ != nullptr) { nets.push_back(switched_power_); } + nets.insert(nets.end(), secondary_power_.begin(), secondary_power_.end()); + nets.insert( + nets.end(), secondary_ground_.begin(), secondary_ground_.end()); nets.push_back(ground_); } else { + // Reverse order: primary ground, secondary ground nets, + // secondary power nets, switched power, primary power. nets.push_back(ground_); - nets.push_back(power_); + nets.insert( + nets.end(), secondary_ground_.begin(), secondary_ground_.end()); + nets.insert(nets.end(), secondary_power_.begin(), secondary_power_.end()); if (switched_power_ != nullptr) { nets.push_back(switched_power_); } + nets.push_back(power_); } - nets.insert(nets.end(), secondary_.begin(), secondary_.end()); - return nets; } @@ -210,13 +240,8 @@ void VoltageDomain::report() const logger_->report(" Switched power net: {}", switched_power_->getName()); } - if (!secondary_.empty()) { - std::string nets; - for (auto* net : secondary_) { - nets += net->getName() + " "; - } - logger_->report(" Secondary nets: {}", nets); - } + reportSecondaryNets(logger_, "power", secondary_power_); + reportSecondaryNets(logger_, "ground", secondary_ground_); for (const auto& grid : grids_) { grid->report(); diff --git a/src/pdn/src/domain.h b/src/pdn/src/domain.h index 39dc9572272..28c8ad5fbad 100644 --- a/src/pdn/src/domain.h +++ b/src/pdn/src/domain.h @@ -33,7 +33,8 @@ class VoltageDomain odb::dbBlock* block, odb::dbNet* power, odb::dbNet* ground, - const std::vector& secondary_nets, + const std::vector& secondary_power, + const std::vector& secondary_ground, odb::dbRegion* region, utl::Logger* logger); @@ -41,7 +42,8 @@ class VoltageDomain odb::dbBlock* block, odb::dbNet* power, odb::dbNet* ground, - const std::vector& secondary_nets, + const std::vector& secondary_power, + const std::vector& secondary_ground, utl::Logger* logger); // Core const std::string& getName() const { return name_; } @@ -54,6 +56,14 @@ class VoltageDomain odb::dbNet* getGround() const { return ground_; } odb::dbNet* getAlwaysOnPower() const { return power_; } odb::dbNet* getSwitchedPower() const { return switched_power_; } + const std::vector& getSecondaryPower() const + { + return secondary_power_; + } + const std::vector& getSecondaryGround() const + { + return secondary_ground_; + } void setSwitchedPower(odb::dbNet* switched_power) { @@ -62,6 +72,9 @@ class VoltageDomain bool hasSwitchedPower() const { return switched_power_ != nullptr; } // returns the order in which the nets should be arranged in the grid shapes + // ordering: [power, switched_power?, ...secondary_power, + // ...secondary_ground, ground] when start_with_power is true, + // and the reverse when false. std::vector getNets(bool start_with_power = true) const; bool hasRegion() const { return region_ != nullptr; } @@ -90,7 +103,8 @@ class VoltageDomain odb::dbNet* power_; odb::dbNet* switched_power_; odb::dbNet* ground_; - std::vector secondary_; + std::vector secondary_power_; + std::vector secondary_ground_; odb::dbRegion* region_; diff --git a/src/pdn/src/pdn.tcl b/src/pdn/src/pdn.tcl index a58fcabc5e3..987b665acf3 100644 --- a/src/pdn/src/pdn.tcl +++ b/src/pdn/src/pdn.tcl @@ -1,6 +1,21 @@ # SPDX-License-Identifier: BSD-3-Clause # Copyright (c) 2022-2025, The OpenROAD Authors +# Resolve a list of net name strings to dbNet objects. +# Reports PDN error $err_id if any name is not found. +proc pdn::collect_secondary_nets { net_names err_id type_label } { + set nets {} + foreach snet $net_names { + set db_net [[ord::get_db_block] findNet $snet] + if { $db_net == "NULL" } { + utl::error PDN $err_id "Unable to find secondary $type_label net: $snet" + } else { + lappend nets $db_net + } + } + return $nets +} + sta::define_cmd_args "pdngen" {[-skip_trim] \ [-dont_add_pins] \ [-reset] \ @@ -62,8 +77,7 @@ proc pdngen { args } { } pdn::check_setup - pdn::build_grids $trim - pdn::write_to_db $add_pins $failed_via_report + pdn::run_pdngen $trim $add_pins $failed_via_report pdn::reset_shapes } @@ -71,12 +85,14 @@ sta::define_cmd_args "set_voltage_domain" {-name domain_name \ -power power_net_name \ -ground ground_net_name \ [-region region_name] \ - [-secondary_power secondary_power_net_name] \ + [-secondary_power list_of_secondary_power_nets] \ + [-secondary_ground list_of_secondary_ground_nets] \ [-switched_power switched_power_net_name]} proc set_voltage_domain { args } { sta::parse_key_args "set_voltage_domain" args \ - keys {-name -region -power -ground -secondary_power -switched_power} flags {} + keys {-name -region -power -ground -secondary_power -secondary_ground \ + -switched_power} flags {} sta::check_argc_eq0 "set_voltage_domain" $args @@ -115,16 +131,16 @@ proc set_voltage_domain { args } { } } - set secondary {} + set secondary_power {} if { [info exists keys(-secondary_power)] } { - foreach snet $keys(-secondary_power) { - set db_net [[ord::get_db_block] findNet $snet] - if { $db_net == "NULL" } { - utl::error PDN 1006 "Unable to find secondary power net: $snet" - } else { - lappend secondary $db_net - } - } + set secondary_power \ + [pdn::collect_secondary_nets $keys(-secondary_power) 1006 "power"] + } + + set secondary_ground {} + if { [info exists keys(-secondary_ground)] } { + set secondary_ground \ + [pdn::collect_secondary_nets $keys(-secondary_ground) 1007 "ground"] } set switched_power "NULL" @@ -148,9 +164,11 @@ proc set_voltage_domain { args } { if { [info exists keys(-name)] && [pdn::modify_voltage_domain_name $keys(-name)] != "Core" } { utl::warn PDN 1042 "Core voltage domain will be named \"Core\"." } - pdn::set_core_domain $pwr $switched_power $gnd $secondary + pdn::set_core_domain $pwr $switched_power $gnd $secondary_power \ + $secondary_ground } else { - pdn::make_region_domain $name $pwr $switched_power $gnd $secondary $region + pdn::make_region_domain $name $pwr $switched_power $gnd $secondary_power \ + $secondary_ground $region } } diff --git a/src/pdn/test/core_grid_secondary_ground.tcl b/src/pdn/test/core_grid_secondary_ground.tcl new file mode 100644 index 00000000000..696cf4da0e2 --- /dev/null +++ b/src/pdn/test/core_grid_secondary_ground.tcl @@ -0,0 +1,34 @@ +# Test that -secondary_ground nets are accepted and placed between the +# secondary power nets and the primary ground net. +# Strap group ordering for VDD / VDDA (secondary power) / VSSA (secondary +# ground) / VSS must be: VDD, VDDA, VSSA, VSS. +source "helpers.tcl" + +read_lef Nangate45/Nangate45.lef +read_def nangate_gcd/floorplan.def + +add_global_connection -net VDD -pin_pattern VDD -power +add_global_connection -net VSS -pin_pattern VSS -ground +add_global_connection -net VDDA -pin_pattern VDDA -power +add_global_connection -net VSSA -pin_pattern VSSA -ground + +set_voltage_domain -power VDD -ground VSS \ + -secondary_power VDDA \ + -secondary_ground VSSA + +define_pdn_grid -name "Core" +add_pdn_stripe -followpins -layer metal1 + +# All four nets participate in the metal4 straps. +add_pdn_stripe -layer metal4 -width 0.48 -pitch 20.0 -offset 2.0 +add_pdn_stripe -layer metal7 -width 1.40 -pitch 40.0 -offset 2.0 \ + -nets {VDD VSS} + +add_pdn_connect -layers {metal1 metal4} +add_pdn_connect -layers {metal4 metal7} + +pdngen + +set def_file [make_result_file core_grid_secondary_ground.def] +write_def $def_file +diff_files core_grid_secondary_ground.defok $def_file diff --git a/src/pdn/test/core_grid_secondary_power_ordering.tcl b/src/pdn/test/core_grid_secondary_power_ordering.tcl new file mode 100644 index 00000000000..ccb77b07cf6 --- /dev/null +++ b/src/pdn/test/core_grid_secondary_power_ordering.tcl @@ -0,0 +1,32 @@ +# Test that secondary power nets are ordered between primary power and ground. +# With -secondary_power VDDA, the strap group ordering must be: +# VDD (primary power), VDDA (secondary power), VSS (ground) +# rather than the old broken ordering of VDD, VSS, VDDA. +source "helpers.tcl" + +read_lef Nangate45/Nangate45.lef +read_def nangate_gcd/floorplan.def + +add_global_connection -net VDD -pin_pattern VDD -power +add_global_connection -net VSS -pin_pattern VSS -ground +add_global_connection -net VDDA -pin_pattern VDDA -power + +set_voltage_domain -power VDD -ground VSS -secondary_power VDDA + +define_pdn_grid -name "Core" +add_pdn_stripe -followpins -layer metal1 + +# All three nets participate in the metal4 straps; ordering within each +# pitch group must be VDD, VDDA, VSS (power rails first, ground last). +add_pdn_stripe -layer metal4 -width 0.48 -pitch 15.0 -offset 2.0 +add_pdn_stripe -layer metal7 -width 1.40 -pitch 20.0 -offset 2.0 \ + -nets {VDD VSS} + +add_pdn_connect -layers {metal1 metal4} +add_pdn_connect -layers {metal4 metal7} + +pdngen + +set def_file [make_result_file core_grid_secondary_power_ordering.def] +write_def $def_file +diff_files core_grid_secondary_power_ordering.defok $def_file