############################################################################### ### 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