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_pi