create_clustered_netlist_def.tcl 7.97 KB
Newer Older
sakundu committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
###############################################################################
### Author:  Zhiang, May, 2022
### Uses OpenROAD API to generate clustered netlist (in def format)
### This file cannot be used directly.  It will be called by
### generate_cluster.py
### The Basic Idea:
###     We set the pin position of each cluster to the center of each cluster
###############################################################################
#-------------------------------------------------------
# Step 1 - Traverse all instances, check cluster id, 
# compute total instance area in each cluster
# ------------------------------------------------------
array set clusters_area {}
set all_insts [$block getInsts]
set dbu [$block getDefUnits]
puts "\[INFO\] def units: $dbu"

set unclustered 0
set total_unclustered_area 0

set total_instance_area 0
set num_clusters 0

foreach inst $all_insts {
    set master [$inst getMaster]
    set type   [$master getType]
    if { $type == "BLOCK"} {
        continue
    }
    
    set width [$master getWidth]
    set height [$master getHeight]
    set area [expr $width * $height]
    set total_instance_area [expr $total_instance_area + $area]

    set cluster_prop [odb::dbStringProperty_find $inst "cluster_id"]
    if { $cluster_prop == "NULL"} {
        set unclustered [expr $unclustered + 1]
        set total_unclustered_area [expr $total_unclustered_area + $area]
        puts "\[INFO\] unclustered instance:    [$inst getConstName]"
        continue
    }
    set cluster_id [$cluster_prop getValue]
    
    if {![info exists clusters_area($cluster_id)]} {
        set clusters_area($cluster_id) 0
        set num_clusters [expr $num_clusters + 1]
    }
    set clusters_area($cluster_id) [expr $clusters_area($cluster_id) + $area]
}

puts "\[INFO\] Num clusters: $num_clusters" 
puts "\[INFO\] Num unclustered instances: $unclustered"
puts "\[INFO\] Tot. unsclustered area: [expr $total_unclustered_area / ($dbu * $dbu)]"
puts "\[INFO\] Tot. inst area: [expr $total_instance_area / ($dbu * $dbu) ]"
#foreach {cluster_id cluster_area} [array get clusters_area] {
#    puts "\[INFO\] area of cluster_${cluster_id} : [expr $clusters_area($cluster_id) / ($dbu * $dbu) ]"
#}


#-------------------------------------------------------------------
# Step 2 - Create fake nets
#-------------------------------------------------------------------
puts "\[INFO\] Creating fake nets ..."
set fake_nets []
array set clusters_terms {  }
foreach {cluster_id cluster_area} [array get clusters_area] {
    set clusters_terms($cluster_id) []
}

set num_nets 0
foreach net [$block getNets] {
    set terms []
    set std_cell_clusters [  ]
    foreach bterm [$net getBTerms] {
        lappend terms [$bterm getConstName]
    }

    foreach iterm [$net getITerms] {
        set inst [$iterm getInst]
        set inst_name [$inst getName]
        set master [$inst getMaster]
        set type   [$master getType]
        if { $type == "BLOCK"} {
            set pin_name [[$iterm getMTerm] getName]
            lappend terms "${inst_name} ${pin_name}"
        } else {
            set cluster_prop [odb::dbStringProperty_find $inst "cluster_id"]
            if { $cluster_prop == "NULL" } {
                continue
            }
            set cluster_id [$cluster_prop getValue]
            lappend std_cell_clusters $cluster_id
            #set pin_id [llength $clusters_terms($cluster_id)]
            #set term_name "p_${pin_id}"
            #lappend clusters_terms($cluster_id) ${term_name}
            #lappend terms "cluster_${cluster_id} ${term_name}"
        }
    }
    
    set std_cell_clusters [lsort -unique $std_cell_clusters]
    foreach cluster_id $std_cell_clusters {
        set pin_id [llength $clusters_terms($cluster_id)]
        set term_name "p_${pin_id}"
        lappend clusters_terms($cluster_id) ${term_name}
        lappend terms "cluster_${cluster_id} ${term_name}"
    }

    set terms [lsort -unique $terms]
    if { [llength $terms] > 1 } {
        lappend fake_nets $terms
    }

    set num_nets [expr $num_nets + 1]
}

