clean-up
diff --git a/.gitmodules b/.gitmodules
index e69de29..7c54d50 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -0,0 +1,6 @@
+[submodule "verilog/rtl/yifive/ycr1c"]
+ path = verilog/rtl/yifive/ycr1c
+ url = https://github.com/dineshannayya/ycr1c.git
+[submodule "verilog/rtl/qspim"]
+ path = verilog/rtl/qspim
+ url = https://github.com/dineshannayya/qspim.git
diff --git a/hacks/patch/pdngen.patch b/hacks/patch/pdngen.patch
new file mode 100644
index 0000000..8d44ec8
--- /dev/null
+++ b/hacks/patch/pdngen.patch
@@ -0,0 +1,27 @@
+diff --git a/src/pdn/src/PdnGen.tcl b/src/pdn/src/PdnGen.tcl
+index 7322d54ad..7f178167f 100644
+--- a/src/pdn/src/PdnGen.tcl
++++ b/src/pdn/src/PdnGen.tcl
+@@ -966,8 +966,9 @@ proc add_pdn_ring {args} {
+
+ 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 grid $current_grid
+ set layers [check_layer_names [dict get $args -layers]]
+
+ set process_args $args
+@@ -6686,7 +6687,10 @@ proc plan_grid {} {
+ }
+
+ add_grid
+- process_channels
++ #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
diff --git a/hacks/patch/resizer.patch b/hacks/patch/resizer.patch
index 44e2796..bf587c7 100644
--- a/hacks/patch/resizer.patch
+++ b/hacks/patch/resizer.patch
@@ -1,8 +1,8 @@
diff --git a/src/rsz/src/Resizer.cc b/src/rsz/src/Resizer.cc
-index b57fce674..cac938c44 100644
+index 3cbe306e3..300c6c476 100644
--- a/src/rsz/src/Resizer.cc
+++ b/src/rsz/src/Resizer.cc
-@@ -1685,9 +1685,11 @@ Resizer::resizeToTargetSlew(const Pin *drvr_pin,
+@@ -1691,9 +1691,11 @@ Resizer::resizeToTargetSlew(const Pin *drvr_pin,
ensureWireParasitic(drvr_pin);
// Includes net parasitic capacitance.
float load_cap = graph_delay_calc_->loadCap(drvr_pin, tgt_slew_dcalc_ap_);
@@ -15,15 +15,3 @@
debugPrint(logger_, RSZ, "resize", 2, "{} {} -> {}",
sdc_network_->pathName(drvr_pin),
cell->name(),
-@@ -2500,8 +2502,10 @@ Resizer::repairSetup(PathRef &path,
- prev_drive = 0.0;
- LibertyCell *upsize = upsizeCell(in_port, drvr_port, load_cap,
- prev_drive, dcalc_ap);
-- if (upsize) {
-+ // DINESH-A: delay cells resize disabled
-+ if (upsize && (strncmp(drvr_port->libertyCell()->name(),"sky130_fd_sc_hd__clkdlybuf4s15_2",26) != 0)) {
- Instance *drvr = network_->instance(drvr_pin);
-+ //printf("Dinesh-A: Upsizing the cells: %s %s %s\n",network_->pathName(drvr_pin),drvr_port->libertyCell()->name(),upsize->name());
- debugPrint(logger_, RSZ, "repair_setup", 2, "resize {} {} -> {}",
- network_->pathName(drvr_pin),
- drvr_port->libertyCell()->name(),
diff --git a/hacks/patch/scan_swap.patch b/hacks/patch/scan_swap.patch
index 41d98b7..e69de29 100644
--- a/hacks/patch/scan_swap.patch
+++ b/hacks/patch/scan_swap.patch
@@ -1,60 +0,0 @@
-diff --git a/src/sta/network/ConcreteNetwork.cc b/src/sta/network/ConcreteNetwork.cc
-index 6f8b842..8096f2e 100644
---- a/src/sta/network/ConcreteNetwork.cc
-+++ b/src/sta/network/ConcreteNetwork.cc
-@@ -1180,11 +1180,14 @@ ConcreteNetwork::replaceCell(Instance *inst,
- ConcreteCell *ccell = reinterpret_cast<ConcreteCell*>(cell);
- int port_count = ccell->portBitCount();
- ConcreteInstance *cinst = reinterpret_cast<ConcreteInstance*>(inst);
-+ // Port count picked from Instance instead of Target cells-Dinesh A
-+ ConcreteCell *instcell = reinterpret_cast<ConcreteCell*>(cinst->cell());
-+ int inst_port_count = instcell->portBitCount();
- ConcretePin **pins = cinst->pins_;
- ConcretePin **rpins = new ConcretePin*[port_count];
-- for (int i = 0; i < port_count; i++)
-- rpins[i] = nullptr;
-- for (int i = 0; i < port_count; i++) {
-+ for (int i = 0; i < port_count; i++)
-+ rpins[i] = pins[inst_port_count-1];
-+ for (int i = 0; i < inst_port_count; i++) {
- ConcretePin *cpin = pins[i];
- if (cpin) {
- ConcretePort *pin_cport = reinterpret_cast<ConcretePort*>(cpin->port());
-diff --git a/src/sta/tcl/NetworkEdit.tcl b/src/sta/tcl/NetworkEdit.tcl
-index 5ce616b..bdd4057 100644
---- a/src/sta/tcl/NetworkEdit.tcl
-+++ b/src/sta/tcl/NetworkEdit.tcl
-@@ -236,6 +236,21 @@ proc replace_cell { instance lib_cell } {
- }
- }
-
-+proc replace_to_scan_cell { instance } {
-+ set inst [get_instance_error "instance" $instance]
-+ set inst_cell [get_full_name [$inst liberty_cell]]
-+ #Target scan cell __d to __sd
-+ #example sky130_fd_sc_hd__dfrtp_2 to sky130_fd_sc_hd__sdfrtp_2
-+ set inst_scell [regsub -all "__d" $inst_cell "__sd"]
-+ puts "Info: Scan Swapping => Instance:$instance From:$inst_cell to:$inst_scell";
-+ if { $inst_cell == "NULL"} {
-+ return 0
-+ }
-+ set scell [get_lib_cell_warn "lib_cell" $inst_scell]
-+ replace_cell_cmd $inst $scell
-+ return 1
-+}
-+
- proc path_regexp {} {
- global hierarchy_separator
- set id_regexp "\[^${hierarchy_separator}\]+"
-diff --git a/src/sta/tcl/Sta.tcl b/src/sta/tcl/Sta.tcl
-index f3de994..a6e8e34 100644
---- a/src/sta/tcl/Sta.tcl
-+++ b/src/sta/tcl/Sta.tcl
-@@ -623,6 +623,7 @@ define_cmd_args "make_instance" {inst_path lib_cell}
- define_cmd_args "make_net" {}
-
- define_cmd_args "replace_cell" {instance lib_cell}
-+define_cmd_args "replace_to_scan_cell" {instance}
-
- define_cmd_args "insert_buffer" {buffer_name buffer_cell net load_pins\
- buffer_out_net_name}
diff --git a/hacks/src/OpenROAD/PdnGen.tcl b/hacks/src/OpenROAD/PdnGen.tcl
new file mode 100644
index 0000000..7f17816
--- /dev/null
+++ b/hacks/src/OpenROAD/PdnGen.tcl
@@ -0,0 +1,6751 @@
+#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
+}
+}
diff --git a/hacks/src/OpenROAD/Resizer.cc b/hacks/src/OpenROAD/Resizer.cc
index a339071..300c6c4 100644
--- a/hacks/src/OpenROAD/Resizer.cc
+++ b/hacks/src/OpenROAD/Resizer.cc
@@ -37,7 +37,6 @@
#include "rsz/SteinerTree.hh"
-#include "ord/OpenRoad.hh"
#include "gui/gui.h"
#include "utl/Logger.h"
@@ -86,7 +85,6 @@
using std::sqrt;
using utl::RSZ;
-using ord::closestPtInRect;
using odb::dbInst;
using odb::dbPlacementStatus;
@@ -509,13 +507,13 @@
string buffer_name = makeUniqueInstName("input");
Instance *parent = db_network_->topInstance();
Net *buffer_out = makeUniqueNet();
- Instance *buffer = db_network_->makeInstance(buffer_cell,
- buffer_name.c_str(),
- parent);
+ Instance *buffer = makeInstance(buffer_cell,
+ buffer_name.c_str(),
+ parent);
if (buffer) {
journalMakeBuffer(buffer);
Point pin_loc = db_network_->location(top_pin);
- Point buf_loc = core_exists_ ? closestPtInRect(core_, pin_loc) : pin_loc;
+ Point buf_loc = core_exists_ ? core_.closestPtInside(pin_loc) : pin_loc;
setLocation(buffer, buf_loc);
designAreaIncr(area(db_network_->cell(buffer_cell)));
inserted_buffer_count_++;
@@ -544,18 +542,13 @@
Resizer::setLocation(Instance *inst,
Point pt)
{
- int x = pt.getX();
- int y = pt.getY();
// Stay inside the lines.
- if (core_exists_) {
- Point in_core = closestPtInRect(core_, x, y);
- x = in_core.getX();
- y = in_core.getY();
- }
+ if (core_exists_)
+ pt = core_.closestPtInside(pt);
dbInst *dinst = db_network_->staToDb(inst);
dinst->setPlacementStatus(dbPlacementStatus::PLACED);
- dinst->setLocation(x, y);
+ dinst->setLocation(pt.getX(), pt.getY());
}
void
@@ -622,9 +615,9 @@
string buffer_name = makeUniqueInstName("output");
Instance *parent = network->topInstance();
Net *buffer_in = makeUniqueNet();
- Instance *buffer = network->makeInstance(buffer_cell,
- buffer_name.c_str(),
- parent);
+ Instance *buffer = makeInstance(buffer_cell,
+ buffer_name.c_str(),
+ parent);
if (buffer) {
journalMakeBuffer(buffer);
setLocation(buffer, db_network_->location(top_pin));
@@ -1330,15 +1323,22 @@
|| load_slew > max_load_slew) {
// Make the wire a bit shorter than necessary to allow for
// offset from instance origin to pin and detailed placement movement.
- double length_margin = .05;
- int stub_length = std::numeric_limits<int>::max();
- if (max_length > 0 && wire_length > max_length)
- stub_length = min(stub_length, max_length);
+ static double length_margin = .05;
+ bool split_wire = false;
+ int split_length = std::numeric_limits<int>::max();
+ if (max_length > 0 && wire_length > max_length) {
+ split_length = min(split_length, max_length);
+ split_wire = true;
+ }
if (wire_cap > 0.0
&& pin_cap < max_cap
- && load_cap > max_cap)
- stub_length = min(stub_length, metersToDbu((max_cap - pin_cap) / wire_cap));
- if (load_slew > max_load_slew) {
+ && load_cap > max_cap) {
+ split_length = min(split_length, metersToDbu((max_cap - pin_cap) / wire_cap));
+ split_wire = true;
+ }
+ if (load_slew > max_load_slew
+ // Check that zero length wire meets max slew.
+ && r_drvr*pin_cap*k_threshold < max_load_slew) {
// Using elmore delay to approximate wire
// load_slew = (Rdrvr + L*Rwire) * (L*Cwire + Cpin) * k_threshold
// Setting this to max_slew is a quadratic in L
@@ -1348,34 +1348,40 @@
float b = r_drvr * wire_cap + wire_res * pin_cap;
float c = r_drvr * pin_cap - max_load_slew / k_threshold;
float l = (-b + sqrt(b*b - 4 * a * c)) / (2 * a);
- stub_length = min(stub_length, metersToDbu(l));
+ if (l > 0.0) {
+ split_length = min(split_length, metersToDbu(l));
+ split_wire = true;
+ }
}
+ if (split_wire) {
+ // Distance from pt to repeater backward toward prev_pt.
+ double buf_dist = length - (wire_length - split_length * (1.0 - length_margin));
+ double dx = prev_x - pt_x;
+ double dy = prev_y - pt_y;
+ double d = (length == 0) ? 0.0 : buf_dist / length;
+ int buf_x = pt_x + d * dx;
+ int buf_y = pt_y + d * dy;
+ makeRepeater("wire", buf_x, buf_y, buffer_lowest_drive_, level,
+ wire_length, pin_cap, fanout, load_pins);
+ // Update for the next round.
+ length -= buf_dist;
+ wire_length = length;
+ pt_x = buf_x;
+ pt_y = buf_y;
- // Distance from pt to repeater backward toward prev_pt.
- double buf_dist = length - (wire_length - stub_length * (1.0 - length_margin));
- double dx = prev_x - pt_x;
- double dy = prev_y - pt_y;
- double d = (length == 0) ? 0.0 : buf_dist / length;
- int buf_x = pt_x + d * dx;
- int buf_y = pt_y + d * dy;
- makeRepeater("wire", buf_x, buf_y, buffer_lowest_drive_, level,
- wire_length, pin_cap, fanout, load_pins);
- // Update for the next round.
- length -= buf_dist;
- wire_length = length;
- pt_x = buf_x;
- pt_y = buf_y;
-
- wire_length1 = dbuToMeters(wire_length);
- load_cap = pin_cap + wire_length1 * wire_cap;
- load_slew = (r_drvr + wire_length1 * wire_res) * load_cap * k_threshold;
- debugPrint(logger_, RSZ, "repair_net", 3, "{:{}s}load_slew={}",
- "", level,
- delayAsString(load_slew, this, 3));
- debugPrint(logger_, RSZ, "repair_net", 3, "{:{}s}wl={} l={}",
- "", level,
- units_->distanceUnit()->asString(dbuToMeters(wire_length), 1),
- units_->distanceUnit()->asString(dbuToMeters(length), 1));
+ wire_length1 = dbuToMeters(wire_length);
+ load_cap = pin_cap + wire_length1 * wire_cap;
+ load_slew = (r_drvr + wire_length1 * wire_res) * load_cap * k_threshold;
+ debugPrint(logger_, RSZ, "repair_net", 3, "{:{}s}load_slew={}",
+ "", level,
+ delayAsString(load_slew, this, 3));
+ debugPrint(logger_, RSZ, "repair_net", 3, "{:{}s}wl={} l={}",
+ "", level,
+ units_->distanceUnit()->asString(dbuToMeters(wire_length), 1),
+ units_->distanceUnit()->asString(dbuToMeters(length), 1));
+ }
+ else
+ break;
}
}
}
@@ -1487,9 +1493,9 @@
}
}
- Instance *buffer = db_network_->makeInstance(buffer_cell,
- buffer_name.c_str(),
- parent);
+ Instance *buffer = makeInstance(buffer_cell,
+ buffer_name.c_str(),
+ parent);
journalMakeBuffer(buffer);
Point buf_loc(x, y);
setLocation(buffer, buf_loc);
@@ -2220,8 +2226,8 @@
Point tie_loc = tieLocation(load, separation_dbu);
Instance *load_inst = network_->instance(load);
string tie_name = makeUniqueInstName(inst_name, true);
- Instance *tie = sta_->makeInstance(tie_name.c_str(),
- tie_cell, top_inst);
+ Instance *tie = makeInstance(tie_cell, tie_name.c_str(),
+ top_inst);
setLocation(tie, tie_loc);
// Make tie output net.
@@ -2318,7 +2324,8 @@
////////////////////////////////////////////////////////////////
void
-Resizer::repairSetup(float slack_margin)
+Resizer::repairSetup(float slack_margin,
+ int max_passes)
{
inserted_buffer_count_ = 0;
resize_count_ = 0;
@@ -2331,7 +2338,8 @@
int pass = 1;
int decreasing_slack_passes = 0;
incrementalParasiticsBegin();
- while (fuzzyLess(worst_slack, slack_margin)) {
+ while (fuzzyLess(worst_slack, slack_margin)
+ && pass <= max_passes) {
PathRef worst_path = sta_->vertexWorstSlackPath(worst_vertex, max_);
bool changed = repairSetup(worst_path, worst_slack);
updateParasitics();
@@ -2444,154 +2452,89 @@
int drvr_index = index_delay.first;
PathRef *drvr_path = expanded.path(drvr_index);
Vertex *drvr_vertex = drvr_path->vertex(sta_);
- Pin *drvr_pin = drvr_vertex->pin();
- PathRef *load_path = expanded.path(drvr_index + 1);
- Vertex *load_vertex = load_path->vertex(sta_);
- Pin *load_pin = load_vertex->pin();
+ const Pin *drvr_pin = drvr_vertex->pin();
+ LibertyPort *drvr_port = network_->libertyPort(drvr_pin);
+ LibertyCell *drvr_cell = drvr_port ? drvr_port->libertyCell() : nullptr;
int fanout = this->fanout(drvr_vertex);
- debugPrint(logger_, RSZ, "repair_setup", 2, "{} fanout = {}",
+ debugPrint(logger_, RSZ, "repair_setup", 2, "{} {} fanout = {}",
network_->pathName(drvr_pin),
+ drvr_cell ? drvr_cell->name() : "none",
fanout);
+
+ if (upsizeDrvr(drvr_path, drvr_index, &expanded)) {
+ changed = true;
+ break;
+ }
+
// For tristate nets all we can do is resize the driver.
bool tristate_drvr = isTristateDriver(drvr_pin);
if (fanout > 1
// Rebuffer blows up on large fanout nets.
&& fanout < rebuffer_max_fanout_
- && !tristate_drvr) {
- int count_before = inserted_buffer_count_;
- rebuffer(drvr_pin);
- int insert_count = inserted_buffer_count_ - count_before;
- if (insert_count > 0) {
+ && !tristate_drvr) {
+ int rebuffer_count = rebuffer(drvr_pin);
+ if (rebuffer_count > 0) {
debugPrint(logger_, RSZ, "repair_setup", 2, "rebuffer {} inserted {}",
network_->pathName(drvr_pin),
- insert_count);
+ rebuffer_count);
changed = true;
break;
}
}
+
// Don't split loads on low fanout nets.
if (fanout > split_load_min_fanout_
&& !tristate_drvr) {
- // Divide and conquer.
- debugPrint(logger_, RSZ, "repair_setup", 2, "split loads {} -> {}",
- network_->pathName(drvr_pin),
- network_->pathName(load_pin));
- splitLoads(drvr_path, path_slack);
+ splitLoads(drvr_path, drvr_index, path_slack, &expanded);
changed = true;
break;
}
- LibertyPort *drvr_port = network_->libertyPort(drvr_pin);
- float load_cap = graph_delay_calc_->loadCap(drvr_pin, dcalc_ap);
- int in_index = drvr_index - 1;
- PathRef *in_path = expanded.path(in_index);
- Pin *in_pin = in_path->pin(sta_);
- LibertyPort *in_port = network_->libertyPort(in_pin);
-
- float prev_drive;
- if (drvr_index >= 2) {
- int prev_drvr_index = drvr_index - 2;
- PathRef *prev_drvr_path = expanded.path(prev_drvr_index);
- Pin *prev_drvr_pin = prev_drvr_path->pin(sta_);
- prev_drive = 0.0;
- LibertyPort *prev_drvr_port = network_->libertyPort(prev_drvr_pin);
- if (prev_drvr_port) {
- prev_drive = prev_drvr_port->driveResistance();
- }
- }
- else
- prev_drive = 0.0;
- LibertyCell *upsize = upsizeCell(in_port, drvr_port, load_cap,
- prev_drive, dcalc_ap);
- // DINESH-A: delay cells resize disabled
- if (upsize && (strncmp(drvr_port->libertyCell()->name(),"sky130_fd_sc_hd__clkdlybuf4s15_2",26) != 0)) {
- Instance *drvr = network_->instance(drvr_pin);
- //printf("Dinesh-A: Upsizing the cells: %s %s %s\n",network_->pathName(drvr_pin),drvr_port->libertyCell()->name(),upsize->name());
- debugPrint(logger_, RSZ, "repair_setup", 2, "resize {} {} -> {}",
- network_->pathName(drvr_pin),
- drvr_port->libertyCell()->name(),
- upsize->name());
- if (replaceCell(drvr, upsize, true)) {
- resize_count_++;
- changed = true;
- break;
- }
- }
}
}
return changed;
}
-void
-Resizer::splitLoads(PathRef *drvr_path,
- Slack drvr_slack)
+bool
+Resizer::upsizeDrvr(PathRef *drvr_path,
+ int drvr_index,
+ PathExpanded *expanded)
{
- Vertex *drvr_vertex = drvr_path->vertex(sta_);
- const RiseFall *rf = drvr_path->transition(sta_);
- // Sort fanouts of the drvr on the critical path by slack margin
- // wrt the critical path slack.
- vector<pair<Vertex*, Slack>> fanout_slacks;
- VertexOutEdgeIterator edge_iter(drvr_vertex, graph_);
- while (edge_iter.hasNext()) {
- Edge *edge = edge_iter.next();
- Vertex *fanout_vertex = edge->to(graph_);
- Slack fanout_slack = sta_->vertexSlack(fanout_vertex, rf, max_);
- Slack slack_margin = fanout_slack - drvr_slack;
- debugPrint(logger_, RSZ, "repair_setup", 3, " fanin {} slack_margin = {}",
- network_->pathName(fanout_vertex->pin()),
- delayAsString(slack_margin, sta_, 3));
- fanout_slacks.push_back(pair<Vertex*, Slack>(fanout_vertex, slack_margin));
- }
+ Pin *drvr_pin = drvr_path->pin(this);
+ const DcalcAnalysisPt *dcalc_ap = drvr_path->dcalcAnalysisPt(sta_);
+ float load_cap = graph_delay_calc_->loadCap(drvr_pin, dcalc_ap);
+ int in_index = drvr_index - 1;
+ PathRef *in_path = expanded->path(in_index);
+ Pin *in_pin = in_path->pin(sta_);
+ LibertyPort *in_port = network_->libertyPort(in_pin);
- sort(fanout_slacks.begin(), fanout_slacks.end(),
- [](pair<Vertex*, Slack> pair1,
- pair<Vertex*, Slack> pair2) {
- return pair1.second > pair2.second;
- });
-
- Pin *drvr_pin = drvr_vertex->pin();
- Net *net = network_->net(drvr_pin);
-
- string buffer_name = makeUniqueInstName("split");
- Instance *parent = db_network_->topInstance();
- LibertyCell *buffer_cell = buffer_lowest_drive_;
- Instance *buffer = db_network_->makeInstance(buffer_cell,
- buffer_name.c_str(),
- parent);
- journalMakeBuffer(buffer);
- inserted_buffer_count_++;
- designAreaIncr(area(db_network_->cell(buffer_cell)));
-
- Net *out_net = makeUniqueNet();
- LibertyPort *input, *output;
- buffer_cell->bufferPorts(input, output);
- Point drvr_loc = db_network_->location(drvr_pin);
- setLocation(buffer, drvr_loc);
-
- // Split the loads with extra slack to an inserted buffer.
- // before
- // drvr_pin -> net -> load_pins
- // after
- // drvr_pin -> net -> load_pins with low slack
- // -> buffer_in -> net -> rest of loads
- sta_->connectPin(buffer, input, net);
- parasiticsInvalid(net);
- sta_->connectPin(buffer, output, out_net);
- int split_index = fanout_slacks.size() / 2;
- for (int i = 0; i < split_index; i++) {
- pair<Vertex*, Slack> fanout_slack = fanout_slacks[i];
- Vertex *load_vertex = fanout_slack.first;
- Pin *load_pin = load_vertex->pin();
- // Leave ports connected to original net so verilog port names are preserved.
- if (!network_->isTopLevelPort(load_pin)) {
- LibertyPort *load_port = network_->libertyPort(load_pin);
- Instance *load = network_->instance(load_pin);
-
- sta_->disconnectPin(load_pin);
- sta_->connectPin(load, load_port, out_net);
+ float prev_drive;
+ if (drvr_index >= 2) {
+ int prev_drvr_index = drvr_index - 2;
+ PathRef *prev_drvr_path = expanded->path(prev_drvr_index);
+ Pin *prev_drvr_pin = prev_drvr_path->pin(sta_);
+ prev_drive = 0.0;
+ LibertyPort *prev_drvr_port = network_->libertyPort(prev_drvr_pin);
+ if (prev_drvr_port) {
+ prev_drive = prev_drvr_port->driveResistance();
}
}
- Pin *buffer_out_pin = network_->findPin(buffer, output);
- resizeToTargetSlew(buffer_out_pin, false);
+ else
+ prev_drive = 0.0;
+ LibertyPort *drvr_port = network_->libertyPort(drvr_pin);
+ LibertyCell *upsize = upsizeCell(in_port, drvr_port, load_cap,
+ prev_drive, dcalc_ap);
+ if (upsize) {
+ Instance *drvr = network_->instance(drvr_pin);
+ debugPrint(logger_, RSZ, "repair_setup", 2, "resize {} {} -> {}",
+ network_->pathName(drvr_pin),
+ drvr_port->libertyCell()->name(),
+ upsize->name());
+ if (replaceCell(drvr, upsize, true)) {
+ resize_count_++;
+ return true;
+ }
+ }
+ return false;
}
LibertyCell *
@@ -2634,6 +2577,89 @@
return nullptr;
}
+void
+Resizer::splitLoads(PathRef *drvr_path,
+ int drvr_index,
+ Slack drvr_slack,
+ PathExpanded *expanded)
+{
+ Pin *drvr_pin = drvr_path->pin(this);
+ PathRef *load_path = expanded->path(drvr_index + 1);
+ Vertex *load_vertex = load_path->vertex(sta_);
+ Pin *load_pin = load_vertex->pin();
+ // Divide and conquer.
+ debugPrint(logger_, RSZ, "repair_setup", 2, "split loads {} -> {}",
+ network_->pathName(drvr_pin),
+ network_->pathName(load_pin));
+
+ Vertex *drvr_vertex = drvr_path->vertex(sta_);
+ const RiseFall *rf = drvr_path->transition(sta_);
+ // Sort fanouts of the drvr on the critical path by slack margin
+ // wrt the critical path slack.
+ vector<pair<Vertex*, Slack>> fanout_slacks;
+ VertexOutEdgeIterator edge_iter(drvr_vertex, graph_);
+ while (edge_iter.hasNext()) {
+ Edge *edge = edge_iter.next();
+ Vertex *fanout_vertex = edge->to(graph_);
+ Slack fanout_slack = sta_->vertexSlack(fanout_vertex, rf, max_);
+ Slack slack_margin = fanout_slack - drvr_slack;
+ debugPrint(logger_, RSZ, "repair_setup", 3, " fanin {} slack_margin = {}",
+ network_->pathName(fanout_vertex->pin()),
+ delayAsString(slack_margin, sta_, 3));
+ fanout_slacks.push_back(pair<Vertex*, Slack>(fanout_vertex, slack_margin));
+ }
+
+ sort(fanout_slacks.begin(), fanout_slacks.end(),
+ [](pair<Vertex*, Slack> pair1,
+ pair<Vertex*, Slack> pair2) {
+ return pair1.second > pair2.second;
+ });
+
+ Net *net = network_->net(drvr_pin);
+
+ string buffer_name = makeUniqueInstName("split");
+ Instance *parent = db_network_->topInstance();
+ LibertyCell *buffer_cell = buffer_lowest_drive_;
+ Instance *buffer = makeInstance(buffer_cell,
+ buffer_name.c_str(),
+ parent);
+ journalMakeBuffer(buffer);
+ inserted_buffer_count_++;
+ designAreaIncr(area(db_network_->cell(buffer_cell)));
+
+ Net *out_net = makeUniqueNet();
+ LibertyPort *input, *output;
+ buffer_cell->bufferPorts(input, output);
+ Point drvr_loc = db_network_->location(drvr_pin);
+ setLocation(buffer, drvr_loc);
+
+ // Split the loads with extra slack to an inserted buffer.
+ // before
+ // drvr_pin -> net -> load_pins
+ // after
+ // drvr_pin -> net -> load_pins with low slack
+ // -> buffer_in -> net -> rest of loads
+ sta_->connectPin(buffer, input, net);
+ parasiticsInvalid(net);
+ sta_->connectPin(buffer, output, out_net);
+ int split_index = fanout_slacks.size() / 2;
+ for (int i = 0; i < split_index; i++) {
+ pair<Vertex*, Slack> fanout_slack = fanout_slacks[i];
+ Vertex *load_vertex = fanout_slack.first;
+ Pin *load_pin = load_vertex->pin();
+ // Leave ports connected to original net so verilog port names are preserved.
+ if (!network_->isTopLevelPort(load_pin)) {
+ LibertyPort *load_port = network_->libertyPort(load_pin);
+ Instance *load = network_->instance(load_pin);
+
+ sta_->disconnectPin(load_pin);
+ sta_->connectPin(load, load_port, out_net);
+ }
+ }
+ Pin *buffer_out_pin = network_->findPin(buffer, output);
+ resizeToTargetSlew(buffer_out_pin, false);
+}
+
////////////////////////////////////////////////////////////////
void
@@ -3013,8 +3039,8 @@
Net *buf_out_net = (i == buffer_count - 1) ? out_net : makeUniqueNet();
// drvr_pin->drvr_net->hold_buffer->net2->load_pins
string buffer_name = makeUniqueInstName("hold");
- buffer = db_network_->makeInstance(buffer_cell, buffer_name.c_str(),
- parent);
+ buffer = makeInstance(buffer_cell, buffer_name.c_str(),
+ parent);
journalMakeBuffer(buffer);
inserted_buffer_count_++;
designAreaIncr(area(db_network_->cell(buffer_cell)));
@@ -3491,6 +3517,8 @@
const Corner *corner)
{
LibertyCell *cell = drvr_port->libertyCell();
+ if (db_network_->staToDb(cell) == nullptr)
+ logger_->error(RSZ, 70, "no LEF cell for {}.", cell->name());
double drvr_r = drvr_port->driveResistance();
// wire_length1 lower bound
// wire_length2 upper bound
@@ -3820,8 +3848,8 @@
Pin *load_pin = load_iter->next();
if (load_pin != out_pin) {
string clone_name = makeUniqueInstName(inv_name, true);
- Instance *clone = sta_->makeInstance(clone_name.c_str(),
- inv_cell, top_inst);
+ Instance *clone = makeInstance(inv_cell, clone_name.c_str(),
+ top_inst);
Point clone_loc = db_network_->location(load_pin);
journalMakeBuffer(clone);
setLocation(clone, clone_loc);
@@ -4092,4 +4120,15 @@
return false;
}
+Instance *Resizer::makeInstance(LibertyCell *cell,
+ const char *name,
+ Instance *parent)
+{
+ debugPrint(logger_, RSZ, "make_instance", 1, "make instance {}", name);
+ Instance *inst = db_network_->makeInstance(cell, name, parent);
+ dbInst *db_inst = db_network_->staToDb(inst);
+ db_inst->setSourceType(odb::dbSourceType::TIMING);
+ return inst;
+}
+
} // namespace
diff --git a/hacks/src/OpenSTA/network/ConcreteNetwork.cc b/hacks/src/OpenSTA/network/ConcreteNetwork.cc
index 8096f2e..e0ee69b 100644
--- a/hacks/src/OpenSTA/network/ConcreteNetwork.cc
+++ b/hacks/src/OpenSTA/network/ConcreteNetwork.cc
@@ -1,5 +1,5 @@
// OpenSTA, Static Timing Analyzer
-// Copyright (c) 2021, Parallax Software, Inc.
+// Copyright (c) 2022, Parallax Software, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -8,11 +8,11 @@
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "ConcreteNetwork.hh"
@@ -1192,8 +1192,10 @@
if (cpin) {
ConcretePort *pin_cport = reinterpret_cast<ConcretePort*>(cpin->port());
ConcretePort *cport = ccell->findPort(pin_cport->name());
- rpins[cport->pinIndex()] = cpin;
- cpin->port_ = cport;
+ if (cport) {
+ rpins[cport->pinIndex()] = cpin;
+ cpin->port_ = cport;
+ }
}
}
delete [] pins;
@@ -1441,7 +1443,9 @@
ConcreteNetwork::addConstantNet(Net *net,
LogicValue value)
{
- constant_nets_[int(value)].insert(net);
+ if (value == LogicValue::zero
+ || value == LogicValue::one)
+ constant_nets_[int(value)].insert(net);
}
ConstantPinIterator *
diff --git a/hacks/src/OpenSTA/tcl/NetworkEdit.tcl b/hacks/src/OpenSTA/tcl/NetworkEdit.tcl
index bdd4057..9df729d 100644
--- a/hacks/src/OpenSTA/tcl/NetworkEdit.tcl
+++ b/hacks/src/OpenSTA/tcl/NetworkEdit.tcl
@@ -1,5 +1,5 @@
# OpenSTA, Static Timing Analyzer
-# Copyright (c) 2021, Parallax Software, Inc.
+# Copyright (c) 2022, Parallax Software, Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -8,11 +8,11 @@
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <https://www.gnu.org/licenses/>.
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
# Network editing commands.
diff --git a/hacks/src/OpenSTA/tcl/Sta.tcl b/hacks/src/OpenSTA/tcl/Sta.tcl
index a6e8e34..64f5a08 100644
--- a/hacks/src/OpenSTA/tcl/Sta.tcl
+++ b/hacks/src/OpenSTA/tcl/Sta.tcl
@@ -1,5 +1,5 @@
# OpenSTA, Static Timing Analyzer
-# Copyright (c) 2021, Parallax Software, Inc.
+# Copyright (c) 2022, Parallax Software, Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -8,11 +8,11 @@
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <https://www.gnu.org/licenses/>.
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
namespace eval sta {
diff --git a/hacks/src/openlane/io_place.py b/hacks/src/openlane/io_place.py
index f3333f6..1e8b2b1 100644
--- a/hacks/src/openlane/io_place.py
+++ b/hacks/src/openlane/io_place.py
@@ -452,7 +452,7 @@
pin_bpin = odb.dbBPin_create(bterm)
if(slot < slot_pre):
- print("ERROR:", "Current Pad:", pin_name, " Slot:" , slot, " is less than Previous One:",slot_pre)
+ print("ERROR:", "Current Pad:", pin_name, " Slot:" , slot, " is less than Previous Slot:",slot_pre)
sys.exit(1)
slot_pre = slot
diff --git a/openlane/io_place.py b/openlane/io_place.py
new file mode 100755
index 0000000..86ac1f2
--- /dev/null
+++ b/openlane/io_place.py
@@ -0,0 +1,528 @@
+# Copyright 2020 Efabless Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+Places the IOs according to an input file. Supports regexes.
+File format:
+#N|#S|#E|#W
+pin1_regex
+pin2_regex
+...
+
+#S|#N|#E|#W
+...
+...
+"""
+import odb
+
+import os
+import re
+import sys
+import click
+import random
+
+
+@click.command()
+@click.option("-l", "--input-lef", required=True, help="Input merged tlef/lef file.")
+@click.option(
+ "-o",
+ "--output-def",
+ default="./output.def",
+ help="Output DEF file with newly placed pins",
+)
+@click.option("-c", "--config", required=False, help="Optional configuration file.")
+@click.option(
+ "-r",
+ "--reverse",
+ default="",
+ type=str,
+ help="Reverse along comma,delimited,cardinals: e.g. N,E",
+)
+@click.option("-L", "--length", default=2, type=float, help="Pin length in microns.")
+@click.option(
+ "-V",
+ "--ver-layer",
+ required=True,
+ help="Name of metal layer to place vertical pins on.",
+)
+@click.option(
+ "-H",
+ "--hor-layer",
+ required=True,
+ help="Name of metal layer to place horizontal pins on.",
+)
+@click.option(
+ "--hor-extension",
+ default=0,
+ type=float,
+ help="Extension for vertical pins in microns.",
+)
+@click.option(
+ "--ver-extension",
+ default=0,
+ type=float,
+ help="Extension for horizontal pins in microns.",
+)
+@click.option(
+ "--ver-width-mult", default=2, type=float, help="Multiplier for vertical pins."
+)
+@click.option(
+ "--hor-width-mult", default=2, type=float, help="Multiplier for horizontal pins."
+)
+@click.option(
+ "--bus-sort/--no-bus-sort",
+ default=False,
+ help="Misnomer: pins are grouped by index instead of bus, i.e. a[0] goes with b[0] instead of a[1].",
+)
+@click.argument("input_def")
+def cli(
+ input_lef,
+ output_def,
+ config,
+ ver_layer,
+ hor_layer,
+ ver_width_mult,
+ hor_width_mult,
+ length,
+ hor_extension,
+ ver_extension,
+ reverse,
+ bus_sort,
+ input_def,
+):
+ """
+ Places the IOs in an input def with an optional config file that supports regexes.
+
+ Config format:
+ #N|#S|#E|#W
+ pin1_regex (low co-ordinates to high co-ordinates; e.g., bottom to top and left to right)
+ pin2_regex
+ ...
+
+ #S|#N|#E|#W
+ """
+
+ def_file_name = input_def
+ lef_file_name = input_lef
+ output_def_file_name = output_def
+ config_file_name = config
+ bus_sort_flag = bus_sort
+
+ #1. Manual Pad Placement - Dinesh A
+ manual_place_flag = False
+
+ h_layer_name = hor_layer
+ v_layer_name = ver_layer
+
+ h_width_mult = float(hor_width_mult)
+ v_width_mult = float(ver_width_mult)
+
+ # Initialize OpenDB
+ db_top = odb.dbDatabase.create()
+ odb.read_lef(db_top, lef_file_name)
+ odb.read_def(db_top, def_file_name)
+ block = db_top.getChip().getBlock()
+
+ micron_in_units = block.getDefUnits()
+
+ LENGTH = int(micron_in_units * length)
+
+ H_EXTENSION = int(micron_in_units * hor_extension)
+ V_EXTENSION = int(micron_in_units * ver_extension)
+
+ if H_EXTENSION < 0:
+ H_EXTENSION = 0
+
+ if V_EXTENSION < 0:
+ V_EXTENSION = 0
+
+ reverse_arr_raw = reverse.split(",")
+ reverse_arr = []
+ for element in reverse_arr_raw:
+ if element.strip() != "":
+ reverse_arr.append(f"#{element}")
+
+ def getGrid(origin, count, step):
+ tracks = []
+ pos = origin
+ for i in range(count):
+ tracks.append(pos)
+ pos += step
+ assert len(tracks) > 0
+ tracks.sort()
+
+ return tracks
+
+ def equallySpacedSeq(m, arr):
+ seq = []
+ n = len(arr)
+ # Bresenham
+ indices = [i * n // m + n // (2 * m) for i in range(m)]
+ for i in indices:
+ seq.append(arr[i])
+ return seq
+
+ # HUMAN SORTING: https://stackoverflow.com/questions/5967500/how-to-correctly-sort-a-string-with-a-number-inside
+ def natural_keys(enum):
+ def atof(text):
+ try:
+ retval = float(text)
+ except ValueError:
+ retval = text
+ return retval
+
+ text = enum[0]
+ text = re.sub(r"(\[|\]|\.|\$)", "", text)
+ """
+ alist.sort(key=natural_keys) sorts in human order
+ http://nedbatchelder.com/blog/200712/human_sorting.html
+ (see toothy's implementation in the comments)
+ float regex comes from https://stackoverflow.com/a/12643073/190597
+ """
+ return [
+ atof(c) for c in re.split(r"[+-]?([0-9]+(?:[.][0-9]*)?|[.][0-9]+)", text)
+ ]
+
+ def bus_keys(enum):
+ text = enum[0]
+ m = re.match(r"^.*\[(\d+)\]$", text)
+ if not m:
+ return -1
+ else:
+ return int(m.group(1))
+
+ #2. Find the Slot matching next nearest slot-DineshA
+ def findSlot(val, arr):
+ for i in arr:
+ if(i > val):
+ return i
+ print("ERROR: Next Valid Position not found :",val)
+ return -1
+
+ # read config
+
+ pin_placement_cfg = {"#N": [], "#E": [], "#S": [], "#W": []}
+ cur_side = None
+ if config_file_name is not None and config_file_name != "":
+ with open(config_file_name, "r") as config_file:
+ for line in config_file:
+ line = line.split()
+ if len(line) == 0:
+ continue
+
+ #3. Dinesh A - Start
+ if(manual_place_flag == False):
+ if len(line) > 1:
+ print("Only one entry allowed per line.")
+ sys.exit(1)
+ token = line[0]
+ else:
+ #During Manual Place we are allowing Four field
+ # <Pad Name> <Offset> <Position> <Multiplier>
+ # Causion: Make sure that you have given absolute name, else it will give issue
+ if len(line) > 4:
+ print("Only Four entry allowed per line.")
+ sys.exit(1)
+ if line[0] not in ["#N", "#E", "#S", "#W", "#NR", "#ER", "#SR", "#WR"]:
+ token = line
+ else:
+ token = line[0]
+
+ if cur_side is not None and token[0] != "#":
+ pin_placement_cfg[cur_side].append(token)
+ elif token not in [
+ "#N",
+ "#E",
+ "#S",
+ "#W",
+ "#NR",
+ "#ER",
+ "#SR",
+ "#WR",
+ "#BUS_SORT",
+ "#MANUAL_PLACE"
+ ]:
+ print(
+ "Valid directives are #N, #E, #S, or #W. Append R for reversing the default order.",
+ "Use #BUS_SORT to group 'bus bits' by index.",
+ "Please make sure you have set a valid side first before listing pins",
+ )
+ sys.exit(1)
+ elif token == "#BUS_SORT":
+ bus_sort_flag = True
+ #4 - Dinesh A
+ elif token == "#MANUAL_PLACE":
+ print("Input token ",token)
+ manual_place_flag = True
+ else:
+ if len(token) == 3:
+ token = token[0:2]
+ reverse_arr.append(token)
+ cur_side = token
+
+ # build a list of pins
+
+ chip_top = db_top.getChip()
+ block_top = chip_top.getBlock()
+ top_design_name = block_top.getName()
+ tech = db_top.getTech()
+
+ H_LAYER = tech.findLayer(h_layer_name)
+ V_LAYER = tech.findLayer(v_layer_name)
+
+ H_WIDTH = int(h_width_mult * H_LAYER.getWidth())
+ V_WIDTH = int(v_width_mult * V_LAYER.getWidth())
+
+ print("Top-level design name:", top_design_name)
+
+ bterms = block_top.getBTerms()
+ bterms_enum = []
+ for bterm in bterms:
+ pin_name = bterm.getName()
+ bterms_enum.append((pin_name, bterm))
+
+ # sort them "humanly"
+ bterms_enum.sort(key=natural_keys)
+ if bus_sort_flag:
+ bterms_enum.sort(key=bus_keys)
+ bterms = [bterm[1] for bterm in bterms_enum]
+
+ pin_placement = {"#N": [], "#E": [], "#S": [], "#W": []}
+ bterm_regex_map = {}
+ #5. Dinesh A
+ if(manual_place_flag == False):
+ for side in pin_placement_cfg:
+ for regex in pin_placement_cfg[side]: # going through them in order
+ regex += "$" # anchor
+ for bterm in bterms:
+ # if a pin name matches multiple regexes, their order will be
+ # arbitrary. More refinement requires more strict regexes (or just
+ # the exact pin name).
+ pin_name = bterm.getName()
+ if re.match(regex, pin_name) is not None:
+ if bterm in bterm_regex_map:
+ print(
+ "Error: Multiple regexes matched",
+ pin_name,
+ ". Those are",
+ bterm_regex_map[bterm],
+ "and",
+ regex,
+ )
+ sys.exit(os.EX_DATAERR)
+ bterm_regex_map[bterm] = regex
+ pin_placement[side].append(bterm) # to maintain the order
+
+ unmatched_bterms = [bterm for bterm in bterms if bterm not in bterm_regex_map]
+
+ if len(unmatched_bterms) > 0:
+ print("Warning: Some pins weren't matched by the config file")
+ print("Those are:", [bterm.getName() for bterm in unmatched_bterms])
+ if True:
+ print("Assigning random sides to the above pins")
+ for bterm in unmatched_bterms:
+ random_side = random.choice(list(pin_placement.keys()))
+ pin_placement[random_side].append(bterm)
+ else:
+ sys.exit(1)
+
+ #6 Dinesh A
+ else:
+ for side in pin_placement_cfg:
+ for regex in pin_placement_cfg[side]: # going through them in order
+ regex = regex[0] # take first value
+ regex += "$" # anchor
+ for bterm in bterms:
+ # if a pin name matches multiple regexes, their order will be
+ # arbitrary. More refinement requires more strict regexes (or just
+ # the exact pin name).
+ pin_name = bterm.getName()
+ if re.match(regex, pin_name) is not None:
+ print("Debug: Serching Pin match",regex)
+ if bterm in bterm_regex_map:
+ #print("Warning: Multiple regexes matched", pin_name)
+ # ". Those are", bterm_regex_map[bterm], "and", regex)
+ sys.exit(1)
+ bterm_regex_map[bterm] = regex
+ pin_placement[side].append(bterm) # to maintain the order
+
+ unmatched_bterms = [bterm for bterm in bterms if bterm not in bterm_regex_map]
+
+ if len(unmatched_bterms) > 0:
+ print("Warning: Some pins weren't matched by the config file")
+ print("Those are:", [bterm.getName() for bterm in unmatched_bterms])
+ sys.exit(1)
+
+
+ assert len(block_top.getBTerms()) == len(
+ pin_placement["#N"]
+ + pin_placement["#E"]
+ + pin_placement["#S"]
+ + pin_placement["#W"]
+ )
+
+ # generate slots
+
+ DIE_AREA = block_top.getDieArea()
+ BLOCK_LL_X = DIE_AREA.xMin()
+ BLOCK_LL_Y = DIE_AREA.yMin()
+ BLOCK_UR_X = DIE_AREA.xMax()
+ BLOCK_UR_Y = DIE_AREA.yMax()
+
+ print("Block boundaries:", BLOCK_LL_X, BLOCK_LL_Y, BLOCK_UR_X, BLOCK_UR_Y)
+
+ origin, count, step = block_top.findTrackGrid(H_LAYER).getGridPatternY(0)
+
+ #7. Save the horizontal origin and step - DineshA
+ h_origin = origin
+ h_step = step
+
+ h_tracks = getGrid(origin, count, step)
+
+ origin, count, step = block_top.findTrackGrid(V_LAYER).getGridPatternX(0)
+
+ #8. Save the horizontal origin and step - DineshA
+ v_origin = origin
+ v_step = step
+
+ v_tracks = getGrid(origin, count, step)
+
+ for rev in reverse_arr:
+ pin_placement[rev].reverse()
+
+ # create the pins
+ #9. DineshA
+ if(manual_place_flag == False):
+ for side in pin_placement:
+ if side in ["#N", "#S"]:
+ slots = equallySpacedSeq(len(pin_placement[side]), v_tracks)
+ else:
+ slots = equallySpacedSeq(len(pin_placement[side]), h_tracks)
+
+ assert len(slots) == len(pin_placement[side])
+
+ for i in range(len(pin_placement[side])):
+ bterm = pin_placement[side][i]
+ slot = slots[i]
+
+ pin_name = bterm.getName()
+ pins = bterm.getBPins()
+ if len(pins) > 0:
+ print("Warning:", pin_name, "already has shapes. Modifying them")
+ assert len(pins) == 1
+ pin_bpin = pins[0]
+ else:
+ pin_bpin = odb.dbBPin_create(bterm)
+
+ pin_bpin.setPlacementStatus("PLACED")
+
+ if side in ["#N", "#S"]:
+ rect = odb.Rect(0, 0, V_WIDTH, LENGTH + V_EXTENSION)
+ if side == "#N":
+ y = BLOCK_UR_Y - LENGTH
+ else:
+ y = BLOCK_LL_Y - V_EXTENSION
+ rect.moveTo(slot - V_WIDTH // 2, y)
+ odb.dbBox_create(pin_bpin, V_LAYER, *rect.ll(), *rect.ur())
+ else:
+ rect = odb.Rect(0, 0, LENGTH + H_EXTENSION, H_WIDTH)
+ if side == "#E":
+ x = BLOCK_UR_X - LENGTH
+ else:
+ x = BLOCK_LL_X - H_EXTENSION
+ rect.moveTo(x, slot - H_WIDTH // 2)
+ odb.dbBox_create(pin_bpin, H_LAYER, *rect.ll(), *rect.ur())
+
+ else:
+ #10.New Logic, Manual Pin Placement - Dinesh A
+ #print("Allowed VTracks",v_tracks)
+ #print("Allowed hTracks",h_tracks)
+
+ for side in pin_placement:
+
+ if(len(pin_placement[side]) != len(pin_placement_cfg[side])):
+ print("ERROR : At Side:", side, " Total Pin Defined ",len(pin_placement_cfg[side]), "More than available:",len(pin_placement[side]))
+
+ #check defined pad are more than avaibale one
+ assert len(pin_placement[side]) == len(pin_placement_cfg[side])
+ start = 0
+
+ start_loc = 0
+ pad_pos = 0
+ slot_pre = 0
+ #Dinesh: Give Step Multipler size *2 for better pad placement
+ multiplier= 2
+ for i in range(len(pin_placement_cfg[side])):
+ #Dinesh: Multiply the offset by 1000 for micro conversion
+ if(len(pin_placement_cfg[side][i]) > 1):
+ start_loc = int(pin_placement_cfg[side][i][1])
+ if(len(pin_placement_cfg[side][i]) > 2):
+ pad_pos = int(pin_placement_cfg[side][i][2])
+ if(len(pin_placement_cfg[side][i]) > 3):
+ multiplier = int(pin_placement_cfg[side][i][3])
+
+ if side in ["#N", "#S"]:
+ slott = start_loc*1000+int(v_origin)+(int(v_step) * pad_pos * multiplier)
+ slot =findSlot(slott,v_tracks)
+ else:
+ slott = start_loc*1000+int(h_origin)+(int(h_step) * pad_pos * multiplier)
+ slot =findSlot(slott,h_tracks)
+
+ pad_pos +=1
+ bterm = pin_placement[side][i]
+
+ pin_name = bterm.getName()
+ pins = bterm.getBPins()
+ if len(pins) > 0:
+ print("Warning:", pin_name, "already has shapes. Modifying them")
+ assert len(pins) == 1
+ pin_bpin = pins[0]
+ else:
+ pin_bpin = odb.dbBPin_create(bterm)
+
+ if(slot < slot_pre):
+ print("ERROR:", "Current Pad:", pin_name, " Slot:" , slot, " is less than Previous One:",slot_pre)
+ sys.exit(1)
+
+ slot_pre = slot
+
+ print("Dinesh: Placing Pad:" ,pin_name, " At Side: ", side, " Slot: ", slot)
+ pin_bpin.setPlacementStatus("PLACED")
+
+ if side in ["#N", "#S"]:
+ rect = odb.Rect(0, 0, V_WIDTH, LENGTH+V_EXTENSION)
+ if side == "#N":
+ y = BLOCK_UR_Y-LENGTH
+ else:
+ y = BLOCK_LL_Y-V_EXTENSION
+ rect.moveTo(slot-V_WIDTH//2, y)
+ odb.dbBox_create(pin_bpin, V_LAYER, *rect.ll(), *rect.ur())
+ else:
+ rect = odb.Rect(0, 0, LENGTH+H_EXTENSION, H_WIDTH)
+ if side == "#E":
+ x = BLOCK_UR_X-LENGTH
+ else:
+ x = BLOCK_LL_X-H_EXTENSION
+ rect.moveTo(x, slot-H_WIDTH//2)
+ odb.dbBox_create(pin_bpin, H_LAYER, *rect.ll(), *rect.ur())
+
+
+ print(
+ f"Writing {output_def_file_name}...",
+ )
+ odb.write_def(block_top, output_def_file_name)
+
+
+if __name__ == "__main__":
+ cli()
diff --git a/openlane/user_project_wrapper/config.tcl b/openlane/user_project_wrapper/config.tcl
index ae4bbce..2895ec4 100644
--- a/openlane/user_project_wrapper/config.tcl
+++ b/openlane/user_project_wrapper/config.tcl
@@ -44,7 +44,6 @@
## Source Verilog Files
set ::env(VERILOG_FILES) "\
- $proj_dir/../../caravel/verilog/rtl/defines.v \
$proj_dir/../../verilog/rtl/user_project_wrapper.v"
## Clock configurations
@@ -58,7 +57,7 @@
set ::env(FP_SIZING) "absolute"
set ::env(MACRO_PLACEMENT_CFG) $proj_dir/macro.cfg
-#set ::env(PDN_CFG) $proj_dir/pdn.tcl
+set ::env(PDN_CFG) $proj_dir/pdn_cfg.tcl
set ::env(SDC_FILE) "$proj_dir/base.sdc"
set ::env(BASE_SDC_FILE) "$proj_dir/base.sdc"
@@ -101,7 +100,7 @@
set ::env(SYNTH_DEFINES) [list SYNTHESIS ]
-set ::env(VERILOG_INCLUDE_DIRS) [glob $proj_dir/../../verilog/rtl/yifive/ycr1c/src/includes ]
+#set ::env(VERILOG_INCLUDE_DIRS) [glob $proj_dir/../../verilog/rtl/yifive/ycr1c/src/includes ]
set ::env(GLB_RT_MAXLAYER) 5
@@ -116,8 +115,8 @@
set ::env(VDD_NETS) "vccd1 vccd2 vdda1 vdda2"
set ::env(GND_NETS) "vssd1 vssd2 vssa1 vssa2"
#
-set ::env(VDD_PIN) "vccd1 vccd2 vdda1 vdda2"
-set ::env(GND_PIN) "vssd1 vssd2 vssa1 vssa2"
+set ::env(VDD_PIN) "vccd1"
+set ::env(GND_PIN) "vssd1"
set ::env(GLB_RT_OBS) " li1 150 2100 833.1 2516.54,\
met1 150 2100 833.1 2516.54,\
@@ -149,7 +148,9 @@
met3 150 200 833.1 616.54,\
met5 0 0 2920 3520"
-set ::env(FP_PDN_MACRO_HOOKS) "\
+set ::env(FP_PDN_POWER_STRAPS) "vccd1 vssd1 1, vccd2 vssd2 0, vdda1 vssa1 0, vdda2 vssa2 0"
+
+set ::env(FP_PDN_MACRO_HOOKS) " \
u_intercon vccd1 vssd1 \
u_pinmux vccd1 vssd1 \
u_qspi_master vccd1 vssd1 \
@@ -163,8 +164,7 @@
u_sram2_2kb vccd1 vssd1 \
u_sram3_2kb vccd1 vssd1 \
u_uart_i2c_usb_spi vccd1 vssd1 \
- u_wb_host vccd1 vssd1 \
- "
+ u_wb_host vccd1 vssd1 "
# The following is because there are no std cells in the example wrapper project.
@@ -190,9 +190,19 @@
set ::env(QUIT_ON_TIMING_VIOLATIONS) "0"
set ::env(QUIT_ON_TR_DRC) "0"
+set ::env(FP_PDN_IRDROP) "1"
+set ::env(FP_PDN_HORIZONTAL_HALO) "10"
+set ::env(FP_PDN_VERTICAL_HALO) "10"
-set ::env(FP_PDN_HPITCH) "90"
-set ::env(FP_PDN_VPITCH) "100"
-set ::env(FP_PDN_HSPACING) "6"
+set ::env(FP_PDN_VOFFSET) "5"
+set ::env(FP_PDN_VPITCH) "80"
+set ::env(FP_PDN_VSPACING) "15.5"
+set ::env(FP_PDN_VWIDTH) "3.1"
+
+set ::env(FP_PDN_HOFFSET) "10"
+set ::env(FP_PDN_HPITCH) "120"
+set ::env(FP_PDN_HSPACING) "10"
+set ::env(FP_PDN_HWIDTH) "3.1"
+
diff --git a/openlane/user_project_wrapper/interactive.mpw4.tcl b/openlane/user_project_wrapper/interactive.mpw4.tcl
new file mode 100644
index 0000000..1c24305
--- /dev/null
+++ b/openlane/user_project_wrapper/interactive.mpw4.tcl
@@ -0,0 +1,394 @@
+#!/usr/bin/tclsh
+# SPDX-FileCopyrightText: 2020 Efabless Corporation
+# Copyright 2020 Efabless Corporation
+# Copyright 2020 Sylvain Munaut
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# SPDX-License-Identifier: Apache-2.0
+
+package require openlane;
+
+
+proc run_placement_step {args} {
+ if { ! [ info exists ::env(PLACEMENT_CURRENT_DEF) ] } {
+ set ::env(PLACEMENT_CURRENT_DEF) $::env(CURRENT_DEF)
+ } else {
+ set ::env(CURRENT_DEF) $::env(PLACEMENT_CURRENT_DEF)
+ }
+
+ run_placement
+}
+
+proc run_placement {args} {
+ puts_info "Running Placement..."
+# |----------------------------------------------------|
+# |---------------- 3. PLACEMENT ------------------|
+# |----------------------------------------------------|
+ set ::env(CURRENT_STAGE) placement
+
+ if { [info exists ::env(PL_TARGET_DENSITY_CELLS)] } {
+ set old_pl_target_density $::env(PL_TARGET_DENSITY)
+ set ::env(PL_TARGET_DENSITY) $::env(PL_TARGET_DENSITY_CELLS)
+ }
+
+ if { $::env(PL_RANDOM_GLB_PLACEMENT) } {
+ # useful for very tiny designs
+ random_global_placement
+ } else {
+ global_placement_or
+ }
+
+ if { [info exists ::env(PL_TARGET_DENSITY_CELLS)] } {
+ set ::env(PL_TARGET_DENSITY) $old_pl_target_density
+ }
+
+ run_resizer_design
+
+ if { [info exists ::env(DONT_BUFFER_PORTS) ]} {
+ remove_buffers
+ }
+ detailed_placement_or
+ scrot_klayout -layout $::env(CURRENT_DEF)
+}
+
+
+proc run_cts_step {args} {
+ if { ! [ info exists ::env(CTS_CURRENT_DEF) ] } {
+ set ::env(CTS_CURRENT_DEF) $::env(CURRENT_DEF)
+ } else {
+ set ::env(CURRENT_DEF) $::env(CTS_CURRENT_DEF)
+ }
+
+ run_cts
+ run_resizer_timing
+}
+
+proc run_routing_step {args} {
+ if { ! [ info exists ::env(ROUTING_CURRENT_DEF) ] } {
+ set ::env(ROUTING_CURRENT_DEF) $::env(CURRENT_DEF)
+ } else {
+ set ::env(CURRENT_DEF) $::env(ROUTING_CURRENT_DEF)
+ }
+ run_routing
+}
+
+proc run_diode_insertion_2_5_step {args} {
+ if { ! [ info exists ::env(DIODE_INSERTION_CURRENT_DEF) ] } {
+ set ::env(DIODE_INSERTION_CURRENT_DEF) $::env(CURRENT_DEF)
+ } else {
+ set ::env(CURRENT_DEF) $::env(DIODE_INSERTION_CURRENT_DEF)
+ }
+ if { ($::env(DIODE_INSERTION_STRATEGY) == 2) || ($::env(DIODE_INSERTION_STRATEGY) == 5) } {
+ run_antenna_check
+ heal_antenna_violators; # modifies the routed DEF
+ }
+
+}
+
+proc run_power_pins_insertion_step {args} {
+ # set_def $::env(tritonRoute_result_file_tag).def
+ if { ! [ info exists ::env(POWER_PINS_INSERTION_CURRENT_DEF) ] } {
+ set ::env(POWER_PINS_INSERTION_CURRENT_DEF) $::env(CURRENT_DEF)
+ } else {
+ set ::env(CURRENT_DEF) $::env(POWER_PINS_INSERTION_CURRENT_DEF)
+ }
+ if { $::env(LVS_INSERT_POWER_PINS) } {
+ write_powered_verilog
+ set_netlist $::env(lvs_result_file_tag).powered.v
+ }
+
+}
+
+
+proc run_lvs_step {{ lvs_enabled 1 }} {
+ if { ! [ info exists ::env(LVS_CURRENT_DEF) ] } {
+ set ::env(LVS_CURRENT_DEF) $::env(CURRENT_DEF)
+ } else {
+ set ::env(CURRENT_DEF) $::env(LVS_CURRENT_DEF)
+ }
+ if { $lvs_enabled } {
+ run_magic_spice_export
+ run_lvs; # requires run_magic_spice_export
+ }
+
+}
+
+proc run_drc_step {{ drc_enabled 1 }} {
+ if { ! [ info exists ::env(DRC_CURRENT_DEF) ] } {
+ set ::env(DRC_CURRENT_DEF) $::env(CURRENT_DEF)
+ } else {
+ set ::env(CURRENT_DEF) $::env(DRC_CURRENT_DEF)
+ }
+ if { $drc_enabled } {
+ run_magic_drc
+ run_klayout_drc
+ }
+}
+
+proc run_antenna_check_step {{ antenna_check_enabled 1 }} {
+ if { ! [ info exists ::env(ANTENNA_CHECK_CURRENT_DEF) ] } {
+ set ::env(ANTENNA_CHECK_CURRENT_DEF) $::env(CURRENT_DEF)
+ } else {
+ set ::env(CURRENT_DEF) $::env(ANTENNA_CHECK_CURRENT_DEF)
+ }
+ if { $antenna_check_enabled } {
+ run_antenna_check
+ }
+}
+
+
+proc gen_pdn {args} {
+ puts_info "Generating PDN..."
+ TIMER::timer_start
+
+ set ::env(SAVE_DEF) [index_file $::env(pdn_tmp_file_tag).def]
+ set ::env(PGA_RPT_FILE) [index_file $::env(pdn_report_file_tag).pga.rpt]
+
+ try_catch $::env(OPENROAD_BIN) -exit $::env(SCRIPTS_DIR)/openroad/pdn.tcl \
+ |& tee $::env(TERMINAL_OUTPUT) [index_file $::env(pdn_log_file_tag).log 0]
+
+
+ TIMER::timer_stop
+ exec echo "[TIMER::get_runtime]" >> [index_file $::env(pdn_log_file_tag)_runtime.txt 0]
+
+ quit_on_unconnected_pdn_nodes
+
+ set_def $::env(SAVE_DEF)
+}
+
+proc run_power_grid_generation {args} {
+
+ if {[info exists ::env(FP_PDN_POWER_STRAPS)]} {
+ set power_domains [split $::env(FP_PDN_POWER_STRAPS) ","]
+ }
+
+ # internal macros power connections
+ if {[info exists ::env(FP_PDN_MACRO_HOOKS)]} {
+ set macro_hooks [dict create]
+ set pdn_hooks [split $::env(FP_PDN_MACRO_HOOKS) ","]
+ foreach pdn_hook $pdn_hooks {
+ set instance_name [lindex $pdn_hook 0]
+ set power_net [lindex $pdn_hook 1]
+ set ground_net [lindex $pdn_hook 2]
+ dict append macro_hooks $instance_name [subst {$power_net $ground_net}]
+ }
+
+ set power_net_indx [lsearch $::env(VDD_NETS) $power_net]
+ set ground_net_indx [lsearch $::env(GND_NETS) $ground_net]
+
+ # make sure that the specified power domains exist.
+ if { $power_net_indx == -1 || $ground_net_indx == -1 || $power_net_indx != $ground_net_indx } {
+ puts_err "Can't find $power_net and $ground_net domain. \
+ Make sure that both exist in $::env(VDD_NETS) and $::env(GND_NETS)."
+ }
+ }
+
+ # generate multiple power grids per pair of (VDD,GND)
+ # offseted by WIDTH + SPACING
+ foreach domain $power_domains {
+ set ::env(VDD_NET) [lindex $domain 0]
+ set ::env(GND_NET) [lindex $domain 1]
+ set ::env(_WITH_STRAPS) [lindex $domain 2]
+
+ puts_info "Connecting Power: $::env(VDD_NET) & $::env(GND_NET) to All internal macros."
+ # internal macros power connections
+ set ::env(FP_PDN_MACROS) ""
+ if { $::env(FP_PDN_ENABLE_MACROS_GRID) == 1 } {
+ # if macros connections to power are explicitly set
+ # default behavoir macro pins will be connected to the first power domain
+ if { [info exists ::env(FP_PDN_MACRO_HOOKS)] } {
+ set ::env(FP_PDN_ENABLE_MACROS_GRID) 0
+ foreach {instance_name hooks} $macro_hooks {
+ set power [lindex $hooks 0]
+ set ground [lindex $hooks 1]
+ if { $power == $::env(VDD_NET) && $ground == $::env(GND_NET) } {
+ set ::env(FP_PDN_ENABLE_MACROS_GRID) 1
+ set ::env(FP_PDN_IRDROP) "1"
+ puts_info "Connecting $instance_name to $power and $ground nets."
+ lappend ::env(FP_PDN_MACROS) $instance_name
+ }
+ }
+ }
+ } else {
+ puts_warn "All internal macros will not be connected to power $::env(VDD_NET) & $::env(GND_NET)."
+ }
+
+ gen_pdn
+
+ set ::env(FP_PDN_ENABLE_RAILS) 0
+ set ::env(FP_PDN_ENABLE_MACROS_GRID) 0
+ set ::env(FP_PDN_IRDROP) "0"
+
+ # allow failure until open_pdks is up to date...
+ catch {set ::env(FP_PDN_VOFFSET) [expr $::env(FP_PDN_VOFFSET)+$::env(FP_PDN_VWIDTH)+$::env(FP_PDN_VSPACING)]}
+ catch {set ::env(FP_PDN_HOFFSET) [expr $::env(FP_PDN_HOFFSET)+$::env(FP_PDN_HWIDTH)+$::env(FP_PDN_HSPACING)]}
+
+ catch {set ::env(FP_PDN_CORE_RING_VOFFSET) \
+ [expr $::env(FP_PDN_CORE_RING_VOFFSET)\
+ +2*($::env(FP_PDN_CORE_RING_VWIDTH)\
+ +max($::env(FP_PDN_CORE_RING_VSPACING), $::env(FP_PDN_CORE_RING_HSPACING)))]}
+ catch {set ::env(FP_PDN_CORE_RING_HOFFSET) [expr $::env(FP_PDN_CORE_RING_HOFFSET)\
+ +2*($::env(FP_PDN_CORE_RING_HWIDTH)+\
+ max($::env(FP_PDN_CORE_RING_VSPACING), $::env(FP_PDN_CORE_RING_HSPACING)))]}
+ puts "FP_PDN_VOFFSET: $::env(FP_PDN_VOFFSET)"
+ puts "FP_PDN_HOFFSET: $::env(FP_PDN_HOFFSET)"
+ puts "FP_PDN_CORE_RING_VOFFSET: $::env(FP_PDN_CORE_RING_VOFFSET)"
+ puts "FP_PDN_CORE_RING_HOFFSET: $::env(FP_PDN_CORE_RING_HOFFSET)"
+
+ }
+ set ::env(FP_PDN_ENABLE_RAILS) 1
+}
+
+
+proc run_floorplan {args} {
+ puts_info "Running Floorplanning..."
+ # |----------------------------------------------------|
+ # |---------------- 2. FLOORPLAN ------------------|
+ # |----------------------------------------------------|
+ #
+ # intial fp
+ init_floorplan
+
+
+ # place io
+ if { [info exists ::env(FP_PIN_ORDER_CFG)] } {
+ place_io_ol
+ } else {
+ if { [info exists ::env(FP_CONTEXT_DEF)] && [info exists ::env(FP_CONTEXT_LEF)] } {
+ place_io
+ global_placement_or
+ place_contextualized_io \
+ -lef $::env(FP_CONTEXT_LEF) \
+ -def $::env(FP_CONTEXT_DEF)
+ } else {
+ place_io
+ }
+ }
+
+ apply_def_template
+
+ if { [info exist ::env(EXTRA_LEFS)] } {
+ if { [info exist ::env(MACRO_PLACEMENT_CFG)] } {
+ file copy -force $::env(MACRO_PLACEMENT_CFG) $::env(TMP_DIR)/macro_placement.cfg
+ manual_macro_placement f
+ } else {
+ global_placement_or
+ basic_macro_placement
+ }
+ }
+
+ # tapcell
+ tap_decap_or
+ scrot_klayout -layout $::env(CURRENT_DEF)
+ # power grid generation
+ run_power_grid_generation
+}
+
+
+proc run_flow {args} {
+ set script_dir [file dirname [file normalize [info script]]]
+
+ set options {
+ {-design required}
+ {-save_path optional}
+ {-no_lvs optional}
+ {-no_drc optional}
+ {-no_antennacheck optional}
+ }
+ set flags {-save}
+ parse_key_args "run_flow" args arg_values $options flags_map $flags -no_consume
+
+ prep {*}$args
+
+ set LVS_ENABLED 1
+ set DRC_ENABLED 0
+ set ANTENNACHECK_ENABLED 1
+
+ set steps [dict create \
+ "synthesis" {run_synthesis "" } \
+ "floorplan" {run_floorplan ""} \
+ "placement" {run_placement_step ""} \
+ "cts" {run_cts_step ""} \
+ "routing" {run_routing_step ""} \
+ "diode_insertion" {run_diode_insertion_2_5_step ""} \
+ "power_pins_insertion" {run_power_pins_insertion_step ""} \
+ "gds_magic" {run_magic ""} \
+ "gds_drc_klayout" {run_klayout ""} \
+ "gds_xor_klayout" {run_klayout_gds_xor ""} \
+ "lvs" "run_lvs_step $LVS_ENABLED" \
+ "drc" "run_drc_step $DRC_ENABLED" \
+ "antenna_check" "run_antenna_check_step $ANTENNACHECK_ENABLED" \
+ "cvc" {run_lef_cvc}
+ ]
+
+ set_if_unset arg_values(-to) "cvc";
+
+ if { [info exists ::env(CURRENT_STEP) ] } {
+ puts "\[INFO\]:Picking up where last execution left off"
+ puts [format "\[INFO\]:Current stage is %s " $::env(CURRENT_STEP)]
+ } else {
+ set ::env(CURRENT_STEP) "synthesis";
+ }
+ set_if_unset arg_values(-from) $::env(CURRENT_STEP);
+ set exe 0;
+ dict for {step_name step_exe} $steps {
+ if { [ string equal $arg_values(-from) $step_name ] } {
+ set exe 1;
+ }
+
+ if { $exe } {
+ # For when it fails
+ set ::env(CURRENT_STEP) $step_name
+ [lindex $step_exe 0] [lindex $step_exe 1] ;
+ }
+
+ if { [ string equal $arg_values(-to) $step_name ] } {
+ set exe 0:
+ break;
+ }
+
+ }
+
+ # for when it resumes
+ set steps_as_list [dict keys $steps]
+ set next_idx [expr [lsearch $steps_as_list $::env(CURRENT_STEP)] + 1]
+ set ::env(CURRENT_STEP) [lindex $steps_as_list $next_idx]
+
+ if { [info exists flags_map(-save) ] } {
+ if { ! [info exists arg_values(-save_path)] } {
+ set arg_values(-save_path) ""
+ }
+ save_views -lef_path $::env(magic_result_file_tag).lef \
+ -def_path $::env(CURRENT_DEF) \
+ -gds_path $::env(magic_result_file_tag).gds \
+ -mag_path $::env(magic_result_file_tag).mag \
+ -maglef_path $::env(magic_result_file_tag).lef.mag \
+ -spice_path $::env(magic_result_file_tag).spice \
+ -spef_path $::env(CURRENT_SPEF) \
+ -verilog_path $::env(CURRENT_NETLIST) \
+ -save_path $arg_values(-save_path) \
+ -tag $::env(RUN_TAG)
+ }
+
+
+ calc_total_runtime
+ save_state
+ generate_final_summary_report
+
+ check_timing_violations
+
+ puts_success "Flow Completed Without Fatal Errors."
+
+}
+
+run_flow {*}$argv
diff --git a/openlane/user_project_wrapper/interactive.tcl b/openlane/user_project_wrapper/interactive.tcl
index 61f45ea..45668cd 100644
--- a/openlane/user_project_wrapper/interactive.tcl
+++ b/openlane/user_project_wrapper/interactive.tcl
@@ -18,7 +18,6 @@
package require openlane;
-
proc run_placement_step {args} {
if { ! [ info exists ::env(PLACEMENT_CURRENT_DEF) ] } {
set ::env(PLACEMENT_CURRENT_DEF) $::env(CURRENT_DEF)
@@ -62,21 +61,6 @@
}
-proc run_power_pins_insertion_step {args} {
- # set_def $::env(tritonRoute_result_file_tag).def
- if { ! [ info exists ::env(POWER_PINS_INSERTION_CURRENT_DEF) ] } {
- set ::env(POWER_PINS_INSERTION_CURRENT_DEF) $::env(CURRENT_DEF)
- } else {
- set ::env(CURRENT_DEF) $::env(POWER_PINS_INSERTION_CURRENT_DEF)
- }
- if { $::env(LVS_INSERT_POWER_PINS) } {
- write_powered_verilog
- set_netlist $::env(lvs_result_file_tag).powered.v
- }
-
-}
-
-
proc run_lvs_step {{ lvs_enabled 1 }} {
if { ! [ info exists ::env(LVS_CURRENT_DEF) ] } {
set ::env(LVS_CURRENT_DEF) $::env(CURRENT_DEF)
@@ -113,100 +97,341 @@
}
}
+proc run_eco_step {args} {
+ if { $::env(ECO_ENABLE) == 1 } {
+ run_eco
+ }
+}
+
+proc save_final_views {args} {
+ set options {
+ {-save_path optional}
+ }
+ set flags {}
+ parse_key_args "save_final_views" args arg_values $options flags_map $flags
+
+ set arg_list [list]
+
+ # If they don't exist, save_views will simply not copy them
+ lappend arg_list -lef_path $::env(finishing_results)/$::env(DESIGN_NAME).lef
+ lappend arg_list -gds_path $::env(finishing_results)/$::env(DESIGN_NAME).gds
+ lappend arg_list -mag_path $::env(finishing_results)/$::env(DESIGN_NAME).mag
+ lappend arg_list -maglef_path $::env(finishing_results)/$::env(DESIGN_NAME).lef.mag
+ lappend arg_list -spice_path $::env(finishing_results)/$::env(DESIGN_NAME).spice
+
+ # Guaranteed to have default values
+ lappend arg_list -def_path $::env(CURRENT_DEF)
+ lappend arg_list -verilog_path $::env(CURRENT_NETLIST)
+
+ # Not guaranteed to have default values
+ if { [info exists ::env(SPEF_TYPICAL)] } {
+ lappend arg_list -spef_path $::env(SPEF_TYPICAL)
+ }
+ if { [info exists ::env(CURRENT_SDF)] } {
+ lappend arg_list -sdf_path $::env(CURRENT_SDF)
+ }
+ if { [info exists ::env(CURRENT_SDC)] } {
+ lappend arg_list -sdc_path $::env(CURRENT_SDC)
+ }
+
+ # Add the path if it exists...
+ if { [info exists arg_values(-save_path) ] } {
+ lappend arg_list -save_path $arg_values(-save_path)
+ }
+
+ # Aaand fire!
+ save_views {*}$arg_list
+
+}
+
+proc run_post_run_hooks {} {
+ if { [file exists $::env(DESIGN_DIR)/hooks/post_run.py]} {
+ puts_info "Running post run hook"
+ set result [exec $::env(OPENROAD_BIN) -python $::env(DESIGN_DIR)/hooks/post_run.py]
+ puts_info "$result"
+ } else {
+ puts_info "hooks/post_run.py not found, skipping"
+ }
+}
+
+
+
+
+proc gen_pdn {args} {
+ puts_info "Generating PDN..."
+ TIMER::timer_start
+
+ set ::env(SAVE_DEF) [index_file $::env(floorplan_tmpfiles).def]
+ set ::env(PGA_RPT_FILE) [index_file $::env(floorplan_tmpfiles).pga.rpt]
+
+ run_openroad_script $::env(SCRIPTS_DIR)/openroad/pdn.tcl \
+ |& -indexed_log [index_file $::env(floorplan_logs)/pdn.log]
+
+
+ TIMER::timer_stop
+ exec echo "[TIMER::get_runtime]" | python3 $::env(SCRIPTS_DIR)/write_runtime.py "pdn generation - openroad"
+
+ quit_on_unconnected_pdn_nodes
+
+ set_def $::env(SAVE_DEF)
+}
+
+proc run_power_grid_generation {args} {
+
+ if {[info exists ::env(FP_PDN_POWER_STRAPS)]} {
+ set power_domains [split $::env(FP_PDN_POWER_STRAPS) ","]
+ }
+
+ # internal macros power connections
+ if {[info exists ::env(FP_PDN_MACRO_HOOKS)]} {
+ set macro_hooks [dict create]
+ set pdn_hooks [split $::env(FP_PDN_MACRO_HOOKS) ","]
+ foreach pdn_hook $pdn_hooks {
+ set instance_name [lindex $pdn_hook 0]
+ set power_net [lindex $pdn_hook 1]
+ set ground_net [lindex $pdn_hook 2]
+ dict append macro_hooks $instance_name [subst {$power_net $ground_net}]
+ }
+
+ set power_net_indx [lsearch $::env(VDD_NETS) $power_net]
+ set ground_net_indx [lsearch $::env(GND_NETS) $ground_net]
+
+ # make sure that the specified power domains exist.
+ if { $power_net_indx == -1 || $ground_net_indx == -1 || $power_net_indx != $ground_net_indx } {
+ puts_err "Can't find $power_net and $ground_net domain. \
+ Make sure that both exist in $::env(VDD_NETS) and $::env(GND_NETS)."
+ }
+ }
+
+ # generate multiple power grids per pair of (VDD,GND)
+ # offseted by WIDTH + SPACING
+ foreach domain $power_domains {
+ set ::env(VDD_NET) [lindex $domain 0]
+ set ::env(GND_NET) [lindex $domain 1]
+ set ::env(_WITH_STRAPS) [lindex $domain 2]
+
+ puts_info "Connecting Power: $::env(VDD_NET) & $::env(GND_NET) to All internal macros."
+ # internal macros power connections
+ set ::env(FP_PDN_MACROS) ""
+ if { $::env(FP_PDN_ENABLE_MACROS_GRID) == 1 } {
+ # if macros connections to power are explicitly set
+ # default behavoir macro pins will be connected to the first power domain
+ if { [info exists ::env(FP_PDN_MACRO_HOOKS)] } {
+ set ::env(FP_PDN_ENABLE_MACROS_GRID) 0
+ foreach {instance_name hooks} $macro_hooks {
+ set power [lindex $hooks 0]
+ set ground [lindex $hooks 1]
+ if { $power == $::env(VDD_NET) && $ground == $::env(GND_NET) } {
+ set ::env(FP_PDN_ENABLE_MACROS_GRID) 1
+ set ::env(FP_PDN_IRDROP) "1"
+ puts_info "Connecting $instance_name to $power and $ground nets."
+ lappend ::env(FP_PDN_MACROS) $instance_name
+ }
+ }
+ }
+ } else {
+ puts_warn "All internal macros will not be connected to power $::env(VDD_NET) & $::env(GND_NET)."
+ }
+
+ gen_pdn
+
+ set ::env(FP_PDN_ENABLE_RAILS) 0
+ set ::env(FP_PDN_ENABLE_MACROS_GRID) 0
+ set ::env(FP_PDN_IRDROP) "0"
+
+ # allow failure until open_pdks is up to date...
+ catch {set ::env(FP_PDN_VOFFSET) [expr $::env(FP_PDN_VOFFSET)+$::env(FP_PDN_VWIDTH)+$::env(FP_PDN_VSPACING)]}
+ catch {set ::env(FP_PDN_HOFFSET) [expr $::env(FP_PDN_HOFFSET)+$::env(FP_PDN_HWIDTH)+$::env(FP_PDN_HSPACING)]}
+
+ catch {set ::env(FP_PDN_CORE_RING_VOFFSET) \
+ [expr $::env(FP_PDN_CORE_RING_VOFFSET)\
+ +2*($::env(FP_PDN_CORE_RING_VWIDTH)\
+ +max($::env(FP_PDN_CORE_RING_VSPACING), $::env(FP_PDN_CORE_RING_HSPACING)))]}
+ catch {set ::env(FP_PDN_CORE_RING_HOFFSET) [expr $::env(FP_PDN_CORE_RING_HOFFSET)\
+ +2*($::env(FP_PDN_CORE_RING_HWIDTH)+\
+ max($::env(FP_PDN_CORE_RING_VSPACING), $::env(FP_PDN_CORE_RING_HSPACING)))]}
+ puts "FP_PDN_VOFFSET: $::env(FP_PDN_VOFFSET)"
+ puts "FP_PDN_HOFFSET: $::env(FP_PDN_HOFFSET)"
+ puts "FP_PDN_CORE_RING_VOFFSET: $::env(FP_PDN_CORE_RING_VOFFSET)"
+ puts "FP_PDN_CORE_RING_HOFFSET: $::env(FP_PDN_CORE_RING_HOFFSET)"
+
+ }
+ set ::env(FP_PDN_ENABLE_RAILS) 1
+}
+
+
+proc run_floorplan {args} {
+ puts_info "Running Floorplanning..."
+ # |----------------------------------------------------|
+ # |---------------- 2. FLOORPLAN ------------------|
+ # |----------------------------------------------------|
+ #
+ # intial fp
+ init_floorplan
+
+ # check for deprecated io variables
+ if { [info exists ::env(FP_IO_HMETAL)]} {
+ set ::env(FP_IO_HLAYER) [lindex $::env(TECH_METAL_LAYERS) [expr {$::env(FP_IO_HMETAL) - 1}]]
+ puts_warn "You're using FP_IO_HMETAL in your configuration, which is a deprecated variable that will be removed in the future."
+ puts_warn "We recommend you update your configuration as follows:"
+ puts_warn "\tset ::env(FP_IO_HLAYER) {$::env(FP_IO_HLAYER)}"
+ }
+
+ if { [info exists ::env(FP_IO_VMETAL)]} {
+ set ::env(FP_IO_VLAYER) [lindex $::env(TECH_METAL_LAYERS) [expr {$::env(FP_IO_VMETAL) - 1}]]
+ puts_warn "You're using FP_IO_VMETAL in your configuration, which is a deprecated variable that will be removed in the future."
+ puts_warn "We recommend you update your configuration as follows:"
+ puts_warn "\tset ::env(FP_IO_VLAYER) {$::env(FP_IO_VLAYER)}"
+ }
+
+
+ # place io
+ if { [info exists ::env(FP_PIN_ORDER_CFG)] } {
+ place_io_ol
+ } else {
+ if { [info exists ::env(FP_CONTEXT_DEF)] && [info exists ::env(FP_CONTEXT_LEF)] } {
+ place_io
+ global_placement_or
+ place_contextualized_io \
+ -lef $::env(FP_CONTEXT_LEF) \
+ -def $::env(FP_CONTEXT_DEF)
+ } else {
+ place_io
+ }
+ }
+
+ apply_def_template
+
+ if { [info exist ::env(EXTRA_LEFS)] } {
+ if { [info exist ::env(MACRO_PLACEMENT_CFG)] } {
+ file copy -force $::env(MACRO_PLACEMENT_CFG) $::env(placement_tmpfiles)/macro_placement.cfg
+ manual_macro_placement f
+ } else {
+ global_placement_or
+ basic_macro_placement
+ }
+ }
+
+ tap_decap_or
+
+ scrot_klayout -layout $::env(CURRENT_DEF) $::env(floorplan_logs)/screenshot.log
+
+ run_power_grid_generation
+}
proc run_flow {args} {
- set script_dir [file dirname [file normalize [info script]]]
-
- set options {
+ set options {
{-design required}
+ {-from optional}
+ {-to optional}
{-save_path optional}
- {-no_lvs optional}
- {-no_drc optional}
- {-no_antennacheck optional}
+ {-override_env optional}
}
- set flags {-save}
- parse_key_args "run_flow" args arg_values $options flags_map $flags -no_consume
-
+ set flags {-save -run_hooks -no_lvs -no_drc -no_antennacheck }
+ parse_key_args "run_non_interactive_mode" args arg_values $options flags_map $flags -no_consume
prep {*}$args
+ # signal trap SIGINT save_state;
- set LVS_ENABLED 1
- set DRC_ENABLED 0
- set ANTENNACHECK_ENABLED 1
+ if { [info exists arg_values(-override_env)] } {
+ set env_overrides [split $arg_values(-override_env) ',']
+ foreach override $env_overrides {
+ set kva [split $override '=']
+ set key [lindex $kva 0]
+ set value [lindex $kva 1]
+ set ::env(${key}) $value
+ }
+ }
- set steps [dict create "synthesis" {run_synthesis "" } \
- "floorplan" {run_floorplan ""} \
- "placement" {run_placement_step ""} \
- "cts" {run_cts_step ""} \
- "routing" {run_routing_step ""} \
- "diode_insertion" {run_diode_insertion_2_5_step ""} \
- "power_pins_insertion" {run_power_pins_insertion_step ""} \
- "gds_magic" {run_magic ""} \
- "gds_drc_klayout" {run_klayout ""} \
- "gds_xor_klayout" {run_klayout_gds_xor ""} \
- "lvs" "run_lvs_step $LVS_ENABLED" \
- "drc" "run_drc_step $DRC_ENABLED" \
- "antenna_check" "run_antenna_check_step $ANTENNACHECK_ENABLED" \
- "cvc" {run_lef_cvc}
- ]
+ set LVS_ENABLED 1
+ set DRC_ENABLED 0
+ set ANTENNACHECK_ENABLED 1
- set_if_unset arg_values(-to) "cvc";
+ set steps [dict create \
+ "synthesis" {run_synthesis "" } \
+ "floorplan" {run_floorplan ""} \
+ "placement" {run_placement_step ""} \
+ "cts" {run_cts_step ""} \
+ "routing" {run_routing_step ""}\
+ "eco" {run_eco_step ""} \
+ "diode_insertion" {run_diode_insertion_2_5_step ""} \
+ "gds_magic" {run_magic ""} \
+ "gds_drc_klayout" {run_klayout ""} \
+ "gds_xor_klayout" {run_klayout_gds_xor ""} \
+ "lvs" "run_lvs_step $LVS_ENABLED" \
+ "drc" "run_drc_step $DRC_ENABLED" \
+ "antenna_check" "run_antenna_check_step $ANTENNACHECK_ENABLED" \
+ "cvc" {run_lef_cvc}
+ ]
- if { [info exists ::env(CURRENT_STEP) ] } {
- puts "\[INFO\]:Picking up where last execution left off"
- puts [format "\[INFO\]:Current stage is %s " $::env(CURRENT_STEP)]
- } else {
- set ::env(CURRENT_STEP) "synthesis";
- }
- set_if_unset arg_values(-from) $::env(CURRENT_STEP);
- set exe 0;
- dict for {step_name step_exe} $steps {
- if { [ string equal $arg_values(-from) $step_name ] } {
- set exe 1;
- }
+ set_if_unset arg_values(-to) "cvc";
- if { $exe } {
- # For when it fails
- set ::env(CURRENT_STEP) $step_name
- [lindex $step_exe 0] [lindex $step_exe 1] ;
- }
+ if { [info exists ::env(CURRENT_STEP) ] } {
+ puts "\[INFO\]:Picking up where last execution left off"
+ puts [format "\[INFO\]:Current stage is %s " $::env(CURRENT_STEP)]
+ } else {
+ set ::env(CURRENT_STEP) "synthesis";
+ }
- if { [ string equal $arg_values(-to) $step_name ] } {
- set exe 0:
- break;
- }
+ set_if_unset arg_values(-from) $::env(CURRENT_STEP);
+ set exe 0;
+ dict for {step_name step_exe} $steps {
+ if { [ string equal $arg_values(-from) $step_name ] } {
+ set exe 1;
+ }
- }
+ if { $exe } {
+ # For when it fails
+ set ::env(CURRENT_STEP) $step_name
+ [lindex $step_exe 0] [lindex $step_exe 1] ;
+ }
- # for when it resumes
- set steps_as_list [dict keys $steps]
- set next_idx [expr [lsearch $steps_as_list $::env(CURRENT_STEP)] + 1]
- set ::env(CURRENT_STEP) [lindex $steps_as_list $next_idx]
+ if { [ string equal $arg_values(-to) $step_name ] } {
+ set exe 0:
+ break;
+ }
+ }
+
+ # for when it resumes
+ set steps_as_list [dict keys $steps]
+ set next_idx [expr [lsearch $steps_as_list $::env(CURRENT_STEP)] + 1]
+ set ::env(CURRENT_STEP) [lindex $steps_as_list $next_idx]
+
+ # Saves to <RUN_DIR>/results/final
+ if { $::env(SAVE_FINAL_VIEWS) == "1" } {
+ save_final_views
+ }
+
+ # Saves to design directory or custom
if { [info exists flags_map(-save) ] } {
if { ! [info exists arg_values(-save_path)] } {
- set arg_values(-save_path) ""
+ set arg_values(-save_path) $::env(DESIGN_DIR)
}
- save_views -lef_path $::env(magic_result_file_tag).lef \
- -def_path $::env(CURRENT_DEF) \
- -gds_path $::env(magic_result_file_tag).gds \
- -mag_path $::env(magic_result_file_tag).mag \
- -maglef_path $::env(magic_result_file_tag).lef.mag \
- -spice_path $::env(magic_result_file_tag).spice \
- -spef_path $::env(CURRENT_SPEF) \
- -verilog_path $::env(CURRENT_NETLIST) \
- -save_path $arg_values(-save_path) \
+ save_final_views\
+ -save_path $arg_values(-save_path)\
-tag $::env(RUN_TAG)
}
-
-
calc_total_runtime
save_state
generate_final_summary_report
check_timing_violations
+
+ if { [info exists arg_values(-save_path)]\
+ && $arg_values(-save_path) != "" } {
+ set ::env(HOOK_OUTPUT_PATH) "[file normalize $arg_values(-save_path)]"
+ } else {
+ set ::env(HOOK_OUTPUT_PATH) $::env(RESULTS_DIR)/final
+ }
+
+ if {[info exists flags_map(-run_hooks)]} {
+ run_post_run_hooks
+ }
+
+ puts_success "Flow complete."
- puts_success "Flow Completed Without Fatal Errors."
+ show_warnings "Note that the following warnings have been generated:"
}
diff --git a/openlane/user_project_wrapper/macro.cfg b/openlane/user_project_wrapper/macro.cfg
index ac93699..729781f 100644
--- a/openlane/user_project_wrapper/macro.cfg
+++ b/openlane/user_project_wrapper/macro.cfg
@@ -15,4 +15,4 @@
u_intercon 1850 700 N
-u_wb_host 1450 075 N
+u_wb_host 1450 100 N
diff --git a/openlane/user_project_wrapper/pdn_cfg.tcl b/openlane/user_project_wrapper/pdn_cfg.tcl
index c23f68f..7813f95 100644
--- a/openlane/user_project_wrapper/pdn_cfg.tcl
+++ b/openlane/user_project_wrapper/pdn_cfg.tcl
@@ -13,10 +13,12 @@
if { [info exists ::env(FP_PDN_ENABLE_GLOBAL_CONNECTIONS)] } {
if { $::env(FP_PDN_ENABLE_GLOBAL_CONNECTIONS) == 1 } {
- add_global_connection -net $::env(VDD_NET) -inst_pattern .* -pin_pattern {VPWR} -power
- add_global_connection -net $::env(VDD_NET) -inst_pattern .* -pin_pattern {VPB} -power
- add_global_connection -net $::env(GND_NET) -inst_pattern .* -pin_pattern {VGND} -ground
- add_global_connection -net $::env(GND_NET) -inst_pattern .* -pin_pattern {VNB} -ground
+ foreach power_pin $::env(STD_CELL_POWER_PINS) {
+ add_global_connection -net $::env(VDD_NET) -inst_pattern .* -pin_pattern $power_pin -power
+ }
+ foreach ground_pin $::env(STD_CELL_GROUND_PINS) {
+ add_global_connection -net $::env(GND_NET) -inst_pattern .* -pin_pattern $ground_pin -ground
+ }
}
}
@@ -27,8 +29,10 @@
if { $::env(DESIGN_IS_CORE) == 1 } {
# Used if the design is the core of the chip
define_pdn_grid -name stdcell_grid -starts_with POWER -voltage_domain CORE -pins [subst {$::env(FP_PDN_LOWER_LAYER) $::env(FP_PDN_UPPER_LAYER)}]
- add_pdn_stripe -grid stdcell_grid -layer $::env(FP_PDN_LOWER_LAYER) -width $::env(FP_PDN_VWIDTH) -pitch $::env(FP_PDN_VPITCH) -offset $::env(FP_PDN_VOFFSET) -starts_with POWER
- add_pdn_stripe -grid stdcell_grid -layer $::env(FP_PDN_UPPER_LAYER) -width $::env(FP_PDN_HWIDTH) -pitch $::env(FP_PDN_HPITCH) -offset $::env(FP_PDN_HOFFSET) -starts_with POWER
+ if { $::env(_WITH_STRAPS) } {
+ add_pdn_stripe -grid stdcell_grid -layer $::env(FP_PDN_LOWER_LAYER) -width $::env(FP_PDN_VWIDTH) -pitch $::env(FP_PDN_VPITCH) -offset $::env(FP_PDN_VOFFSET) -starts_with POWER
+ add_pdn_stripe -grid stdcell_grid -layer $::env(FP_PDN_UPPER_LAYER) -width $::env(FP_PDN_HWIDTH) -pitch $::env(FP_PDN_HPITCH) -offset $::env(FP_PDN_HOFFSET) -starts_with POWER
+ }
add_pdn_connect -grid stdcell_grid -layers [subst {$::env(FP_PDN_LOWER_LAYER) $::env(FP_PDN_UPPER_LAYER)}]
} else {
# Used if the design is a macro in the core
@@ -45,10 +49,10 @@
# Adds the core ring if enabled.
if { $::env(FP_PDN_CORE_RING) == 1 } {
- add_pdn_ring -grid stdcell_grid -layer [subst {$::env(FP_PDN_LOWER_LAYER) $::env(FP_PDN_UPPER_LAYER)}] \
- -widths [subst {$::env(FP_PDN_CORE_RING_VWIDTH) $::env(FP_PDN_CORE_RING_HWIDTH)}] \
- -spacings [subst {$::env(FP_PDN_CORE_RING_VSPACING) $::env(FP_PDN_CORE_RING_HSPACING)}] \
- -core_offset [subst {$::env(FP_PDN_CORE_RING_VOFFSET) $::env(FP_PDN_CORE_RING_HOFFSET)}]
+ add_pdn_ring -grid stdcell_grid -layer [subst {$::env(FP_PDN_LOWER_LAYER) $::env(FP_PDN_UPPER_LAYER)}] \
+ -widths [subst {$::env(FP_PDN_CORE_RING_VWIDTH) $::env(FP_PDN_CORE_RING_HWIDTH)}] \
+ -spacings [subst {$::env(FP_PDN_CORE_RING_VSPACING) $::env(FP_PDN_CORE_RING_HSPACING)}] \
+ -core_offset [subst {$::env(FP_PDN_CORE_RING_VOFFSET) $::env(FP_PDN_CORE_RING_HOFFSET)}]
}
# A general macro that follows the premise of the set heirarchy. You may want to modify this or add other macro configs
@@ -58,7 +62,7 @@
orient {R0 R180 MX MY R90 R270 MXR90 MYR90}
power_pins $::env(VDD_NET)
ground_pins $::env(GND_NET)
- blockages "li1 met1 met2 met3 met4"
+ blockages $::env(MACRO_BLOCKAGES_LAYER)
straps {
}
connect {{$::env(FP_PDN_LOWER_LAYER)_PIN_ver $::env(FP_PDN_UPPER_LAYER)}}
@@ -70,18 +74,18 @@
foreach macro_instance $::env(FP_PDN_MACROS) {
set macro_instance_grid [subst $macro]
dict append $macro_instance_grid instance $macro_instance
- set ::halo [list $::env(FP_HORIZONTAL_HALO) $::env(FP_VERTICAL_HALO)]
+ set ::halo [list $::env(FP_PDN_HORIZONTAL_HALO) $::env(FP_PDN_VERTICAL_HALO)]
pdngen::specify_grid macro [subst $macro_instance_grid]
}
} else {
- set ::halo [list $::env(FP_HORIZONTAL_HALO) $::env(FP_VERTICAL_HALO)]
+ set ::halo [list $::env(FP_PDN_HORIZONTAL_HALO) $::env(FP_PDN_VERTICAL_HALO)]
pdngen::specify_grid macro [subst $macro]
}
# CAN NOT ENABLE THE TCL COMMAND BECAUSE THERE IS NO ARGUMENT FOR SPECIFYING THE POWER AND GROUND PIN NAMES ON THE MACRO
- # define_pdn_grid -macro -orient {R0 R180 MX MY R90 R270 MXR90 MYR90} -grid_over_pg_pins -starts_with POWER -pin_direction vertical -halo [subst {$::env(FP_HORIZONTAL_HALO) $::env(FP_VERTICAL_HALO)}]
+ # define_pdn_grid -macro -orient {R0 R180 MX MY R90 R270 MXR90 MYR90} -grid_over_pg_pins -starts_with POWER -pin_direction vertical -halo [subst {$::env(FP_PDN_HORIZONTAL_HALO) $::env(FP_PDN_VERTICAL_HALO)}]
# add_pdn_connect -layers [subst {$::env(FP_PDN_LOWER_LAYER) $::env(FP_PDN_UPPER_LAYER)}]
} else {
- define_pdn_grid -macro -orient {R0 R180 MX MY R90 R270 MXR90 MYR90} -grid_over_pg_pins -starts_with POWER -halo [subst {$::env(FP_HORIZONTAL_HALO) $::env(FP_VERTICAL_HALO)}]
+ define_pdn_grid -macro -orient {R0 R180 MX MY R90 R270 MXR90 MYR90} -grid_over_pg_pins -starts_with POWER -halo [subst {$::env(FP_PDN_HORIZONTAL_HALO) $::env(FP_PDN_VERTICAL_HALO)}]
}
# POWER or GROUND #Std. cell rails starting with power or ground rails at the bottom of the core area
diff --git a/openlane/wb_host/config.tcl b/openlane/wb_host/config.tcl
index cb28644..fcd1da4 100755
--- a/openlane/wb_host/config.tcl
+++ b/openlane/wb_host/config.tcl
@@ -75,7 +75,7 @@
set ::env(FP_PIN_ORDER_CFG) $::env(DESIGN_DIR)/pin_order.cfg
set ::env(FP_SIZING) absolute
-set ::env(DIE_AREA) "0 0 800 250"
+set ::env(DIE_AREA) "0 0 800 200"
# If you're going to use multiple power domains, then keep this disabled.
@@ -85,7 +85,7 @@
set ::env(PL_TIME_DRIVEN) 1
-set ::env(PL_TARGET_DENSITY) "0.33"
+set ::env(PL_TARGET_DENSITY) "0.35"
diff --git a/openlane/wb_host/pin_order.cfg b/openlane/wb_host/pin_order.cfg
index 4fac1a3..a29f24b 100644
--- a/openlane/wb_host/pin_order.cfg
+++ b/openlane/wb_host/pin_order.cfg
@@ -167,7 +167,7 @@
#E
-uartm_rxd 200 0 2
+uartm_rxd 100 0 2
uartm_txd
diff --git a/signoff/mbist_wrapper/final_summary_report.csv b/signoff/mbist_wrapper/final_summary_report.csv
index b34cb4b..b6c97f9 100644
--- a/signoff/mbist_wrapper/final_summary_report.csv
+++ b/signoff/mbist_wrapper/final_summary_report.csv
@@ -1,2 +1,2 @@
,design,design_name,config,flow_status,total_runtime,routed_runtime,(Cell/mm^2)/Core_Util,DIEAREA_mm^2,CellPer_mm^2,OpenDP_Util,Peak_Memory_Usage_MB,cell_count,tritonRoute_violations,Short_violations,MetSpc_violations,OffGrid_violations,MinHole_violations,Other_violations,Magic_violations,antenna_violations,lvs_total_errors,cvc_total_errors,klayout_violations,wire_length,vias,wns,pl_wns,optimized_wns,fastroute_wns,spef_wns,tns,pl_tns,optimized_tns,fastroute_tns,spef_tns,HPWL,routing_layer1_pct,routing_layer2_pct,routing_layer3_pct,routing_layer4_pct,routing_layer5_pct,routing_layer6_pct,wires_count,wire_bits,public_wires_count,public_wire_bits,memories_count,memory_bits,processes_count,cells_pre_abc,AND,DFF,NAND,NOR,OR,XOR,XNOR,MUX,inputs,outputs,level,EndCaps,TapCells,Diodes,Total_Physical_Cells,suggested_clock_frequency,suggested_clock_period,CLOCK_PERIOD,SYNTH_STRATEGY,SYNTH_MAX_FANOUT,FP_CORE_UTIL,FP_ASPECT_RATIO,FP_PDN_VPITCH,FP_PDN_HPITCH,PL_TARGET_DENSITY,GLB_RT_ADJUSTMENT,STD_CELL_LIBRARY,CELL_PAD,DIODE_INSERTION_STRATEGY
-0,/project/openlane/mbist_wrapper,mbist_wrapper,mbist_wrapper,flow_completed,0h12m21s,-1,22596.825396825396,0.315,11298.412698412698,14.27,692.75,3559,0,0,0,0,0,0,-1,14,0,0,-1,493339,48549,-0.67,-6.16,-1,-1.48,-1,-0.67,-2838.32,-1,-1.64,-1,413236407.0,0.23,48.61,12.47,22.58,0.0,-1,2596,7282,753,5382,0,0,0,2664,0,0,0,0,0,0,0,4,1049,798,17,138,4082,0,4220,90.9090909090909,11,10,AREA 0,4,50,1,140,140,0.3,0.0,sky130_fd_sc_hd,4,4
+0,/project/openlane/mbist_wrapper,mbist_wrapper,mbist_wrapper,flow_completed,0h14m13s,-1,26073.260073260073,0.273,13036.630036630037,16.48,695.21,3559,0,0,0,0,0,0,-1,16,0,0,-1,445806,47865,-0.67,-5.91,-1,-1.78,-1,-0.67,-2737.45,-1,-1.78,-1,357342288.0,5.91,47.85,15.03,24.02,0.01,-1,2596,7282,753,5382,0,0,0,2664,0,0,0,0,0,0,0,4,1049,798,17,138,3514,0,3652,90.9090909090909,11,10,AREA 0,4,50,1,140,140,0.35,0.0,sky130_fd_sc_hd,4,4
diff --git a/signoff/user_project_wrapper/PDK_SOURCES b/signoff/user_project_wrapper/PDK_SOURCES
index ca3684a..22e7dc1 100644
--- a/signoff/user_project_wrapper/PDK_SOURCES
+++ b/signoff/user_project_wrapper/PDK_SOURCES
@@ -1,6 +1,3 @@
--ne openlane
-8d686c081c2c9aefa16dbbd8ccf5bc8f4dcabc4b
--ne skywater-pdk
-c094b6e83a4f9298e47f696ec5a7fd53535ec5eb
--ne open_pdks
-14db32aa8ba330e88632ff3ad2ff52f4f4dae1ad
+openlane 70923d7fbd8998c8da87d905cf9e69bffc13709f
+skywater-pdk c094b6e83a4f9298e47f696ec5a7fd53535ec5eb
+open_pdks 476f7428f7f686de51a5164c702629a9b9f2da46
diff --git a/signoff/user_project_wrapper/final_summary_report.csv b/signoff/user_project_wrapper/final_summary_report.csv
index ffcbae2..ad0b712 100644
--- a/signoff/user_project_wrapper/final_summary_report.csv
+++ b/signoff/user_project_wrapper/final_summary_report.csv
@@ -1,2 +1,2 @@
,design,design_name,config,flow_status,total_runtime,routed_runtime,(Cell/mm^2)/Core_Util,DIEAREA_mm^2,CellPer_mm^2,OpenDP_Util,Peak_Memory_Usage_MB,cell_count,tritonRoute_violations,Short_violations,MetSpc_violations,OffGrid_violations,MinHole_violations,Other_violations,Magic_violations,antenna_violations,lvs_total_errors,cvc_total_errors,klayout_violations,wire_length,vias,wns,pl_wns,optimized_wns,fastroute_wns,spef_wns,tns,pl_tns,optimized_tns,fastroute_tns,spef_tns,HPWL,routing_layer1_pct,routing_layer2_pct,routing_layer3_pct,routing_layer4_pct,routing_layer5_pct,routing_layer6_pct,wires_count,wire_bits,public_wires_count,public_wire_bits,memories_count,memory_bits,processes_count,cells_pre_abc,AND,DFF,NAND,NOR,OR,XOR,XNOR,MUX,inputs,outputs,level,EndCaps,TapCells,Diodes,Total_Physical_Cells,suggested_clock_frequency,suggested_clock_period,CLOCK_PERIOD,SYNTH_STRATEGY,SYNTH_MAX_FANOUT,FP_CORE_UTIL,FP_ASPECT_RATIO,FP_PDN_VPITCH,FP_PDN_HPITCH,PL_TARGET_DENSITY,GLB_RT_ADJUSTMENT,STD_CELL_LIBRARY,CELL_PAD,DIODE_INSERTION_STRATEGY
-0,/project/openlane/user_project_wrapper,user_project_wrapper,user_project_wrapper,flow_completed,0h50m22s,-1,2.724159402241594,10.2784,1.362079701120797,-1,536.41,14,0,0,0,0,0,0,-1,0,0,-1,-1,1382277,9457,0.0,-1,-1,0.0,-1,0.0,-1,-1,0.0,-1,-1,64380.99,4.47,4.45,0.78,0.67,-1,313,2877,313,2877,0,0,0,14,0,0,0,0,0,0,0,0,-1,-1,-1,0,0,0,0,90.9090909090909,11,10,AREA 0,5,50,1,100,90,0.55,0.0,sky130_fd_sc_hd,4,0
+0,/project/openlane/user_project_wrapper,user_project_wrapper,user_project_wrapper,flow completed,0h47m6s0ms,0h3m47s0ms,-2.0,-1,-1,-1,551.22,14,0,0,0,0,0,0,-1,0,0,-1,-1,1417832,9034,0.0,-1,-1,0.0,0.0,0.0,-1,-1,0.0,0.0,-1,0.0,6.57,6.6,1.03,1.28,-1,313,2877,313,2877,0,0,0,14,0,0,0,0,0,0,0,0,-1,-1,-1,0,0,0,0,100.0,10.0,10,AREA 0,5,50,1,80,120,0.55,0.3,sky130_fd_sc_hd,4,0
diff --git a/signoff/wb_host/final_summary_report.csv b/signoff/wb_host/final_summary_report.csv
index 8afeccf..7be1787 100644
--- a/signoff/wb_host/final_summary_report.csv
+++ b/signoff/wb_host/final_summary_report.csv
@@ -1,2 +1,2 @@
,design,design_name,config,flow_status,total_runtime,routed_runtime,(Cell/mm^2)/Core_Util,DIEAREA_mm^2,CellPer_mm^2,OpenDP_Util,Peak_Memory_Usage_MB,cell_count,tritonRoute_violations,Short_violations,MetSpc_violations,OffGrid_violations,MinHole_violations,Other_violations,Magic_violations,antenna_violations,lvs_total_errors,cvc_total_errors,klayout_violations,wire_length,vias,wns,pl_wns,optimized_wns,fastroute_wns,spef_wns,tns,pl_tns,optimized_tns,fastroute_tns,spef_tns,HPWL,routing_layer1_pct,routing_layer2_pct,routing_layer3_pct,routing_layer4_pct,routing_layer5_pct,routing_layer6_pct,wires_count,wire_bits,public_wires_count,public_wire_bits,memories_count,memory_bits,processes_count,cells_pre_abc,AND,DFF,NAND,NOR,OR,XOR,XNOR,MUX,inputs,outputs,level,EndCaps,TapCells,Diodes,Total_Physical_Cells,suggested_clock_frequency,suggested_clock_period,CLOCK_PERIOD,SYNTH_STRATEGY,SYNTH_MAX_FANOUT,FP_CORE_UTIL,FP_ASPECT_RATIO,FP_PDN_VPITCH,FP_PDN_HPITCH,PL_TARGET_DENSITY,GLB_RT_ADJUSTMENT,STD_CELL_LIBRARY,CELL_PAD,DIODE_INSERTION_STRATEGY
-0,/project/openlane/wb_host,wb_host,wb_host,flow_completed,0h9m5s,-1,41570.0,0.2,20785.0,27.16,665.55,4157,0,0,0,0,0,0,0,7,0,0,-1,342762,47421,0.0,-0.39,-1,-0.15,-1,0.0,-47.54,-1,-0.52,-1,279734014.0,2.28,55.55,17.8,16.11,0.01,-1,3490,6163,1024,3553,0,0,0,3793,0,0,0,0,0,0,0,4,1233,1205,17,166,2592,0,2758,90.9090909090909,11,10,AREA 0,4,50,1,100,100,0.33,0.0,sky130_fd_sc_hd,4,4
+0,/project/openlane/wb_host,wb_host,wb_host,flow_completed,0h10m23s,-1,51962.5,0.16,25981.25,34.69,645.3,4157,0,0,0,0,0,0,0,5,0,0,-1,329531,47752,0.0,-0.23,-1,0.0,-1,0.0,-26.36,-1,0.0,-1,265017619.0,4.63,61.49,19.26,29.21,0.09,-1,3490,6163,1024,3553,0,0,0,3793,0,0,0,0,0,0,0,4,1233,1205,17,130,2043,0,2173,90.9090909090909,11,10,AREA 0,4,50,1,100,100,0.35,0.0,sky130_fd_sc_hd,4,4
diff --git a/signoff/yifive/final_summary_report.csv b/signoff/yifive/final_summary_report.csv
index cedb7e9..09acd87 100644
--- a/signoff/yifive/final_summary_report.csv
+++ b/signoff/yifive/final_summary_report.csv
@@ -1,2 +1,2 @@
,design,design_name,config,flow_status,total_runtime,routed_runtime,(Cell/mm^2)/Core_Util,DIEAREA_mm^2,CellPer_mm^2,OpenDP_Util,Peak_Memory_Usage_MB,cell_count,tritonRoute_violations,Short_violations,MetSpc_violations,OffGrid_violations,MinHole_violations,Other_violations,Magic_violations,antenna_violations,lvs_total_errors,cvc_total_errors,klayout_violations,wire_length,vias,wns,pl_wns,optimized_wns,fastroute_wns,spef_wns,tns,pl_tns,optimized_tns,fastroute_tns,spef_tns,HPWL,routing_layer1_pct,routing_layer2_pct,routing_layer3_pct,routing_layer4_pct,routing_layer5_pct,routing_layer6_pct,wires_count,wire_bits,public_wires_count,public_wire_bits,memories_count,memory_bits,processes_count,cells_pre_abc,AND,DFF,NAND,NOR,OR,XOR,XNOR,MUX,inputs,outputs,level,EndCaps,TapCells,Diodes,Total_Physical_Cells,suggested_clock_frequency,suggested_clock_period,CLOCK_PERIOD,SYNTH_STRATEGY,SYNTH_MAX_FANOUT,FP_CORE_UTIL,FP_ASPECT_RATIO,FP_PDN_VPITCH,FP_PDN_HPITCH,PL_TARGET_DENSITY,GLB_RT_ADJUSTMENT,STD_CELL_LIBRARY,CELL_PAD,DIODE_INSERTION_STRATEGY
-0,/project/openlane/yifive,ycr1_top_wb,yifive,flow_completed,1h10m32s,-1,62420.32667876588,1.033125,31210.16333938294,35.63,1498.7,32244,0,-1,-1,-1,-1,0,-1,1,0,-1,-1,2409836,367161,-15.33,-49.98,-1,-0.01,-1,-31946.93,-11474.41,-1,-0.01,-1,1780383901.0,5.46,44.53,58.67,5.38,10.76,0.0,26922,45935,1722,20366,0,0,0,32127,0,0,0,0,0,0,0,4,7920,8456,48,1030,14217,0,15247,90.9090909090909,11,10,AREA 0,4,50,1,100,100,0.36,0.0,sky130_fd_sc_hd,4,4
+0,/project/openlane/yifive,ycr1_top_wb,yifive,flow_completed,1h17m16s,-1,62420.32667876588,1.033125,31210.16333938294,35.63,1498.63,32244,0,-1,-1,-1,-1,0,-1,1,0,-1,-1,2409836,367161,-15.33,-49.98,-1,-0.01,-1,-31946.93,-11474.41,-1,-0.01,-1,1780383901.0,5.46,44.53,58.67,5.38,10.76,0.0,26922,45935,1722,20366,0,0,0,32127,0,0,0,0,0,0,0,4,7920,8456,48,1030,14217,0,15247,90.9090909090909,11,10,AREA 0,4,50,1,100,100,0.36,0.0,sky130_fd_sc_hd,4,4
diff --git a/verilog/rtl/qspim b/verilog/rtl/qspim
new file mode 160000
index 0000000..644fc5e
--- /dev/null
+++ b/verilog/rtl/qspim
@@ -0,0 +1 @@
+Subproject commit 644fc5e86bf08279ed257519456199e85d9584f9
diff --git a/verilog/rtl/user_project_wrapper.v b/verilog/rtl/user_project_wrapper.v
index 865063a..cb381a3 100644
--- a/verilog/rtl/user_project_wrapper.v
+++ b/verilog/rtl/user_project_wrapper.v
@@ -217,7 +217,7 @@
// Note that analog I/O is not available on the 7 lowest-numbered
// GPIO pads, and so the analog_io indexing is offset from the
// GPIO indexing by 7 (also upper 2 GPIOs do not have analog_io).
- inout [`MPRJ_IO_PADS-10:0] analog_io,
+ inout [28:0] analog_io,
// Logic Analyzer Signals
input wire [127:0] la_data_in ,
diff --git a/verilog/rtl/yifive/ycr1c b/verilog/rtl/yifive/ycr1c
new file mode 160000
index 0000000..7727aeb
--- /dev/null
+++ b/verilog/rtl/yifive/ycr1c
@@ -0,0 +1 @@
+Subproject commit 7727aeba9e18475aff727af00effb72ad6930969