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