# There may multiple duplicate nets. The replication of each degree represents the weight of the net
puts "\[INFO\] [llength $fake_nets] nets after post-processing."


#--------------------------------------------------------------------------
# Step 3 - Create fake lef for each cluster
# We set the asepct ratio of each cluster to 1.0
# Each cluster only have one pin located at the center of the cluster
# The size of the pin is 100 dbu x 100 dbu
#--------------------------------------------------------------------------
set lib [lindex [$db getLibs] 0]
set all_rows [$block getRows]
set first_row [lindex $all_rows 0]
set site [$first_row getSite]
set site_width [$site getWidth]
set site_height [$site getHeight]
puts "\[INFO\] site_width: ${site_width}  site_height: ${site_height}"
set new_area 0 
set tech [$db getTech]
set first_routing_layer [$tech findRoutingLayer 2]
foreach {cluster_id cluster_area} [array get clusters_area] {
    set cluster_name cluster_${cluster_id}
    set master [odb::dbMaster_create $lib $cluster_name]
    set length [expr round(sqrt($cluster_area))]
    set width  [expr int(ceil($length / $site_width) * $site_width)]
    set height [expr int(ceil($length / $site_height) * $site_height)]
    #puts "\[INFO\] cluster_name:    $cluster_name  width:    $width   height:   $height"
    odb::dbMaster_setWidth $master $width
    odb::dbMaster_setHeight $master $height
    odb::dbMaster_setSite $master $site
    odb::dbMaster_setType $master "BLOCK"    
    set new_area [expr $height * $width + $new_area]
    foreach term $clusters_terms($cluster_id) {
        set mterm [odb::dbMTerm_create $master $term "INOUT"]
        set mpin [odb::dbMPin_create $mterm]
        set lx [expr int(floor($width / 2))]
        set ly [expr int(floor($height /2))]
        set ux [expr $lx + 100]
        set uy [expr $ly + 100]
        odb::dbBox_create $mpin $first_routing_layer $lx $ly $ux $uy
    }
    $master setFrozen
}

odb::write_lef $lib  $cluster_lef_file
puts "\[INFO\] Total cluster area: [expr $new_area / ($dbu * $dbu)]"
puts "\[INFO\] Finish creating fake lefs"

#-----------------------------------------------------------------
# Step 4 - Create fake block, instances and nets
#-----------------------------------------------------------------
# destory all the standard cell instances
foreach inst [$block getInsts] {
    set master [$inst getMaster]
    set inst_name [$inst getName]
    set type   [$master getType]
    if { $type != "BLOCK"} {
        odb::dbInst_destroy $inst
    } else  {
        odb::dbInst_destroy $inst
        odb::dbInst_create $block $master $inst_name
    }
}
 
foreach bterm [$block getBTerms] {
    $bterm disconnect
}
 
foreach net [$block getNets] {
    odb::dbNet_destroy $net
}
 

# create an instance for each standard cell cluster
foreach {cluster_id cluster_area} [array get clusters_area] {
    set cluster_name "cluster_${cluster_id}"
    set master [$db findMaster $cluster_name]
    odb::dbInst_create $block $master $cluster_name
}
 
 
set num_fake_nets 0
foreach net $fake_nets {
    set fake_net_name "n_${num_fake_nets}"
    # change the name of net if the net is connected to an IO pin
    foreach term $net {
        if { [$block findBTerm $term] != "NULL" } {
            set fake_net_name $term
            continue
        }
    }
 
    set fake_net [odb::dbNet_create $block $fake_net_name]
    foreach term $net {
        set bterm [$block findBTerm $term]
        if {$bterm != "NULL"} {
            odb::dbBTerm_connect $bterm $fake_net
        } else {
            lassign  [split $term] inst_name term_name
            set inst [$block findInst $inst_name]
            if {$inst == "NULL"} {
                puts "\[ERROR\] Instance ${inst_name} does not exist"
                continue
            }
            set iterm [$inst findITerm $term_name]
            odb::dbITerm_connect $iterm $fake_net
        }
    }
    set num_fake_nets [expr $num_fake_nets + 1]
}
 
puts "\[INFO\] Finish creating fake def"
odb::write_def $block $cluster_def_file

exit