blob: 7f178167fc9d41bcba2b24ef6f05c20e4b2cc5b9 [file] [log] [blame]
#BSD 3-Clause License
#
#Copyright (c) 2019, The Regents of the University of California
#All rights reserved.
#
#Redistribution and use in source and binary forms, with or without
#modification, are permitted provided that the following conditions are met:
#
#1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
#2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
#3. Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
#THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
#AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
#IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
#DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
#FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
#DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
#SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
#CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
#OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
#OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
sta::define_cmd_args "pdngen" {[-verbose] [config_file]}
proc pdngen { args } {
sta::parse_key_args "pdngen" args \
keys {} flags {-verbose}
if {[info exists flags(-verbose)]} {
pdngen::set_verbose
}
if {[llength $args] > 0} {
set config_file [file nativename [lindex $args 0]]
pdngen::apply $config_file
} else {
pdngen::apply
}
}
sta::define_cmd_args "add_global_connection" {-net <net_name> \
-inst_pattern <inst_name_pattern> \
-pin_pattern <pin_name_pattern> \
[(-power|-ground)]}
proc add_global_connection {args} {
if {[ord::get_db_block] == "NULL"} {
utl::error PDN 91 "Design must be loaded before calling the add_global_connection command."
}
sta::parse_key_args "add_global_connection" args \
keys {-net -inst_pattern -pin_pattern} \
flags {-power -ground}
if {[llength $args] > 0} {
utl::error PDN 131 "Unexpected argument [lindex $args 0] for add_global_connection command."
}
if {[info exists flags(-power)] && [info exists flags(-ground)]} {
utl::error PDN 92 "The flags -power and -ground of the add_global_connection command are mutually exclusive."
}
if {![info exists keys(-net)]} {
utl::error PDN 93 "The -net option of the add_global_connection command is required."
}
if {![info exists keys(-inst_pattern)]} {
set keys(-inst_pattern) {.*}
} else {
if {[catch {regexp $keys(-inst_pattern) ""}]} {
utl::error PDN 142 "The -inst_pattern argument ($keys(-inst_pattern)) is not a valid regular expression."
}
}
if {![info exists keys(-pin_pattern)]} {
utl::error PDN 94 "The -pin_pattern option of the add_global_connection command is required."
} else {
if {[catch {regexp $keys(-pin_pattern) ""}]} {
utl::error PDN 157 "The -pin_pattern argument ($keys(-pin_pattern)) is not a valid regular expression."
}
}
if {[info exists flags(-power)]} {
if {[set net [[ord::get_db_block] findNet $keys(-net)]] == "NULL"} {
set net [odb::dbNet_create [ord::get_db_block] $keys(-net)]
}
$net setSpecial
$net setSigType POWER
pdngen::check_power $keys(-net)
pdngen::add_power_net $keys(-net)
}
if {[info exists flags(-ground)]} {
if {[set net [[ord::get_db_block] findNet $keys(-net)]] == "NULL"} {
set net [odb::dbNet_create [ord::get_db_block] $keys(-net)]
}
$net setSpecial
$net setSigType GROUND
pdngen::check_ground $keys(-net)
pdngen::add_ground_net $keys(-net)
}
dict lappend pdngen::global_connections $keys(-net) [list inst_name $keys(-inst_pattern) pin_name $keys(-pin_pattern)]
if {[set net [[ord::get_db_block] findNet $keys(-net)]] == "NULL"} {
utl::warn PDN 167 "Net created for $keys(-net), if intended as power or ground net add the -power/-ground switch as appropriate."
set net [odb::dbNet_create [ord::get_db_block] $keys(-net)]
}
pdn::add_global_connect $keys(-inst_pattern) $keys(-pin_pattern) $net
pdn::global_connect [ord::get_db_block]
}
# define_pdn_grid -name main_grid -pins {metal7} -voltage_domains {CORE VIN}
# define_pdn_grid -macro -name ram -orient {R0 R180 MX MY} -starts_with POWER -pin_direction vertical -block metal6
sta::define_cmd_args "define_pdn_grid" {[-name <name>] \
[-macro] \
[-grid_over_pg_pins|-grid_over_boundary] \
[-voltage_domains <list_of_voltage_domains>] \
[-orient <list_of_valid_orientations>] \
[-instances <list_of_instances>] \
[-cells <list_of_cell_names> ] \
[-halo <list_of_halo_values>] \
[-pin_direction (horizontal|vertical)] \
[-pins <list_of_pin_layers>] \
[-starts_with (POWER|GROUND)]}
proc define_pdn_grid {args} {
pdngen::check_design_state
sta::parse_key_args "define_pdn_grid" args \
keys {-name -voltage_domains -orient -instances -cells -halo -pin_direction -pins -starts_with} \
flags {-macro -grid_over_pg_pins -grid_over_boundary}
if {[llength $args] > 0} {
utl::error PDN 132 "Unexpected argument [lindex $args 0] for define_pdn_grid command."
}
if {[info exists flags(-macro)]} {
set keys(-macro) 1
if {[info exists flags(-grid_over_pg_pins)] && [info exists flags(-grid_over_bounary)]} {
utl::error PDN 175 "Options -grid_over_pg_pins and -grid_over_boundary are mutually exclusive."
}
set keys(-grid_over_pg_pins) 1
if {[info exists flags(-grid_over_boundary)]} {
set keys(-grid_over_pg_pins) 0
}
}
if {[llength $args] > 0} {
utl::error PDN 73 "Unrecognized argument [lindex $args 0] for define_pdn_grid."
}
if {[info exists keys(-halo)]} {
if {[llength $keys(-halo)] == 1} {
set keys(-halo) [list $keys(-halo) $keys(-halo) $keys(-halo) $keys(-halo)]
} elseif {[llength $keys(-halo)] == 2} {
set keys(-halo) [list {*}$keys(-halo) {*}$keys(-halo)]
} elseif {[llength $keys(-halo)] != 4} {
utl::error PDN 163 "Argument -halo of define_pdn_grid must consist of 1, 2 or 4 entries."
}
}
pdngen::define_pdn_grid {*}[array get keys]
}
# set_voltage_domain -name CORE -power_net VDD -ground_net VSS
# set_voltage_domain -name VIN -region_name TEMP_ANALOG -power_net VPWR -ground_net VSS
sta::define_cmd_args "set_voltage_domain" {-name domain_name \
[-region region_name] \
-power power_net_name \
[-secondary_power secondary_power_net_name] \
-ground ground_net_name}
proc set_voltage_domain {args} {
pdngen::check_design_state
sta::parse_key_args "set_voltage_domain" args \
keys {-name -region -power -secondary_power -ground}
if {[llength $args] > 0} {
utl::error PDN 133 "Unexpected argument [lindex $args 0] for set_voltage_domain command."
}
if {![info exists keys(-name)]} {
utl::error PDN 97 "The -name argument is required."
}
if {![info exists keys(-power)]} {
utl::error PDN 98 "The -power argument is required."
}
if {![info exists keys(-ground)]} {
utl::error PDN 99 "The -ground argument is required."
}
if {[llength $args] > 0} {
utl::error PDN 120 "Unrecognized argument [lindex $args 0] for set_voltage_domain."
}
pdngen::set_voltage_domain {*}[array get keys]
}
# add_pdn_stripe -grid main_grid -layer metal1 -width 0.17 -followpins
# add_pdn_stripe -grid main_grid -layer metal2 -width 0.17 -followpins
# add_pdn_stripe -grid main_grid -layer metal4 -width 0.48 -pitch 56.0 -offset 2 -starts_with POWER
# add_pdn_stripe -grid main_grid -layer metal7 -width 1.40 -pitch 40.0 -offset 2 -starts_with POWER
sta::define_cmd_args "add_pdn_stripe" {[-grid grid_name] \
-layer layer_name \
-width width_value \
[-followpins] \
[-extend_to_core_ring] \
[-pitch pitch_value] \
[-spacing spacing_value] \
[-offset offset_value] \
[-starts_width (POWER|GROUND)]}
proc add_pdn_stripe {args} {
pdngen::check_design_state
sta::parse_key_args "add_pdn_stripe" args \
keys {-grid -layer -width -pitch -spacing -offset -starts_with} \
flags {-followpins -extend_to_core_ring}
if {[llength $args] > 0} {
utl::error PDN 134 "Unexpected argument [lindex $args 0] for add_pdn_stripe command."
}
if {![info exists keys(-layer)]} {
utl::error PDN 100 "The -layer argument is required."
}
if {![info exists keys(-width)]} {
utl::error PDN 101 "The -width argument is required."
}
if {![info exists flags(-followpins)] && ![info exists keys(-pitch)]} {
utl::error PDN 102 "The -pitch argument is required for non-followpins stripes."
}
if {[info exists flags(-followpins)]} {
set keys(stripe) rails
} else {
set keys(stripe) straps
}
if {[info exists flags(-extend_to_core_ring)]} {
set keys(-extend_to_core_ring) 1
}
pdngen::add_pdn_stripe {*}[array get keys]
}
# add_pdn_ring -grid main_grid -layer metal6 -width 5.0 -spacing 3.0 -core_offset 5
# add_pdn_ring -grid main_grid -layer metal7 -width 5.0 -spacing 3.0 -core_offset 5
sta::define_cmd_args "add_pdn_ring" {[-grid grid_name] \
-layers list_of_2_layer_names \
-widths (width_value|list_of_width_values) \
-spacings (spacing_value|list_of_spacing_values) \
[-core_offsets (offset_value|list_of_offset_values)] \
[-pad_offsets (offset_value|list_of_offset_values)] \
[-power_pads list_of_core_power_padcells] \
[-ground_pads list_of_core_ground_padcells]}
proc add_pdn_ring {args} {
pdngen::check_design_state
sta::parse_key_args "add_pdn_ring" args \
keys {-grid -layers -widths -spacings -core_offsets -pad_offsets -power_pads -ground_pads}
if {[llength $args] > 0} {
utl::error PDN 135 "Unexpected argument [lindex $args 0] for add_pdn_ring command."
}
if {![info exists keys(-layers)]} {
utl::error PDN 103 "The -layers argument is required."
}
if {[llength $keys(-layers)] != 2} {
utl::error PDN 137 "Expecting a list of 2 elements for -layers option of add_pdn_ring command, found [llength $keys(-layers)]."
}
if {![info exists keys(-widths)]} {
utl::error PDN 104 "The -widths argument is required."
}
if {![info exists keys(-spacings)]} {
utl::error PDN 105 "The -spacings argument is required."
}
if {[info exists keys(-core_offsets)] && [info exists keys(-pad_offsets)]} {
utl::error PDN 106 "Only one of -pad_offsets or -core_offsets can be specified."
}
if {![info exists keys(-core_offsets)] && ![info exists keys(-pad_offsets)]} {
utl::error PDN 107 "One of -pad_offsets or -core_offsets must be specified."
}
if {[info exists keys(-pad_offsets)]} {
if {![info exists keys(-power_pads)]} {
utl::error PDN 143 "The -power_pads option is required when the -pad_offsets option is used."
}
if {![info exists keys(-ground_pads)]} {
utl::error PDN 144 "The -ground_pads option is required when the -pad_offsets option is used."
}
} else {
if {[info exists keys(-power_pads)] || [info exists keys(-ground_pads)]} {
utl::warn PDN 145 "Options -power_pads and -ground_pads are only used when the -pad_offsets option is specified."
}
}
pdngen::add_pdn_ring {*}[array get keys]
}
sta::define_cmd_args "add_pdn_connect" {[-grid grid_name] \
-layers list_of_2_layers \
[-cut_pitch pitch_value] \
[-fixed_vias list_of_vias]}
# add_pdn_connect -grid main_grid -layers {metal1 metal2} -cut_pitch 0.16
# add_pdn_connect -grid main_grid -layers {metal2 metal4}
# add_pdn_connect -grid main_grid -layers {metal4 metal7}
proc add_pdn_connect {args} {
pdngen::check_design_state
sta::parse_key_args "add_pdn_connect" args \
keys {-grid -layers -cut_pitch -fixed_vias} \
if {[llength $args] > 0} {
utl::error PDN 136 "Unexpected argument [lindex $args 0] for add_pdn_connect command."
}
if {![info exists keys(-layers)]} {
utl::error PDN 108 "The -layers argument is required."
}
pdngen::add_pdn_connect {*}[array get keys]
}
namespace eval pdngen {
variable block_masters {}
variable logical_viarules {}
variable physical_viarules {}
variable vias {}
variable stripe_locs
variable layers {}
variable block
variable tech
variable libs
variable design_data {}
variable default_grid_data {}
variable def_output
variable widths
variable pitches
variable loffset
variable boffset
variable site
variable row_height
variable metal_layers {}
variable blockages {}
variable padcell_blockages {}
variable instances {}
variable default_template_name {}
variable template {}
variable default_cutclass {}
variable twowidths_table {}
variable twowidths_table_wrongdirection {}
variable stdcell_area ""
variable power_nets {}
variable ground_nets {}
variable macros {}
variable verbose 0
variable global_connections {}
variable default_global_connections {
VDD {
{inst_name .* pin_name ^VDD$}
{inst_name .* pin_name ^VDDPE$}
{inst_name .* pin_name ^VDDCE$}
}
VSS {
{inst_name .* pin_name ^VSS$}
{inst_name .* pin_name ^VSSE$}
}
}
variable voltage_domains {
CORE {
primary_power VDD primary_ground VSS
}
}
proc check_design_state {} {
if {[ord::get_db_block] == "NULL"} {
utl::error PDN 72 "Design must be loaded before calling pdngen commands."
}
}
proc check_orientations {orientations} {
set valid_orientations {R0 R90 R180 R270 MX MY MXR90 MYR90}
set lef_orientations {N R0 FN MY S R180 FS MX E R270 FE MYR90 W R90 FW MXR90}
set checked_orientations {}
foreach orient $orientations {
if {[lsearch -exact $valid_orientations $orient] > -1} {
lappend checked_orientations $orient
} elseif {[dict exists $lef_orientations $orient]} {
lappend checked_orientations [dict get $lef_orientations $orient]
} else {
utl::error PDN 74 "Invalid orientation $orient specified, must be one of [join $valid_orientations {, }]."
}
}
return $checked_orientations
}
proc check_layer_names {layer_names} {
set tech [ord::get_db_tech]
foreach layer_name $layer_names {
if {[$tech findLayer $layer_name] == "NULL"} {
if {[regexp {(.*)_PIN_(hor|ver)$} $layer_name - actual_layer_name]} {
if {[$tech findLayer $actual_layer_name] == "NULL"} {
utl::error "PDN" 75 "Layer $actual_layer_name not found in loaded technology data."
}
} else {
utl::error "PDN" 76 "Layer $layer_name not found in loaded technology data."
}
}
}
return $layer_names
}
proc check_layer_width {layer_name width} {
set tech [ord::get_db_tech]
set layer [$tech findLayer $layer_name]
set minWidth [$layer getMinWidth]
set maxWidth [$layer getMaxWidth]
if {[ord::microns_to_dbu $width] < $minWidth} {
utl::error "PDN" 77 "Width ($width) specified for layer $layer_name is less than minimum width ([ord::dbu_to_microns $minWidth])."
}
if {[ord::microns_to_dbu $width] > $maxWidth} {
utl::error "PDN" 78 "Width ($width) specified for layer $layer_name is greater than maximum width ([ord::dbu_to_microns $maxWidth])."
}
return $width
}
proc check_layer_spacing {layer_name spacing} {
set tech [ord::get_db_tech]
set layer [$tech findLayer $layer_name]
set minSpacing [$layer getSpacing]
if {[ord::microns_to_dbu $spacing] < $minSpacing} {
utl::error "PDN" 79 "Spacing ($spacing) specified for layer $layer_name is less than minimum spacing ([ord::dbu_to_microns $minSpacing)]."
}
return $spacing
}
proc check_rails {rails_spec} {
if {[llength $rails_spec] % 2 == 1} {
utl::error "PDN" 81 "Expected an even number of elements in the list for -rails option, got [llength $rails_spec]."
}
check_layer_names [dict keys $rails_spec]
foreach layer_name [dict keys $rails_spec] {
if {[dict exists $rails_spec $layer_name width]} {
check_layer_width $layer_name [dict get $rails_spec $layer_name width]
}
if {[dict exists $rails_spec $layer_name spacing]} {
check_layer_spacing $layer_name [dict get $rails_spec $layer_name spacing]
}
if {![dict exists $rails_spec $layer_name pitch]} {
dict set rails_spec $layer_name pitch [ord::dbu_to_microns [expr [get_row_height] * 2]]
}
}
return $rails_spec
}
proc check_straps {straps_spec} {
if {[llength $straps_spec] % 2 == 1} {
utl::error "PDN" 83 "Expected an even number of elements in the list for straps specification, got [llength $straps_spec]."
}
check_layer_names [dict keys $straps_spec]
foreach layer_name [dict keys $straps_spec] {
if {[dict exists $straps_spec $layer_name width]} {
check_layer_width $layer_name [dict get $straps_spec $layer_name width]
} else {
utl::error PDN 84 "Missing width specification for strap on layer $layer_name."
}
set width [ord::microns_to_dbu [dict get $straps_spec $layer_name width]]
if {![dict exists $straps_spec $layer_name spacing]} {
dict set straps_spec $layer_name spacing [expr [dict get $straps_spec $layer_name pitch] / 2.0]
}
check_layer_spacing $layer_name [dict get $straps_spec $layer_name spacing]
set spacing [ord::microns_to_dbu [dict get $straps_spec $layer_name spacing]]
if {[dict exists $straps_spec $layer_name pitch]} {
set layer [[ord::get_db_tech] findLayer $layer_name]
set minPitch [expr 2 * ([$layer getSpacing] + $width)]
if {[ord::microns_to_dbu [dict get $straps_spec $layer_name pitch]] < $minPitch} {
utl::error "PDN" 85 "Pitch [dict get $straps_spec $layer_name pitch] specified for layer $layer_name is less than 2 x (width + spacing) (width=[ord::dbu_to_microns $width], spacing=[ord::dbu_to_microns $spacing])."
}
} else {
utl::error PDN 86 "No pitch specified for strap on layer $layer_name."
}
}
return $straps_spec
}
proc check_connect {grid connect_spec} {
foreach connect_statement $connect_spec {
if {[llength $connect_statement] < 2} {
utl::error PDN 87 "Connect statement must consist of at least 2 entries."
}
check_layer_names [lrange $connect_statement 0 1]
dict set layers [lindex $connect_statement 0] 1
dict set layers [lindex $connect_statement 1] 1
}
if {[dict get $grid type] == "macro"} {
set pin_layer_defined 0
set actual_layers {}
foreach layer_name [dict keys $layers] {
if {[regexp {(.*)_PIN_(hor|ver)$} $layer_name - layer]} {
lappend actual_layers $layer
} else {
lappend actual_layers $layer_name
}
}
}
return $connect_spec
}
proc check_core_ring {core_ring_spec} {
if {[llength $core_ring_spec] % 2 == 1} {
utl::error "PDN" 109 "Expected an even number of elements in the list for core_ring specification, got [llength $core_ring_spec]."
}
set layer_directions {}
check_layer_names [dict keys $core_ring_spec]
foreach layer_name [dict keys $core_ring_spec] {
if {[dict exists $core_ring_spec $layer_name width]} {
check_layer_width $layer_name [dict get $core_ring_spec $layer_name width]
} else {
utl::error PDN 121 "Missing width specification for strap on layer $layer_name."
}
set width [ord::microns_to_dbu [dict get $core_ring_spec $layer_name width]]
if {![dict exists $core_ring_spec $layer_name spacing]} {
dict set core_ring_spec $layer_name spacing [expr [dict get $core_ring_spec $layer_name pitch] / 2.0]
}
check_layer_spacing $layer_name [dict get $core_ring_spec $layer_name spacing]
set spacing [ord::microns_to_dbu [dict get $core_ring_spec $layer_name spacing]]
dict set layer_directions [get_dir $layer_name] $layer_name
if {[dict exists $core_ring_spec $layer_name core_offset]} {
check_layer_spacing $layer_name [dict get $core_ring_spec $layer_name core_offset]
} elseif {[dict exists $core_ring_spec $layer_name pad_offset]} {
check_layer_spacing $layer_name [dict get $core_ring_spec $layer_name pad_offset]
} else {
utl::error PDN 146 "Must specifu a pad_offset or core_offset for rings."
}
}
if {[llength [dict keys $layer_directions]] == 0} {
utl::error PDN 139 "No direction defiend for layers [dict keys $core_ring_spec]."
} elseif {[llength [dict keys $layer_directions]] == 1} {
set dir [dict keys $layer_directions]
set direction [expr $dir == "ver" ? "vertical" : "horizontal"]
set missing_direction [expr $dir == "ver" ? "horizontal" : "vertical"]
utl::error PDN 140 "Layers [dict keys $core_ring_spec] are both $direction, missing layer in direction $other_direction."
} elseif {[llength [dict keys $layer_directions]] > 2} {
utl::error PDN 141 "Unexpected number of directions found for layers [dict keys $core_ring_spec], ([dict keys $layer_directions])."
}
return $core_ring_spec
}
proc check_starts_with {value} {
if {$value != "POWER" && $value != "GROUND"} {
utl::error PDN 95 "Value specified for -starts_with option ($value), must be POWER or GROUND."
}
return $value
}
proc check_voltage_domains {domains} {
variable voltage_domains
foreach domain $domains {
if {[lsearch [dict keys $voltage_domains] $domain] == -1} {
utl::error PDN 110 "Voltage domain $domain has not been specified, use set_voltage_domain to create this voltage domain."
}
}
return $domains
}
proc check_instances {instances} {
variable $block
foreach instance $instances {
if {[$block findInst $instance] == "NULL"} {
utl::error PDN 111 "Instance $instance does not exist in the design."
}
}
return $instances
}
proc check_cells {cells} {
foreach cell $cells {
if {[[ord::get_db] findMaster $cell] == "NULL"} {
utl::warn PDN 112 "Cell $cell not loaded into the database."
}
}
return $cells
}
proc check_region {region_name} {
set block [ord::get_db_block]
if {[$block findRegion $region_name] == "NULL"} {
utl::error PDN 127 "No region $region_name found in the design for voltage_domain."
}
return $region_name
}
proc check_power {power_net_name} {
set block [ord::get_db_block]
if {[set net [$block findNet $power_net_name]] == "NULL"} {
set net [odb::dbNet_create $block $power_net_name]
$net setSpecial
$net setSigType "POWER"
} else {
if {[$net getSigType] != "POWER"} {
utl::error PDN 128 "Net $power_net_name already exists in the design, but is of signal type [$net getSigType]."
}
}
return $power_net_name
}
proc check_secondary_power {secondary_power_net_name} {
set block [ord::get_db_block]
foreach secondary_power $secondary_power_net_name {
if {[set net [$block findNet $secondary_power]] == "NULL"} {
set net [odb::dbNet_create $block $secondary_power]
$net setSpecial
$net setSigType "POWER"
} else {
if {[$net getSigType] != "POWER"} {
utl::error PDN 176 "Net $secondary_power already exists in the design, but is of signal type [$net getSigType]."
}
}
}
return $secondary_power_net_name
}
proc check_ground {ground_net_name} {
set block [ord::get_db_block]
if {[set net [$block findNet $ground_net_name]] == "NULL"} {
set net [odb::dbNet_create $block $ground_net_name]
$net setSpecial
$net setSigType "GROUND"
} else {
if {[$net getSigType] != "GROUND"} {
utl::error PDN 129 "Net $ground_net_name already exists in the design, but is of signal type [$net getSigType]."
}
}
return $ground_net_name
}
proc set_voltage_domain {args} {
variable voltage_domains
set voltage_domain {}
set process_args $args
while {[llength $process_args] > 0} {
set arg [lindex $process_args 0]
set value [lindex $process_args 1]
switch $arg {
-name {dict set voltage_domain name $value}
-power {dict set voltage_domain primary_power [check_power $value]}
-secondary_power {dict set voltage_domain secondary_power [check_secondary_power $value]}
-ground {dict set voltage_domain primary_ground [check_ground $value]}
-region {dict set voltage_domain region [check_region $value]}
default {utl::error PDN 130 "Unrecognized argument $arg, should be one of -name, -power, -ground -region."}
}
set process_args [lrange $process_args 2 end]
}
dict set voltage_domains [dict get $voltage_domain name] $voltage_domain
}
proc check_direction {direction} {
if {$direction != "horizontal" && $direction != "vertical"} {
utl::error PDN 138 "Unexpected value for direction ($direction), should be horizontal or vertical."
}
return $direction
}
proc check_number {value} {
if {![string is double $value]} {
error "value ($value) not recognized as a number."
}
return $value
}
proc check_halo {value} {
foreach item $value {
if {[catch {check_number $item} msg]} {
utl::error PDN 164 "Problem with halo specification, $msg."
}
}
return $value
}
proc define_pdn_grid {args} {
variable current_grid
set grid {}
set process_args $args
while {[llength $process_args] > 0} {
set arg [lindex $process_args 0]
set value [lindex $process_args 1]
switch $arg {
-name {dict set grid name $value}
-voltage_domains {dict set grid voltage_domains [check_voltage_domains $value]}
-macro {dict set grid type macro}
-grid_over_pg_pins {dict set grid grid_over_pg_pins $value}
-orient {dict set grid orient [check_orientations $value]}
-instances {dict set grid instances [check_instances $value]}
-cells {dict set grid macro [check_cells $value]}
-halo {dict set grid halo [check_halo [lmap x $value {ord::microns_to_dbu [check_number $x]}]]}
-pins {dict set grid pins [check_layer_names $value]}
-starts_with {dict set grid starts_with [check_starts_with $value]}
-pin_direction {dict set grid pin_direction [check_direction $value]}
default {utl::error PDN 88 "Unrecognized argument $arg, should be one of -name, -orient, -instances -cells -pins -starts_with."}
}
set process_args [lrange $process_args 2 end]
}
set current_grid [verify_grid $grid]
}
proc get_grid {grid_name} {
variable design_data
if {[dict exists $design_data grid]} {
dict for {type grids} [dict get $design_data grid] {
dict for {name grid} $grids {
if {$name == $grid_name} {
return $grid
}
}
}
}
return {}
}
proc check_grid {grid} {
if {$grid == {}} {
utl::error PDN 113 "The grid $grid_name has not been defined."
}
return $grid
}
proc check_power_ground {value} {
if {$value == "POWER" || $value == "GROUND"} {
return $value
}
utl::error PDN 114 "Unexpected value ($value), must be either POWER or GROUND."
}
proc add_pdn_stripe {args} {
variable current_grid
if {[dict exists $args -grid]} {
set current_grid [check_grid [get_grid [dict get $args -grid]]]
}
set grid $current_grid
set stripe [dict get $args stripe]
set layer [check_layer_names [dict get $args -layer]]
set process_args $args
while {[llength $process_args] > 0} {
set arg [lindex $process_args 0]
set value [lindex $process_args 1]
switch $arg {
-grid {;}
-layer {;}
-width {dict set grid $stripe $layer width $value}
-spacing {dict set grid $stripe $layer spacing $value}
-offset {dict set grid $stripe $layer offset $value}
-pitch {dict set grid $stripe $layer pitch $value}
-starts_with {dict set grid $stripe $layer starts_with [check_power_ground $value]}
-extend_to_core_ring {dict set grid $stripe $layer extend_to_core_ring 1}
stripe {;}
default {utl::error PDN 124 "Unrecognized argument $arg, should be one of -grid, -type, -orient, -power_pins, -ground_pins, -blockages, -rails, -straps, -connect."}
}
set process_args [lrange $process_args 2 end]
}
set current_grid [verify_grid $grid]
}
proc check_max_length {values max_length} {
if {[llength $values] > $max_length} {
error "[llength $values] provided, maximum of $max_length values allowed."
}
}
proc check_grid_voltage_domains {grid} {
if {![dict exists $grid voltage_domains]} {
utl::error PDN 158 "No voltage domains defined for grid."
}
}
proc get_voltage_domain_by_name {domain_name} {
variable voltage_domains
if {[dict exists $voltage_domains $domain_name]} {
return [dict get $voltage_domains $domain_name]
}
utl::error PDN 159 "Voltage domains $domain_name has not been defined."
}
proc match_inst_connection {inst net_name} {
variable global_connections
foreach pattern [dict get $global_connections $net_name] {
if {[regexp [dict get $pattern inst_name] [$inst getName]]} {
foreach pin [[$inst getMaster] getMTerms] {
if {[regexp [dict get $pattern pin_name] [$pin getName]]} {
return 1
}
}
}
}
return 0
}
proc is_inst_in_voltage_domain {inst domain_name} {
set voltage_domain [get_voltage_domain_by_name $domain_name]
# The instance is in the voltage domain if it connected to both related power and ground nets
set power_net [dict get $voltage_domain primary_power]
set ground_net [dict get $voltage_domain primary_ground]
return [match_inst_connection $inst $power_net] && [match_inst_connection $inst $ground_net]
}
proc get_block_inst_masters {} {
variable block_masters
if {[llength $block_masters] == 0} {
foreach inst [[ord::get_db_block] getInsts] {
if {[lsearch $block_masters [[$inst getMaster] getName]] == -1} {
lappend block_masters [[$inst getMaster] getName]
}
}
}
return $block_masters
}
proc is_cell_present {cell_name} {
return [lsearch [get_block_inst_masters] $cell_name] > -1
}
proc check_pwr_pads {grid cells} {
check_grid_voltage_domains $grid
set voltage_domains [dict get $grid voltage_domains]
set pwr_pads {}
set inst_example {}
foreach voltage_domain $voltage_domains {
set net_name [get_voltage_domain_power $voltage_domain]
if {[set net [[ord::get_db_block] findNet $net_name]] == "NULL"} {
utl::error PDN 149 "Power net $net_name not found."
}
set find_cells $cells
foreach inst [[ord::get_db_block] getInsts] {
if {[set idx [lsearch $find_cells [[$inst getMaster] getName]]] > -1} {
if {![is_inst_in_voltage_domain $inst $voltage_domain]} {continue}
# Only need one example of each cell
set cell_name [lindex $find_cells $idx]
set find_cells [lreplace $find_cells $idx $idx]
dict set inst_example $cell_name $inst
}
if {[llength $find_cells] == 0} {break}
}
if {[llength $find_cells] > 0} {
utl::warn PDN 150 "Cannot find cells ([join $find_cells {, }]) in voltage domain $voltage_domain."
}
dict for {cell inst} $inst_example {
set pin_name [get_inst_pin_connected_to_net $inst $net]
dict lappend pwr_pads $pin_name $cell
}
}
return $pwr_pads
}
proc check_gnd_pads {grid cells} {
check_grid_voltage_domains $grid
set voltage_domains [dict get $grid voltage_domains]
set gnd_pads {}
set inst_example {}
foreach voltage_domain $voltage_domains {
set net_name [get_voltage_domain_ground $voltage_domain]
if {[set net [[ord::get_db_block] findNet $net_name]] == "NULL"} {
utl::error PDN 151 "Ground net $net_name not found."
}
set find_cells $cells
foreach inst [[ord::get_db_block] getInsts] {
if {[set idx [lsearch $find_cells [[$inst getMaster] getName]]] > -1} {
if {![is_inst_in_voltage_domain $inst $voltage_domain]} {continue}
# Only need one example of each cell
set cell_name [lindex $find_cells $idx]
set find_cells [lreplace $find_cells $idx $idx]
dict set inst_example $cell_name $inst
}
if {[llength $find_cells] == 0} {break}
}
if {[llength $find_cells] > 0} {
utl::warn PDN 152 "Cannot find cells ([join $find_cells {, }]) in voltage domain $voltage_domain."
}
dict for {cell inst} $inst_example {
set pin_name [get_inst_pin_connected_to_net $inst $net]
dict lappend gnd_pads $pin_name $cell
}
}
return $gnd_pads
}
proc add_pdn_ring {args} {
variable current_grid
if {[dict exists $args -grid]} {
set current_grid [check_grid [get_grid [dict get $args -grid]]]
#Dinesh-A: Core Ring without Strap
set grid $current_grid
}
set layers [check_layer_names [dict get $args -layers]]
set process_args $args
while {[llength $process_args] > 0} {
set arg [lindex $process_args 0]
set value [lindex $process_args 1]
switch $arg {
-grid {;}
-layers {;}
-widths {
if {[catch {check_max_length $value 2} msg]} {
utl::error PDN 115 "Unexpected number of values for -widths, $msg."
}
if {[llength $value] == 1} {
set values [list $value $value]
} else {
set values $value
}
foreach layer $layers width $values {
dict set grid core_ring $layer width $width
}
}
-spacings {
if {[catch {check_max_length $value 2} msg]} {
utl::error PDN 116 "Unexpected number of values for -spacings, $msg."
}
if {[llength $value] == 1} {
set values [list $value $value]
} else {
set values $value
}
foreach layer $layers spacing $values {
dict set grid core_ring $layer spacing $spacing
}
}
-core_offsets {
if {[catch {check_max_length $value 2} msg]} {
utl::error PDN 117 "Unexpected number of values for -core_offsets, $msg."
}
if {[llength $value] == 1} {
set values [list $value $value]
} else {
set values $value
}
foreach layer $layers offset $values {
dict set grid core_ring $layer core_offset $offset
}
}
-pad_offsets {
if {[catch {check_max_length $value 2} msg]} {
utl::error PDN 118 "Unexpected number of values for -pad_offsets, $msg."
}
if {[llength $value] == 1} {
set values [list $value $value]
} else {
set values $value
}
foreach layer $layers offset $values {
dict set grid core_ring $layer pad_offset $offset
}
}
-power_pads {dict set grid pwr_pads [check_pwr_pads $grid $value]}
-ground_pads {dict set grid gnd_pads [check_gnd_pads $grid $value]}
default {utl::error PDN 125 "Unrecognized argument $arg, should be one of -grid, -type, -orient, -power_pins, -ground_pins, -blockages, -rails, -straps, -connect."}
}
set process_args [lrange $process_args 2 end]
}
set current_grid [verify_grid $grid]
}
proc check_fixed_vias {via_names} {
set tech [ord::get_db_tech]
foreach via_name $via_names {
if {[set via [$tech findVia $via_name]] == "NULL"} {
utl::error "PDN" 119 "Via $via_name specified in the grid specification does not exist in this technology."
}
}
return $via_names
}
proc add_pdn_connect {args} {
variable current_grid
if {[dict exists $args -grid]} {
set current_grid [check_grid [get_grid [dict get $args -grid]]]
}
set grid $current_grid
set layers [check_layer_names [dict get $args -layers]]
set process_args $args
while {[llength $process_args] > 0} {
set arg [lindex $process_args 0]
set value [lindex $process_args 1]
switch $arg {
-grid {;}
-layers {;}
-cut_pitch {dict set layers constraints cut_pitch $value}
-fixed_vias {dict set layers fixed_vias [check_fixed_vias $value]}
default {utl::error PDN 126 "Unrecognized argument $arg, should be one of -grid, -type, -orient, -power_pins, -ground_pins, -blockages, -rails, -straps, -connect."}
}
set process_args [lrange $process_args 2 end]
}
dict lappend grid connect $layers
set current_grid [verify_grid $grid]
}
proc convert_grid_to_def_units {grid} {
if {![dict exists $grid units]} {
if {[dict exists $grid core_ring]} {
dict for {layer data} [dict get $grid core_ring] {
dict set grid core_ring $layer [convert_layer_spec_to_def_units $data]
}
}
if {[dict exists $grid rails]} {
dict for {layer data} [dict get $grid rails] {
dict set grid rails $layer [convert_layer_spec_to_def_units $data]
if {[dict exists $grid template]} {
foreach template [dict get $grid template names] {
if {[dict exists $grid layers $layer $template]} {
dict set grid rails $layer $template [convert_layer_spec_to_def_units [dict get $grid rails $layer $template]]
}
}
}
}
}
if {[dict exists $grid straps]} {
dict for {layer data} [dict get $grid straps] {
dict set grid straps $layer [convert_layer_spec_to_def_units $data]
if {[dict exists $grid template]} {
foreach template [dict get $grid template names] {
if {[dict exists $grid straps $layer $template]} {
dict set grid straps $layer $template [convert_layer_spec_to_def_units [dict get $grid straps $layer $template]]
}
}
}
}
}
dict set grid units "db"
}
return $grid
}
proc get_inst_pin_connected_to_net {inst net} {
foreach iterm [$inst getITerms] {
# debug "[$inst getName] [$iterm getNet] == $net"
if {[$iterm getNet] == $net} {
return [[$iterm getMTerm] getName]
}
}
}
proc filter_out_selected_by {instances selection} {
dict for {inst_name instance} $instances {
if {[dict exists $instance selected_by]} {
if {[dict get $instance selected_by] == $selection} {
set instances [dict remove $instances $inst_name]
}
}
}
return $instances
}
proc get_priority_value {priority} {
if {$priority == "inst_name"} {return 4}
if {$priority == "cell_name"} {return 3}
if {$priority == "orient"} {return 2}
if {$priority == "none"} {return 1}
}
proc set_instance_grid {inst_name grid priority} {
variable instances
# debug "start- inst_name $inst_name, grid: [dict get $grid name], priority: $priority"
set grid_name [dict get $grid name]
set priority_value [get_priority_value $priority]
set instance [dict get $instances $inst_name]
if {[dict exists $instance grid]} {
if {[dict get $instance grid] != $grid_name} {
set current_priority_value [get_priority_value [dict get $instance selected_by]]
if {$priority_value < $current_priority_value} {
return
} elseif {$priority_value == $current_priority_value} {
utl::error PDN 165 "Conflict found, instance $inst_name is part of two grid definitions ($grid_name, [dict get $instances $inst_name grid])."
}
}
} else {
dict set instances $inst_name grid $grid_name
}
if {[dict exists $grid halo]} {
set_instance_halo $inst_name [dict get $grid halo]
}
dict set instances $inst_name selected_by $priority
dict set instances $inst_name grid $grid_name
dict set insts $inst_name selected_by $priority
dict set insts $inst_name grid $grid_name
}
proc verify_grid {grid} {
variable design_data
variable default_grid_data
if {![dict exists $grid type]} {
dict set grid type stdcell
}
set type [dict get $grid type]
if {![dict exists $grid voltage_domains]} {
dict set grid voltage_domains "CORE"
}
set voltage_domains [dict get $grid voltage_domains]
if {![dict exists $grid name]} {
set idx 1
set name "[join [dict get $grid voltage_domains] {_}]_${type}_grid_$idx"
while {[get_grid $name] != {}} {
incr idx
set name "[join [dict get $grid voltage_domains] {_}]_${type}_grid_$idx"
}
dict set grid name $name
}
set grid_name [dict get $grid name]
if {[dict exists $grid core_ring]} {
check_core_ring [dict get $grid core_ring]
set layer [lindex [dict keys [dict get $grid core_ring]]]
if {[dict exist $grid core_ring $layer pad_offset]} {
if {![dict exists $grid pwr_pads]} {
utl::error PDN 147 "No definition of power padcells provided, required when using pad_offset."
}
if {![dict exists $grid gnd_pads]} {
utl::error PDN 148 "No definition of ground padcells provided, required when using pad_offset."
}
}
}
if {[dict exists $grid pwr_pads]} {
dict for {pin_name cells} [dict get $grid pwr_pads] {
foreach cell $cells {
if {[set master [[ord::get_db] findMaster $cell]] == "NULL"} {
utl::error PDN 153 "Core power padcell ($cell) not found in the database."
}
if {[$master findMTerm $pin_name] == "NULL"} {
utl::error PDN 154 "Cannot find pin ($pin_name) on core power padcell ($cell)."
}
}
}
}
if {[dict exists $grid gnd_pads]} {
dict for {pin_name cells} [dict get $grid gnd_pads] {
foreach cell $cells {
if {[set master [[ord::get_db] findMaster $cell]] == "NULL"} {
utl::error PDN 155 "Core ground padcell ($cell) not found in the database."
}
if {[$master findMTerm $pin_name] == "NULL"} {
utl::error PDN 156 "Cannot find pin ($pin_name) on core ground padcell ($cell)."
}
}
}
}
if {[dict exists $grid macro]} {
check_cells [dict get $grid macro]
}
if {[dict exists $grid rails]} {
dict set grid rails [check_rails [dict get $grid rails]]
}
if {[dict exists $grid straps]} {
check_straps [dict get $grid straps]
}
if {[dict exists $grid template]} {
set_template_size {*}[dict get $grid template size]
}
if {[dict exists $grid orient]} {
if {$type == "stdcell"} {
utl::error PDN 90 "The orient attribute cannot be used with stdcell grids."
}
dict set grid orient [check_orientations [dict get $grid orient]]
}
if {[dict exists $grid connect]} {
dict set grid connect [check_connect $grid [dict get $grid connect]]
}
if {$type == "macro"} {
if {![dict exists $grid halo]} {
dict set grid halo [get_default_halo]
}
check_halo [dict get $grid halo]
} else {
set default_grid_data $grid
}
# debug $grid
dict set design_data grid $type $grid_name $grid
return $grid
}
proc complete_macro_grid_specifications {} {
variable design_data
variable instances
variable macros
set macros [get_macro_blocks]
dict for {type grid_types} [dict get $design_data grid] {
dict for {name grid} $grid_types {
dict set design_data grid $type $name [convert_grid_to_def_units $grid]
}
}
if {![dict exists $design_data grid macro]} {
return
}
########################################
# Creating blockages based on macro locations
#######################################
# debug "import_macro_boundaries"
import_macro_boundaries
# Associate each block instance with a grid specification
set macro_names [dict keys $macros]
dict for {grid_name grid} [dict get $design_data grid macro] {
set insts [find_instances_of $macro_names]
set boundary [odb::newSetFromRect {*}[get_core_area]]
set insts [filtered_insts_within $insts $boundary]
if {[dict exists $grid instances]} {
# debug "Check macro name for [dict get $grid name]"
dict for {inst_name instance} $insts {
if {[lsearch [dict get $grid instances] $inst_name] > -1} {
set_instance_grid $inst_name $grid inst_name
}
}
set insts [set_instance_grid $selected_insts $grid inst_name]
} elseif {[dict exists $grid macro]} {
# set insts [filter_out_selected_by $insts inst_name]
# debug "Check instance name for [dict get $grid name]"
dict for {inst_name instance} $insts {
set cell_name [dict get $instance macro]
if {[lsearch [dict get $grid macro] $cell_name] > -1} {
set_instance_grid $inst_name $grid cell_name
}
}
} elseif {[dict exists $grid orient]} {
# set insts [filter_out_selected_by $insts inst_name]
# set insts [filter_out_selected_by $insts cell_name]
# debug "Check orientation for [dict get $grid name]"
dict for {inst_name instance} $insts {
set orient [dict get $instance orient]
# debug "Inst: $inst_name, orient: $orient, compare to: [dict get $grid orient]"
if {[lsearch [dict get $grid orient] $orient] > -1} {
set_instance_grid $inst_name $grid orient
}
}
}
}
dict for {grid_name grid} [dict get $design_data grid macro] {
set related_instances {}
dict for {inst instance} $instances {
if {![dict exists $instance grid]} {
# utl::error PDN 166 "Instance $inst of cell [dict get $instance macro] is not associated with any grid."
dict set instance grid "__none__"
}
if {[dict get $instance grid] == $grid_name} {
dict set related_instances $inst $instance
}
}
dict set design_data grid macro $grid_name _related_instances $related_instances
}
dict for {grid_name grid} [dict get $design_data grid macro] {
# Set the pin layer on the connect statement to the pin layer of the def to be _PIN_<dir>
set blockages {}
set pin_layers {}
set power_pins {}
set ground_pins {}
dict for {instance_name instance} [dict get $grid _related_instances] {
lappend blockages {*}[dict get $macros [dict get $instance macro] blockage_layers]
lappend pin_layers {*}[dict get $macros [dict get $instance macro] pin_layers]
lappend power_pins {*}[dict get $macros [dict get $instance macro] power_pins]
lappend ground_pins {*}[dict get $macros [dict get $instance macro] ground_pins]
}
dict set design_data grid macro $grid_name power_pins [lsort -unique $power_pins]
dict set design_data grid macro $grid_name ground_pins [lsort -unique $ground_pins]
if {[dict exists $grid pin_direction]} {
if {[dict get $grid pin_direction] == "vertical"} {
set direction ver
} else {
set direction hor
}
set pin_layers [lsort -unique $pin_layers]
foreach pin_layer $pin_layers {
set new_connections {}
foreach connect [dict get $grid connect] {
if {[lindex $connect 0] == $pin_layer} {
set connect [lreplace $connect 0 0 ${pin_layer}_PIN_$direction]
}
if {[lindex $connect 1] == $pin_layer} {
set connect [lreplace $connect 1 1 ${pin_layer}_PIN_$direction]
}
lappend new_connections $connect
}
dict set design_data grid macro $grid_name connect $new_connections
}
}
if {[dict exists $grid straps]} {
foreach strap_layer [dict keys [dict get $grid straps]] {
lappend blockages $strap_layer
}
}
# debug "Grid: $grid_name"
# debug " instances: [dict keys [dict get $grid _related_instances]]"
# debug " blockages: [lsort -unique $blockages]"
# debug " connect: [dict get $design_data grid macro $grid_name connect]"
dict set design_data grid macro $grid_name blockages [lsort -unique $blockages]
}
# debug "get_memory_instance_pg_pins"
get_memory_instance_pg_pins
}
#This file contains procedures that are used for PDN generation
proc debug {message} {
set state [info frame -1]
set str ""
if {[dict exists $state file]} {
set str "$str[dict get $state file]:"
}
if {[dict exists $state proc]} {
set str "$str[dict get $state proc]:"
}
if {[dict exists $state line]} {
set str "$str[dict get $state line]"
}
puts "\[DEBUG\] $str: $message"
}
proc lmap {args} {
set result {}
set var [lindex $args 0]
foreach item [lindex $args 1] {
uplevel 1 "set $var $item"
lappend result [uplevel 1 [lindex $args end]]
}
return $result
}
proc get_routing_direction {layer_name} {
variable layers
if {$layers == ""} {
init_metal_layers
}
if {![dict exists $layers $layer_name direction]} {
utl::error "PDN" 33 "Unknown direction for layer $layer_name."
}
return [dict get $layers $layer_name direction]
}
proc get_dir {layer_name} {
if {[regexp {.*_PIN_(hor|ver)} $layer_name - dir]} {
return $dir
}
if {[is_rails_layer $layer_name]} {
return "hor"
}
return [get_routing_direction $layer_name]
}
proc get_rails_layers {} {
variable design_data
if {[dict exists $design_data grid]} {
foreach type [dict keys [dict get $design_data grid]] {
dict for {name specification} [dict get $design_data grid $type] {
if {[dict exists $specification rails]} {
return [dict keys [dict get $specification rails]]
}
}
}
}
return {}
}
proc is_rails_layer {layer} {
return [expr {[lsearch -exact [get_rails_layers] $layer] > -1}]
}
proc via_number {layer_rule1 layer_rule2} {
return [expr [[$layer_rule1 getLayer] getNumber] - [[$layer_rule2 getLayer] getNumber]]
}
proc init_via_tech {} {
variable tech
variable def_via_tech
set def_via_tech {}
foreach via_rule [$tech getViaGenerateRules] {
set levels [list [$via_rule getViaLayerRule 0] [$via_rule getViaLayerRule 1] [$via_rule getViaLayerRule 2]]
set levels [lsort -command via_number $levels]
lassign $levels lower cut upper
dict set def_via_tech [$via_rule getName] [list \
lower [list layer [[$lower getLayer] getName] enclosure [$lower getEnclosure]] \
upper [list layer [[$upper getLayer] getName] enclosure [$upper getEnclosure]] \
cut [list layer [[$cut getLayer] getName] spacing [$cut getSpacing] size [list [[$cut getRect] dx] [[$cut getRect] dy]]] \
]
}
# debug "def_via_tech: $def_via_tech"
}
proc set_prop_lines {obj prop_name} {
variable prop_line
if {[set prop [::odb::dbStringProperty_find $obj $prop_name]] != "NULL"} {
set prop_line [$prop getValue]
} else {
set prop_line {}
}
}
proc read_propline {} {
variable prop_line
set word [lindex $prop_line 0]
set prop_line [lrange $prop_line 1 end]
set line {}
while {[llength $prop_line] > 0 && $word != ";"} {
lappend line $word
set word [lindex $prop_line 0]
set prop_line [lrange $prop_line 1 end]
}
return $line
}
proc empty_propline {} {
variable prop_line
return [expr ![llength $prop_line]]
}
proc find_layer {layer_name} {
variable tech
if {[set layer [$tech findLayer $layer_name]] == "NULL"} {
utl::error "PDN" 19 "Cannot find layer $layer_name in loaded technology."
}
return $layer
}
proc read_spacing {layer_name} {
variable layers
variable def_units
set layer [find_layer $layer_name]
set_prop_lines $layer LEF58_SPACING
set spacing {}
while {![empty_propline]} {
set line [read_propline]
if {[set idx [lsearch -exact $line CUTCLASS]] > -1} {
set cutclass [lindex $line [expr $idx + 1]]
set line [lreplace $line $idx [expr $idx + 1]]
if {[set idx [lsearch -exact $line LAYER]] > -1} {
set other_layer [lindex $line [expr $idx + 1]]
set line [lreplace $line $idx [expr $idx + 1]]
if {[set idx [lsearch -exact $line CONCAVECORNER]] > -1} {
set line [lreplace $line $idx $idx]
if {[set idx [lsearch -exact $line SPACING]] > -1} {
dict set spacing $cutclass $other_layer concave [expr round([lindex $line [expr $idx + 1]] * $def_units)]
# set line [lreplace $line $idx [expr $idx + 1]]
}
}
}
}
}
# debug "$layer_name $spacing"
dict set layers $layer_name spacing $spacing
# debug "$layer_name [dict get $layers $layer_name]"
}
proc read_spacingtables {layer_name} {
variable layers
variable def_units
set layer [find_layer $layer_name]
set prls {}
if {[$layer hasTwoWidthsSpacingRules]} {
set type "TWOWIDTHS"
set subtype "NONE"
set table_size [$layer getTwoWidthsSpacingTableNumWidths]
for {set i 0} {$i < $table_size} {incr i} {
set width [$layer getTwoWidthsSpacingTableWidth $i]
if {[$layer getTwoWidthsSpacingTableHasPRL $i]} {
set prl [$layer getTwoWidthsSpacingTablePRL $i]
} else {
set prl 0
}
set spacings {}
for {set j 0} {$j < $table_size} {incr j} {
lappend spacings [$layer getTwoWidthsSpacingTableEntry $i $j]
}
dict set layers $layer_name spacingtable $type $subtype $width [list prl $prl spacings $spacings]
}
}
set_prop_lines $layer LEF58_SPACINGTABLE
set spacing {}
while {![empty_propline]} {
set line [read_propline]
# debug "$line"
set type [lindex $line 1]
set subtype [lindex $line 2]
set table_entry_indexes [lsearch -exact -all $line "WIDTH"]
set num_entries [llength $table_entry_indexes]
foreach start_index $table_entry_indexes {
set pos $start_index
incr pos
set width [expr round([lindex $line $pos] * $def_units)]
incr pos
if {[lindex $line $pos] == "PRL"} {
incr pos
set prl [expr round([lindex $line $pos] * $def_units)]
incr pos
} else {
set prl 0
}
set spacings {}
for {set i 0} {$i < $num_entries} {incr i} {
# debug "[expr $i + $pos] [lindex $line [expr $i + $pos]]"
lappend spacings [expr round([lindex $line [expr $i + $pos]] * $def_units)]
}
dict set layers $layer_name spacingtable $type $subtype $width [list prl $prl spacings $spacings]
}
}
if {![dict exists $layers $layer_name spacingtable]} {
dict set layers $layer_name spacingtable {}
}
# debug "$layer_name [dict get $layers $layer_name]"
}
proc get_spacingtables {layer_name} {
variable layers
if {![dict exists $layers $layer_name spacingtable]} {
read_spacingtables $layer_name
}
return [dict get $layers $layer_name spacingtable]
}
proc get_concave_spacing_value {layer_name other_layer_name} {
variable layers
variable default_cutclass
if {![dict exists $layers $layer_name spacing]} {
read_spacing $layer_name
}
# debug "$layer_name [dict get $layers $layer_name]"
if {[dict exists $layers $layer_name spacing [dict get $default_cutclass $layer_name] $other_layer_name concave]} {
return [dict get $layers $layer_name spacing [dict get $default_cutclass $layer_name] $other_layer_name concave]
}
return 0
}
proc read_arrayspacing {layer_name} {
variable layers
variable def_units
set layer [find_layer $layer_name]
set_prop_lines $layer LEF58_ARRAYSPACING
set arrayspacing {}
while {![empty_propline]} {
set line [read_propline]
if {[set idx [lsearch -exact $line PARALLELOVERLAP]] > -1} {
dict set arrayspacing paralleloverlap 1
set line [lreplace $line $idx $idx]
}
if {[set idx [lsearch -exact $line LONGARRAY]] > -1} {
dict set arrayspacing longarray 1
set line [lreplace $line $idx $idx]
}
if {[set idx [lsearch -exact $line CUTSPACING]] > -1} {
dict set arrayspacing cutspacing [expr round([lindex $line [expr $idx + 1]] * $def_units)]
set line [lreplace $line $idx [expr $idx + 1]]
}
while {[set idx [lsearch -exact $line ARRAYCUTS]] > -1} {
dict set arrayspacing arraycuts [lindex $line [expr $idx + 1]] spacing [expr round([lindex $line [expr $idx + 3]] * $def_units)]
set line [lreplace $line $idx [expr $idx + 3]]
}
}
dict set layers $layer_name arrayspacing $arrayspacing
}
proc read_cutclass {layer_name} {
variable layers
variable def_units
variable default_cutclass
set layer [find_layer $layer_name]
set_prop_lines $layer LEF58_CUTCLASS
dict set layers $layer_name cutclass {}
set min_area -1
while {![empty_propline]} {
set line [read_propline]
if {![regexp {CUTCLASS\s+([^\s]+)\s+WIDTH\s+([^\s]+)} $line - cut_class width]} {
utl::error "PDN" 20 "Failed to read CUTCLASS property '$line'."
}
if {[regexp {LENGTH\s+([^\s]+)} $line - length]} {
set area [expr $width * $length]
} else {
set area [expr $width * $width]
}
if {$min_area == -1 || $area < $min_area} {
dict set default_cutclass $layer_name $cut_class
set min_area $area
}
dict set layers $layer_name cutclass $cut_class [list width [expr round($width * $def_units)] length [expr round($length * $def_units)]]
}
}
proc read_enclosures {layer_name} {
variable layers
variable def_units
set layer [find_layer $layer_name]
set_prop_lines $layer LEF58_ENCLOSURE
set prev_cutclass ""
while {![empty_propline]} {
set line [read_propline]
# debug "$line"
set enclosure {}
if {[set idx [lsearch -exact $line EOL]] > -1} {
continue
dict set enclosure eol [expr round([lindex $line [expr $idx + 1]] * $def_units)]
set line [lreplace $line $idx [expr $idx + 1]]
}
if {[set idx [lsearch -exact $line EOLONLY]] > -1} {
dict set enclosure eolonly 1
set line [lreplace $line $idx $idx]
}
if {[set idx [lsearch -exact $line SHORTEDGEONEOL]] > -1} {
dict set enclosure shortedgeoneol 1
set line [lreplace $line $idx $idx]
}
if {[set idx [lsearch -exact $line MINLENGTH]] > -1} {
dict set enclosure minlength [expr round([lindex $line [expr $idx + 1]] * $def_units)]
set line [lreplace $line $idx [expr $idx + 1]]
}
if {[set idx [lsearch -exact $line ABOVE]] > -1} {
dict set enclosure above 1
set line [lreplace $line $idx $idx]
}
if {[set idx [lsearch -exact $line BELOW]] > -1} {
dict set enclosure below 1
set line [lreplace $line $idx $idx]
}
if {[set idx [lsearch -exact $line END]] > -1} {
dict set enclosure end 1
set line [lreplace $line $idx $idx]
}
if {[set idx [lsearch -exact $line SIDE]] > -1} {
dict set enclosure side 1
set line [lreplace $line $idx $idx]
}
set width 0
regexp {WIDTH\s+([^\s]+)} $line - width
set width [expr round($width * $def_units)]
if {![regexp {ENCLOSURE CUTCLASS\s+([^\s]+)\s+([^\s]+)\s+([^\s]+)} $line - cut_class overlap1 overlap2]} {
utl::error "PDN" 21 "Failed to read ENCLOSURE property '$line'."
}
dict set enclosure overlap1 [expr round($overlap1 * $def_units)]
dict set enclosure overlap2 [expr round($overlap2 * $def_units)]
# debug "class - $cut_class enclosure - $enclosure"
if {$prev_cutclass != $cut_class} {
set enclosures {}
set prev_cutclass $cut_class
}
dict lappend enclosures $width $enclosure
dict set layers $layer_name cutclass $cut_class enclosures $enclosures
}
# debug "end"
}
proc read_minimumcuts {layer_name} {
variable layers
variable def_units
variable default_cutclass
set layer [find_layer $layer_name]
set_prop_lines $layer LEF58_MINIMUMCUT
while {![empty_propline]} {
set line [read_propline]
set classes {}
set constraints {}
set fromabove 0
set frombelow 0
if {[set idx [lsearch -exact $line FROMABOVE]] > -1} {
set fromabove 1
set line [lreplace $line $idx $idx]
} elseif {[set idx [lsearch -exact $line FROMBELOW]] > -1} {
set frombelow 1
set line [lreplace $line $idx $idx]
} else {
set fromabove 1
set frombelow 1
}
if {[set idx [lsearch -exact $line WIDTH]] > -1} {
set width [expr round([lindex $line [expr $idx + 1]] * $def_units)]
}
if {[regexp {LENGTH ([0-9\.]*) WITHIN ([0-9\.]*)} $line - length within]} {
# Not expecting to deal with this king of structure, so can ignore
set line [regsub {LENGTH ([0-9\.]*) WITHIN ([0-9\.]*)} $line {}]
}
if {[regexp {AREA ([0-9\.]*) WITHIN ([0-9\.]*)} $line - area within]} {
# Not expecting to deal with this king of structure, so can ignore
set line [regsub {AREA ([0-9\.]*) WITHIN ([0-9\.]*)} $line {}]
}
while {[set idx [lsearch -exact $line CUTCLASS]] > -1} {
set cutclass [lindex $line [expr $idx + 1]]
set num_cuts [lindex $line [expr $idx + 2]]
if {$fromabove == 1} {
dict set layers $layer_name minimumcut width $width fromabove $cutclass $num_cuts
}
if {$frombelow == 1} {
dict set layers $layer_name minimumcut width $width frombelow $cutclass $num_cuts
}
set line [lreplace $line $idx [expr $idx + 2]]
}
}
}
proc get_minimumcuts {layer_name width from cutclass} {
variable layers
# debug "$layer_name, $width, $from, $cutclass"
if {![dict exists $layers $layer_name minimumcut]} {
read_minimumcuts $layer_name
# debug "[dict get $layers $layer_name minimumcut]"
}
set min_cuts 1
if {![dict exists $layers $layer_name minimumcut]} {
# debug "No mincut rule for layer $layer_name"
return $min_cuts
}
set idx 0
set widths [lsort -integer -decreasing [dict keys [dict get $layers $layer_name minimumcut width]]]
if {$width <= [lindex $widths end]} {
# debug "width $width less than smallest width boundary [lindex $widths end]"
return $min_cuts
}
foreach width_boundary [lreverse $widths] {
if {$width > $width_boundary && [dict exists $layers $layer_name minimumcut width $width_boundary $from]} {
# debug "[dict get $layers $layer_name minimumcut width $width_boundary]"
if {[dict exists $layers $layer_name minimumcut width $width_boundary $from $cutclass]} {
set min_cuts [dict get $layers $layer_name minimumcut width $width_boundary $from $cutclass]
}
# debug "Selected width boundary $width_boundary for $layer_name, $width $from, $cutclass [dict get $layers $layer_name minimumcut width $width_boundary $from $cutclass]"
break
}
}
return $min_cuts
}
proc get_via_enclosure {via_info lower_width upper_width} {
variable layers
variable default_cutclass
variable min_lower_enclosure
variable max_lower_enclosure
variable min_upper_enclosure
variable max_upper_enclosure
# debug "via_info $via_info width $lower_width,$upper_width"
set layer_name [dict get $via_info cut layer]
if {![dict exists $layers $layer_name cutclass]} {
read_cutclass $layer_name
read_enclosures $layer_name
}
if {!([dict exists $default_cutclass $layer_name] && [dict exists $layers $layer_name cutclass [dict get $default_cutclass $layer_name] enclosures])} {
set lower_enclosure [dict get $via_info lower enclosure]
set upper_enclosure [dict get $via_info upper enclosure]
set min_lower_enclosure [lindex $lower_enclosure 0]
set max_lower_enclosure [lindex $lower_enclosure 1]
if {$max_lower_enclosure < $min_lower_enclosure} {
set swap $min_lower_enclosure
set min_lower_enclosure $max_lower_enclosure
set max_lower_enclosure $swap
}
set min_upper_enclosure [lindex $upper_enclosure 0]
set max_upper_enclosure [lindex $upper_enclosure 1]
if {$max_upper_enclosure < $min_upper_enclosure} {
set swap $min_upper_enclosure
set min_upper_enclosure $max_upper_enclosure
set max_upper_enclosure $swap
}
set selected_enclosure [list $min_lower_enclosure $max_lower_enclosure $min_upper_enclosure $max_upper_enclosure]
} else {
set enclosures [dict get $layers $layer_name cutclass [dict get $default_cutclass $layer_name] enclosures]
# debug "Enclosure set $enclosures"
set upper_enclosures {}
set lower_enclosures {}
set width $lower_width
foreach size [lreverse [dict keys $enclosures]] {
if {$width >= $size} {
break
}
}
set enclosure_list [dict get $enclosures $size]
# debug "Initial enclosure_list (size = $size)- $enclosure_list"
if {$size > 0} {
foreach enclosure $enclosure_list {
if {![dict exists $enclosure above]} {
lappend lower_enclosures $enclosure
}
}
}
set width $upper_width
foreach size [lreverse [dict keys $enclosures]] {
if {$width >= $size} {
break
}
}
set enclosure_list [dict get $enclosures $size]
# debug "Initial enclosure_list (size = $size)- $enclosure_list"
if {$size > 0} {
foreach enclosure $enclosure_list {
if {![dict exists $enclosure below]} {
lappend upper_enclosures $enclosure
}
}
}
if {[llength $upper_enclosures] == 0} {
set zero_enclosures_list [dict get $enclosures 0]
foreach enclosure $zero_enclosures_list {
if {![dict exists $enclosure below]} {
lappend upper_enclosures $enclosure
}
}
}
if {[llength $lower_enclosures] == 0} {
set zero_enclosures_list [dict get $enclosures 0]
foreach enclosure $zero_enclosures_list {
if {![dict exists $enclosure above]} {
lappend lower_enclosures $enclosure
}
}
}
set upper_min -1
set lower_min -1
if {[llength $upper_enclosures] > 1} {
foreach enclosure $upper_enclosures {
# debug "upper enclosure - $enclosure"
set this_min [expr min([dict get $enclosure overlap1], [dict get $enclosure overlap2])]
if {$upper_min < 0 || $this_min < $upper_min} {
set upper_min $this_min
set upper_enc [list [dict get $enclosure overlap1] [dict get $enclosure overlap2]]
# debug "upper_enc: $upper_enc"
}
}
} else {
set enclosure [lindex $upper_enclosures 0]
set upper_enc [list [dict get $enclosure overlap1] [dict get $enclosure overlap2]]
}
if {[llength $lower_enclosures] > 1} {
foreach enclosure $lower_enclosures {
# debug "lower enclosure - $enclosure"
set this_min [expr min([dict get $enclosure overlap1], [dict get $enclosure overlap2])]
if {$lower_min < 0 || $this_min < $lower_min} {
set lower_min $this_min
set lower_enc [list [dict get $enclosure overlap1] [dict get $enclosure overlap2]]
}
}
# debug "[llength $lower_enclosures] lower_enc: $lower_enc"
} else {
set enclosure [lindex $lower_enclosures 0]
set lower_enc [list [dict get $enclosure overlap1] [dict get $enclosure overlap2]]
# debug "1 lower_enc: lower_enc: $lower_enc"
}
set selected_enclosure [list {*}$lower_enc {*}$upper_enc]
}
# debug "selected $selected_enclosure"
set min_lower_enclosure [expr min([lindex $selected_enclosure 0], [lindex $selected_enclosure 1])]
set max_lower_enclosure [expr max([lindex $selected_enclosure 0], [lindex $selected_enclosure 1])]
set min_upper_enclosure [expr min([lindex $selected_enclosure 2], [lindex $selected_enclosure 3])]
set max_upper_enclosure [expr max([lindex $selected_enclosure 2], [lindex $selected_enclosure 3])]
# debug "enclosures - min_lower $min_lower_enclosure max_lower $max_lower_enclosure min_upper $min_upper_enclosure max_upper $max_upper_enclosure"
}
proc select_via_info {lower} {
variable def_via_tech
set layer_name $lower
regexp {(.*)_PIN} $lower - layer_name
return [dict filter $def_via_tech script {rule_name rule} {expr {[dict get $rule lower layer] == $layer_name}}]
}
proc set_layer_info {layer_info} {
variable layers
set layers $layer_info
}
proc read_widthtable {layer_name} {
variable tech
variable def_units
set table {}
set layer [find_layer $layer_name]
set_prop_lines $layer LEF58_WIDTHTABLE
while {![empty_propline]} {
set line [read_propline]
set flags {}
if {[set idx [lsearch -exact $line ORTHOGONAL]] > -1} {
dict set flags orthogonal 1
set line [lreplace $line $idx $idx]
}
if {[set idx [lsearch -exact $line WRONGDIRECTION]] > -1} {
dict set flags wrongdirection 1
set line [lreplace $line $idx $idx]
}
regexp {WIDTHTABLE\s+(.*)} $line - widthtable
set widthtable [lmap x $widthtable {ord::microns_to_dbu $x}]
if {[dict exists $flags wrongdirection]} {
dict set table wrongdirection $widthtable
} else {
dict set table rightdirection $widthtable
}
}
return $table
}
proc get_widthtable {layer_name direction} {
variable layers
if {![dict exists $layers $layer_name widthtable]} {
dict set layers $layer_name widthtable [read_widthtable $layer_name]
}
if {![dict exists $layers $layer_name widthtable $direction]} {
if {$direction == "wrongdirection" && [dict exists $layers $layer_name widthtable rightdirection]} {
dict set layers $layer_name widthtable $direction [dict get $layers $layer_name widthtable rightdirection]
} else {
dict set layers $layer_name widthtable $direction {}
}
}
return [dict get $layers $layer_name widthtable $direction]
}
# Layers that have a widthtable will only support some width values, the widthtable defines the
# set of widths that are allowed, or any width greater than or equal to the last value in the
# table
#
proc adjust_width {widthtable width} {
if {[llength $widthtable] == 0} {return $width}
if {[lsearch -exact $widthtable $width] > -1} {return $width}
if {$width > [lindex $widthtable end]} {return $width}
foreach value $widthtable {
if {$value > $width} {
# debug "Adjust width from $width to $value"
return $value
}
}
return $width
}
proc get_adjusted_dX {layer width} {
if {[get_routing_direction $layer] == "ver"} {
# debug "Using rightdirection adjustment for layer $layer (dX)"
return [adjust_width [get_widthtable $layer rightdirection] $width]
} else {
# debug "Using wrongdirection adjustment for layer $layer (dX)"
return [adjust_width [get_widthtable $layer wrongdirection] $width]
}
}
proc get_adjusted_dY {layer height} {
if {[get_routing_direction $layer] == "hor"} {
# debug "Using rightdirection adjustment for layer $layer (dY)"
return [adjust_width [get_widthtable $layer rightdirection] $height]
} else {
# debug "Using wrongdirection adjustment for layer $layer (dY)"
return [adjust_width [get_widthtable $layer wrongdirection] $height]
}
}
proc get_arrayspacing_rule {layer_name} {
variable layers
if {![dict exists $layers $layer_name arrayspacing]} {
read_arrayspacing $layer_name
}
return [dict get $layers $layer_name arrayspacing]
}
proc use_arrayspacing {layer_name rows columns} {
set arrayspacing [get_arrayspacing_rule $layer_name]
# debug "$arrayspacing"
# debug "$rows $columns"
if {[llength $arrayspacing] == 0} {
# debug "No array spacing rule defined"
return 0
}
# debug "[dict keys [dict get $arrayspacing arraycuts]]"
if {[dict exists $arrayspacing arraycuts [expr min($rows,$columns)]]} {
# debug "Matching entry in arrayspacing"
return 1
}
if {min($rows,$columns) < [lindex [dict keys [dict get $arrayspacing arraycuts]] 0]} {
# debug "row/columns less than min array spacing"
return 0
}
if {min($rows,$columns) > [lindex [dict keys [dict get $arrayspacing arraycuts]] end]} {
# debug "row/columns greater than min array spacing"
return 1
}
# debug "default 1"
return 1
}
proc determine_num_via_columns {via_info constraints} {
variable upper_width
variable lower_width
variable upper_height
variable lower_height
variable lower_dir
variable upper_dir
variable min_lower_enclosure
variable max_lower_enclosure
variable min_upper_enclosure
variable max_upper_enclosure
variable cut_width
variable xcut_pitch
variable xcut_spacing
variable def_units
# What are the maximum number of columns that we can fit in this space?
set i 1
if {$lower_dir == "hor"} {
set via_width_lower [expr $cut_width + $xcut_pitch * ($i - 1) + 2 * $min_lower_enclosure]
set via_width_upper [expr $cut_width + $xcut_pitch * ($i - 1) + 2 * $max_upper_enclosure]
} else {
set via_width_lower [expr $cut_width + $xcut_pitch * ($i - 1) + 2 * $max_lower_enclosure]
set via_width_upper [expr $cut_width + $xcut_pitch * ($i - 1) + 2 * $min_upper_enclosure]
}
if {[dict exists $constraints cut_pitch]} {set xcut_pitch [expr round([dict get $constraints cut_pitch] * $def_units)]}
while {$via_width_lower <= $lower_width && $via_width_upper <= $upper_width} {
incr i
if {$lower_dir == "hor"} {
set via_width_lower [expr $cut_width + $xcut_pitch * ($i - 1) + 2 * $max_lower_enclosure]
set via_width_upper [expr $cut_width + $xcut_pitch * ($i - 1) + 2 * $min_upper_enclosure]
} else {
set via_width_lower [expr $cut_width + $xcut_pitch * ($i - 1) + 2 * $min_lower_enclosure]
set via_width_upper [expr $cut_width + $xcut_pitch * ($i - 1) + 2 * $max_upper_enclosure]
}
}
set xcut_spacing [expr $xcut_pitch - $cut_width]
set columns [expr max(1, $i - 1)]
# debug "cols $columns W: via_width_lower $via_width_lower >= lower_width $lower_width || via_width_upper $via_width_upper >= upper_width $upper_width"
if {[dict exists $constraints max_columns]} {
if {$columns > [dict get $constraints max_columns]} {
set columns [dict get $constraints max_columns]
set lower_concave_enclosure [get_concave_spacing_value [dict get $via_info cut layer] [dict get $via_info lower layer]]
# debug "$lower_concave_enclosure $max_lower_enclosure"
if {$lower_concave_enclosure > $max_lower_enclosure} {
set max_lower_enclosure $lower_concave_enclosure
}
set upper_concave_enclosure [get_concave_spacing_value [dict get $via_info cut layer] [dict get $via_info upper layer]]
# debug "$upper_concave_enclosure $max_upper_enclosure"
if {$upper_concave_enclosure > $max_upper_enclosure} {
set max_upper_enclosure $upper_concave_enclosure
}
}
}
# debug "Lower: [dict get $via_info lower layer] $lower_dir"
# debug "Upper: [dict get $via_info upper layer] $upper_dir"
if {[get_routing_direction [dict get $via_info upper layer]] == "ver"} {
if {[dict get $constraints stack_top] != [dict get $via_info upper layer]} {
# debug "Adjust width of [dict get $via_info upper layer]"
get_via_enclosure $via_info [expr min($lower_width,$lower_height)] [expr min([expr $cut_width + $xcut_pitch * ($columns - 1)],$upper_height)]
set upper_width [expr $cut_width + $xcut_pitch * ($columns - 1) + 2 * $min_upper_enclosure]
}
}
if {[get_routing_direction [dict get $via_info lower layer]] == "ver"} {
if {[dict get $constraints stack_bottom] != [dict get $via_info lower layer]} {
# debug "Adjust width of [dict get $via_info lower layer]"
get_via_enclosure $via_info [expr min([expr $cut_width + $xcut_pitch * ($columns - 1)],$lower_height)] [expr min($upper_width,$upper_height)]
set lower_width [expr $cut_width + $xcut_pitch * ($columns - 1) + 2 * $min_lower_enclosure]
}
}
# debug "cols $columns W: lower $lower_width upper $upper_width"
set lower_width [get_adjusted_dX [dict get $via_info lower layer] $lower_width]
set upper_width [get_adjusted_dX [dict get $via_info upper layer] $upper_width]
# debug "cols $columns W: lower $lower_width upper $upper_width"
return $columns
}
proc determine_num_via_rows {via_info constraints} {
variable cut_height
variable ycut_pitch
variable ycut_spacing
variable upper_height
variable lower_height
variable lower_width
variable upper_width
variable lower_dir
variable upper_dir
variable min_lower_enclosure
variable max_lower_enclosure
variable min_upper_enclosure
variable max_upper_enclosure
variable def_units
# What are the maximum number of rows that we can fit in this space?
set i 1
if {$lower_dir == "hor"} {
set via_height_lower [expr $cut_height + $ycut_pitch * ($i - 1) + 2 * $min_lower_enclosure]
set via_height_upper [expr $cut_height + $ycut_pitch * ($i - 1) + 2 * $max_upper_enclosure]
} else {
set via_height_lower [expr $cut_height + $ycut_pitch * ($i - 1) + 2 * $max_lower_enclosure]
set via_height_upper [expr $cut_height + $ycut_pitch * ($i - 1) + 2 * $min_upper_enclosure]
}
if {[dict exists $constraints cut_pitch]} {set ycut_pitch [expr round([dict get $constraints cut_pitch] * $def_units)]}
while {$via_height_lower < $lower_height && $via_height_upper < $upper_height} {
incr i
if {$lower_dir == "hor"} {
set via_height_lower [expr $cut_height + $ycut_pitch * ($i - 1) + 2 * $min_lower_enclosure]
set via_height_upper [expr $cut_height + $ycut_pitch * ($i - 1) + 2 * $max_upper_enclosure]
} else {
set via_height_lower [expr $cut_height + $ycut_pitch * ($i - 1) + 2 * $max_lower_enclosure]
set via_height_upper [expr $cut_height + $ycut_pitch * ($i - 1) + 2 * $min_upper_enclosure]
}
}
set ycut_spacing [expr $ycut_pitch - $cut_height]
set rows [expr max(1,$i - 1)]
# debug "$rows H: $via_height_lower >= $lower_height && $via_height_upper >= $upper_height"
if {[dict exists $constraints max_rows]} {
if {$rows > [dict get $constraints max_rows]} {
set rows [dict get $constraints max_rows]
set lower_concave_enclosure [get_concave_spacing_value [dict get $via_info cut layer] [dict get $via_info lower layer]]
# debug "$lower_concave_enclosure $max_lower_enclosure"
if {$lower_concave_enclosure > $max_lower_enclosure} {
set max_lower_enclosure $lower_concave_enclosure
}
set upper_concave_enclosure [get_concave_spacing_value [dict get $via_info cut layer] [dict get $via_info upper layer]]
# debug "$upper_concave_enclosure $max_upper_enclosure"
if {$upper_concave_enclosure > $max_upper_enclosure} {
set max_upper_enclosure $upper_concave_enclosure
}
}
}
if {[get_routing_direction [dict get $via_info lower layer]] == "hor"} {
# debug "[dict get $constraints stack_bottom] != [dict get $via_info lower layer]"
if {[dict get $constraints stack_bottom] != [dict get $via_info lower layer]} {
# debug "Adjust height of [dict get $via_info lower layer]"
get_via_enclosure $via_info [expr min($lower_width,[expr $cut_height + $ycut_pitch * ($rows - 1)])] [expr min($upper_width,$upper_height)]
set lower_height [expr $cut_height + $ycut_pitch * ($rows - 1) + 2 * $min_lower_enclosure]
# debug "modify lower_height to $lower_height ($cut_height + $ycut_pitch * ($rows - 1) + 2 * $min_lower_enclosure"
}
}
if {[get_routing_direction [dict get $via_info upper layer]] == "hor"} {
# debug "[dict get $constraints stack_top] != [dict get $via_info upper layer]"
if {[dict get $constraints stack_top] != [dict get $via_info upper layer]} {
# debug "Adjust height of [dict get $via_info upper layer]"
get_via_enclosure $via_info [expr min($lower_width,$lower_height)] [expr min($upper_width,[expr $cut_height + $ycut_pitch * ($rows - 1)])]
set upper_height [expr $cut_height + $ycut_pitch * ($rows - 1) + 2 * $min_upper_enclosure]
# debug "modify upper_height to $upper_height ($cut_height + $ycut_pitch * ($rows - 1) + 2 * $min_upper_enclosure"
}
}
# debug "$rows H: lower $lower_height upper $upper_height"
set lower_height [get_adjusted_dY [dict get $via_info lower layer] $lower_height]
set upper_height [get_adjusted_dY [dict get $via_info upper layer] $upper_height]
# debug "$rows H: lower $lower_height upper $upper_height"
return $rows
}
proc init_via_width_height {via_info lower_layer width height constraints} {
variable def_units
variable upper_width
variable lower_width
variable upper_height
variable lower_height
variable lower_dir
variable upper_dir
variable min_lower_enclosure
variable max_lower_enclosure
variable min_upper_enclosure
variable max_upper_enclosure
variable cut_width
variable cut_height
variable xcut_pitch
variable ycut_pitch
variable xcut_spacing
variable ycut_spacing
set upper_layer [dict get $via_info upper layer]
set xcut_pitch [lindex [dict get $via_info cut spacing] 0]
set ycut_pitch [lindex [dict get $via_info cut spacing] 0]
set cut_width [lindex [dict get $via_info cut size] 0]
set cut_height [lindex [dict get $via_info cut size] 1]
if {[dict exists $constraints split_cuts $lower_layer]} {
if {[get_dir $lower_layer] == "hor"} {
set ycut_pitch [expr round([dict get $constraints split_cuts $lower_layer] * $def_units)]
} else {
set xcut_pitch [expr round([dict get $constraints split_cuts $lower_layer] * $def_units)]
}
}
if {[dict exists $constraints split_cuts $upper_layer]} {
if {[get_dir $upper_layer] == "hor"} {
set ycut_pitch [expr round([dict get $constraints split_cuts $upper_layer] * $def_units)]
} else {
set xcut_pitch [expr round([dict get $constraints split_cuts $upper_layer] * $def_units)]
}
}
if {[dict exists $constraints width $lower_layer]} {
if {[get_dir $lower_layer] == "hor"} {
set lower_height [expr round([dict get $constraints width $lower_layer] * $def_units)]
set lower_width [get_adjusted_dX $lower_layer $width]
} else {
set lower_width [expr round([dict get $constraints width $lower_layer] * $def_units)]
set lower_height [get_adjusted_dY $lower_layer $height]
}
} else {
# Adjust the width and height values to the next largest allowed value if necessary
set lower_width [get_adjusted_dX $lower_layer $width]
set lower_height [get_adjusted_dY $lower_layer $height]
}
if {[dict exists $constraints width $upper_layer]} {
if {[get_dir $upper_layer] == "hor"} {
set upper_height [expr round([dict get $constraints width $upper_layer] * $def_units)]
set upper_width [get_adjusted_dX $upper_layer $width]
} else {
set upper_width [expr round([dict get $constraints width $upper_layer] * $def_units)]
set upper_height [get_adjusted_dY $upper_layer $height]
}
} else {
set upper_width [get_adjusted_dX $upper_layer $width]
set upper_height [get_adjusted_dY $upper_layer $height]
}
# debug "lower (width $lower_width height $lower_height) upper (width $upper_width height $upper_height)"
# debug "min - \[expr min($lower_width,$lower_height,$upper_width,$upper_height)\]"
}
proc get_enclosure_by_direction {layer xenc yenc max_enclosure min_enclosure} {
set info {}
if {$xenc > $max_enclosure && $yenc > $min_enclosure || $xenc > $min_enclosure && $yenc > $max_enclosure} {
# If the current enclosure values meet the min/max enclosure requirements either way round, then keep
# the current enclsoure settings
dict set info xEnclosure $xenc
dict set info yEnclosure $yenc
} else {
# Enforce min/max enclosure rule, with max_enclosure along the preferred direction of the layer.
if {[get_dir $layer] == "hor"} {
dict set info xEnclosure [expr max($xenc,$max_enclosure)]
dict set info yEnclosure [expr max($yenc,$min_enclosure)]
} else {
dict set info xEnclosure [expr max($xenc,$min_enclosure)]
dict set info yEnclosure [expr max($yenc,$max_enclosure)]
}
}
return $info
}
proc via_generate_rule {viarule_name via_info rule_name rows columns constraints} {
variable xcut_pitch
variable ycut_pitch
variable xcut_spacing
variable ycut_spacing
variable cut_height
variable cut_width
variable upper_width
variable lower_width
variable upper_height
variable lower_height
variable lower_dir
variable min_lower_enclosure
variable max_lower_enclosure
variable min_upper_enclosure
variable max_upper_enclosure
set lower_enc_width [expr round(($lower_width - ($cut_width + $xcut_pitch * ($columns - 1))) / 2)]
set lower_enc_height [expr round(($lower_height - ($cut_height + $ycut_pitch * ($rows - 1))) / 2)]
set upper_enc_width [expr round(($upper_width - ($cut_width + $xcut_pitch * ($columns - 1))) / 2)]
set upper_enc_height [expr round(($upper_height - ($cut_height + $ycut_pitch * ($rows - 1))) / 2)]
set lower [get_enclosure_by_direction [dict get $via_info lower layer] $lower_enc_width $lower_enc_height $max_lower_enclosure $min_lower_enclosure]
set upper [get_enclosure_by_direction [dict get $via_info upper layer] $upper_enc_width $upper_enc_height $max_upper_enclosure $min_upper_enclosure]
# debug "rule $rule_name"
# debug "lower: width $lower_width height $lower_height"
# debug "lower: enc_width $lower_enc_width enc_height $lower_enc_height enclosure_rule $max_lower_enclosure $min_lower_enclosure"
# debug "lower: enclosure [dict get $lower xEnclosure] [dict get $lower yEnclosure]"
# debug "upper: enc_width $upper_enc_width enc_height $upper_enc_height enclosure_rule $max_upper_enclosure $min_upper_enclosure"
# debug "upper: enclosure [dict get $upper xEnclosure] [dict get $upper yEnclosure]"
return [list [list \
name $rule_name \
rule $viarule_name \
cutsize [dict get $via_info cut size] \
layers [list [dict get $via_info lower layer] [dict get $via_info cut layer] [dict get $via_info upper layer]] \
cutspacing [list $xcut_spacing $ycut_spacing] \
rowcol [list $rows $columns] \
lower_rect [list [expr -1 * $lower_width / 2] [expr -1 * $lower_height / 2] [expr $lower_width / 2] [expr $lower_height / 2]] \
upper_rect [list [expr -1 * $upper_width / 2] [expr -1 * $upper_height / 2] [expr $upper_width / 2] [expr $upper_height / 2]] \
enclosure [list \
[dict get $lower xEnclosure] \
[dict get $lower yEnclosure] \
[dict get $upper xEnclosure] \
[dict get $upper yEnclosure] \
] \
origin_x 0 origin_y 0
]]
}
proc via_generate_array_rule {viarule_name via_info rule_name rows columns} {
variable xcut_pitch
variable ycut_pitch
variable xcut_spacing
variable ycut_spacing
variable cut_height
variable cut_width
variable upper_width
variable lower_width
variable upper_height
variable lower_height
variable min_lower_enclosure
variable max_lower_enclosure
variable min_upper_enclosure
variable max_upper_enclosure
# We need array vias -
# if the min(rows,columns) > ARRAYCUTS
# determine which direction gives best number of CUTs wide using min(ARRAYCUTS)
# After adding ARRAYs, is there space for more vias
# Add vias to the rule with appropriate origin setting
# else
# add a single via with min(rows,columns) cuts - hor/ver as required
set spacing_rule [get_arrayspacing_rule [dict get $via_info cut layer]]
set array_size [expr min($rows, $columns)]
set lower_enc_width [expr round(($lower_width - ($cut_width + $xcut_pitch * ($columns - 1))) / 2)]
set lower_enc_height [expr round(($lower_height - ($cut_height + $ycut_pitch * ($rows - 1))) / 2)]
set upper_enc_width [expr round(($upper_width - ($cut_width + $xcut_pitch * ($columns - 1))) / 2)]
set upper_enc_height [expr round(($upper_height - ($cut_height + $ycut_pitch * ($rows - 1))) / 2)]
if {$array_size > [lindex [dict keys [dict get $spacing_rule arraycuts]] end]} {
# debug "Multi-viaArrayspacing rule"
set use_array_size [lindex [dict keys [dict get $spacing_rule arraycuts]] 0]
foreach other_array_size [lrange [dict keys [dict get $spacing_rule arraycuts]] 1 end] {
if {$array_size % $use_array_size > $array_size % $other_array_size} {
set use_array_size $other_array_size
}
}
set num_arrays [expr $array_size / $use_array_size]
set array_spacing [expr max($xcut_spacing,$ycut_spacing,[dict get $spacing_rule arraycuts $use_array_size spacing])]
set rule [list \
rule $viarule_name \
cutsize [dict get $via_info cut size] \
layers [list [dict get $via_info lower layer] [dict get $via_info cut layer] [dict get $via_info upper layer]] \
cutspacing [list $xcut_spacing $ycut_spacing] \
lower_rect [list [expr -1 * $lower_width / 2] [expr -1 * $lower_height / 2] [expr $lower_width / 2] [expr $lower_height / 2]] \
upper_rect [list [expr -1 * $upper_width / 2] [expr -1 * $upper_height / 2] [expr $upper_width / 2] [expr $upper_height / 2]] \
origin_x 0 \
origin_y 0 \
]
# debug "$rule"
set rule_list {}
if {$array_size == $rows} {
# Split into num_arrays rows of arrays
set array_min_size [expr [lindex [dict get $via_info cut size] 0] * $use_array_size + [dict get $spacing_rule cutspacing] * ($use_array_size - 1)]
set total_array_size [expr $array_min_size * $num_arrays + $array_spacing * ($num_arrays - 1)]
# debug "Split into $num_arrays rows of arrays"
set lower_enc_height [expr round(($lower_height - ($cut_height + $ycut_pitch * ($use_array_size - 1))) / 2)]
set upper_enc_height [expr round(($upper_height - ($cut_height + $ycut_pitch * ($use_array_size - 1))) / 2)]
set lower_enc [get_enclosure_by_direction [dict get $via_info lower layer] $lower_enc_width $lower_enc_height $max_lower_enclosure $min_lower_enclosure]
set upper_enc [get_enclosure_by_direction [dict get $via_info upper layer] $upper_enc_width $upper_enc_height $max_upper_enclosure $min_upper_enclosure]
dict set rule rowcol [list $use_array_size $columns]
dict set rule name "[dict get $via_info cut layer]_ARRAY_${use_array_size}X${columns}"
dict set rule enclosure [list \
[dict get $lower_enc xEnclosure] \
[dict get $lower_enc yEnclosure] \
[dict get $upper_enc xEnclosure] \
[dict get $upper_enc yEnclosure] \
]
set y [expr $array_min_size / 2 - $total_array_size / 2]
for {set i 0} {$i < $num_arrays} {incr i} {
dict set rule origin_y $y
lappend rule_list $rule
set y [expr $y + $array_spacing + $array_min_size]
}
} else {
# Split into num_arrays columns of arrays
set array_min_size [expr [lindex [dict get $via_info cut size] 1] * $use_array_size + [dict get $spacing_rule cutspacing] * ($use_array_size - 1)]
set total_array_size [expr $array_min_size * $num_arrays + $array_spacing * ($num_arrays - 1)]
# debug "Split into $num_arrays columns of arrays"
set lower_enc_width [expr round(($lower_width - ($cut_width + $xcut_pitch * ($use_array_size - 1))) / 2)]
set upper_enc_width [expr round(($upper_width - ($cut_width + $xcut_pitch * ($use_array_size - 1))) / 2)]
set lower_enc [get_enclosure_by_direction [dict get $via_info lower layer] $lower_enc_width $lower_enc_height $max_lower_enclosure $min_lower_enclosure]
set upper_enc [get_enclosure_by_direction [dict get $via_info upper layer] $upper_enc_width $upper_enc_height $max_upper_enclosure $min_upper_enclosure]
dict set rule rowcol [list $rows $use_array_size]
dict set rule name "[dict get $via_info cut layer]_ARRAY_${rows}X${use_array_size}"
dict set rule enclosure [list \
[dict get $lower_enc xEnclosure] \
[dict get $lower_enc yEnclosure] \
[dict get $upper_enc xEnclosure] \
[dict get $upper_enc yEnclosure] \
]
set x [expr $array_min_size / 2 - $total_array_size / 2]
for {set i 0} {$i < $num_arrays} {incr i} {
dict set rule origin_x $x
lappend rule_list $rule
set x [expr $x + $array_spacing + $array_min_size]
}
}
} else {
# debug "Arrayspacing rule"
set lower_enc [get_enclosure_by_direction [dict get $via_info lower layer] $lower_enc_width $lower_enc_height $max_lower_enclosure $min_lower_enclosure]
set upper_enc [get_enclosure_by_direction [dict get $via_info upper layer] $upper_enc_width $upper_enc_height $max_upper_enclosure $min_upper_enclosure]
set rule [list \
name $rule_name \
rule $viarule_name \
cutsize [dict get $via_info cut size] \
layers [list [dict get $via_info lower layer] [dict get $via_info cut layer] [dict get $via_info upper layer]] \
cutspacing [list $xcut_spacing $ycut_spacing] \
rowcol [list $rows $columns] \
enclosure [list \
[dict get $lower_enc xEnclosure] \
[dict get $lower_enc yEnclosure] \
[dict get $upper_enc xEnclosure] \
[dict get $upper_enc yEnclosure] \
] \
origin_x 0 \
origin_y 0 \
]
set rule_list [list $rule]
}
return $rule_list
}
proc via_split_cuts_rule {rule_name via_info rows columns constraints} {
variable tech
variable def_units
variable min_lower_enclosure
variable max_lower_enclosure
variable min_upper_enclosure
variable max_upper_enclosure
variable cut_width
variable cut_height
variable xcut_pitch
variable ycut_pitch
variable xcut_spacing
variable ycut_spacing
set lower_rects {}
set cut_rects {}
set upper_rects {}
set lower [dict get $via_info lower layer]
set upper [dict get $via_info upper layer]
# debug $via_info
# debug "lower $lower upper $upper"
set rule {}
set rule [list \
rule $rule_name \
cutsize [dict get $via_info cut size] \
layers [list $lower [dict get $via_info cut layer] $upper] \
cutspacing [list $xcut_spacing $ycut_spacing] \
rowcol [list 1 1] \
]
# Enclosure was calculated from full width of intersection - need to recalculate for min cut size.
get_via_enclosure $via_info 0 0
# Area is stored in real units, adjust to def_units
set lower_area [expr round([[find_layer $lower] getArea] * $def_units * $def_units)]
set upper_area [expr round([[find_layer $upper] getArea] * $def_units * $def_units)]
if {[get_dir $lower] == "hor"} {
set lower_height [expr $cut_height + $min_lower_enclosure]
set lower_width [expr $cut_width + $max_lower_enclosure]
set upper_height [expr $cut_height + $max_upper_enclosure]
set upper_width [expr $cut_width + $min_upper_enclosure]
if {[dict exists $constraints split_cuts $lower]} {
set lower_width [expr $lower_area / $lower_height]
if {$lower_width % 2 == 1} {incr lower_width}
set max_lower_enclosure [expr max(($lower_width - $cut_width) / 2, $max_lower_enclosure)]
}
if {[dict exists $constraints split_cuts $upper]} {
set upper_height [expr $upper_area / $upper_width]
if {$upper_height % 2 == 1} {incr upper_height}
set max_upper_enclosure [expr max(($upper_height - $cut_height) / 2, $max_upper_enclosure)]
}
set width [expr $max_lower_enclosure * 2 + $cut_width]
set height [expr $max_upper_enclosure * 2 + $cut_width]
dict set rule name [get_viarule_name $lower $width $height]
dict set rule enclosure [list $max_lower_enclosure $min_lower_enclosure $min_upper_enclosure $max_upper_enclosure]
} else {
set lower_height [expr $cut_height + $max_lower_enclosure]
set lower_width [expr $cut_width + $min_lower_enclosure]
set upper_height [expr $cut_height + $min_upper_enclosure]
set upper_width [expr $cut_width + $max_upper_enclosure]
if {[dict exists $constraints split_cuts $lower]} {
set lower_width [expr $cut_width + $min_lower_enclosure]
set lower_height [expr $cut_width + $max_lower_enclosure]
set min_lower_length [expr $lower_area / $lower_width]
if {$min_lower_length % 2 == 1} {incr min_lower_length}
set max_lower_enclosure [expr max(($min_lower_length - $cut_width) / 2, $max_lower_enclosure)]
}
if {[dict exists $constraints split_cuts $upper]} {
set upper_width [expr $cut_height + $max_upper_enclosure]
set upper_height [expr $cut_height + $min_upper_enclosure]
set min_upper_length [expr $upper_area / $upper_height]
if {$min_upper_length % 2 == 1} {incr min_upper_length}
set max_upper_enclosure [expr max(($min_upper_length - $cut_height) / 2, $max_upper_enclosure)]
}
set width [expr $max_upper_enclosure * 2 + $cut_width]
set height [expr $max_lower_enclosure * 2 + $cut_width]
dict set rule name [get_viarule_name $lower $width $height]
dict set rule enclosure [list $min_lower_enclosure $max_lower_enclosure $max_upper_enclosure $min_upper_enclosure]
}
dict set rule lower_rect [list [expr -1 * $lower_width / 2] [expr -1 * $lower_height / 2] [expr $lower_width / 2] [expr $lower_height / 2]]
dict set rule upper_rect [list [expr -1 * $upper_width / 2] [expr -1 * $upper_height / 2] [expr $upper_width / 2] [expr $upper_height / 2]]
# debug "min_lower_enclosure $min_lower_enclosure"
# debug "lower $lower upper $upper enclosure [dict get $rule enclosure]"
for {set i 0} {$i < $rows} {incr i} {
for {set j 0} {$j < $columns} {incr j} {
set centre_x [expr round(($j - (($columns - 1) / 2.0)) * $xcut_pitch)]
set centre_y [expr round(($i - (($rows - 1) / 2.0)) * $ycut_pitch)]
dict set rule origin_x $centre_x
dict set rule origin_y $centre_y
lappend rule_list $rule
}
}
# debug "split into [llength $rule_list] vias"
return $rule_list
}
# viarule structure:
# {
# name <via_name>
# rule <via_rule_name>
# cutsize {<cut_size>}
# layers {<lower> <cut> <upper>}
# cutspacing {<x_spacing> <y_spacing>}
# rowcol {<rows> <columns>}
# origin_x <x_location>
# origin_y <y_location>
# enclosure {<x_lower_enclosure> <y_lower_enclosure> <x_upper_enclosure> <y_upper_enclosure>}
# lower_rect {<llx> <lly> <urx> <ury>}
# }
# Given the via rule expressed in via_info, what is the via with the largest cut area that we can make
# Try using a via generate rule
proc get_via_option {viarule_name via_info lower width height constraints} {
variable upper_width
variable lower_width
variable upper_height
variable lower_height
variable lower_dir
variable upper_dir
variable min_lower_enclosure
variable max_lower_enclosure
variable min_upper_enclosure
variable max_upper_enclosure
variable default_cutclass
variable grid_data
variable via_location
variable def_units
set upper [dict get $via_info upper layer]
# debug "{$lower $width $height}"
set lower_dir [get_dir $lower]
set upper_dir [get_dir $upper]
init_via_width_height $via_info $lower $width $height $constraints
# debug "lower: $lower, width: $width, height: $height, lower_width: $lower_width, lower_height: $lower_height"
get_via_enclosure $via_info [expr min($lower_width,$lower_height)] [expr min($upper_width,$upper_height)]
# debug "split cuts? [dict exists $constraints split_cuts]"
# debug "lower $lower upper $upper"
# debug [dict get $via_info cut layer]
# Determines the maximum number of rows and columns that can fit into this width/height
set columns [determine_num_via_columns $via_info $constraints]
set rows [determine_num_via_rows $via_info $constraints]
# debug "columns: $columns, rows: $rows"
# debug "lower_width $lower_width lower_height: $lower_height, min_lower_enclosure $min_lower_enclosure"
# debug "upper_width $upper_width upper_height: $upper_height, min_upper_enclosure $min_upper_enclosure"
if {[dict exists $constraints split_cuts] && ([lsearch -exact [dict get $constraints split_cuts] $lower] > -1 || [lsearch -exact [dict get $constraints split_cuts] $upper] > -1)} {
# debug "via_split_cuts_rule"
set rules [via_split_cuts_rule $viarule_name $via_info $rows $columns $constraints]
} elseif {[use_arrayspacing [dict get $via_info cut layer] $rows $columns]} {
# debug "via_generate_array_rule"
set rules [via_generate_array_rule $viarule_name $via_info [get_viarule_name $lower $width $height] $rows $columns]
} else {
# debug "via_generate_rule"
set rules [via_generate_rule $viarule_name $via_info [get_viarule_name $lower $width $height] $rows $columns $constraints]
}
# Check minimum_cuts
set checked_rules {}
foreach via_rule $rules {
# debug "$via_rule"
set num_cuts [expr [lindex [dict get $via_rule rowcol] 0] * [lindex [dict get $via_rule rowcol] 1]]
if {[dict exists $default_cutclass [lindex [dict get $via_rule layers] 1]]} {
set cut_class [dict get $default_cutclass [lindex [dict get $via_rule layers] 1]]
} else {
set cut_class "NONE"
}
set lower_layer [lindex [dict get $via_rule layers] 0]
if {[dict exists $constraints stack_bottom]} {
if {[dict exists $grid_data straps $lower_layer] || [dict exists $grid_data rails $lower_layer]} {
set lower_width [get_grid_wire_width $lower_layer]
} else {
set lower_rect [dict get $via_rule lower_rect]
set lower_width [expr min(([lindex $lower_rect 2] - [lindex $lower_rect 0]), ([lindex $lower_rect 3] - [lindex $lower_rect 1]))]
}
} else {
set lower_rect [dict get $via_rule lower_rect]
set lower_width [expr min(([lindex $lower_rect 2] - [lindex $lower_rect 0]), ([lindex $lower_rect 3] - [lindex $lower_rect 1]))]
}
set min_cut_rule [get_minimumcuts $lower_layer $lower_width fromabove $cut_class]
if {$num_cuts < $min_cut_rule} {
utl::warn "PDN" 38 "Illegal via: number of cuts ($num_cuts), does not meet minimum cut rule ($min_cut_rule) for $lower_layer to $cut_class with width [expr 1.0 * $lower_width / $def_units]."
dict set via_rule illegal 1
} else {
# debug "Legal number of cuts ($num_cuts) meets minimum cut rule ($min_cut_rule) for $lower_layer, $lower_width, $cut_class"
}
set upper_layer [lindex [dict get $via_rule layers] 2]
if {[dict exists $constraints stack_top]} {
if {[dict exists $grid_data straps $upper_layer width] || [dict exists $grid_data rails $upper_layer width]} {
set upper_width [get_grid_wire_width $upper_layer]
} else {
set upper_rect [dict get $via_rule upper_rect]
set upper_width [expr min(([lindex $upper_rect 2] - [lindex $upper_rect 0]), ([lindex $upper_rect 3] - [lindex $upper_rect 1]))]
}
} else {
set upper_rect [dict get $via_rule upper_rect]
set upper_width [expr min(([lindex $upper_rect 2] - [lindex $upper_rect 0]), ([lindex $upper_rect 3] - [lindex $upper_rect 1]))]
}
set min_cut_rule [get_minimumcuts $upper_layer $upper_width frombelow $cut_class]
if {$num_cuts < $min_cut_rule} {
utl::warn "PDN" 39 "Illegal via: number of cuts ($num_cuts), does not meet minimum cut rule ($min_cut_rule) for $upper_layer to $cut_class with width [expr 1.0 * $upper_width / $def_units]."
dict set via_rule illegal 1
} else {
# debug "Legal number of cuts ($num_cuts) meets minimum cut rule ($min_cut_rule) for $upper_layer, $upper_width $cut_class"
}
if {[dict exists $via_rule illegal]} {
utl::warn "PDN" 36 "Attempt to add illegal via at : ([expr 1.0 * [lindex $via_location 0] / $def_units] [expr 1.0 * [lindex $via_location 1] / $def_units]), via will not be added."
}
lappend checked_rules $via_rule
}
return $checked_rules
}
proc get_viarule_name {lower width height} {
set rules [select_via_info $lower]
if {[llength $rules] > 0} {
set first_key [lindex [dict keys $rules] 0]
#if {![dict exists $rules $first_key cut layer]} {
# debug "$lower $width $height"
# debug "$rules"
# debug "$first_key"
#}
set cut_layer [dict get $rules $first_key cut layer]
} else {
set cut_layer $lower
}
return ${cut_layer}_${width}x${height}
}
proc get_cut_area {rule} {
set area 0
foreach via $rule {
set area [expr [lindex [dict get $via rowcol] 0] * [lindex [dict get $via rowcol] 0] * [lindex [dict get $via cutsize] 0] * [lindex [dict get $via cutsize] 1]]
}
return $area
}
proc select_rule {rule1 rule2} {
if {[get_cut_area $rule2] > [get_cut_area $rule1]} {
return $rule2
}
return $rule1
}
proc connection_specifies_fixed_via {constraints lower} {
if {[dict exists $constraints use_fixed_via]} {
return [dict exists $constraints use_fixed_via $lower]
}
return 0
}
proc get_via {lower width height constraints} {
# First cur will assume that all crossing points (x y) are on grid for both lower and upper layers
# TODO: Refine the algorithm to cope with offgrid intersection points
variable physical_viarules
set rule_name [get_viarule_name $lower $width $height]
if {![dict exists $physical_viarules $rule_name]} {
set selected_rule {}
# debug "$constraints"
if {[connection_specifies_fixed_via $constraints $lower]} {
# debug "Using fixed_via for $rule_name"
set via_name [dict get $constraints use_fixed_via $lower]
dict set physical_viarules $rule_name [list [list name $via_name fixed $via_name origin_x 0 origin_y 0 layers [list $lower "cut" "upper"]]]
} else {
dict for {name rule} [select_via_info $lower] {
set result [get_via_option $name $rule $lower $width $height $constraints]
if {$selected_rule == {}} {
set selected_rule $result
} else {
# Choose the best between selected rule and current result, the winner becomes the new selected rule
set selected_rule [select_rule $selected_rule $result]
}
}
dict set physical_viarules $rule_name $selected_rule
# debug "Via [dict size $physical_viarules]: $rule_name"
}
}
return $rule_name
}
proc instantiate_via {physical_via_name x y constraints} {
variable physical_viarules
variable block
variable layers
set via_insts {}
foreach via [dict get $physical_viarules $physical_via_name] {
# debug "via x $x y $y $via"
# Dont instantiate illegal vias
if {[dict exists $via illegal]} {continue}
set x_location [expr $x + [dict get $via origin_x]]
set y_location [expr $y + [dict get $via origin_y]]
set lower_layer_name [lindex [dict get $via layers] 0]
set upper_layer_name [lindex [dict get $via layers] 2]
if {[dict exists $constraints ongrid]} {
if {[lsearch -exact [dict get $constraints ongrid] $lower_layer_name] > -1} {
if {[get_dir $lower_layer_name] == "hor"} {
set y_pitch [dict get $layers $lower_layer_name pitch]
set y_offset [dict get $layers $lower_layer_name offsetY]
set y_location [expr ($y - $y_offset + $y_pitch / 2) / $y_pitch * $y_pitch + $y_offset + [dict get $via origin_y]]
} else {
set x_pitch [dict get $layers $lower_layer_name pitch]
set x_offset [dict get $layers $lower_layer_name offsetX]
set x_location [expr ($x - $x_offset + $x_pitch / 2) / $x_pitch * $x_pitch + $x_offset + [dict get $via origin_x]]
}
}
if {[lsearch -exact [dict get $constraints ongrid] $upper_layer_name] > -1} {
if {[get_dir $lower_layer_name] == "hor"} {
set x_pitch [dict get $layers $upper_layer_name pitch]
set x_offset [dict get $layers $upper_layer_name offsetX]
set x_location [expr ($x - $x_offset + $x_pitch / 2) / $x_pitch * $x_pitch + $x_offset + [dict get $via origin_x]]
} else {
set y_pitch [dict get $layers $upper_layer_name pitch]
set y_offset [dict get $layers $upper_layer_name offsetY]
set y_location [expr ($y - $y_offset + $y_pitch / 2) / $y_pitch * $y_pitch + $y_offset + [dict get $via origin_y]]
}
}
}
# debug "x: $x -> $x_location"
# debug "y: $y -> $y_location"
dict set via x $x_location
dict set via y $y_location
lappend via_insts $via
}
return $via_insts
}
proc generate_vias {layer1 layer2 intersections connection} {
variable logical_viarules
variable metal_layers
variable via_location
variable tech
set constraints {}
if {[dict exists $connection constraints]} {
set constraints [dict get $connection constraints]
}
if {[dict exists $connection fixed_vias]} {
foreach via_name [dict get $connection fixed_vias] {
if {[set via [$tech findVia $via_name]] != "NULL"} {
set lower_layer_name [[$via getBottomLayer] getName]
dict set constraints use_fixed_via $lower_layer_name $via_name
} else {
utl::warn "PDN" 63 "Via $via_name specified in the grid specification does not exist in this technology."
}
}
}
# debug " Constraints: $constraints"
set vias {}
set layer1_name $layer1
set layer2_name $layer2
regexp {(.*)_PIN_(hor|ver)} $layer1 - layer1_name layer1_direction
set i1 [lsearch -exact $metal_layers $layer1_name]
set i2 [lsearch -exact $metal_layers $layer2_name]
if {$i1 == -1} {utl::error "PDN" 22 "Cannot find lower metal layer $layer1."}
if {$i2 == -1} {utl::error "PDN" 23 "Cannot find upper metal layer $layer2."}
# For each layer between l1 and l2, add vias at the intersection
# debug " # Intersections [llength $intersections]"
set count 0
foreach intersection $intersections {
if {![dict exists $logical_viarules [dict get $intersection rule]]} {
utl::error "PDN" 24 "Missing logical viarule [dict get $intersection rule].\nAvailable logical viarules [dict keys $logical_viarules]."
}
set logical_rule [dict get $logical_viarules [dict get $intersection rule]]
set x [dict get $intersection x]
set y [dict get $intersection y]
set width [dict get $logical_rule width]
set height [dict get $logical_rule height]
set via_location [list $x $y]
set connection_layers [lrange $metal_layers $i1 [expr $i2 - 1]]
# debug " # Connection layers: [llength $connection_layers]"
# debug " Connection layers: $connection_layers"
dict set constraints stack_top $layer2_name
dict set constraints stack_bottom $layer1_name
foreach lay $connection_layers {
set via_name [get_via $lay $width $height $constraints]
foreach via [instantiate_via $via_name $x $y $constraints] {
lappend vias $via
}
}
incr count
#if {$count % 1000 == 0} {
# debug " # $count / [llength $intersections]"
#}
}
return $vias
}
proc get_layers_from_to {from to} {
variable metal_layers
set layers {}
for {set i [lsearch -exact $metal_layers $from]} {$i <= [lsearch -exact $metal_layers $to]} {incr i} {
lappend layers [lindex $metal_layers $i]
}
return $layers
}
proc get_grid_channel_layers {} {
variable grid_data
set channel_layers {}
if {[dict exists $grid_data rails]} {
lappend channel_layers [lindex [dict keys [dict get $grid_data rails]] end]
}
foreach layer_name [dict keys [dict get $grid_data straps]] {
lappend channel_layers $layer_name
}
return $channel_layers
}
proc get_grid_channel_spacing {layer_name parallel_length} {
variable grid_data
variable def_units
if {[dict exists $grid_data straps $layer_name channel_spacing]} {
return [expr round([dict get $grid_data straps $layer_name channel_spacing] * $def_units)]
} elseif {[dict exists $grid_data straps $layer_name] && [dict exists $grid_data template names]} {
set template_name [lindex [dict get $grid_data template names] 0]
if {[dict exists $grid_data straps $layer_name $template_name channel_spacing]} {
return [expr round([dict get $grid_data straps $layer_name $template_name channel_spacing]]
}
} else {
set layer [[ord::get_db_tech] findLayer $layer_name]
if {$layer == "NULL"} {
utl::error PDN 168 "Layer $layer_name does not exist"
}
set layer_width [get_grid_wire_width $layer_name]
if {[$layer hasTwoWidthsSpacingRules]} {
set num_widths [$layer getTwoWidthsSpacingTableNumWidths]
set current_width 0
set prl_rule -1
for {set rule 0} {$rule < $num_widths} {incr rule} {
set width [$layer getTwoWidthsSpacingTableWidth $rule]
if {$width == $current_width && $prl_rule != -1} {
continue
} else {
set current_width $width
if {[$layer getTwoWidthsSpacingTableHasPRL $rule] == 0} {
set non_prl_rule $rule
set prl_rule -1
} else {
if {$parallel_length > [$layer getTwoWidthsSpacingTablePRL $rule]} {
set prl_rule $rule
}
}
}
if {$layer_width < [$layer getTwoWidthsSpacingTableWidth $rule]} {
if {$prl_rule == 0} {
set use_rule $non_prl_rule
} else {
set use_rule $prl_rule
}
break
}
}
set spacing [$layer getTwoWidthsSpacingTableEntry $use_rule $use_rule]
# debug "Two widths spacing: layer: $layer_name, rule: $use_rule, spacing: $spacing"
} elseif {[$layer hasV55SpacingRules]} {
set layer_width [get_grid_wire_width $layer_name]
set spacing [$layer findV55Spacing $layer_width $parallel_length]
} else {
set spacing [$layer getSpacing]
}
# Can't store value, since it depends on channel height
return $spacing
}
utl::error "PDN" 52 "Unable to get channel_spacing setting for layer $layer_name."
}
proc get_grid_wire_width {layer_name} {
variable grid_data
variable default_grid_data
variable design_data
if {[info exists grid_data]} {
if {[dict exists $grid_data rails $layer_name width]} {
set width [dict get $grid_data rails $layer_name width]
return $width
} elseif {[dict exists $grid_data straps $layer_name width]} {
set width [dict get $grid_data straps $layer_name width]
return $width
} elseif {[dict exists $grid_data straps $layer_name] && [dict exists $grid_data template names]} {
set template_name [lindex [dict get $grid_data template names] 0]
set width [dict get $grid_data straps $layer_name $template_name width]
return $width
} elseif {[dict exists $grid_data core_ring $layer_name width]} {
set width [dict get $grid_data core_ring $layer_name width]
return $width
}
}
if {[info exists default_grid_data]} {
if {[dict exists $default_grid_data rails $layer_name width]} {
set width [dict get $default_grid_data rails $layer_name width]
return $width
} elseif {[dict exists $default_grid_data straps $layer_name width]} {
set width [dict get $default_grid_data straps $layer_name width]
return $width
} elseif {[dict exists $default_grid_data straps $layer_name] && [dict exists $default_grid_data template names]} {
set template_name [lindex [dict get $default_grid_data template names] 0]
set width [dict get $default_grid_data straps $layer_name $template_name width]
return $width
}
}
utl::error "PDN" 44 "No width information found for $layer_name."
}
proc get_grid_wire_pitch {layer_name} {
variable grid_data
variable default_grid_data
variable design_data
if {[dict exists $grid_data rails $layer_name pitch]} {
set pitch [dict get $grid_data rails $layer_name pitch]
} elseif {[dict exists $grid_data straps $layer_name pitch]} {
set pitch [dict get $grid_data straps $layer_name pitch]
} elseif {[dict exists $grid_data straps $layer_name] && [dict exists $grid_data template names]} {
set template_name [lindex [dict get $grid_data template names] 0]
set pitch [dict get $grid_data straps $layer_name $template_name pitch]
} elseif {[dict exists $default_grid_data straps $layer_name pitch]} {
set pitch [dict get $default_grid_data straps $layer_name pitch]
} elseif {[dict exists $default_grid_data straps $layer_name] && [dict exists $default_grid_data template names]} {
set template_name [lindex [dict get $default_grid_data template names] 0]
set pitch [dict get $default_grid_data straps $layer_name $template_name pitch]
} else {
utl::error "PDN" 45 "No pitch information found for $layer_name."
}
return $pitch
}
## Proc to generate via locations, both for a normal via and stacked via
proc generate_via_stacks {l1 l2 tag connection} {
variable logical_viarules
variable stripe_locs
variable def_units
variable grid_data
set area [dict get $grid_data area]
# debug "From $l1 to $l2"
if {[dict exists $grid_data core_ring_area combined]} {
set grid_area [dict get $grid_data core_ring_area combined]
set factor [expr max([lindex $area 2] - [lindex $area 0], [lindex $area 3] - [lindex $area 1]) * 2]
set grid_area [odb::shrinkSet [odb::bloatSet $grid_area $factor] $factor]
# debug "Old area ($area)"
set bbox [lindex [odb::getRectangles $grid_area] 0]
set area [list {*}[$bbox ll] {*}[$bbox ur]]
# debug "Recalculated area to be ($area)"
}
#this variable contains locations of intersecting points of two orthogonal metal layers, between which via needs to be inserted
#for every intersection. Here l1 and l2 are layer names, and i1 and i2 and their indices, tag represents domain (power or ground)
set intersections ""
#check if layer pair is orthogonal, case 1
set layer1 $l1
regexp {(.*)_PIN_(hor|ver)} $l1 - layer1 direction
set layer2 $l2
set ignore_count 0
if {[array names stripe_locs "$l1,$tag"] == ""} {
utl::warn "PDN" 2 "No shapes on layer $l1 for $tag."
return {}
}
if {[array names stripe_locs "$l2,$tag"] == ""} {
utl::warn "PDN" 3 "No shapes on layer $l2 for $tag."
return {}
}
set intersection [odb::andSet [odb::andSet $stripe_locs($l1,$tag) $stripe_locs($l2,$tag)] [odb::newSetFromRect {*}$area]]
# debug "Detected [llength [::odb::getPolygons $intersection]] intersections of $l1 and $l2"
foreach shape [::odb::getPolygons $intersection] {
set points [::odb::getPoints $shape]
if {[llength $points] != 4} {
variable def_units
utl::warn "PDN" 4 "Unexpected number of points in connection shape ($l1,$l2 $tag [llength $points])."
set str " "
foreach point $points {set str "$str ([expr 1.0 * [$point getX] / $def_units ] [expr 1.0 * [$point getY] / $def_units]) "}
utl::warn "PDN" 5 $str
continue
}
set xMin [expr min([[lindex $points 0] getX], [[lindex $points 1] getX], [[lindex $points 2] getX], [[lindex $points 3] getX])]
set xMax [expr max([[lindex $points 0] getX], [[lindex $points 1] getX], [[lindex $points 2] getX], [[lindex $points 3] getX])]
set yMin [expr min([[lindex $points 0] getY], [[lindex $points 1] getY], [[lindex $points 2] getY], [[lindex $points 3] getY])]
set yMax [expr max([[lindex $points 0] getY], [[lindex $points 1] getY], [[lindex $points 2] getY], [[lindex $points 3] getY])]
set width [expr $xMax - $xMin]
set height [expr $yMax - $yMin]
# Ensure that the intersections are not partial
if {![regexp {(.*)_PIN_(hor|ver)} $l1]} {
if {[get_dir $layer1] == "hor"} {
if {$height < [get_grid_wire_width $layer1]} {
# If the intersection doesnt cover the whole width of the bottom level wire, then ignore
utl::warn "PDN" 40 "No via added at ([expr 1.0 * $xMin / $def_units] [expr 1.0 * $yMin / $def_units] [expr 1.0 * $xMax / $def_units] [expr 1.0 * $yMax / $def_units]) because the full height of $layer1 ([expr 1.0 * [get_grid_wire_width $layer1] / $def_units]) is not covered by the overlap."
continue
}
} else {
if {$width < [get_grid_wire_width $layer1]} {
# If the intersection doesnt cover the whole width of the bottom level wire, then ignore
utl::warn "PDN" 41 "No via added at ([expr 1.0 * $xMin / $def_units] [expr 1.0 * $yMin / $def_units] [expr 1.0 * $xMax / $def_units] [expr 1.0 * $yMax / $def_units]) because the full width of $layer1 ([expr 1.0 * [get_grid_wire_width $layer1] / $def_units]) is not covered by the overlap."
continue
}
}
}
if {[get_dir $layer2] == "hor"} {
if {$height < [get_grid_wire_width $layer2]} {
# If the intersection doesnt cover the whole width of the top level wire, then ignore
utl::warn "PDN" 42 "No via added at ([expr 1.0 * $xMin / $def_units] [expr 1.0 * $yMin / $def_units] [expr 1.0 * $xMax / $def_units] [expr 1.0 * $yMax / $def_units]) because the full height of $layer2 ([expr 1.0 * [get_grid_wire_width $layer2] / $def_units]) is not covered by the overlap."
continue
}
} else {
if {$width < [get_grid_wire_width $layer2]} {
# If the intersection doesnt cover the whole width of the top level wire, then ignore
utl::warn "PDN" 43 "No via added at ([expr 1.0 * $xMin / $def_units] [expr 1.0 * $yMin / $def_units] [expr 1.0 * $xMax / $def_units] [expr 1.0 * $yMax / $def_units]) because the full width of $layer2 ([expr 1.0 * [get_grid_wire_width $layer2] / $def_units]) is not covered by the overlap."
continue
}
}
set rule_name ${l1}${layer2}_${width}x${height}
if {![dict exists $logical_viarules $rule_name]} {
dict set logical_viarules $rule_name [list lower $l1 upper $layer2 width $width height $height]
}
lappend intersections "rule $rule_name x [expr ($xMax + $xMin) / 2] y [expr ($yMax + $yMin) / 2]"
}
# debug "Added [llength $intersections] intersections"
return [generate_vias $l1 $l2 $intersections $connection]
}
proc add_stripe {layer type polygon_set} {
variable stripes
# debug "start"
lappend stripes($layer,$type) $polygon_set
# debug "end"
}
proc merge_stripes {} {
variable stripes
variable stripe_locs
foreach stripe_set [array names stripes] {
# debug "$stripe_set [llength $stripes($stripe_set)]"
if {[llength $stripes($stripe_set)] > 0} {
set merged_stripes [shapes_to_polygonSet $stripes($stripe_set)]
if {[array names stripe_locs $stripe_set] != ""} {
# debug "$stripe_locs($stripe_set)"
set stripe_locs($stripe_set) [odb::orSet $stripe_locs($stripe_set) $merged_stripes]
} else {
set stripe_locs($stripe_set) $merged_stripes
}
}
set stripes($stripe_set) {}
}
}
proc get_core_ring_vertical_layer_name {} {
variable grid_data
if {![dict exists $grid_data core_ring]} {
return ""
}
foreach layer_name [dict keys [dict get $grid_data core_ring]] {
if {[get_dir $layer_name] == "ver"} {
return $layer_name
}
}
return ""
}
proc is_extend_to_core_ring {layer_name} {
variable grid_data
if {![dict exists $grid_data rails $layer_name extend_to_core_ring]} {
return 0
}
if {![dict get $grid_data rails $layer_name extend_to_core_ring]} {
return 0
}
if {[get_core_ring_vertical_layer_name] == ""} {
return 0
}
return 1
}
# proc to generate follow pin layers or standard cell rails
proc generate_lower_metal_followpin_rails {} {
variable block
variable grid_data
variable design_data
set stdcell_area [get_extent [get_stdcell_area]]
set stdcell_min_x [lindex $stdcell_area 0]
set stdcell_max_x [lindex $stdcell_area 2]
if {[set ring_vertical_layer [get_core_ring_vertical_layer_name]] != ""} {
# debug "Ring vertical layer: $ring_vertical_layer"
# debug "Grid_data: $grid_data"
if {[dict exists $grid_data core_ring $ring_vertical_layer pad_offset]} {
set pad_area [find_pad_offset_area]
set offset [expr [dict get $grid_data core_ring $ring_vertical_layer pad_offset]]
set ring_adjustment [expr $stdcell_min_x - ([lindex $pad_area 0] + $offset)]
}
if {[dict exists $grid_data core_ring $ring_vertical_layer core_offset]} {
set ring_adjustment [expr \
[dict get $grid_data core_ring $ring_vertical_layer core_offset] + \
[dict get $grid_data core_ring $ring_vertical_layer spacing] + \
3 * [dict get $grid_data core_ring $ring_vertical_layer width] / 2 \
]
}
}
foreach row [$block getRows] {
set orient [$row getOrient]
set box [$row getBBox]
switch -exact $orient {
R0 {
set vdd_y [$box yMax]
set vss_y [$box yMin]
}
MX {
set vdd_y [$box yMin]
set vss_y [$box yMax]
}
default {
utl::error "PDN" 25 "Unexpected row orientation $orient for row [$row getName]."
}
}
foreach lay [get_rails_layers] {
set xMin [$box xMin]
set xMax [$box xMax]
if {[is_extend_to_core_ring $lay]} {
# debug "Extending to core_ring - adjustment $ring_adjustment ($xMin/$xMax) ($stdcell_min_x/$stdcell_max_x)"
set voltage_domain [get_voltage_domain $xMin [$box yMin] $xMax [$box yMax]]
if {$voltage_domain == [dict get $design_data core_domain]} {
if {$xMin == $stdcell_min_x} {
set xMin [expr $xMin - $ring_adjustment]
}
if {$xMax == $stdcell_max_x} {
set xMax [expr $xMax + $ring_adjustment]
}
} else {
#Create lower metal followpin rails for voltage domains where the starting positions are not stdcell_min_x
set core_power [get_voltage_domain_power [dict get $design_data core_domain]]
set core_ground [get_voltage_domain_ground [dict get $design_data core_domain]]
set domain_power [get_voltage_domain_power $voltage_domain]
set domain_ground [get_voltage_domain_ground $voltage_domain]
set first_rect [lindex [[$block findRegion $voltage_domain] getBoundaries] 0]
set domain_xMin [$first_rect xMin]
set domain_xMax [$first_rect xMax]
if {$xMin == $domain_xMin} {
set xMin [expr $xMin - $ring_adjustment]
}
if {$xMax == $domain_xMax} {
set xMax [expr $xMax + $ring_adjustment]
}
}
# debug "Extended to core_ring - adjustment $ring_adjustment ($xMin/$xMax)"
}
set width [dict get $grid_data rails $lay width]
# debug "VDD: $xMin [expr $vdd_y - $width / 2] $xMax [expr $vdd_y + $width / 2]"
set vdd_box [::odb::newSetFromRect $xMin [expr $vdd_y - $width / 2] $xMax [expr $vdd_y + $width / 2]]
set vdd_name [get_voltage_domain_power [get_voltage_domain $xMin [expr $vdd_y - $width / 2] $xMax [expr $vdd_y + $width / 2]]]
set vss_box [::odb::newSetFromRect $xMin [expr $vss_y - $width / 2] $xMax [expr $vss_y + $width / 2]]
set vss_name [get_voltage_domain_ground [get_voltage_domain $xMin [expr $vss_y - $width / 2] $xMax [expr $vss_y + $width / 2]]]
# generate power_rails using first domain_power
set first_power_name [lindex $vdd_name 0]
# debug "[$box xMin] [expr $vdd_y - $width / 2] [$box xMax] [expr $vdd_y + $width / 2]"
if {$first_power_name == [get_voltage_domain_power [dict get $design_data core_domain]]} {
add_stripe $lay "POWER" $vdd_box
} else {
add_stripe $lay "POWER_$first_power_name" $vdd_box
}
if {$vss_name == [get_voltage_domain_ground [dict get $design_data core_domain]]} {
add_stripe $lay "GROUND" $vss_box
} else {
add_stripe $lay "GROUND_$vss_name" $vss_box
}
}
}
}
proc starts_with {lay} {
variable grid_data
variable stripes_start_with
if {[dict exists $grid_data straps $lay starts_with]} {
set starts_with [dict get $grid_data straps $lay starts_with]
} elseif {[dict exists $grid_data starts_with]} {
set starts_with [dict get $grid_data starts_with]
} else {
set starts_with $stripes_start_with
}
return $starts_with
}
# proc for creating pdn mesh for upper metal layers
proc generate_upper_metal_mesh_stripes {tag layer layer_info area} {
# If the grid_data defines a spacing for the layer, then:
# place the second stripe spacing + width away from the first,
# otherwise:
# place the second stripe pitch / 2 away from the first,
#
set width [dict get $layer_info width]
set start_with [starts_with $layer]
# debug "Starts with: $start_with"
if {[get_dir $layer] == "hor"} {
set offset [expr [lindex $area 1] + [dict get $layer_info offset]]
if {![regexp "$start_with.*" $tag match]} { ;#If not starting from bottom with this net,
if {[dict exists $layer_info spacing]} {
set offset [expr {$offset + [dict get $layer_info spacing] + [dict get $layer_info width]}]
} else {
set offset [expr {$offset + ([dict get $layer_info pitch] / 2)}]
}
}
for {set y $offset} {$y < [expr {[lindex $area 3] - [dict get $layer_info width]}]} {set y [expr {[dict get $layer_info pitch] + $y}]} {
set box [::odb::newSetFromRect [lindex $area 0] [expr $y - $width / 2] [lindex $area 2] [expr $y + $width / 2]]
add_stripe $layer $tag $box
}
} elseif {[get_dir $layer] == "ver"} {
set offset [expr [lindex $area 0] + [dict get $layer_info offset]]
if {![regexp "$start_with.*" $tag match]} { ;#If not starting from bottom with this net,
if {[dict exists $layer_info spacing]} {
set offset [expr {$offset + [dict get $layer_info spacing] + [dict get $layer_info width]}]
} else {
set offset [expr {$offset + ([dict get $layer_info pitch] / 2)}]
}
}
for {set x $offset} {$x < [expr {[lindex $area 2] - [dict get $layer_info width]}]} {set x [expr {[dict get $layer_info pitch] + $x}]} {
set box [::odb::newSetFromRect [expr $x - $width / 2] [lindex $area 1] [expr $x + $width / 2] [lindex $area 3]]
add_stripe $layer $tag $box
}
} else {
utl::error "PDN" 26 "Invalid direction \"[get_dir $layer]\" for metal layer ${layer}. Should be either \"hor\" or \"ver\"."
}
}
proc adjust_area_for_core_rings {layer area number} {
variable grid_data
# When core_rings overlap with the stdcell area, we need to block out the area
# where the core rings have been placed.
if {[dict exists $grid_data core_ring_area $layer]} {
set core_ring_area [dict get $grid_data core_ring_area $layer]
set grid_area [odb::newSetFromRect {*}$area]
set grid_area [odb::subtractSet $grid_area $core_ring_area]
set area [get_extent $grid_area]
}
# Calculate how far to extend the grid to meet with the core rings
if {[dict exists $grid_data core_ring $layer pad_offset]} {
set pad_area [find_pad_offset_area]
set width [dict get $grid_data core_ring $layer width]
set offset [expr [dict get $grid_data core_ring $layer pad_offset]]
set spacing [dict get $grid_data core_ring $layer spacing]
set xMin [expr [lindex $pad_area 0] + $offset]
set yMin [expr [lindex $pad_area 1] + $offset]
set xMax [expr [lindex $pad_area 2] - $offset]
set yMax [expr [lindex $pad_area 3] - $offset]
} elseif {[dict exists $grid_data core_ring $layer core_offset]} {
set offset [dict get $grid_data core_ring $layer core_offset]
set width [dict get $grid_data core_ring $layer width]
set spacing [dict get $grid_data core_ring $layer spacing]
# debug "Area: $area"
# debug "Offset: $offset, Width $width, Spacing $spacing"
# The area figure includes a y offset for the width of the stdcell rail - so need to subtract it here
set rail_width [get_rails_max_width]
# set extension area according to the number of power rings of the voltage domain, the default number is 2
set xMin [expr [lindex $area 0] - $offset - $width - $spacing - $width / 2 - ($number - 2) * ($width + $spacing)]
set yMin [expr [lindex $area 1] - $offset - $width - $spacing - $width / 2 + $rail_width / 2 - ($number - 2) * ($width + $spacing)]
set xMax [expr [lindex $area 2] + $offset + $width + $spacing + $width / 2 + ($number - 2) * ($width + $spacing)]
set yMax [expr [lindex $area 3] + $offset + $width + $spacing + $width / 2 - $rail_width / 2 + ($number - 2) * ($width + $spacing)]
}
if {[get_dir $layer] == "hor"} {
set extended_area [list $xMin [lindex $area 1] $xMax [lindex $area 3]]
} else {
set extended_area [list [lindex $area 0] $yMin [lindex $area 2] $yMax]
}
return $extended_area
}
## this is a top-level proc to generate PDN stripes and insert vias between these stripes
proc generate_stripes {tag net_name} {
variable plan_template
variable template
variable grid_data
variable block
variable design_data
variable voltage_domains
# debug "start: grid_name: [dict get $grid_data name]"
if {![dict exists $grid_data straps]} {return}
foreach lay [dict keys [dict get $grid_data straps]] {
# debug " Layer $lay ..."
#Upper layer stripes
if {[dict exists $grid_data straps $lay width]} {
set area [dict get $grid_data area]
# debug "Area $area"
# Calculate the numebr of rings of core_domain
set ring_number 2
if {[dict exists $grid_data core_ring] && [dict exists $grid_data core_ring $lay]} {
set area [adjust_area_for_core_rings $lay $area $ring_number]
}
# debug "area=$area (spec area=[dict get $grid_data area])"
# Create stripes for core domain's pwr/gnd nets
if {$net_name == [get_voltage_domain_power [dict get $design_data core_domain]] ||
$net_name == [get_voltage_domain_ground [dict get $design_data core_domain]]} {
generate_upper_metal_mesh_stripes $tag $lay [dict get $grid_data straps $lay] $area
# Split core domains pwr/gnd nets when they cross other voltage domains that have different pwr/gnd nets
update_mesh_stripes_with_volatge_domains $tag $lay $net_name
}
# Create stripes for each voltage domains
foreach domain_name [dict keys $voltage_domains] {
if {$domain_name == [dict get $design_data core_domain]} {continue}
set domain [$block findRegion $domain_name]
set rect [lindex [$domain getBoundaries] 0]
set domain_name [$domain getName]
set domain_xMin [$rect xMin]
set domain_yMin [$rect yMin]
set domain_xMax [$rect xMax]
set domain_yMax [$rect yMax]
set width [dict get $grid_data core_ring $lay width]
set spacing [dict get $grid_data core_ring $lay spacing]
set rail_width [get_rails_max_width]
# Do not create duplicate stripes if the voltage domain has the same pwr/gnd nets as the core domain
if {($net_name == [get_voltage_domain_power $domain_name] && $net_name != [get_voltage_domain_power [dict get $design_data core_domain]]) ||
($net_name == [get_voltage_domain_ground $domain_name] && $net_name != [get_voltage_domain_ground [dict get $design_data core_domain]])} {
set area [list $domain_xMin [expr $domain_yMin - $rail_width / 2] $domain_xMax [expr $domain_yMax + $rail_width / 2]]
set area [adjust_area_for_core_rings $lay $area 2]
set tag "$tag\_$net_name"
generate_upper_metal_mesh_stripes $tag $lay [dict get $grid_data straps $lay] $area
}
if {[lsearch -exact [get_voltage_domain_secondary_power $domain_name] $net_name] > -1} {
#Calculate the ring number of power_domain
set ring_number [lsearch -exact [get_voltage_domain_secondary_power $domain_name] $net_name]
set area [list [expr $domain_xMin + $ring_number * ($width + $spacing)] [expr $domain_yMin - $rail_width / 2] [expr $domain_xMax + $ring_number * ($width + $spacing)] [expr $domain_yMax + $rail_width / 2]]
set area [adjust_area_for_core_rings $lay $area [expr 3 + $ring_number]]
set tag "$tag\_$net_name"
generate_upper_metal_mesh_stripes $tag $lay [dict get $grid_data straps $lay] $area
}
}
} else {
foreach x [lsort -integer [dict keys $plan_template]] {
foreach y [lsort -integer [dict keys [dict get $plan_template $x]]] {
set template_name [dict get $plan_template $x $y]
set layer_info [dict get $grid_data straps $lay $template_name]
set area [list $x $y [expr $x + [dict get $template width]] [expr $y + [dict get $template height]]]
generate_upper_metal_mesh_stripes $tag $lay $layer_info $area
}
}
}
}
}
proc cut_blocked_areas {tag} {
variable stripe_locs
variable grid_data
if {![dict exists $grid_data straps]} {return}
foreach layer_name [dict keys [dict get $grid_data straps]] {
set width [get_grid_wire_width $layer_name]
set blockages [get_blockages]
if {[dict exists $blockages $layer_name]} {
set stripe_locs($layer_name,$tag) [::odb::subtractSet $stripe_locs($layer_name,$tag) [dict get $blockages $layer_name]]
# Trim any shapes that are less than the width of the wire
set size_by [expr $width / 2 - 1]
set trimmed_set [::odb::shrinkSet $stripe_locs($layer_name,$tag) $size_by]
set stripe_locs($layer_name,$tag) [::odb::bloatSet $trimmed_set $size_by]
}
}
}
proc generate_grid_vias {tag net_name} {
variable vias
variable grid_data
variable design_data
if {$net_name != [get_voltage_domain_power [dict get $design_data core_domain]] &&
$net_name != [get_voltage_domain_ground [dict get $design_data core_domain]]} {
set tag "$tag\_$net_name"
}
#Via stacks
# debug "grid_data $grid_data"
if {[dict exists $grid_data connect]} {
# debug "Adding vias for $net_name ([llength [dict get $grid_data connect]] connections)..."
foreach connection [dict get $grid_data connect] {
set l1 [lindex $connection 0]
set l2 [lindex $connection 1]
# debug " $l1 to $l2"
set connections [generate_via_stacks $l1 $l2 $tag $connection]
lappend vias [list net_name $net_name connections $connections]
}
}
# debug "End"
}
proc get_core_ring_centre {type side layer_info} {
variable grid_data
set spacing [dict get $layer_info spacing]
set width [dict get $layer_info width]
if {[dict exists $layer_info pad_offset]} {
set area [find_pad_offset_area]
lassign $area xMin yMin xMax yMax
set offset [expr [dict get $layer_info pad_offset] + $width / 2]
# debug "area $area"
# debug "pad_offset $offset"
# debug "spacing $spacing"
# debug "width $width"
switch $type {
"GROUND" {
switch $side {
"t" {return [expr $yMax - $offset]}
"b" {return [expr $yMin + $offset]}
"l" {return [expr $xMin + $offset]}
"r" {return [expr $xMax - $offset]}
}
}
"POWER" {
switch $side {
"t" {return [expr $yMax - $offset - $spacing - $width]}
"b" {return [expr $yMin + $offset + $spacing + $width]}
"l" {return [expr $xMin + $offset + $spacing + $width]}
"r" {return [expr $xMax - $offset - $spacing - $width]}
}
}
}
} elseif {[dict exists $layer_info core_offset]} {
set area [find_core_area]
set xMin [lindex $area 0]
set yMin [lindex $area 1]
set xMax [lindex $area 2]
set yMax [lindex $area 3]
set offset [dict get $layer_info core_offset]
# debug "area $area"
# debug "core_offset $offset"
# debug "spacing $spacing"
# debug "width $width"
switch $type {
"POWER" {
switch $side {
"t" {return [expr $yMax + $offset]}
"b" {return [expr $yMin - $offset]}
"l" {return [expr $xMin - $offset]}
"r" {return [expr $xMax + $offset]}
}
}
"GROUND" {
switch $side {
"t" {return [expr $yMax + $offset + $spacing + $width]}
"b" {return [expr $yMin - $offset - $spacing - $width]}
"l" {return [expr $xMin - $offset - $spacing - $width]}
"r" {return [expr $xMax + $offset + $spacing + $width]}
}
}
}
}
}
proc real_value {value} {
variable def_units
return [expr $value * 1.0 / $def_units]
}
proc find_pad_offset_area {} {
variable block
variable grid_data
variable design_data
if {!([dict exists $grid_data pwr_pads] && [dict exists $grid_data gnd_pads])} {
utl::error "PDN" 48 "Need to define pwr_pads and gnd_pads in config file to use pad_offset option."
}
if {![dict exists $design_data config pad_offset_area]} {
set pad_names {}
dict for {pin_name pads} [dict get $grid_data pwr_pads] {
set pad_names [concat $pad_names $pads]
}
dict for {pin_name pads} [dict get $grid_data gnd_pads] {
set pad_names [concat $pad_names $pads]
}
set pad_names [lsort -unique $pad_names]
set die_area [dict get $design_data config die_area]
set xMin [lindex $die_area 0]
set yMin [lindex $die_area 1]
set xMax [lindex $die_area 2]
set yMax [lindex $die_area 3]
# debug "pad_names: $pad_names"
set found_b 0
set found_r 0
set found_t 0
set found_l 0
foreach inst [$block getInsts] {
if {[lsearch $pad_names [[$inst getMaster] getName]] > -1} {
# debug "inst_master: [[$inst getMaster] getName]"
set quadrant [get_design_quadrant {*}[$inst getOrigin]]
switch $quadrant {
"b" {
# debug "inst: [$inst getName], side: $quadrant, yMax: [real_value [[$inst getBBox] yMax]]"
set found_b 1
if {$yMin < [set y [[$inst getBBox] yMax]]} {
set yMin $y
}
}
"r" {
# debug "inst: [$inst getName], side: $quadrant, xMin: [real_value [[$inst getBBox] xMin]]"
set found_r 1
if {$xMax > [set x [[$inst getBBox] xMin]]} {
set xMax $x
}
}
"t" {
# debug "inst: [$inst getName], side: $quadrant, yMin: [real_value [[$inst getBBox] yMin]]"
set found_t 1
if {$yMax > [set y [[$inst getBBox] yMin]]} {
set yMax $y
}
}
"l" {
# debug "inst: [$inst getName], side: $quadrant, xMax: [real_value [[$inst getBBox] xMax]]"
set found_l 1
if {$xMin < [set x [[$inst getBBox] xMax]]} {
set xMin $x
}
}
}
}
}
if {$found_b == 0} {
utl::warn "PDN" 64 "No power/ground pads found on bottom edge."
}
if {$found_r == 0} {
utl::warn "PDN" 65 "No power/ground pads found on right edge."
}
if {$found_t == 0} {
utl::warn "PDN" 66 "No power/ground pads found on top edge."
}
if {$found_l == 0} {
utl::warn "PDN" 67 "No power/ground pads found on left edge."
}
if {$found_b == 0 || $found_r == 0 || $found_t == 0 || $found_l == 0} {
utl::error "PDN" 68 "Cannot place core rings without pwr/gnd pads on each side."
}
# debug "pad_area: ([real_value $xMin] [real_value $yMin]) ([real_value $xMax] [real_value $yMax])"
dict set design_data config pad_offset_area [list $xMin $yMin $xMax $yMax]
}
return [dict get $design_data config pad_offset_area]
}
proc generate_core_rings {core_ring_data} {
variable grid_data
dict for {layer layer_info} $core_ring_data {
if {[dict exists $layer_info pad_offset]} {
set area [find_pad_offset_area]
set offset [expr [dict get $layer_info pad_offset] + [dict get $layer_info width] / 2]
set xMin [lindex $area 0]
set yMin [lindex $area 1]
set xMax [lindex $area 2]
set yMax [lindex $area 3]
set spacing [dict get $layer_info spacing]
set width [dict get $layer_info width]
set outer_lx [expr $xMin + $offset]
set outer_ly [expr $yMin + $offset]
set outer_ux [expr $xMax - $offset]
set outer_uy [expr $yMax - $offset]
set inner_lx [expr $xMin + $offset + $spacing + $width]
set inner_ly [expr $yMin + $offset + $spacing + $width]
set inner_ux [expr $xMax - $offset - $spacing - $width]
set inner_uy [expr $yMax - $offset - $spacing - $width]
} elseif {[dict exists $layer_info core_offset]} {
set area [list {*}[[ord::get_db_core] ll] {*}[[ord::get_db_core] ur]]
set offset [dict get $layer_info core_offset]
set xMin [lindex $area 0]
set yMin [lindex $area 1]
set xMax [lindex $area 2]
set yMax [lindex $area 3]
set spacing [dict get $layer_info spacing]
set width [dict get $layer_info width]
set inner_lx [expr $xMin - $offset]
set inner_ly [expr $yMin - $offset]
set inner_ux [expr $xMax + $offset]
set inner_uy [expr $yMax + $offset]
set outer_lx [expr $xMin - $offset - $spacing - $width]
set outer_ly [expr $yMin - $offset - $spacing - $width]
set outer_ux [expr $xMax + $offset + $spacing + $width]
set outer_uy [expr $yMax + $offset + $spacing + $width]
}
if {[get_dir $layer] == "hor"} {
set lower_power \
[odb::newSetFromRect \
[expr $inner_lx - $width / 2] \
[expr $inner_ly - $width / 2] \
[expr $inner_ux + $width / 2] \
[expr $inner_ly + $width / 2] \
]
set upper_power \
[odb::newSetFromRect \
[expr $inner_lx - $width / 2] \
[expr $inner_uy - $width / 2] \
[expr $inner_ux + $width / 2] \
[expr $inner_uy + $width / 2] \
]
set lower_ground \
[odb::newSetFromRect \
[expr $outer_lx - $width / 2] \
[expr $outer_ly - $width / 2] \
[expr $outer_ux + $width / 2] \
[expr $outer_ly + $width / 2] \
]
set upper_ground \
[odb::newSetFromRect \
[expr $outer_lx - $width / 2] \
[expr $outer_uy - $width / 2] \
[expr $outer_ux + $width / 2] \
[expr $outer_uy + $width / 2] \
]
add_stripe $layer "POWER" $upper_power
add_stripe $layer "POWER" $lower_power
add_stripe $layer "GROUND" $upper_ground
add_stripe $layer "GROUND" $lower_ground
set core_rings [odb::orSets [list \
[odb::newSetFromRect [expr $outer_lx - $width / 2] [expr $outer_ly - $width / 2] [expr $outer_ux + $width / 2] [expr $inner_ly + $width / 2]] \
[odb::newSetFromRect [expr $outer_lx - $width / 2] [expr $inner_uy - $width / 2] [expr $outer_ux + $width / 2] [expr $outer_uy + $width / 2]] \
]]
set core_ring_area [odb::bloatSet $core_rings $spacing]
dict set grid_data core_ring_area $layer $core_ring_area
} else {
set lhs_power \
[odb::newSetFromRect \
[expr $inner_lx - $width / 2] \
[expr $inner_ly - $width / 2] \
[expr $inner_lx + $width / 2] \
[expr $inner_uy + $width / 2] \
]
set rhs_power \
[odb::newSetFromRect \
[expr $inner_ux - $width / 2] \
[expr $inner_ly - $width / 2] \
[expr $inner_ux + $width / 2] \
[expr $inner_uy + $width / 2] \
]
set lhs_ground \
[odb::newSetFromRect \
[expr $outer_lx - $width / 2] \
[expr $outer_ly - $width / 2] \
[expr $outer_lx + $width / 2] \
[expr $outer_uy + $width / 2] \
]
set rhs_ground \
[odb::newSetFromRect \
[expr $outer_ux - $width / 2] \
[expr $outer_ly - $width / 2] \
[expr $outer_ux + $width / 2] \
[expr $outer_uy + $width / 2] \
]
add_stripe $layer "POWER" $lhs_power
add_stripe $layer "POWER" $rhs_power
add_stripe $layer "GROUND" $lhs_ground
add_stripe $layer "GROUND" $rhs_ground
set core_rings [odb::orSets [list \
[odb::newSetFromRect [expr $outer_lx - $width / 2] [expr $outer_ly - $width / 2] [expr $inner_lx + $width / 2] [expr $outer_uy + $width / 2]] \
[odb::newSetFromRect [expr $inner_ux - $width / 2] [expr $outer_ly - $width / 2] [expr $outer_ux + $width / 2] [expr $outer_uy + $width / 2]] \
]]
set core_ring_area [odb::bloatSet $core_rings $spacing]
dict set grid_data core_ring_area $layer $core_ring_area
}
}
set ring_areas {}
foreach layer [dict keys [dict get $grid_data core_ring_area]] {
lappend ring_areas [dict get $grid_data core_ring_area $layer]
}
dict set grid_data core_ring_area combined [odb::orSets $ring_areas]
}
proc get_macro_boundaries {} {
variable instances
set boundaries {}
foreach instance [dict keys $instances] {
lappend boundaries [dict get $instances $instance macro_boundary]
}
return $boundaries
}
proc get_stdcell_specification {} {
variable design_data
if {[dict exists $design_data grid stdcell]} {
set grid_name [lindex [dict keys [dict get $design_data grid stdcell]] 0]
return [dict get $design_data grid stdcell $grid_name]
} else {
if {![dict exists $design_data grid stdcell]} {
utl::error "PDN" 17 "No stdcell grid specification found - no rails can be inserted."
}
}
return {}
}
proc get_rail_width {} {
variable default_grid_data
set max_width 0
foreach layer [get_rails_layers] {
set max_width [expr max($max_width,[get_grid_wire_width $layer])]
}
if {![dict exists $default_grid_data units]} {
set max_width [ord::microns_to_dbu $max_width]
}
return $max_width
}
proc get_macro_blocks {} {
variable macros
if {[llength $macros] > 0} {return $macros}
# debug "start"
foreach lib [[ord::get_db] getLibs] {
foreach cell [$lib getMasters] {
if {![$cell isBlock] && ![$cell isPad]} {continue}
set macro_name [$cell getName]
dict set macros $macro_name width [$cell getWidth]
dict set macros $macro_name height [$cell getHeight]
set blockage_layers {}
foreach obs [$cell getObstructions] {
set layer_name [[$obs getTechLayer] getName]
dict set blockage_layers $layer_name 1
}
dict set macros $macro_name blockage_layers [dict keys $blockage_layers]
set pin_layers {}
set power_pins {}
set ground_pins {}
set first_shape 1
foreach term [$cell getMTerms] {
set sig_type [$term getSigType]
if {$sig_type == "POWER"} {
lappend power_pins [$term getName]
} elseif {$sig_type == "GROUND"} {
lappend ground_pins [$term getName]
} else {
continue
}
foreach pin [$term getMPins] {
foreach shape [$pin getGeometry] {
lappend pin_layers [[$shape getTechLayer] getName]
if {$first_shape == 1} {
set xMin [$shape xMin]
set xMax [$shape xMax]
set yMin [$shape yMin]
set yMax [$shape yMax]
set first_shape 0
} else {
set xMin [expr min($xMin,[$shape xMin])]
set xMax [expr max($xMax,[$shape xMax])]
set yMin [expr min($yMin,[$shape yMin])]
set yMax [expr max($yMax,[$shape yMax])]
}
}
}
}
dict set macros $macro_name pin_layers [lsort -unique $pin_layers]
dict set macros $macro_name power_pins [lsort -unique $power_pins]
dict set macros $macro_name ground_pins [lsort -unique $ground_pins]
if {$first_shape == 0} {
dict set macros $macro_name pins_area [list $xMin $yMin $xMax $yMax]
} else {
dict set macros $macro_name pins_area [list 0 0 0 0]
}
}
}
return $macros
}
proc filtered_insts_within {instances boundary} {
set filtered_instances {}
dict for {instance_name instance} $instances {
# If there are no shapes left after 'and'ing the boundard with the cell, then
# the cell lies outside the area where we are adding a power grid.
set llx [dict get $instance xmin]
set lly [dict get $instance ymin]
set urx [dict get $instance xmax]
set ury [dict get $instance ymax]
set box [odb::newSetFromRect $llx $lly $urx $ury]
if {[llength [odb::getPolygons [odb::andSet $boundary $box]]] != 0} {
dict set filtered_instances $instance_name $instance
}
}
return $filtered_instances
}
proc import_macro_boundaries {} {
variable libs
variable instances
set macros [get_macro_blocks]
set instances [find_instances_of [dict keys $macros]]
# debug "end"
}
proc get_instances {} {
variable instances
if {[llength $instances] > 0} {return $instances}
set block [ord::get_db_block]
foreach inst [$block getInsts] {
if {![[$inst getMaster] isBlock] && ![[$inst getMaster] isPad]} {continue}
set instance {}
dict set instance name [$inst getName]
dict set instance inst $inst
dict set instance macro [[$inst getMaster] getName]
dict set instance x [lindex [$inst getOrigin] 0]
dict set instance y [lindex [$inst getOrigin] 1]
dict set instance xmin [[$inst getBBox] xMin]
dict set instance ymin [[$inst getBBox] yMin]
dict set instance xmax [[$inst getBBox] xMax]
dict set instance ymax [[$inst getBBox] yMax]
dict set instance orient [$inst getOrient]
set llx [dict get $instance xmin]
set lly [dict get $instance ymin]
set urx [dict get $instance xmax]
set ury [dict get $instance ymax]
dict set instance macro_boundary [list $llx $lly $urx $ury]
dict set instances [$inst getName] $instance
set_instance_halo [$inst getName] [get_default_halo]
}
return $instances
}
proc get_master_pg_pins_area {macro_name} {
variable macros
return [dict get $macros $macro_name pins_area]
}
proc get_instance_pg_pins_area {inst_name} {
variable instances
set instance [dict get $instances $inst_name]
set inst [dict get $instance inst]
set master_area [transform_box {*}[get_master_pg_pins_area [[$inst getMaster] getName]] [$inst getOrigin] [$inst getOrient]]
}
proc set_instance_halo {inst_name halo} {
variable instances
set instance [dict get $instances $inst_name]
set inst [dict get $instance inst]
if {[$inst getHalo] != "NULL"} {
set halo [list \
[[$inst getHalo] xMin] \
[[$inst getHalo] yMin] \
[[$inst getHalo] xMax] \
[[$inst getHalo] yMax] \
]
}
dict set instances $inst_name halo $halo
# debug "Inst: [$inst getName], halo: [dict get $instances $inst_name halo]"
set llx [expr round([dict get $instance xmin] - [lindex $halo 0])]
set lly [expr round([dict get $instance ymin] - ([lindex $halo 1] - [get_rail_width] / 2))]
set urx [expr round([dict get $instance xmax] + [lindex $halo 2])]
set ury [expr round([dict get $instance ymax] + ([lindex $halo 3] - [get_rail_width] / 2))]
dict set instances $inst_name halo_boundary [list $llx $lly $urx $ury]
}
proc find_instances_of {macro_names} {
variable design_data
variable macros
set selected_instances {}
dict for {inst_name instance} [get_instances] {
set macro_name [dict get $instance macro]
if {[lsearch -exact $macro_names $macro_name] == -1} {continue}
dict set selected_instances $inst_name $instance
}
return $selected_instances
}
proc export_opendb_vias {} {
variable physical_viarules
variable block
variable tech
# debug "[llength $physical_viarules]"
dict for {name rules} $physical_viarules {
foreach rule $rules {
# Dont create illegal vias
if {[dict exists $rule illegal]} {continue}
if {[dict exists $rule fixed]} {continue}
# debug "$rule"
set via [$block findVia [dict get $rule name]]
if {$via == "NULL"} {
set via [odb::dbVia_create $block [dict get $rule name]]
# debug "Via $via"
$via setViaGenerateRule [$tech findViaGenerateRule [dict get $rule rule]]
set params [$via getViaParams]
$params setBottomLayer [$tech findLayer [lindex [dict get $rule layers] 0]]
$params setCutLayer [$tech findLayer [lindex [dict get $rule layers] 1]]
$params setTopLayer [$tech findLayer [lindex [dict get $rule layers] 2]]
$params setXCutSize [lindex [dict get $rule cutsize] 0]
$params setYCutSize [lindex [dict get $rule cutsize] 1]
$params setXCutSpacing [lindex [dict get $rule cutspacing] 0]
$params setYCutSpacing [lindex [dict get $rule cutspacing] 1]
$params setXBottomEnclosure [lindex [dict get $rule enclosure] 0]
$params setYBottomEnclosure [lindex [dict get $rule enclosure] 1]
$params setXTopEnclosure [lindex [dict get $rule enclosure] 2]
$params setYTopEnclosure [lindex [dict get $rule enclosure] 3]
$params setNumCutRows [lindex [dict get $rule rowcol] 0]
$params setNumCutCols [lindex [dict get $rule rowcol] 1]
$via setViaParams $params
}
}
}
# debug "end"
}
proc get_global_connect_list_default {voltage_domain is_region} {
variable block
variable voltage_domains
foreach net_type "primary_power primary_ground" {
set net_name [dict get $voltage_domains $voltage_domain $net_type]
foreach sub_net $net_name {
set net [$block findNet $sub_net]
foreach term [get_valid_mterms $sub_net] {
if {$is_region} {
pdn::add_global_connect $block $voltage_domain ".*" $term $net
} else {
pdn::add_global_connect ".*" $term $net
}
}
}
}
}
proc get_global_connect_list {net_name} {
variable design_data
variable global_connections
variable voltage_domains
set connect_patterns {}
if {[dict exist $global_connections $net_name]} {
foreach pattern [dict get $global_connections $net_name] {
lappend connect_patterns $pattern
}
}
return $connect_patterns
}
proc export_opendb_global_connection {} {
variable block
variable design_data
variable global_connections
variable voltage_domains
## Do global connect statements first
get_global_connect_list_default [dict get $design_data core_domain] false
foreach net_type "power_nets ground_nets" {
foreach net_name [dict get $design_data $net_type] {
set net [$block findNet $net_name]
foreach pattern [get_global_connect_list $net_name] {
pdn::add_global_connect [dict get $pattern inst_name] [dict get $pattern pin_name] $net
}
}
}
## Do regions second
set core_domain_name [dict get $design_data core_domain]
foreach voltage_domain [dict keys $voltage_domains] {
if {$voltage_domain != $core_domain_name} {
get_global_connect_list_default $voltage_domain true
foreach {net_type netname} [dict get $voltage_domains $voltage_domain] {
set net [$block findNet $net_name]
# loop over all patterns
foreach pattern [get_global_connect_list $net_name] {
pdn::add_global_connect $block $voltage_domain [dict get $pattern inst_name] [dict get $pattern pin_name] $net
}
}
}
}
pdn::global_connect $block
}
proc export_opendb_specialnet {net_name signal_type} {
variable block
variable instances
variable metal_layers
variable tech
variable stripe_locs
variable global_connections
variable design_data
set net [$block findNet $net_name]
if {$net == "NULL"} {
set net [odb::dbNet_create $block $net_name]
}
$net setSpecial
$net setSigType $signal_type
# debug "net $net_name. signaltype, $signal_type, global_connections: $global_connections"
if {[check_snet_is_unique $net]} {
$net setWildConnected
}
set swire [odb::dbSWire_create $net "ROUTED"]
if {$net_name != [get_voltage_domain_power [dict get $design_data core_domain]] &&
$net_name != [get_voltage_domain_ground [dict get $design_data core_domain]]} {
set signal_type "$signal_type\_$net_name"
}
# debug "layers - $metal_layers"
foreach lay $metal_layers {
if {[array names stripe_locs "$lay,$signal_type"] == ""} {continue}
set layer [find_layer $lay]
foreach rect [::odb::getRectangles $stripe_locs($lay,$signal_type)] {
set xMin [$rect xMin]
set xMax [$rect xMax]
set yMin [$rect yMin]
set yMax [$rect yMax]
set width [expr $xMax - $xMin]
set height [expr $yMax - $yMin]
set wire_type "STRIPE"
if {[is_rails_layer $lay]} {set wire_type "FOLLOWPIN"}
# debug "$xMin $yMin $xMax $yMax $wire_type"
odb::dbSBox_create $swire $layer $xMin $yMin $xMax $yMax $wire_type
}
}
variable vias
# debug "vias - [llength $vias]"
foreach via $vias {
if {[dict get $via net_name] == $net_name} {
# For each layer between l1 and l2, add vias at the intersection
foreach via_inst [dict get $via connections] {
# debug "$via_inst"
set via_name [dict get $via_inst name]
set x [dict get $via_inst x]
set y [dict get $via_inst y]
# debug "$via_name $x $y [$block findVia $via_name]"
if {[set defvia [$block findVia $via_name]] != "NULL"} {
odb::dbSBox_create $swire $defvia $x $y "STRIPE"
} elseif {[set techvia [$tech findVia $via_name]] != "NULL"} {
odb::dbSBox_create $swire $techvia $x $y "STRIPE"
} else {
utl::error "PDN" 69 "Cannot find via $via_name."
}
# debug "via created"
}
}
}
# debug "end"
}
proc export_opendb_specialnets {} {
variable block
variable design_data
foreach net_name [dict get $design_data power_nets] {
export_opendb_specialnet $net_name "POWER"
}
foreach net_name [dict get $design_data ground_nets] {
export_opendb_specialnet $net_name "GROUND"
}
export_opendb_global_connection
}
proc export_opendb_power_pin {net_name signal_type} {
variable metal_layers
variable block
variable stripe_locs
variable tech
variable voltage_domains
variable design_data
if {![dict exists $design_data grid stdcell]} {return}
set pins_layers {}
dict for {grid_name grid} [dict get $design_data grid stdcell] {
if {[dict exists $grid pins]} {
lappend pins_layers {*}[dict get $grid pins]
}
}
set pins_layers [lsort -unique $pins_layers]
if {[llength $pins_layers] == 0} {return}
set net [$block findNet $net_name]
if {$net == "NULL"} {
utl::error PDN 70 "Cannot find net $net_name in the design."
}
set bterms [$net getBTerms]
if {[llength $bterms] < 1} {
set bterm [odb::dbBTerm_create $net "${net_name}"]
if {$bterm == "NULL"} {
utl::error PDN 71 "Cannot create terminal for net $net_name."
}
}
# debug $bterm
foreach bterm [$net getBTerms] {
$bterm setSigType $signal_type
}
set bterm [lindex [$net getBTerms] 0]
set bpin [odb::dbBPin_create $bterm]
$bpin setPlacementStatus "FIRM"
dict for {domain domain_info} $voltage_domains {
if {$domain != [dict get $design_data core_domain] &&
$net_name == [dict get $domain_info primary_power]} {
set r_pin "r_$net_name"
set r_net [odb::dbNet_create $block $r_pin]
set r_bterm [odb::dbBTerm_create $r_net "${r_pin}"]
set r_bpin [odb::dbBPin_create $r_bterm]
$r_bpin setPlacementStatus "FIRM"
}
}
if {$net_name != [get_voltage_domain_power [dict get $design_data core_domain]] &&
$net_name != [get_voltage_domain_ground [dict get $design_data core_domain]]} {
set signal_type "$signal_type\_$net_name"
}
foreach lay [lreverse $metal_layers] {
if {[array names stripe_locs "$lay,$signal_type"] == "" ||
[lsearch -exact $pins_layers $lay] == -1} {continue}
foreach shape [::odb::getPolygons $stripe_locs($lay,$signal_type)] {
set points [::odb::getPoints $shape]
if {[llength $points] != 4} {
# We already issued a message for this - no need to repeat
continue
}
set xMin [expr min([[lindex $points 0] getX], [[lindex $points 1] getX], [[lindex $points 2] getX], [[lindex $points 3] getX])]
set xMax [expr max([[lindex $points 0] getX], [[lindex $points 1] getX], [[lindex $points 2] getX], [[lindex $points 3] getX])]
set yMin [expr min([[lindex $points 0] getY], [[lindex $points 1] getY], [[lindex $points 2] getY], [[lindex $points 3] getY])]
set yMax [expr max([[lindex $points 0] getY], [[lindex $points 1] getY], [[lindex $points 2] getY], [[lindex $points 3] getY])]
set layer [$tech findLayer $lay]
odb::dbBox_create $bpin $layer $xMin $yMin $xMax $yMax
if {[info exists r_bpin]} {
odb::dbBox_create $r_bpin $layer $xMin $yMin $xMax $yMax
}
}
}
}
proc export_opendb_power_pins {} {
variable block
variable design_data
foreach net_name [dict get $design_data power_nets] {
export_opendb_power_pin $net_name "POWER"
}
foreach net_name [dict get $design_data ground_nets] {
export_opendb_power_pin $net_name "GROUND"
}
}
## procedure for file existence check, returns 0 if file does not exist or file exists, but empty
proc file_exists_non_empty {filename} {
return [expr [file exists $filename] && [file size $filename] > 0]
}
proc get {args} {
variable design_data
return [dict get $design_data {*}$args]
}
proc get_macro_power_pins {inst_name} {
set specification [select_instance_specification $inst_name]
if {[dict exists $specification power_pins]} {
return [dict get $specification power_pins]
}
return "VDDPE VDDCE"
}
proc get_macro_ground_pins {inst_name} {
set specification [select_instance_specification $inst_name]
if {[dict exists $specification ground_pins]} {
return [dict get $specification ground_pins]
}
return "VSSE"
}
proc transform_box {xmin ymin xmax ymax origin orientation} {
switch -exact $orientation {
R0 {set new_box [list $xmin $ymin $xmax $ymax]}
R90 {set new_box [list [expr -1 * $ymax] $xmin [expr -1 * $ymin] $xmax]}
R180 {set new_box [list [expr -1 * $xmax] [expr -1 * $ymax] [expr -1 * $xmin] [expr -1 * $ymin]]}
R270 {set new_box [list $ymin [expr -1 * $xmax] $ymax [expr -1 * $xmin]]}
MX {set new_box [list $xmin [expr -1 * $ymax] $xmax [expr -1 * $ymin]]}
MY {set new_box [list [expr -1 * $xmax] $ymin [expr -1 * $xmin] $ymax]}
MXR90 {set new_box [list $ymin $xmin $ymax $xmax]}
MYR90 {set new_box [list [expr -1 * $ymax] [expr -1 * $xmax] [expr -1 * $ymin] [expr -1 * $xmin]]}
default {utl::error "PDN" 27 "Illegal orientation $orientation specified."}
}
return [list \
[expr [lindex $new_box 0] + [lindex $origin 0]] \
[expr [lindex $new_box 1] + [lindex $origin 1]] \
[expr [lindex $new_box 2] + [lindex $origin 0]] \
[expr [lindex $new_box 3] + [lindex $origin 1]] \
]
}
proc set_template_size {width height} {
variable template
variable def_units
dict set template width [expr round($width * $def_units)]
dict set template height [expr round($height * $def_units)]
}
proc get_memory_instance_pg_pins {} {
variable block
variable metal_layers
# debug "start"
set boundary [odb::newSetFromRect {*}[get_core_area]]
foreach inst [$block getInsts] {
set inst_name [$inst getName]
set master [$inst getMaster]
if {![$master isBlock]} {continue}
# If there are no shapes left after 'and'ing the boundard with the cell, then
# the cell lies outside the area where we are adding a power grid.
set bbox [$inst getBBox]
set box [odb::newSetFromRect [$bbox xMin] [$bbox yMin] [$bbox xMax] [$bbox yMax]]
if {[llength [odb::getPolygons [odb::andSet $boundary $box]]] == 0} {
# debug "Instance [$inst getName] does not lie in the cell area"
continue
}
# debug "cell name - [$master getName]"
foreach term_name [concat [get_macro_power_pins $inst_name] [get_macro_ground_pins $inst_name]] {
set master [$inst getMaster]
set mterm [$master findMTerm $term_name]
if {$mterm == "NULL"} {
utl::warn "PDN" 37 "Cannot find pin $term_name on instance [$inst getName] ([[$inst getMaster] getName])."
continue
}
set type [$mterm getSigType]
foreach mPin [$mterm getMPins] {
foreach geom [$mPin getGeometry] {
set layer [[$geom getTechLayer] getName]
if {[lsearch -exact $metal_layers $layer] == -1} {continue}
set box [transform_box [$geom xMin] [$geom yMin] [$geom xMax] [$geom yMax] [$inst getOrigin] [$inst getOrient]]
set width [expr abs([lindex $box 2] - [lindex $box 0])]
set height [expr abs([lindex $box 3] - [lindex $box 1])]
if {$width > $height} {
set layer_name ${layer}_PIN_hor
} else {
set layer_name ${layer}_PIN_ver
}
# debug "Adding pin for [$inst getName]:[$mterm getName] to layer $layer_name ($box)"
add_stripe $layer_name $type [odb::newSetFromRect {*}$box]
}
}
}
}
# debug "Total walltime till macro pin geometry creation = [expr {[expr {[clock clicks -milliseconds] - $::start_time}]/1000.0}] seconds"
# debug "end"
}
proc set_core_area {xmin ymin xmax ymax} {
variable design_data
dict set design_data config core_area [list $xmin $ymin $xmax $ymax]
}
proc get_core_area {} {
variable design_data
return [get_extent [get_stdcell_area]]
}
proc write_pdn_strategy {} {
variable design_data
if {[dict exists $design_data grid]} {
set_pdn_string_property_value "strategy" [dict get $design_data grid]
}
}
proc init_tech {} {
variable db
variable block
variable tech
variable libs
variable def_units
set db [ord::get_db]
set tech [ord::get_db_tech]
set libs [$db getLibs]
set block [ord::get_db_block]
set def_units [$block getDefUnits]
init_metal_layers
init_via_tech
}
proc add_power_net {net_name} {
variable power_nets
if {[lsearch -exact $power_nets $net_name] == -1} {
lappend power_nets $net_name
}
}
proc add_ground_net {net_name} {
variable ground_nets
if {[lsearch -exact $ground_nets $net_name] == -1} {
lappend ground_nets $net_name
}
}
proc get_default_halo {} {
if {[info vars ::halo] != ""} {
if {[llength $::halo] == 1} {
set default_halo "$::halo $::halo $::halo $::halo"
} elseif {[llength $::halo] == 2} {
set default_halo "$::halo $::halo"
} elseif {[llength $::halo] == 4} {
set default_halo $::halo
} else {
utl::error "PDN" 29 "Illegal number of elements defined for ::halo \"$::halo\" (1, 2 or 4 allowed)."
}
} else {
set default_halo "0 0 0 0"
}
return [lmap x $default_halo {ord::microns_to_dbu $x}]
}
proc get_row_height {} {
set first_row [lindex [[ord::get_db_block] getRows] 0]
set row_site [$first_row getSite]
return [$row_site getHeight]
}
proc init {args} {
variable db
variable block
variable tech
variable libs
variable design_data
variable def_output
variable default_grid_data
variable design_name
variable stripe_locs
variable site
variable row_height
variable metal_layers
variable def_units
variable stripes_start_with
variable physical_viarules
variable stdcell_area
variable voltage_domains
variable global_connections
variable default_global_connections
variable power_nets
variable ground_nets
# debug "start"
init_tech
set design_name [$block getName]
set physical_viarules {}
set stdcell_area ""
set die_area [$block getDieArea]
utl::info "PDN" 8 "Design name is $design_name."
set def_output "${design_name}_pdn.def"
# debug "examine vars"
if {$power_nets == {}} {
if {[info vars ::power_nets] == ""} {
set power_nets "VDD"
} else {
set power_nets $::power_nets
}
}
if {$ground_nets == {}} {
if {[info vars ::ground_nets] == ""} {
set ground_nets "VSS"
} else {
set ground_nets $::ground_nets
}
}
if {[info vars ::core_domain] == ""} {
set core_domain "CORE"
if {![dict exists $voltage_domains $core_domain primary_power]} {
dict set voltage_domains $core_domain primary_power [lindex $power_nets 0]
}
if {![dict exists $voltage_domains $core_domain primary_ground]} {
dict set voltage_domains $core_domain primary_ground [lindex $ground_nets 0]
}
} else {
set core_domain $::core_domain
}
if {[info vars ::stripes_start_with] == ""} {
set stripes_start_with "GROUND"
} else {
set stripes_start_with $::stripes_start_with
}
dict set design_data power_nets $power_nets
dict set design_data ground_nets $ground_nets
dict set design_data core_domain $core_domain
# Sourcing user inputs file
#
set row_height [get_row_height]
##### Get information from BEOL LEF
utl::info "PDN" 9 "Reading technology data."
if {[info vars ::layers] != ""} {
foreach layer $::layers {
if {[dict exists $::layers $layer widthtable]} {
dict set ::layers $layer widthtable [lmap x [dict get $::layers $layer widthtable] {expr $x * $def_units}]
}
}
set_layer_info $::layers
}
dict set design_data config def_output $def_output
dict set design_data config design $design_name
dict set design_data config die_area [list [$die_area xMin] [$die_area yMin] [$die_area xMax] [$die_area yMax]]
array unset stripe_locs
########################################
# Remove existing power/ground nets
#######################################
foreach pg_net [concat [dict get $design_data power_nets] [dict get $design_data ground_nets]] {
set net [$block findNet $pg_net]
if {$net != "NULL"} {
foreach swire [$net getSWires] {
odb::dbSWire_destroy $swire
}
}
}
# debug "Set the core area"
# Set the core area
if {[info vars ::core_area_llx] != "" && [info vars ::core_area_lly] != "" && [info vars ::core_area_urx] != "" && [info vars ::core_area_ury] != ""} {
# The core area is larger than the stdcell area by half a rail, since the stdcell rails extend beyond the rails
set_core_area \
[expr round($::core_area_llx * $def_units)] \
[expr round($::core_area_lly * $def_units)] \
[expr round($::core_area_urx * $def_units)] \
[expr round($::core_area_ury * $def_units)]
} else {
set_core_area {*}[get_extent [get_stdcell_plus_area]]
}
##### Basic sanity checks to see if inputs are given correctly
foreach layer [get_rails_layers] {
if {[lsearch -exact $metal_layers $layer] < 0} {
utl::error "PDN" 30 "Layer specified for stdcell rails '$layer' not in list of layers."
}
}
# debug "end"
return $design_data
}
proc convert_layer_spec_to_def_units {data} {
foreach key {width pitch spacing offset pad_offset core_offset} {
if {[dict exists $data $key]} {
dict set data $key [ord::microns_to_dbu [dict get $data $key]]
}
}
return $data
}
proc specify_grid {type specification} {
if {![dict exists $specification type]} {
dict set specification type $type
}
verify_grid $specification
}
proc get_quadrant {rect x y} {
set dw [expr [lindex $rect 2] - [lindex $rect 0]]
set dh [expr [lindex $rect 3] - [lindex $rect 1]]
set test_x [expr $x - [lindex $rect 0]]
set test_y [expr $y - [lindex $rect 1]]
# debug "$dw * $test_y ([expr $dw * $test_y]) > expr $dh * $test_x ([expr $dh * $test_x])"
if {$dw * $test_y > $dh * $test_x} {
# Top or left
if {($dw * $test_y) + ($dh * $test_x) > ($dw * $dh)} {
# Top or right
return "t"
} else {
# Bottom or left
return "l"
}
} else {
# Bottom or right
if {($dw * $test_y) + ($dh * $test_x) > ($dw * $dh)} {
# Top or right
return "r"
} else {
# Bottom or left
return "b"
}
}
}
proc get_design_quadrant {x y} {
variable design_data
set die_area [dict get $design_data config die_area]
return [get_quadrant $die_area $x $y]
}
proc get_core_facing_pins {instance pin_name side layer} {
variable block
set geoms {}
set core_pins {}
set inst [$block findInst [dict get $instance name]]
if {[set iterm [$inst findITerm $pin_name]] == "NULL"} {
utl::warn "PDN" 55 "Cannot find pin $pin_name on inst [$inst getName]."
return {}
}
if {[set mterm [$iterm getMTerm]] == "NULL"} {
utl::warn "PDN" 56 "Cannot find master pin $pin_name for cell [[$inst getMaster] getName]."
return {}
}
set pins [$mterm getMPins]
# debug "start"
foreach pin $pins {
foreach geom [$pin getGeometry] {
if {[[$geom getTechLayer] getName] != $layer} {continue}
lappend geoms $geom
}
}
# debug "$pins"
foreach geom $geoms {
set ipin [transform_box [$geom xMin] [$geom yMin] [$geom xMax] [$geom yMax] [$inst getOrigin] [$inst getOrient]]
# debug "$ipin [[$inst getBBox] xMin] [[$inst getBBox] yMin] [[$inst getBBox] xMax] [[$inst getBBox] yMax] "
switch $side {
"t" {
if {[lindex $ipin 1] == [[$inst getBBox] yMin]} {
lappend core_pins [list \
centre [expr ([lindex $ipin 2] + [lindex $ipin 0]) / 2] \
width [expr [lindex $ipin 2] - [lindex $ipin 0]] \
]
}
}
"b" {
if {[lindex $ipin 3] == [[$inst getBBox] yMax]} {
lappend core_pins [list \
centre [expr ([lindex $ipin 2] + [lindex $ipin 0]) / 2] \
width [expr [lindex $ipin 2] - [lindex $ipin 0]] \
]
}
}
"l" {
if {[lindex $ipin 2] == [[$inst getBBox] xMax]} {
lappend core_pins [list \
centre [expr ([lindex $ipin 3] + [lindex $ipin 1]) / 2] \
width [expr [lindex $ipin 3] - [lindex $ipin 1]] \
]
}
}
"r" {
if {[lindex $ipin 0] == [[$inst getBBox] xMin]} {
lappend core_pins [list \
centre [expr ([lindex $ipin 3] + [lindex $ipin 1]) / 2] \
width [expr [lindex $ipin 3] - [lindex $ipin 1]] \
]
}
}
}
}
# debug "$core_pins"
return $core_pins
}
proc connect_pads_to_core_ring {type pin_name pads} {
variable grid_data
variable pad_cell_blockages
dict for {inst_name instance} [find_instances_of $pads] {
set side [get_design_quadrant [dict get $instance x] [dict get $instance y]]
switch $side {
"t" {
set required_direction "ver"
}
"b" {
set required_direction "ver"
}
"l" {
set required_direction "hor"
}
"r" {
set required_direction "hor"
}
}
foreach non_pref_layer [dict keys [dict get $grid_data core_ring]] {
if {[get_dir $non_pref_layer] != $required_direction} {
set non_pref_layer_info [dict get $grid_data core_ring $non_pref_layer]
break
}
}
# debug "find_layer"
foreach pref_layer [dict keys [dict get $grid_data core_ring]] {
if {[get_dir $pref_layer] == $required_direction} {
break
}
}
switch $side {
"t" {
set y_min [expr [get_core_ring_centre $type $side $non_pref_layer_info] - [dict get $grid_data core_ring $non_pref_layer width] / 2]
set y_min_blk [expr $y_min - [dict get $grid_data core_ring $non_pref_layer spacing]]
set y_max [dict get $instance ymin]
# debug "t: [dict get $instance xmin] $y_min_blk [dict get $instance xmax] [dict get $instance ymax]"
add_padcell_blockage $pref_layer [odb::newSetFromRect [dict get $instance xmin] $y_min_blk [dict get $instance xmax] [dict get $instance ymax]]
}
"b" {
# debug "[get_core_ring_centre $type $side $non_pref_layer_info] + [dict get $grid_data core_ring $non_pref_layer width] / 2"
set y_max [expr [get_core_ring_centre $type $side $non_pref_layer_info] + [dict get $grid_data core_ring $non_pref_layer width] / 2]
set y_max_blk [expr $y_max + [dict get $grid_data core_ring $non_pref_layer spacing]]
set y_min [dict get $instance ymax]
# debug "b: [dict get $instance xmin] [dict get $instance ymin] [dict get $instance xmax] $y_max"
add_padcell_blockage $pref_layer [odb::newSetFromRect [dict get $instance xmin] [dict get $instance ymin] [dict get $instance xmax] $y_max_blk]
# debug "end b"
}
"l" {
set x_max [expr [get_core_ring_centre $type $side $non_pref_layer_info] + [dict get $grid_data core_ring $non_pref_layer width] / 2]
set x_max_blk [expr $x_max + [dict get $grid_data core_ring $non_pref_layer spacing]]
set x_min [dict get $instance xmax]
# debug "l: [dict get $instance xmin] [dict get $instance ymin] $x_max [dict get $instance ymax]"
add_padcell_blockage $pref_layer [odb::newSetFromRect [dict get $instance xmin] [dict get $instance ymin] $x_max_blk [dict get $instance ymax]]
}
"r" {
set x_min [expr [get_core_ring_centre $type $side $non_pref_layer_info] - [dict get $grid_data core_ring $non_pref_layer width] / 2]
set x_min_blk [expr $x_min - [dict get $grid_data core_ring $non_pref_layer spacing]]
set x_max [dict get $instance xmin]
# debug "r: $x_min_blk [dict get $instance ymin] [dict get $instance xmax] [dict get $instance ymax]"
add_padcell_blockage $pref_layer [odb::newSetFromRect $x_min_blk [dict get $instance ymin] [dict get $instance xmax] [dict get $instance ymax]]
}
}
# debug "$pref_layer"
foreach pin_geometry [get_core_facing_pins $instance $pin_name $side $pref_layer] {
set centre [dict get $pin_geometry centre]
set width [dict get $pin_geometry width]
variable tech
if {[[set layer [$tech findLayer $pref_layer]] getMaxWidth] != "NULL" && $width > [$layer getMaxWidth]} {
set width [$layer getMaxWidth]
}
if {$required_direction == "hor"} {
# debug "added_strap $pref_layer $type $x_min [expr $centre - $width / 2] $x_max [expr $centre + $width / 2]"
add_stripe $pref_layer "PAD_$type" [odb::newSetFromRect $x_min [expr $centre - $width / 2] $x_max [expr $centre + $width / 2]]
} else {
# debug "added_strap $pref_layer $type [expr $centre - $width / 2] $y_min [expr $centre + $width / 2] $y_max"
add_stripe $pref_layer "PAD_$type" [odb::newSetFromRect [expr $centre - $width / 2] $y_min [expr $centre + $width / 2] $y_max]
}
}
}
# debug "end"
}
proc add_pad_straps {tag} {
variable stripe_locs
foreach pad_connection [array names stripe_locs "*,PAD_*"] {
if {![regexp "(.*),PAD_$tag" $pad_connection - layer]} {continue}
# debug "$pad_connection"
if {[array names stripe_locs "$layer,$tag"] != ""} {
# debug add_pad_straps "Before: $layer [llength [::odb::getPolygons $stripe_locs($layer,$tag)]]"
# debug add_pad_straps "Adding: [llength [::odb::getPolygons $stripe_locs($pad_connection)]]"
add_stripe $layer $tag $stripe_locs($pad_connection)
# debug add_pad_straps "After: $layer [llength [::odb::getPolygons $stripe_locs($layer,$tag)]]"
}
}
}
proc print_spacing_table {layer_name} {
set layer [find_layer $layer_name]
if {[$layer hasTwoWidthsSpacingRules]} {
set table_size [$layer getTwoWidthsSpacingTableNumWidths]
for {set i 0} {$i < $table_size} {incr i} {
set width [$layer getTwoWidthsSpacingTableWidth $i]
set report_width "WIDTH $width"
if {[$layer getTwoWidthsSpacingTableHasPRL $i]} {
set prl [$layer getTwoWidthsSpacingTablePRL $i]
set report_prl " PRL $prl"
} else {
set report_prl ""
}
set report_spacing " [$layer getTwoWidthsSpacingTableEntry 0 $i] "
}
utl::report "${report_width}${report_prl}${report_spacing}"
}
}
proc get_twowidths_table {table_type} {
variable metal_layers
set twowidths_table {}
foreach layer_name $metal_layers {
set spacing_table [get_spacingtables $layer_name]
set prls {}
if {[dict exists $spacing_table TWOWIDTHS $table_type]} {
set layer_spacing_table [dict get $spacing_table TWOWIDTHS $table_type]
set table_size [dict size $layer_spacing_table]
set table_widths [dict keys $layer_spacing_table]
for {set i 0} {$i < $table_size} {incr i} {
set width [lindex $table_widths $i]
set spacing [lindex [dict get $layer_spacing_table $width spacings] $i]
if {[dict get $layer_spacing_table $width prl] != 0} {
set prl [dict get $layer_spacing_table $width prl]
set update_prls {}
dict for {prl_entry prl_setting} $prls {
if {$prl <= [lindex $prl_entry 0]} {break}
dict set update_prls $prl_entry $prl_setting
dict set twowidths_table $layer_name $width $prl_entry $prl_setting
}
dict set update_prls $prl $spacing
dict set twowidths_table $layer_name $width $prl $spacing
set prls $update_prls
} else {
set prls {}
dict set prls 0 $spacing
dict set twowidths_table $layer_name $width 0 $spacing
}
}
}
}
return $twowidths_table
}
proc get_twowidths_tables {} {
variable twowidths_table
variable twowidths_table_wrongdirection
set twowidths_table [get_twowidths_table NONE]
set twowidths_table_wrongdirection [get_twowidths_table WRONGDIRECTION]
}
proc select_from_table {table width} {
foreach value [lreverse [lsort -integer [dict keys $table]]] {
if {$width > $value} {
return $value
}
}
return [lindex [dict keys $table] 0]
}
proc get_preferred_direction_spacing {layer_name width prl} {
variable twowidths_table
# debug "$layer_name $width $prl"
# debug "twowidths_table $twowidths_table"
if {$twowidths_table == {}} {
return [[find_layer $layer_name] getSpacing]
} else {
set width_key [select_from_table [dict get $twowidths_table $layer_name] $width]
set prl_key [select_from_table [dict get $twowidths_table $layer_name $width_key] $prl]
}
return [dict get $twowidths_table $layer_name $width_key $prl_key]
}
proc get_nonpreferred_direction_spacing {layer_name width prl} {
variable twowidths_table_wrongdirection
# debug "twowidths_table_wrong_direction $twowidths_table_wrongdirection"
if {[dict exists $twowidths_table_wrongdirection $layer_name]} {
set width_key [select_from_table [dict get $twowidths_table_wrongdirection $layer_name] $width]
set prl_key [select_from_table [dict get $twowidths_table_wrongdirection $layer_name $width_key] $prl]
} else {
return [get_preferred_direction_spacing $layer_name $width $prl]
}
return [dict get $twowidths_table_wrongdirection $layer_name $width_key $prl_key]
}
proc create_obstructions {layer_name polygons} {
set layer [find_layer $layer_name]
set min_spacing [get_preferred_direction_spacing $layer_name 0 0]
# debug "Num polygons [llength $polygons]"
foreach polygon $polygons {
set points [::odb::getPoints $polygon]
if {[llength $points] != 4} {
utl::warn "PDN" 6 "Unexpected number of points in stripe of $layer_name."
continue
}
set xMin [expr min([[lindex $points 0] getX], [[lindex $points 1] getX], [[lindex $points 2] getX], [[lindex $points 3] getX])]
set xMax [expr max([[lindex $points 0] getX], [[lindex $points 1] getX], [[lindex $points 2] getX], [[lindex $points 3] getX])]
set yMin [expr min([[lindex $points 0] getY], [[lindex $points 1] getY], [[lindex $points 2] getY], [[lindex $points 3] getY])]
set yMax [expr max([[lindex $points 0] getY], [[lindex $points 1] getY], [[lindex $points 2] getY], [[lindex $points 3] getY])]
if {[get_dir $layer_name] == "hor"} {
set required_spacing_pref [get_preferred_direction_spacing $layer_name [expr $yMax - $yMin] [expr $xMax - $xMin]]
set required_spacing_nonpref [get_nonpreferred_direction_spacing $layer_name [expr $xMax - $xMin] [expr $yMax - $yMin]]
set y_change [expr $required_spacing_pref - $min_spacing]
set x_change [expr $required_spacing_nonpref - $min_spacing]
} else {
set required_spacing_pref [get_preferred_direction_spacing $layer_name [expr $xMax - $xMin] [expr $yMax - $yMin]]
set required_spacing_nonpref [get_nonpreferred_direction_spacing $layer_name [expr $yMax - $yMin] [expr $xMax - $xMin]]
set x_change [expr $required_spacing_pref - $min_spacing]
set y_change [expr $required_spacing_nonpref - $min_spacing]
}
create_obstruction_object_blockage $layer $min_spacing [expr $xMin - $x_change] [expr $yMin - $y_change] [expr $xMax + $x_change] [expr $yMax + $y_change]
}
}
proc combine {lside rside} {
# debug "l [llength $lside] r [llength $rside]"
if {[llength $lside] > 1} {
set lside [combine [lrange $lside 0 [expr [llength $lside] / 2 - 1]] [lrange $lside [expr [llength $lside] / 2] end]]
}
if {[llength $rside] > 1} {
set rside [combine [lrange $rside 0 [expr [llength $rside] / 2 - 1]] [lrange $rside [expr [llength $rside] / 2] end]]
}
return [odb::orSet $lside $rside]
}
proc shapes_to_polygonSet {shapes} {
if {[llength $shapes] == 1} {
return $shapes
}
return [combine [lrange $shapes 0 [expr [llength $shapes] / 2 - 1]] [lrange $shapes [expr [llength $shapes] / 2] end]]
}
proc generate_obstructions {layer_name} {
variable stripe_locs
# debug "layer $layer_name"
get_twowidths_tables
set block_shapes {}
foreach tag {"POWER" "GROUND"} {
if {[array names stripe_locs $layer_name,$tag] == ""} {
# debug "No polygons on $layer_name,$tag"
continue
}
if {$block_shapes == {}} {
set block_shapes $stripe_locs($layer_name,$tag)
} else {
set block_shapes [odb::orSet $block_shapes $stripe_locs($layer_name,$tag)]
}
}
set via_shapes 0
variable vias
# debug "vias - [llength $vias]"
foreach via $vias {
# For each layer between l1 and l2, add vias at the intersection
foreach via_inst [dict get $via connections] {
# debug "$via_inst"
set via_name [dict get $via_inst name]
set x [dict get $via_inst x]
set y [dict get $via_inst y]
set lower_layer_name [lindex [dict get $via_inst layers] 0]
set upper_layer_name [lindex [dict get $via_inst layers] 2]
if {$lower_layer_name == $layer_name && [dict exists $via_inst lower_rect]} {
lappend block_shapes [odb::newSetFromRect {*}[transform_box {*}[dict get $via_inst lower_rect] [list $x $y] "R0"]]
incr via_shapes
} elseif {$upper_layer_name == $layer_name && [dict exists $via_inst upper_rect]} {
lappend block_shapes [odb::newSetFromRect {*}[transform_box {*}[dict get $via_inst upper_rect] [list $x $y] "R0"]]
incr via_shapes
}
}
}
# debug "Via shapes $layer_name $via_shapes"
if {$block_shapes != {}} {
# debug "create_obstructions [llength $block_shapes]"
create_obstructions $layer_name [odb::getPolygons [shapes_to_polygonSet $block_shapes]]
}
# debug "end"
}
proc create_obstruction_object_blockage {layer min_spacing xMin yMin xMax yMax} {
variable block
set layer_pitch [get_pitch $layer]
set layer_width [$layer getWidth]
# debug "Layer - [$layer getName], pitch $layer_pitch, width $layer_width"
set tracks [$block findTrackGrid $layer]
set offsetX [lindex [$tracks getGridX] 0]
set offsetY [lindex [$tracks getGridY] 0]
# debug "OBS: [$layer getName] $xMin $yMin $xMax $yMax (dx [expr $xMax - $xMin] dy [expr $yMax - $yMin])"
# debug "Offsets: x $offsetX y $offsetY"
set relative_xMin [expr $xMin - $offsetX]
set relative_xMax [expr $xMax - $offsetX]
set relative_yMin [expr $yMin - $offsetY]
set relative_yMax [expr $yMax - $offsetY]
# debug "relative to core area $relative_xMin $relative_yMin $relative_xMax $relative_yMax"
# debug "OBS: [$layer getName] $xMin $yMin $xMax $yMax"
# Determine which tracks are blocked
if {[get_dir [$layer getName]] == "hor"} {
set pitch_start [expr $relative_yMin / $layer_pitch]
if {$relative_yMin % $layer_pitch >= ($min_spacing + $layer_width / 2)} {
incr pitch_start
}
set pitch_end [expr $relative_yMax / $layer_pitch]
if {$relative_yMax % $layer_pitch > $layer_width / 2} {
incr pitch_end
}
# debug "pitch: start $pitch_start end $pitch_end"
for {set i $pitch_start} {$i <= $pitch_end} {incr i} {
set obs [odb::dbObstruction_create $block $layer \
$xMin \
[expr $i * $layer_pitch + $offsetY - $layer_width / 2] \
$xMax \
[expr $i * $layer_pitch + $offsetY + $layer_width / 2] \
]
}
} else {
set pitch_start [expr $relative_xMin / $layer_pitch]
if {$relative_xMin % $layer_pitch >= ($min_spacing + $layer_width / 2)} {
incr pitch_start
}
set pitch_end [expr $relative_xMax / $layer_pitch]
if {$relative_xMax % $layer_pitch > $layer_width / 2} {
incr pitch_end
}
# debug "pitch: start $pitch_start end $pitch_end"
for {set i $pitch_start} {$i <= $pitch_end} {incr i} {
set obs [odb::dbObstruction_create $block $layer \
[expr $i * $layer_pitch + $offsetX - $layer_width / 2] \
$yMin \
[expr $i * $layer_pitch + $offsetX + $layer_width / 2] \
$yMax \
]
}
}
}
proc create_obstruction_object_net {layer min_spacing xMin yMin xMax yMax} {
variable block
variable obstruction_index
incr obstruction_index
set net_name "obstruction_$obstruction_index"
if {[set obs_net [$block findNet $net_name]] == "NULL"} {
set obs_net [odb::dbNet_create $block $net_name]
}
# debug "obs_net [$obs_net getName]"
if {[set wire [$obs_net getWire]] == "NULL"} {
set wire [odb::dbWire_create $obs_net]
}
# debug "Wire - net [[$wire getNet] getName]"
set encoder [odb::dbWireEncoder]
$encoder begin $wire
set layer_pitch [$layer getPitch]
set layer_width [$layer getWidth]
# debug "Layer - [$layer getName], pitch $layer_pitch, width $layer_width"
set core_area [get_core_area]
# debug "core_area $core_area"
set relative_xMin [expr $xMin - [lindex $core_area 0]]
set relative_xMax [expr $xMax - [lindex $core_area 0]]
set relative_yMin [expr $yMin - [lindex $core_area 1]]
set relative_yMax [expr $yMax - [lindex $core_area 1]]
# debug "relative to core area $relative_xMin $relative_yMin $relative_xMax $relative_yMax"
# debug "OBS: [$layer getName] $xMin $yMin $xMax $yMax"
# Determine which tracks are blocked
if {[get_dir [$layer getName]] == "hor"} {
set pitch_start [expr $relative_yMin / $layer_pitch]
if {$relative_yMin % $layer_pitch > ($min_spacing + $layer_width / 2)} {
incr pitch_start
}
set pitch_end [expr $relative_yMax / $layer_pitch]
if {$relative_yMax % $layer_pitch > $layer_width / 2} {
incr pitch_end
}
for {set i $pitch_start} {$i <= $pitch_end} {incr i} {
$encoder newPath $layer ROUTED
$encoder addPoint [expr $relative_xMin + [lindex $core_area 0]] [expr $i * $layer_pitch + [lindex $core_area 1]]
$encoder addPoint [expr $relative_xMax + [lindex $core_area 0]] [expr $i * $layer_pitch + [lindex $core_area 1]]
}
} else {
set pitch_start [expr $relative_xMin / $layer_pitch]
if {$relative_xMin % $layer_pitch > ($min_spacing + $layer_width / 2)} {
incr pitch_start
}
set pitch_end [expr $relative_xMax / $layer_pitch]
if {$relative_xMax % $layer_pitch > $layer_width / 2} {
incr pitch_end
}
for {set i $pitch_start} {$i <= $pitch_end} {incr i} {
$encoder newPath $layer ROUTED
$encoder addPoint [expr $i * $layer_pitch + [lindex $core_area 0]] [expr $relative_yMin + [lindex $core_area 1]]
$encoder addPoint [expr $i * $layer_pitch + [lindex $core_area 0]] [expr $relative_yMax + [lindex $core_area 1]]
}
}
$encoder end
}
proc add_grid {} {
variable design_data
variable grid_data
if {[dict exists $grid_data core_ring]} {
set area [dict get $grid_data area]
# debug "Area $area"
generate_core_rings [dict get $grid_data core_ring]
if {[dict exists $grid_data gnd_pads]} {
dict for {pin_name cells} [dict get $grid_data gnd_pads] {
connect_pads_to_core_ring "GROUND" $pin_name $cells
}
}
if {[dict exists $grid_data pwr_pads]} {
dict for {pin_name cells} [dict get $grid_data pwr_pads] {
connect_pads_to_core_ring "POWER" $pin_name $cells
}
}
generate_voltage_domain_rings [dict get $grid_data core_ring]
# merge_stripes
# set intersections [odb::andSet $stripe_locs(G1,POWER) $stripe_locs(G2,POWER)]
# debug "# intersections [llength [odb::getPolygons $intersections]]"
# foreach pwr_net [dict get $design_data power_nets] {
# generate_grid_vias "POWER" $pwr_net
# }
# foreach gnd_net [dict get $design_data ground_nets] {
# generate_grid_vias "GROUND" $gnd_net
# }
apply_padcell_blockages
}
set area [dict get $grid_data area]
if {[dict exists $grid_data rails]} {
# debug "Adding stdcell rails"
# debug "area: [dict get $grid_data area]"
set area [dict get $grid_data area]
# debug "Area $area"
generate_lower_metal_followpin_rails
}
## Power nets
foreach pwr_net [dict get $design_data power_nets] {
set tag "POWER"
generate_stripes $tag $pwr_net
}
## Ground nets
foreach gnd_net [dict get $design_data ground_nets] {
set tag "GROUND"
generate_stripes $tag $gnd_net
}
merge_stripes
## Power nets
# debug "Power straps"
foreach pwr_net [dict get $design_data power_nets] {
set tag "POWER"
cut_blocked_areas $tag
add_pad_straps $tag
}
## Ground nets
# debug "Ground straps"
foreach gnd_net [dict get $design_data ground_nets] {
set tag "GROUND"
cut_blocked_areas $tag
add_pad_straps $tag
}
merge_stripes
if {[dict exists $grid_data obstructions]} {
utl::info "PDN" 32 "Generating blockages for TritonRoute."
# debug "Obstructions: [dict get $grid_data obstructions]"
foreach layer_name [dict get $grid_data obstructions] {
generate_obstructions $layer_name
}
}
# debug "end"
}
proc select_instance_specification {instance} {
variable design_data
variable instances
if {![dict exists $instances $instance grid]} {
utl::error PAD 248 "Instance $instance is not associated with any grid"
}
return [dict get $design_data grid macro [dict get $instances $instance grid]]
}
proc get_instance_specification {instance} {
variable instances
set specification [select_instance_specification $instance]
if {![dict exists $specification blockages]} {
dict set specification blockages {}
}
dict set specification area [dict get $instances $instance macro_boundary]
return $specification
}
proc get_pitch {layer} {
if {[$layer hasXYPitch]} {
if {[get_dir [$layer getName]] == "hor"} {
return [$layer getPitchY]
} else {
return [$layer getPitchX]
}
} else {
return [$layer getPitch]
}
}
proc get_layer_number {layer_name} {
set layer [[ord::get_db_tech] findLayer $layer_name]
if {$layer == "NULL"} {
utl::error PDN 160 "Cannot find layer $layer_name."
}
return [$layer getNumber]
}
proc init_metal_layers {} {
variable metal_layers
variable layers
variable block
set tech [ord::get_db_tech]
set block [ord::get_db_block]
set metal_layers {}
foreach layer [$tech getLayers] {
if {[$layer getType] == "ROUTING"} {
set_prop_lines $layer LEF58_TYPE
# Layers that have LEF58_TYPE are not normal ROUTING layers, so should not be considered
if {![empty_propline]} {continue}
set layer_name [$layer getName]
lappend metal_layers $layer_name
# debug "Direction ($layer_name): [$layer getDirection]"
if {[$layer getDirection] == "HORIZONTAL"} {
dict set layers $layer_name direction "hor"
} else {
dict set layers $layer_name direction "ver"
}
dict set layers $layer_name pitch [get_pitch $layer]
set tracks [$block findTrackGrid $layer]
if {$tracks == "NULL"} {
utl::warn "PDN" 35 "No track information found for layer $layer_name."
} else {
dict set layers $layer_name offsetX [lindex [$tracks getGridX] 0]
dict set layers $layer_name offsetY [lindex [$tracks getGridY] 0]
}
}
}
}
proc get_instance_llx {instance} {
variable instances
return [lindex [dict get $instances $instance halo_boundary] 0]
}
proc get_instance_lly {instance} {
variable instances
return [lindex [dict get $instances $instance halo_boundary] 1]
}
proc get_instance_urx {instance} {
variable instances
return [lindex [dict get $instances $instance halo_boundary] 2]
}
proc get_instance_ury {instance} {
variable instances
return [lindex [dict get $instances $instance halo_boundary] 3]
}
proc get_macro_blockage_layers {instance} {
variable metal_layers
set specification [select_instance_specification $instance]
if {[dict exists $specification blockages]} {
# debug "Block [dict get $specification blockages] for $instance"
return [dict get $specification blockages]
}
return $metal_layers
}
proc report_layer_details {layer} {
variable def_units
set str " - "
foreach element {width pitch spacing offset pad_offset core_offset} {
if {[dict exists $layer $element]} {
set str [format "$str $element: %.3f " [expr 1.0 * [dict get $layer $element] / $def_units]]
}
}
return $str
}
proc print_strategy {type specification} {
if {[dict exists $specification name]} {
set header "Type: ${type}, [dict get $specification name]"
} else {
set header "Type: $type"
}
if {$type == "macro"} {
if {[dict exists $specification grid_over_pg_pins]} {
if {[dict get $specification grid_over_pg_pins] == 1} {
set header "$header -grid_over_pg_pins"
} else {
set header "$header -grid_over_boundary"
}
}
}
utl::report $header
if {[dict exists $specification core_ring]} {
utl::report " Core Rings"
dict for {layer_name layer} [dict get $specification core_ring] {
set str " Layer: $layer_name"
if {[dict exists $layer width]} {
set str "$str [report_layer_details $layer]"
utl::report $str
} else {
utl::report $str
foreach template [dict keys $layer] {
utl::report -nonewline [format " %-14s %s" $template [report_layer_details [dict get $layer $template]]]
}
}
}
}
if {[dict exists $specification rails]} {
utl::report " Stdcell Rails"
dict for {layer_name layer} [dict get $specification rails] {
if {[dict exists $layer width]} {
utl::report " Layer: $layer_name [report_layer_details $layer]"
} else {
utl::report " Layer: $layer_name"
foreach template [dict keys $layer] {
utl::report [format " %-14s %s" $template [report_layer_details [dict get $layer $template]]]
}
}
}
}
if {[dict exists $specification instance]} {
utl::report " Instance: [dict get $specification instance]"
}
if {[dict exists $specification macro]} {
utl::report " Macro: [dict get $specification macro]"
}
if {[dict exists $specification orient]} {
utl::report " Macro orientation: [dict get $specification orient]"
}
if {[dict exists $specification straps]} {
utl::report " Straps"
dict for {layer_name layer} [dict get $specification straps] {
if {[dict exists $layer width]} {
utl::report " Layer: $layer_name [report_layer_details $layer]"
} else {
utl::report " Layer: $layer_name"
foreach template [dict keys $layer] {
utl::report [format " %-14s %s" $template [report_layer_details [dict get $layer $template]]]
}
}
}
}
if {[dict exists $specification connect]} {
utl::report " Connect: [dict get $specification connect]"
}
}
proc read_template_placement {} {
variable plan_template
variable def_units
variable prop_line
if {![is_defined_pdn_property "plan_template"]} {
define_template_grid
} else {
set plan_template {}
set prop_line [get_pdn_string_property_value "plan_template"]
while {![empty_propline]} {
set line [read_propline]
if {[llength $line] == 0} {continue}
set x [expr round([lindex $line 0] * $def_units)]
set y [expr round([lindex $line 1] * $def_units)]
set x1 [expr round([lindex $line 2] * $def_units)]
set y1 [expr round([lindex $line 3] * $def_units)]
set template [lindex $line end]
dict set plan_template $x $y $template
}
}
}
proc is_defined_pdn_property {name} {
variable block
if {[set pdn_props [::odb::dbBoolProperty_find $block PDN]] == "NULL"} {
return 0
}
if {[::odb::dbStringProperty_find $pdn_props $name] == "NULL"} {
return 0
}
return 1
}
proc get_pdn_string_property {name} {
variable block
if {[set pdn_props [::odb::dbBoolProperty_find $block PDN]] == "NULL"} {
set pdn_props [::odb::dbBoolProperty_create $block PDN 1]
}
if {[set prop [::odb::dbStringProperty_find $pdn_props $name]] == "NULL"} {
set prop [::odb::dbStringProperty_create $pdn_props $name ""]
}
return $prop
}
proc set_pdn_string_property_value {name value} {
set prop [get_pdn_string_property $name]
$prop setValue $value
}
proc get_pdn_string_property_value {name} {
set prop [get_pdn_string_property $name]
return [$prop getValue]
}
proc write_template_placement {} {
variable plan_template
variable template
variable def_units
set str ""
foreach x [lsort -integer [dict keys $plan_template]] {
foreach y [lsort -integer [dict keys [dict get $plan_template $x]]] {
set str [format "${str}%.3f %.3f %.3f %.3f %s ;\n" \
[expr 1.0 * $x / $def_units] [expr 1.0 * $y / $def_units] \
[expr 1.0 * ($x + [dict get $template width]) / $def_units] [expr 1.0 * ($y + [dict get $template height]) / $def_units] \
[dict get $plan_template $x $y]
]
}
}
set_pdn_string_property_value "plan_template" $str
}
proc get_extent {polygon_set} {
set first_point [lindex [odb::getPoints [lindex [odb::getPolygons $polygon_set] 0]] 0]
set minX [set maxX [$first_point getX]]
set minY [set maxY [$first_point getY]]
foreach shape [odb::getPolygons $polygon_set] {
foreach point [odb::getPoints $shape] {
set x [$point getX]
set y [$point getY]
set minX [expr min($minX,$x)]
set maxX [expr max($maxX,$x)]
set minY [expr min($minY,$y)]
set maxY [expr max($maxY,$y)]
}
}
return [list $minX $minY $maxX $maxY]
}
proc round_to_routing_grid {layer_name location} {
variable tech
variable block
set grid [$block findTrackGrid [$tech findLayer $layer_name]]
if {[get_dir $layer_name] == "hor"} {
set grid_points [$grid getGridY]
} else {
set grid_points [$grid getGridX]
}
set size [llength $grid_points]
set pos [expr ($size + 1) / 2]
if {[lsearch -exact $grid_points $location] != -1} {
return $location
}
set prev_pos -1
set size [expr ($size + 1) / 2]
while {!(([lindex $grid_points $pos] < $location) && ($location < [lindex $grid_points [expr $pos + 1]]))} {
if {$prev_pos == $pos} {utl::error "PDN" 51 "Infinite loop detected trying to round to grid."}
set prev_pos $pos
set size [expr ($size + 1) / 2]
if {$location > [lindex $grid_points $pos]} {
set pos [expr $pos + $size]
} else {
set pos [expr $pos - $size]
}
# debug "[lindex $grid_points $pos] < $location < [lindex $grid_points [expr $pos + 1]]"
# debug [expr (([lindex $grid_points $pos] < $location) && ($location < [lindex $grid_points [expr $pos + 1]]))]
}
return [lindex $grid_points $pos]
}
proc identify_channels {lower_layer_name upper_layer_name tag} {
variable block
variable stripe_locs
set channels {}
set wire_groups {}
set upper_pitch_check [expr round(1.1 * [get_grid_wire_pitch $upper_layer_name])]
set lower_pitch_check [expr round(1.1 * [get_grid_wire_pitch $lower_layer_name])]
# debug "stripes $lower_layer_name, tag: $tag, $stripe_locs($lower_layer_name,$tag)"
# debug "Direction (lower-$lower_layer_name): [get_dir $lower_layer_name] (upper-$upper_layer_name): [get_dir $upper_layer_name]"
# debug "Pitch check (lower): [ord::dbu_to_microns $lower_pitch_check], (upper): [ord::dbu_to_microns $upper_pitch_check]"
if {[get_dir $lower_layer_name] == "hor"} {
set channel_wires [odb::subtractSet $stripe_locs($lower_layer_name,$tag) [odb::bloatSet [odb::shrinkSet $stripe_locs($lower_layer_name,$tag) $upper_pitch_check 0] $upper_pitch_check 0]]
# Group wires with same xMin and xMax so that the channels form rectangles
foreach wire [odb::getRectangles $channel_wires] {
set xMin [$wire xMin]
set xMax [$wire xMax]
dict lappend wire_groups "$xMin,$xMax" [odb::newSetFromRect [$wire xMin] [$wire yMin] [$wire xMax] [$wire yMax]]
}
if {[dict size $wire_groups] > 1} {
dict for {position wires} $wire_groups {
lappend channels [odb::shrinkSet [odb::bloatSet [odb::orSets $wires] 0 $lower_pitch_check] 0 $lower_pitch_check]
}
}
set channels [odb::orSets $channels]
# debug "Channel wires: [llength [odb::getRectangles $channel_wires]]"
# debug "Channels: [llength [odb::getRectangles $channels]]"
} else {
set channel_wires [odb::subtractSet $stripe_locs($lower_layer_name,$tag) [odb::bloatSet [odb::shrinkSet $stripe_locs($lower_layer_name,$tag) 0 $upper_pitch_check] 0 $upper_pitch_check]]
# Group wires with same yMin and yMax so that the channels form rectangles
foreach wire [odb::getRectangles $channel_wires] {
set yMin [$wire yMin]
set yMax [$wire yMax]
dict lappend wire_groups "$yMin,$yMax" [odb::newSetFromRect [$wire xMin] [$wire yMin] [$wire xMax] [$wire yMax]]
}
if {[dict size $wire_groups] > 1} {
dict for {position wires} $wire_groups {
lappend channels [odb::shrinkSet [odb::bloatSet [odb::orSets $wires] $lower_pitch_check 0] $lower_pitch_check 0]
}
}
set channels [odb::orSets $channels]
# debug "Channel wires: [llength [odb::getRectangles $channel_wires]]"
# debug "Channels: [llength [odb::getRectangles $channels]]"
}
foreach rect [odb::getRectangles $channels] {
# debug "([ord::dbu_to_microns [$rect xMin]] [ord::dbu_to_microns [$rect yMin]]) - ([ord::dbu_to_microns [$rect xMax]] [ord::dbu_to_microns [$rect yMax]])"
}
# debug "Number of channels [llength [::odb::getPolygons $channels]]"
return $channels
}
proc repair_channel {channel layer_name tag min_size} {
variable stripe_locs
if {[get_dir $layer_name] == "hor"} {
set channel_height [$channel dx]
} else {
set channel_height [$channel dy]
}
set width [get_grid_wire_width $layer_name]
set xMin [$channel xMin]
set xMax [$channel xMax]
set yMin [$channel yMin]
set yMax [$channel yMax]
if {$tag == "POWER"} {
set other_tag "GROUND"
} else {
set other_tag "POWER"
}
if {[channel_has_pg_strap $channel $layer_name $other_tag]} {
set other_strap [lindex [odb::getRectangles [odb::andSet [odb::newSetFromRect $xMin $yMin $xMax $yMax] $stripe_locs($layer_name,$other_tag)]] 0]
if {[get_dir $layer_name] == "hor"} {
set center [expr ($xMax + $xMin) / 2]
set mid_channel [expr ($yMax + $yMin) / 2]
if {$xMax - $xMin < $min_size} {
set xMin [expr $center - $min_size / 2]
set xMax [expr $center + $min_size / 2]
}
set channel_spacing [get_grid_channel_spacing $layer_name [expr $xMax - $xMin]]
set other_strap_mid [expr ([$other_strap yMin] + [$other_strap yMax]) / 2]
if {($mid_channel <= $other_strap_mid) && ([$other_strap yMin] - $channel_spacing - $width > $yMin)} {
# debug "Stripe below $other_strap"
set stripe [odb::newSetFromRect $xMin [expr [$other_strap yMin] - $channel_spacing - $width] $xMax [expr [$other_strap yMin] - $channel_spacing]]
} elseif {($mid_channel > $other_strap_mid) && ([$other_strap yMax] + $channel_spacing + $width < $yMax)} {
# debug "Stripe above $other_strap"
set stripe [odb::newSetFromRect $xMin [expr [$other_strap yMax] + $channel_spacing] $xMax [expr [$other_strap yMax] + $channel_spacing + $width]]
} else {
set stripe {}
utl::warn PDN 169 "Cannot fit additional $tag horizontal strap in channel ([ord::dbu_to_microns $xMin] [ord::dbu_to_microns $yMin]) - ([ord::dbu_to_microns $xMax], [ord::dbu_to_microns $yMax])"
}
} else {
set center [expr ($yMax + $yMin) / 2]
set mid_channel [expr ($xMax + $xMin) / 2]
if {$yMax - $yMin < $min_size} {
set yMin [expr $center - $min_size / 2]
set yMax [expr $center + $min_size / 2]
}
set channel_spacing [get_grid_channel_spacing $layer_name [expr $yMax - $yMin]]
set other_strap_mid [expr ([$other_strap xMin] + [$other_strap xMax]) / 2]
if {($mid_channel <= $other_strap_mid) && ([$other_strap xMin] - $channel_spacing - $width > $xMin)} {
# debug "Stripe left of $other_strap on layer $layer_name, spacing: $channel_spacing, width $width, strap_edge: [$other_strap xMin]"
set stripe [odb::newSetFromRect [expr [$other_strap xMin] - $channel_spacing - $width] $yMin [expr [$other_strap xMin] - $channel_spacing] $yMax]
} elseif {($mid_channel > $other_strap_mid) && ([$other_strap xMax] + $channel_spacing + $width < $xMax)} {
# debug "Stripe right of $other_strap"
set stripe [odb::newSetFromRect [expr [$other_strap xMax] + $channel_spacing] $yMin [expr [$other_strap xMax] + $channel_spacing + $width] $yMax]
} else {
set stripe {}
utl::warn PDN 170 "Cannot fit additional $tag vertical strap in channel ([ord::dbu_to_microns $xMin] [ord::dbu_to_microns $yMin]) - ([ord::dbu_to_microns $xMax], [ord::dbu_to_microns $yMax])"
}
}
} else {
if {[get_dir $layer_name] == "hor"} {
set channel_spacing [get_grid_channel_spacing $layer_name [expr $xMax - $xMin]]
set routing_grid [round_to_routing_grid $layer_name [expr ($yMax + $yMin - $channel_spacing) / 2]]
if {([expr $routing_grid - $width / 2] < $yMin) || ([expr $routing_grid + $width / 2] > $yMax)} {
utl::warn "PDN" 171 "Channel ([ord::dbu_to_microns $xMin] [ord::dbu_to_microns $yMin] [ord::dbu_to_microns $xMax] [ord::dbu_to_microns $yMax]) too narrow. Channel on layer $layer_name must be at least [ord::dbu_to_microns [expr round(2.0 * $width + $channel_spacing)]] wide."
}
set stripe [odb::newSetFromRect $xMin [expr $routing_grid - $width / 2] $xMax [expr $routing_grid + $width / 2]]
} else {
set channel_spacing [get_grid_channel_spacing $layer_name [expr $yMax - $yMin]]
set routing_grid [round_to_routing_grid $layer_name [expr ($xMax + $xMin - $channel_spacing) / 2]]
if {([expr $routing_grid - $width / 2] < $xMin) || ([expr $routing_grid + $width / 2] > $xMax)} {
utl::warn "PDN" 172 "Channel ([ord::dbu_to_microns $xMin] [ord::dbu_to_microns $yMin] [ord::dbu_to_microns $xMax] [ord::dbu_to_microns $yMax]) too narrow. Channel on layer $layer_name must be at least [ord::dbu_to_microns [expr round(2.0 * $width + $channel_spacing)]] wide."
}
set stripe [odb::newSetFromRect [expr $routing_grid - $width / 2] $yMin [expr $routing_grid + $width / 2] $yMax]
}
}
if {$stripe != {}} {
add_stripe $layer_name $tag $stripe
}
}
proc channel_has_pg_strap {channel layer_name tag} {
variable stripe_locs
# debug "start, channel: $channel, layer: $layer_name"
# debug " ([ord::dbu_to_microns [$channel xMin]] [ord::dbu_to_microns [$channel yMin]]) - ([ord::dbu_to_microns [$channel xMax]] [ord::dbu_to_microns [$channel yMax]])"
set power_strap 0
set ground_strap 0
set channel_set [odb::newSetFromRect [$channel xMin] [$channel yMin] [$channel xMax] [$channel yMax]]
set check_set [odb::andSet $stripe_locs($layer_name,$tag) $channel_set]
foreach rect [odb::getRectangles $check_set] {
if {[get_dir $layer_name] == "ver" && [$rect dx] < [get_grid_wire_width $layer_name]} {continue}
if {[get_dir $layer_name] == "hor" && [$rect dy] < [get_grid_wire_width $layer_name]} {continue}
# debug "Overlap found"
# debug " Direction: [get_dir $layer_name]"
# debug " Layer width: [get_grid_wire_width $layer_name]"
# debug " ([ord::dbu_to_microns [$rect xMin]] [ord::dbu_to_microns [$rect yMin]]) - ([ord::dbu_to_microns [$rect xMax]] [ord::dbu_to_microns [$rect yMax]])"
return 1
}
# debug "end: channel needs repair"
return 0
}
proc process_channels {} {
set layers [get_grid_channel_layers]
set lower_layers [lrange $layers 0 end-1]
set upper_layers [lrange $layers 1 end]
foreach lower_layer_name $lower_layers upper_layer_name $upper_layers {
foreach tag {POWER GROUND} {
set channels [identify_channels $lower_layer_name $upper_layer_name $tag]
if {$channels == {}} {continue}
# debug "Tag: $tag, Channels found: [llength [odb::getPolygons $channels]]"
foreach channel [::odb::getRectangles $channels] {
if {![channel_has_pg_strap $channel $upper_layer_name $tag]} {
set next_upper_layer_idx [expr [lsearch -exact $layers $upper_layer_name] + 1]
if {$next_upper_layer_idx < [llength $layers]} {
set next_upper_layer [lindex $layers $next_upper_layer_idx]
set min_size [expr [get_grid_wire_pitch $next_upper_layer] + [get_grid_wire_width $next_upper_layer]]
} else {
set min_size 0
}
# debug "Repair channel: ([ord::dbu_to_microns [$channel xMin]] [ord::dbu_to_microns [$channel yMin]])-([ord::dbu_to_microns [$channel xMax]] [ord::dbu_to_microns [$channel yMax]]]), tag: $tag, layer: $upper_layer_name, min_size: $min_size"
repair_channel $channel $upper_layer_name $tag $min_size
}
}
merge_stripes
}
}
}
proc get_stdcell_plus_area {} {
variable stdcell_area
variable stdcell_plus_area
if {$stdcell_area == ""} {
get_stdcell_area
}
# debug "stdcell_area [get_extent $stdcell_area]"
# debug "stdcell_plus_area [get_extent $stdcell_plus_area]"
return $stdcell_plus_area
}
proc get_stdcell_area {} {
variable stdcell_area
variable stdcell_plus_area
if {$stdcell_area != ""} {return $stdcell_area}
set rails_width [get_rails_max_width]
set rows [[ord::get_db_block] getRows]
set first_row [[lindex $rows 0] getBBox]
set minX [$first_row xMin]
set maxX [$first_row xMax]
set minY [$first_row yMin]
set maxY [$first_row yMax]
set stdcell_area [odb::newSetFromRect $minX $minY $maxX $maxY]
set stdcell_plus_area [odb::newSetFromRect $minX [expr $minY - $rails_width / 2] $maxX [expr $maxY + $rails_width / 2]]
foreach row [lrange $rows 1 end] {
set box [$row getBBox]
set minX [$box xMin]
set maxX [$box xMax]
set minY [$box yMin]
set maxY [$box yMax]
set stdcell_area [odb::orSet $stdcell_area [odb::newSetFromRect $minX $minY $maxX $maxY]]
set stdcell_plus_area [odb::orSet $stdcell_plus_area [odb::newSetFromRect $minX [expr $minY - $rails_width / 2] $maxX [expr $maxY + $rails_width / 2]]]
}
return $stdcell_area
}
proc find_core_area {} {
variable block
set area [get_stdcell_area]
return [get_extent $area]
}
proc get_rails_max_width {} {
variable design_data
variable default_grid_data
set max_width 0
foreach layer [get_rails_layers] {
if {[dict exists $default_grid_data rails $layer]} {
if {[set width [dict get $default_grid_data rails $layer width]] > $max_width} {
set max_width $width
}
}
}
if {![dict exists $default_grid_data units]} {
set max_width [ord::microns_to_dbu $max_width]
}
return $max_width
}
# This is a proc to get the first voltage domain that overlaps with the input box
proc get_voltage_domain {llx lly urx ury} {
variable block
variable design_data
variable voltage_domains
set name [dict get $design_data core_domain]
foreach domain_name [dict keys $voltage_domains] {
if {$domain_name == [dict get $design_data core_domain]} {continue}
set domain [$block findRegion $domain_name]
set rect [lindex [$domain getBoundaries] 0]
set domain_xMin [$rect xMin]
set domain_yMin [$rect yMin]
set domain_xMax [$rect xMax]
set domain_yMax [$rect yMax]
if {!($domain_yMin >= $ury || $domain_xMin >= $urx || $domain_xMax <= $llx || $domain_yMax <= $lly)} {
set name [$domain getName]
break
}
}
return $name
}
proc get_voltage_domain_power {domain} {
variable voltage_domains
return [dict get $voltage_domains $domain primary_power]
}
proc get_voltage_domain_secondary_power {domain} {
variable voltage_domains
if {[dict exists $voltage_domains $domain secondary_power]} {
return [dict get $voltage_domains $domain secondary_power]
} else {
return []
}
}
proc get_voltage_domain_ground {domain} {
variable voltage_domains
return [dict get $voltage_domains $domain primary_ground]
}
# This proc is to split core domain's power stripes if they cross interal voltage domains that have different pwr/gnd nets
proc update_mesh_stripes_with_volatge_domains {tag lay snet_name} {
variable block
variable stripes
variable grid_data
variable design_data
variable voltage_domains
set rails_width [get_rails_max_width]
set stdcell_area [get_extent [get_stdcell_area]]
set stdcell_min_x [lindex $stdcell_area 0]
set stdcell_min_y [lindex $stdcell_area 1]
set stdcell_max_x [lindex $stdcell_area 2]
set stdcell_max_y [lindex $stdcell_area 3]
set ring_adjustment 0
if {[set ring_vertical_layer [get_core_ring_vertical_layer_name]] != ""} {
if {[dict exists $grid_data core_ring $ring_vertical_layer pad_offset]} {
set pad_area [find_pad_offset_area]
set offset [expr [dict get $grid_data core_ring $ring_vertical_layer pad_offset]]
set ring_adjustment [expr $stdcell_min_x - ([lindex $pad_area 0] + $offset)]
}
if {[dict exists $grid_data core_ring $ring_vertical_layer core_offset]} {
set ring_adjustment [expr \
[dict get $grid_data core_ring $ring_vertical_layer core_offset] + \
[dict get $grid_data core_ring $ring_vertical_layer spacing] + \
3 * [dict get $grid_data core_ring $ring_vertical_layer width] / 2 \
]
}
}
set first_row [lindex [$block getRows] 0]
set row_site [$first_row getSite]
set site_width [$row_site getWidth]
set row_height [$row_site getHeight]
# This voltage domain to core domain margin is hard coded for now
set MARGIN 6
set X_MARGIN [expr ($MARGIN * $row_height / $site_width) * $site_width]
set Y_MARGIN [expr $MARGIN * $row_height]
foreach domain_name [dict keys $voltage_domains] {
if {$domain_name == [dict get $design_data core_domain]} {continue}
set domain [$block findRegion $domain_name]
set first_rect [lindex [$domain getBoundaries] 0]
# voltage domain area
set domain_xMin [expr [$first_rect xMin]]
set domain_yMin [expr [$first_rect yMin]]
set domain_xMax [expr [$first_rect xMax]]
set domain_yMax [expr [$first_rect yMax]]
# voltage domain area + margin
set domain_boundary_xMin [expr [$first_rect xMin] - $X_MARGIN]
set domain_boundary_yMin [expr [$first_rect yMin] - $Y_MARGIN + $rails_width / 2]
set domain_boundary_xMax [expr [$first_rect xMax] + $X_MARGIN]
set domain_boundary_yMax [expr [$first_rect yMax] + $Y_MARGIN - $rails_width / 2]
if {[get_dir $lay] == "hor"} {
if {$domain_boundary_xMin < $stdcell_min_x + $site_width} {
set domain_boundary_xMin [expr $stdcell_min_x - $ring_adjustment]
}
if {$domain_boundary_xMax > $stdcell_max_x - $site_width} {
set domain_boundary_xMax [expr $stdcell_max_x + $ring_adjustment]
}
} else {
if {$domain_boundary_yMin < $stdcell_min_y + $row_height} {
set domain_boundary_yMin [expr $stdcell_min_y - $ring_adjustment]
}
if {$domain_boundary_yMax > $stdcell_max_y - $row_height} {
set domain_boundary_yMax [expr $stdcell_max_y + $ring_adjustment]
}
}
# Core domain's pwr/gnd nets that are not shared should not cross the entire voltage domain area
set boundary_box \
[odb::newSetFromRect \
$domain_boundary_xMin \
$domain_boundary_yMin \
$domain_boundary_xMax \
$domain_boundary_yMax \
]
if {[get_dir $lay] == "hor"} {
set domain_box \
[odb::newSetFromRect \
$domain_boundary_xMin \
$domain_yMin \
$domain_boundary_xMax \
$domain_yMax \
]
} else {
set domain_box \
[odb::newSetFromRect \
$domain_xMin \
$domain_boundary_yMin \
$domain_xMax \
$domain_boundary_yMax \
]
}
# Core domain's pwr/gnd nets shared with a voltage domain should not cross the domains' pwr/gnd rings
set boundary_box_for_crossing_core_net [odb::subtractSet $boundary_box $domain_box]
for {set i 0} {$i < [llength $stripes($lay,$tag)]} {incr i} {
set updated_polygonSet [lindex $stripes($lay,$tag) $i]
# Check if core domain's power is the same as voltage domain's power
if {$snet_name == [get_voltage_domain_power $domain_name] ||
$snet_name == [get_voltage_domain_ground $domain_name]} {
set updated_polygonSet [odb::subtractSet $updated_polygonSet $boundary_box_for_crossing_core_net]
} else {
set updated_polygonSet [odb::subtractSet $updated_polygonSet $boundary_box]
}
# This if statemet prevents from deleting domain rings
if {[llength [odb::getPolygons $updated_polygonSet]] > 0} {
set stripes($lay,$tag) [lreplace $stripes($lay,$tag) $i $i $updated_polygonSet]
}
}
}
}
# This proc is to check if a pwr/gnd net is unique for all voltage domains, the setWildConnected can be used
proc check_snet_is_unique {net} {
variable voltage_domains
set is_unique_power 1
foreach vd_key [dict keys $voltage_domains] {
if {[dict get $voltage_domains $vd_key primary_power] != [$net getName]} {
set is_unique_power 0
break
}
}
set is_unique_ground 1
foreach vd_key [dict keys $voltage_domains] {
if {[dict get $voltage_domains $vd_key primary_ground] != [$net getName]} {
set is_unique_ground 0
break
}
}
return [expr $is_unique_power || $is_unique_ground]
}
# This proc generates power rings for voltage domains, tags for the core domain are POWER/GROUND, tags for the other
# voltage domains are defined as POWER_<pwr-net> and GROUND_<gnd-net>
proc generate_voltage_domain_rings {core_ring_data} {
variable block
variable voltage_domains
variable grid_data
variable design_data
foreach domain_name [dict keys $voltage_domains] {
if {$domain_name == [dict get $design_data core_domain]} {continue}
set domain [$block findRegion $domain_name]
set rect [lindex [$domain getBoundaries] 0]
set power_net [get_voltage_domain_power $domain_name]
set secondary_power_net [get_voltage_domain_secondary_power $domain_name]
set ground_net [get_voltage_domain_ground $domain_name]
set domain_xMin [$rect xMin]
set domain_yMin [$rect yMin]
set domain_xMax [$rect xMax]
set domain_yMax [$rect yMax]
dict for {layer layer_info} $core_ring_data {
if {[dict exists $layer_info core_offset]} {
set offset [dict get $layer_info core_offset]
set spacing [dict get $layer_info spacing]
set width [dict get $layer_info width]
set inner_lx [expr $domain_xMin - $offset]
set inner_ly [expr $domain_yMin - $offset]
set inner_ux [expr $domain_xMax + $offset]
set inner_uy [expr $domain_yMax + $offset]
set outer_lx [expr $domain_xMin - $offset - $spacing - $width]
set outer_ly [expr $domain_yMin - $offset - $spacing - $width]
set outer_ux [expr $domain_xMax + $offset + $spacing + $width]
set outer_uy [expr $domain_yMax + $offset + $spacing + $width]
}
set number_of_rings 0
if {[get_dir $layer] == "hor"} {
set lower_inner_ring \
[odb::newSetFromRect \
[expr $inner_lx - $width / 2] \
[expr $inner_ly - $width / 2] \
[expr $inner_ux + $width / 2] \
[expr $inner_ly + $width / 2] \
]
set upper_inner_ring \
[odb::newSetFromRect \
[expr $inner_lx - $width / 2] \
[expr $inner_uy - $width / 2] \
[expr $inner_ux + $width / 2] \
[expr $inner_uy + $width / 2] \
]
set lower_outer_ring \
[odb::newSetFromRect \
[expr $outer_lx - $width / 2] \
[expr $outer_ly - $width / 2] \
[expr $outer_ux + $width / 2] \
[expr $outer_ly + $width / 2] \
]
set upper_outer_ring \
[odb::newSetFromRect \
[expr $outer_lx - $width / 2] \
[expr $outer_uy - $width / 2] \
[expr $outer_ux + $width / 2] \
[expr $outer_uy + $width / 2] \
]
if {$power_net == [get_voltage_domain_power [dict get $design_data core_domain]]} {
add_stripe $layer "POWER" $lower_inner_ring
add_stripe $layer "POWER" $upper_inner_ring
} else {
add_stripe $layer "POWER_$power_net" $lower_inner_ring
add_stripe $layer "POWER_$power_net" $upper_inner_ring
}
if {$ground_net == [get_voltage_domain_ground [dict get $design_data core_domain]]} {
add_stripe $layer "GROUND" $lower_outer_ring
add_stripe $layer "GROUND" $upper_outer_ring
} else {
add_stripe $layer "GROUND_$ground_net" $lower_outer_ring
add_stripe $layer "GROUND_$ground_net" $upper_outer_ring
}
set number_of_rings [llength $secondary_power_net]
foreach secondary_power $secondary_power_net {
set outer_lx [expr $outer_lx - $spacing - $width]
set outer_ly [expr $outer_ly - $spacing - $width]
set outer_ux [expr $outer_ux + $spacing + $width]
set outer_uy [expr $outer_uy + $spacing + $width]
set upper_domain_power \
[odb::newSetFromRect \
[expr $outer_lx - $width / 2] \
[expr $outer_uy - $width / 2] \
[expr $outer_ux + $width / 2] \
[expr $outer_uy + $width / 2] \
]
set lower_domain_power \
[odb::newSetFromRect \
[expr $outer_lx - $width / 2] \
[expr $outer_ly - $width / 2] \
[expr $outer_ux + $width / 2] \
[expr $outer_ly + $width / 2] \
]
add_stripe $layer "POWER_$secondary_power" $upper_domain_power
add_stripe $layer "POWER_$secondary_power" $lower_domain_power
}
} else {
set lhs_inner_ring \
[odb::newSetFromRect \
[expr $inner_lx - $width / 2] \
[expr $inner_ly - $width / 2] \
[expr $inner_lx + $width / 2] \
[expr $inner_uy + $width / 2] \
]
set rhs_inner_ring \
[odb::newSetFromRect \
[expr $inner_ux - $width / 2] \
[expr $inner_ly - $width / 2] \
[expr $inner_ux + $width / 2] \
[expr $inner_uy + $width / 2] \
]
set lhs_outer_ring \
[odb::newSetFromRect \
[expr $outer_lx - $width / 2] \
[expr $outer_ly - $width / 2] \
[expr $outer_lx + $width / 2] \
[expr $outer_uy + $width / 2] \
]
set rhs_outer_ring \
[odb::newSetFromRect \
[expr $outer_ux - $width / 2] \
[expr $outer_ly - $width / 2] \
[expr $outer_ux + $width / 2] \
[expr $outer_uy + $width / 2] \
]
if {$power_net == [get_voltage_domain_power [dict get $design_data core_domain]]} {
add_stripe $layer "POWER" $lhs_inner_ring
add_stripe $layer "POWER" $rhs_inner_ring
} else {
add_stripe $layer "POWER_$power_net" $lhs_inner_ring
add_stripe $layer "POWER_$power_net" $rhs_inner_ring
}
if {$ground_net == [get_voltage_domain_ground [dict get $design_data core_domain]]} {
add_stripe $layer "GROUND" $lhs_outer_ring
add_stripe $layer "GROUND" $rhs_outer_ring
} else {
add_stripe $layer "GROUND_$ground_net" $lhs_outer_ring
add_stripe $layer "GROUND_$ground_net" $rhs_outer_ring
}
foreach secondary_power $secondary_power_net {
set outer_lx [expr $outer_lx - $spacing - $width]
set outer_ly [expr $outer_ly - $spacing - $width]
set outer_ux [expr $outer_ux + $spacing + $width]
set outer_uy [expr $outer_uy + $spacing + $width]
set lhs_domain_power \
[odb::newSetFromRect \
[expr $outer_lx - $width / 2] \
[expr $outer_ly - $width / 2] \
[expr $outer_lx + $width / 2] \
[expr $outer_uy + $width / 2] \
]
set rhs_domain_power \
[odb::newSetFromRect \
[expr $outer_ux - $width / 2] \
[expr $outer_ly - $width / 2] \
[expr $outer_ux + $width / 2] \
[expr $outer_uy + $width / 2] \
]
add_stripe $layer "POWER_$secondary_power" $lhs_domain_power
add_stripe $layer "POWER_$secondary_power" $rhs_domain_power
}
}
}
}
}
# This proc detects pins used in pdn.cfg for global connections
proc get_valid_mterms {net_name} {
variable global_connections
variable default_global_connections
if {$global_connections == {}} {
set global_connections $default_global_connections
}
set mterms_list {}
if {![dict exists $global_connections $net_name]} {
utl::error PDN 174 "Net $net_name has no global connections defined."
}
foreach pattern [dict get $global_connections $net_name] {
lappend mterms_list [dict get $pattern pin_name]
}
return $mterms_list
}
proc core_area_boundary {} {
variable design_data
variable template
variable metal_layers
variable grid_data
set core_area [find_core_area]
# We need to allow the rails to extend by half a rails width in the y direction, since the rails overlap the core_area
set llx [lindex $core_area 0]
set lly [lindex $core_area 1]
set urx [lindex $core_area 2]
set ury [lindex $core_area 3]
if {[dict exists $template width]} {
set width [dict get $template width]
} else {
set width 2000
}
if {[dict exists $template height]} {
set height [dict get $template height]
} else {
set height 2000
}
# Add blockages around the outside of the core area in order to trim back the templates.
#
set boundary [odb::newSetFromRect [expr $llx - $width] [expr $lly - $height] $llx [expr $ury + $height]]
set boundary [odb::orSet $boundary [odb::newSetFromRect [expr $llx - $width] [expr $lly - $height] [expr $urx + $width] $lly]]
set boundary [odb::orSet $boundary [odb::newSetFromRect [expr $llx - $width] $ury [expr $urx + $width] [expr $ury + $height]]]
set boundary [odb::orSet $boundary [odb::newSetFromRect $urx [expr $lly - $height] [expr $urx + $width] [expr $ury + $height]]]
set boundary [odb::subtractSet $boundary [get_stdcell_plus_area]]
foreach layer $metal_layers {
if {[dict exists $grid_data core_ring] && [dict exists $grid_data core_ring $layer]} {continue}
dict set blockages $layer $boundary
}
return $blockages
}
proc get_instance_blockages {insts} {
variable instances
set blockages {}
foreach inst $insts {
foreach layer [get_macro_blockage_layers $inst] {
# debug "Inst $inst"
# debug "Macro boundary: [dict get $instances $inst macro_boundary]"
# debug "Halo boundary: [dict get $instances $inst halo_boundary]"
set box [odb::newSetFromRect [get_instance_llx $inst] [get_instance_lly $inst] [get_instance_urx $inst] [get_instance_ury $inst]]
if {[dict exists $blockages $layer]} {
dict set blockages $layer [odb::orSet [dict get $blockages $layer] $box]
} else {
dict set blockages $layer $box
}
}
}
return $blockages
}
proc define_template_grid {} {
variable design_data
variable template
variable plan_template
variable block
variable default_grid_data
variable default_template_name
set core_area [dict get $design_data config core_area]
set llx [lindex $core_area 0]
set lly [lindex $core_area 1]
set urx [lindex $core_area 2]
set ury [lindex $core_area 3]
set core_width [expr $urx - $llx]
set core_height [expr $ury - $lly]
set template_width [dict get $template width]
set template_height [dict get $template height]
set x_sections [expr round($core_width / $template_width)]
set y_sections [expr round($core_height / $template_height)]
dict set template offset x [expr [lindex $core_area 0] + ($core_width - $x_sections * $template_width) / 2]
dict set template offset y [expr [lindex $core_area 1] + ($core_height - $y_sections * $template_height) / 2]
if {$default_template_name == {}} {
set template_name [lindex [dict get $default_grid_data template names] 0]
} else {
set template_name $default_template_name
}
for {set i -1} {$i <= $x_sections} {incr i} {
for {set j -1} {$j <= $y_sections} {incr j} {
set llx [expr $i * $template_width + [dict get $template offset x]]
set lly [expr $j * $template_height + [dict get $template offset y]]
dict set plan_template $llx $lly $template_name
}
}
write_template_placement
}
proc set_blockages {these_blockages} {
variable blockages
set blockages $these_blockages
}
proc get_blockages {} {
variable blockages
return $blockages
}
proc add_blockage {layer blockage} {
variable blockages
if {[dict exists $blockages $layer]} {
dict set blockages $layer [odb::orSet [dict get $blockages $layer] $blockage]
} else {
dict set blockages $layer $blockage
}
}
proc add_padcell_blockage {layer blockage} {
variable padcell_blockages
if {[dict exists $padcell_blockages $layer]} {
dict set padcell_blockages $layer [odb::orSet [dict get $padcell_blockages $layer] $blockage]
} else {
dict set padcell_blockages $layer $blockage
}
}
proc apply_padcell_blockages {} {
variable padcell_blockages
variable global_connections
dict for {layer_name blockages} $padcell_blockages {
add_blockage $layer_name $blockages
}
}
proc add_blockages {more_blockages} {
variable blockages
dict for {layer blockage} $more_blockages {
add_blockage $layer $blockage
}
}
proc add_macro_based_grids {} {
variable grid_data
variable design_data
variable verbose
if {![dict exists $design_data grid macro]} {return}
foreach {key grid_data} [dict get $design_data grid macro] {
if {![dict exists $grid_data _related_instances]} {continue}
set instances [dict get $grid_data _related_instances]
set_blockages {}
if {[llength [dict keys $instances]] > 0} {
utl::info "PDN" 10 "Inserting macro grid for [llength [dict keys $instances]] macros."
foreach instance [dict keys $instances] {
if {$verbose == 1} {
utl::info "PDN" 34 " - grid [dict get $grid_data name] for instance $instance"
}
if {[dict exists $grid_data grid_over_pg_pins]} {
# debug "Grid over pins: [get_instance_pg_pins_area $instance]"
dict set grid_data area [get_instance_pg_pins_area $instance]
} else {
# debug "Grid boundary: [get_instance_pg_pins_area $instance]"
dict set grid_data area [dict get $instances $instance macro_boundary]
}
add_grid
# debug "Generate vias for [dict get $design_data power_nets] [dict get $design_data ground_nets]"
foreach pwr_net [dict get $design_data power_nets] {
generate_grid_vias "POWER" $pwr_net
}
foreach gnd_net [dict get $design_data ground_nets] {
generate_grid_vias "GROUND" $gnd_net
}
}
}
}
}
proc plan_grid {} {
variable design_data
variable instances
variable default_grid_data
variable def_units
variable grid_data
################################## Main Code #################################
if {![dict exists $design_data grid macro]} {
utl::warn "PDN" 18 "No macro grid specifications found - no straps added."
}
utl::info "PDN" 11 "****** INFO ******"
print_strategy stdcell [get_stdcell_specification]
if {[dict exists $design_data grid macro]} {
dict for {name specification} [dict get $design_data grid macro] {
print_strategy macro $specification
}
}
utl::info "PDN" 12 "**** END INFO ****"
set specification $default_grid_data
if {[dict exists $specification name]} {
utl::info "PDN" 13 "Inserting stdcell grid - [dict get $specification name]."
} else {
utl::info "PDN" 14 "Inserting stdcell grid."
}
if {![dict exists $specification area]} {
dict set specification area [dict get $design_data config core_area]
}
set grid_data $specification
set boundary [odb::newSetFromRect {*}[dict get $grid_data area]]
set insts_in_core_area [filtered_insts_within $instances $boundary]
set_blockages [get_instance_blockages [dict keys $insts_in_core_area]]
add_blockages [core_area_boundary]
if {[dict exists $specification template]} {
read_template_placement
}
add_grid
#Dinesh-A: Core Ring without Strap
if {[info exists $grid_data]} {
process_channels
}
foreach pwr_net [dict get $design_data power_nets] {
generate_grid_vias "POWER" $pwr_net
}
foreach gnd_net [dict get $design_data ground_nets] {
generate_grid_vias "GROUND" $gnd_net
}
add_macro_based_grids
}
proc opendb_update_grid {} {
utl::info "PDN" 15 "Writing to database."
export_opendb_vias
export_opendb_specialnets
export_opendb_power_pins
}
proc set_verbose {} {
variable verbose
set verbose 1
}
proc apply {args} {
variable verbose
variable default_grid_data
if {[llength $args] > 0 && $verbose} {
set config [lindex $args 0]
utl::info "PDN" 16 "Power Delivery Network Generator: Generating PDN\n config: $config"
}
if {[llength $args] == 1} {
set PDN_cfg [lindex $args 0]
if {![file exists $PDN_cfg]} {
utl::error "PDN" 62 "File $PDN_cfg does not exist."
}
if {![file_exists_non_empty $PDN_cfg]} {
utl::error "PDN" 28 "File $PDN_cfg is empty."
}
source $PDN_cfg
}
init {*}$args
complete_macro_grid_specifications
set default_grid_data [get_stdcell_specification]
plan_grid
write_pdn_strategy
opendb_update_grid
}
}