#----------------------------------------------------------------- # mazeroute.tcl #----------------------------------------------------------------- # Defines procedure "mazeroute ", requiring a .net file as # an argument. Attempts a single-pass maze route of the contents # of the netlist. #----------------------------------------------------------------- global Opts proc mazeroute {netfile} { if [catch {open $netfile r} fnet] { set netname [file rootname $netfile] if [catch {open ${netfile}.net r} fnet] { puts stderr "Can't read netlist file $netfile" return 1; } } # 1st line of the netlist file is throw-away gets $fnet line set destnet {} while {[gets $fnet line] >= 0} { if {$line == ""} { set destnet {} } elseif {$destnet == {}} { set destnet $line } else { set startnet $line iroute route -dlabel $destnet -slabel $startnet -timeout 3 set destnet $startnet } } } #----------------------------------------------------------------- # Interactive mazerouter GUI. Shows a list of nets from the # selected netlist. Allows one to select the order of routing, # route individual nets, rip up individual nets, etc. # # This GUI more or less replaces the "specialopen netlist" window. #----------------------------------------------------------------- proc genmazehelper {} { global Opts set Opts(preproutes) 0 set Opts(fenced) 0 if {![catch {wm deiconify .mazehelper ; raise .mazehelper}]} {return} toplevel .mazehelper wm protocol .mazehelper WM_DELETE_WINDOW {destroy .mazehelper} frame .mazehelper.mazemenu frame .mazehelper.unrouted frame .mazehelper.transfer frame .mazehelper.routed pack .mazehelper.mazemenu -side top -anchor w button .mazehelper.mazemenu.load -text "Load" -command {loadnetlist} label .mazehelper.mazemenu.netlist -text "(no netlist loaded)" button .mazehelper.mazemenu.fence -text "Fence" -command {buildfence} button .mazehelper.mazemenu.sort -text "Sort" -command {sortnets} button .mazehelper.mazemenu.params -text "Params" -command {genmazeparams} pack .mazehelper.mazemenu.load -side left pack .mazehelper.mazemenu.netlist -side left -fill x -expand true pack .mazehelper.mazemenu.fence -side left pack .mazehelper.mazemenu.sort -side left pack .mazehelper.mazemenu.params -side left label .mazehelper.unrouted.title -text "Unrouted:" listbox .mazehelper.unrouted.contents -background white -selectmode extended label .mazehelper.routed.title -text "Routed:" listbox .mazehelper.routed.contents -background white -selectmode extended button .mazehelper.transfer.ripup -text "<--" -command {ripupnet} button .mazehelper.transfer.route -text "-->" -command {routenet} pack .mazehelper.transfer.ripup -side top pack .mazehelper.transfer.route -side top pack .mazehelper.unrouted.title -side top pack .mazehelper.unrouted.contents -side top -fill both -expand true pack .mazehelper.routed.title -side top pack .mazehelper.routed.contents -side top -fill both -expand true pack .mazehelper.unrouted -side left -fill both -expand true pack .mazehelper.transfer -side left pack .mazehelper.routed -side left -fill both -expand true } #----------------------------------------------------------------- # Route parameters (to be completed) #----------------------------------------------------------------- proc genmazeparams {} { global Opts if {![catch {wm deiconify .mazeparams ; raise .mazeparams}]} {return} toplevel .mazeparams wm protocol .mazeparams WM_DELETE_WINDOW {destroy .mazeparams} set routeparams {layer active width hCost vCost jogCost hintCost overCost} set curRparams [iroute layers -list] set contparams {contact active width cost} set curCparams [iroute contact -list] set k 0 label .mazeparams.t1 -text "Layer" label .mazeparams.t2 -text "Active" label .mazeparams.t3 -text "Width" label .mazeparams.t4 -text "Horizontal Cost" label .mazeparams.t5 -text "Vertical Cost" label .mazeparams.t6 -text "Jog Cost" label .mazeparams.t7 -text "Hint Cost" label .mazeparams.t8 -text "Overroute Cost" foreach layer $curRparams { incr k label .mazeparams.r${k}0 -text [lindex $layer 0] checkbox .mazeparams.r${k}1 for {set j 2} {$j < 8} {incr j} { entry .mazeparams.r${k}${j} -text [lindex $contact $j] } } incr k label .mazeparams.t1 -text "Contact" label .mazeparams.t2 -text "Active" label .mazeparams.t3 -text "Size" label .mazeparams.t4 -text "Cost" foreach contact $curCparams { incr k label .mazeparams.r${k}0 -text [lindex $contact 0] checkbox .mazeparams.r${k}1 for {set j 2} {$j < 4} {incr j} { entry .mazeparams.r${k}${j} -text [lindex $contact $j] } } } #----------------------------------------------------------------- # Load a magic-style netlist #----------------------------------------------------------------- proc loadnetlist { {netfile {}} } { global Opts if {$netfile == {}} { set netfile [ tk_getOpenFile -filetypes \ {{NET {.net {.net}}} {"All files" {*}}}] } if [catch {open $netfile r} fnet] { set netname [file rootname $netfile] if [catch {open ${netfile}.net r} fnet] { puts stderr "Can't read netlist file $netfile" return 1; } } # Clear out the listbox contents. .mazehelper.unrouted.contents delete 0 end .mazehelper.routed.contents delete 0 end set $Opts(preproutes) 0 # 1st line of the netlist file is throw-away gets $fnet line set currentnet {} while {[gets $fnet line] >= 0} { if {$line == ""} { if {[llength $currentnet] > 0} { .mazehelper.unrouted.contents insert end $currentnet set currentnet {} } } else { lappend currentnet $line } } # Make sure final net gets added. . . if {[llength $currentnet] > 0} { .mazehelper.unrouted.contents insert end $currentnet } .mazehelper.mazemenu.netlist configure -text [file tail $netfile] # Verify all unrouted nets (check if any are already routed) .mazehelper.unrouted.contents select set 0 end verifynet unrouted quiet .mazehelper.unrouted.contents select clear 0 end close $fnet } #----------------------------------------------------------------- # Place a contact on each network endpoint. Aids in preventing # the router from routing over pins by blocking access to the # space over every pin that will be routed to. # # Changed 9/25/06---contact only in subcells. # Changed 9/26/06---use obstruction layer, not contacts #----------------------------------------------------------------- proc obstructendpoints {} { global Opts set Opts(preproutes) 1 box values 0 0 0 0 foreach net [.mazehelper.unrouted.contents get 0 end] { foreach endpoint $net { set layer [goto $endpoint] # Ignore via layers; these are already obstructed for our purposes. if {[string first metal $layer] == 0} { set lnum [string range $layer 5 end] incr lnum set obslayer obsm${lnum} set viasize [tech drc width m${lnum}c] box size ${viasize}i ${viasize}i paint $obslayer } } } } #----------------------------------------------------------------- # Free the obstruction above endpoints for each endpoint in the # current network. #----------------------------------------------------------------- proc freeendpoints {net} { box values 0 0 0 0 foreach endpoint $net { if {[string first / $endpoint] > 0} { set layer [goto $endpoint] if {[string first metal $layer] == 0} { set lnum [string range $layer 5 end] incr lnum set obslayer obsm${lnum} set viasize [tech drc width m${lnum}c] box size ${viasize}i ${viasize}i erase $obslayer } } } } #----------------------------------------------------------------- # Free the obstruction above each pin in the current network. #----------------------------------------------------------------- proc freepinobstructions {net} { box values 0 0 0 0 foreach endpoint $net { if {[string first / $endpoint] <= 0} { set layer [goto $endpoint] if {[string first metal $layer] == 0} { set lnum [string range $layer 5 end] incr lnum set obslayer obsm${lnum} set viasize [tech drc width m${lnum}c] box size ${viasize}i ${viasize}i erase $obslayer } } } } #----------------------------------------------------------------- # Sorting routine for two pins---sort by leftmost pin position. #----------------------------------------------------------------- proc sortpinslr {pina pinb} { goto $pina set xa [lindex [box values] 0] goto $pinb set xb [lindex [box values] 0] if {$xa > $xb} {return 1} else {return -1} } #----------------------------------------------------------------- # Sort a net so that the endpoints are ordered left to right #----------------------------------------------------------------- proc sortnetslr {} { set allnets [.mazehelper.unrouted.contents get 0 end] .mazehelper.unrouted.contents delete 0 end magic::suspendall foreach net $allnets { set netafter [lsort -command sortpinslr $net] .mazehelper.unrouted.contents insert 0 $netafter } magic::resumeall } #----------------------------------------------------------------- # Procedure to find the leftmost point of a net. #----------------------------------------------------------------- proc getnetleft {net} { foreach endpoint $net { set layer [goto $endpoint] set xtest [lindex [box values] 0] if [catch {if {$xtest < $xmin} {set xmin $xtest}}] {set xmin $xtest} } return $xmin } #----------------------------------------------------------------- # Procedure to compare nets according to the number of nodes #----------------------------------------------------------------- proc routecomp {a b} { set alen [llength $a] set blen [llength $b] if {$alen > $blen} { return -1 } elseif {$alen < $blen} { return 1 } else { # Sort by leftmost route. set aleft [getnetleft $a] set bleft [getnetleft $b] if {$aleft > $bleft} {return 1} else {return -1} } } #----------------------------------------------------------------- # Sort unrouted nets from longest to shortest #----------------------------------------------------------------- proc sortnets {} { set listbefore [.mazehelper.unrouted.contents get 0 end] .mazehelper.unrouted.contents delete 0 end magic::suspendall set listafter [lsort -command routecomp $listbefore] foreach net $listafter { .mazehelper.unrouted.contents insert end $net } magic::resumeall } #----------------------------------------------------------------- # Disassemble all networks into 2-point routes #----------------------------------------------------------------- proc disassemble {} { set allnets [.mazehelper.unrouted.contents get 0 end] .mazehelper.unrouted.contents delete 0 end foreach net $allnets { for {set i 1} {$i < [llength $net]} {incr i} { set j $i incr j -1 set newnet [lrange $net $j $i] .mazehelper.unrouted.contents insert end $newnet } } } #----------------------------------------------------------------- # Fence the area around the cell #----------------------------------------------------------------- proc buildfence {} { global Opts pushbox if {$Opts(fenced) == 0} { select top cell box grow c 1i set ibounds [box values] set illx [lindex $ibounds 0] set illy [lindex $ibounds 1] set iurx [lindex $ibounds 2] set iury [lindex $ibounds 3] box grow c 10i set obounds [box values] set ollx [lindex $obounds 0] set olly [lindex $obounds 1] set ourx [lindex $obounds 2] set oury [lindex $obounds 3] box values ${ollx}i ${iury}i ${ourx}i ${oury}i paint fence box values ${ollx}i ${olly}i ${ourx}i ${illy}i paint fence box values ${ollx}i ${olly}i ${illx}i ${oury}i paint fence box values ${iurx}i ${olly}i ${ourx}i ${oury}i paint fence set Opts(fenced) 1 } else { select top cell box grow c 12i erase fence set Opts(fenced) 0 } popbox } #----------------------------------------------------------------- # Load a list of failed routes #----------------------------------------------------------------- proc loadfailed { {netfile {}} } { if {$netfile == {}} { set netfile [ tk_getOpenFile -filetypes \ {{FAILED {.failed {.failed}}} {"All files" {*}}}] } if [catch {open $netfile r} fnet] { set netname [file rootname $netfile] if [catch {open ${netfile}.failed r} fnet] { puts stderr "Can't read file of failed routes $netfile" return 1; } } # Clear out the listbox contents. .mazehelper.unrouted.contents delete 0 end .mazehelper.routed.contents delete 0 end # Read each line into the "unrouted" list. while {[gets $fnet line] >= 0} { .mazehelper.unrouted.contents insert end $line } .mazehelper.mazemenu.netlist configure -text $netfile close $fnet } #----------------------------------------------------------------- # Save the list of failed routes #----------------------------------------------------------------- proc savefailed { {netfile {}} } { set netfile [.mazehelper.mazemenu.netlist cget -text] if {$netfile == {}} { set netfile [ tk_getOpenFile -filetypes \ {{FAILED {.failed {.failed}}} {"All files" {*}}}] } else { set netname [file rootname $netfile] set netname ${netname}.failed } if [catch {open $netfile w} fnet] { set netname [file rootname $netfile] if [catch {open ${netfile}.failed w} fnet] { puts stderr "Can't write file of failed routes $netfile" return 1; } } foreach net [.mazehelper.unrouted.contents get 0 end] { puts $fnet "$net" } close $fnet } #----------------------------------------------------------------- # Get the selected network and maze route it #----------------------------------------------------------------- proc routenet {} { global Opts set drcstate [drc status] drc off # Prepare routes by placing an obstruction layer on each pin # Only do this if we have defined obstruction layers! if {$Opts(preproutes) == 0} { set allLayers [tech layers *] if {[lsearch $allLayers obs*] >= 0} { magic::suspendall obstructendpoints magic::resumeall } } set unroutable {} set sellist [.mazehelper.unrouted.contents curselection] set startidx [lindex $sellist 0] while {[llength $sellist] > 0} { set cidx [lindex $sellist 0] set rlist [.mazehelper.unrouted.contents get $cidx] if {$Opts(preproutes) == 1} {freeendpoints $rlist} for {set i 1} {$i < [llength $rlist]} {incr i} { set j $i incr j -1 set startnet [lindex $rlist $j] set destnet [lindex $rlist $i] set rresult [iroute route -slabel $startnet -dlabel $destnet -timeout 3] # break on any failure if {$rresult != "Route success" && \ $rresult != "Route best before interrupt" && \ $rresult != "Route already routed"} { break } else { set rresult "success" } } .mazehelper.unrouted.contents delete $cidx $cidx if {$rresult == "success"} { .mazehelper.routed.contents insert end $rlist } else { # scramble list; we may have better luck routing in a different order set rfirst [lindex $rlist 0] set rlist [lrange $rlist 1 end] lappend rlist $rfirst lappend unroutable $rlist } set sellist [.mazehelper.unrouted.contents curselection] if {$Opts(preproutes) == 1} {freepinobstructions $rlist} } .mazehelper.unrouted.contents selection set $startidx foreach badnet $unroutable { .mazehelper.unrouted.contents insert $startidx $badnet } if {$drcstate == 1} {drc on} } #----------------------------------------------------------------- # Get the selected network and remove it #----------------------------------------------------------------- proc ripupnet {} { set sellist [.mazehelper.routed.contents curselection] set startidx [lindex $sellist 0] while {[llength $sellist] > 0} { set cidx [lindex $sellist 0] set rlist [.mazehelper.routed.contents get $cidx] set netname [lindex $rlist 0] select clear set layertype [goto $netname] select more box ${layertype},connect ;# chunk select more box ${layertype},connect ;# region select more box ${layertype},connect ;# net delete .mazehelper.routed.contents delete $cidx $cidx .mazehelper.unrouted.contents insert end $rlist set sellist [.mazehelper.routed.contents curselection] } .mazehelper.routed.contents selection set $startidx } #----------------------------------------------------------------- # Get the selected network and verify the route #----------------------------------------------------------------- proc verifynet { {column routed} {infolevel verbose} } { set sellist [.mazehelper.${column}.contents curselection] set startidx [lindex $sellist 0] magic::suspendall while {$sellist != {}} { set cidx [lindex $sellist 0] set errors 0 set rlist [.mazehelper.${column}.contents get $cidx] set netname [lindex $rlist 0] select clear set layertype [goto $netname] if {$layertype == {}} { incr errors } else { select more box ${layertype},connect ;# chunk select more box ${layertype},connect ;# region select more box ${layertype},connect ;# net set sellist [what -list] set sellabels [lindex $sellist 1] set labellist {} foreach label $sellabels { set labtext [lindex $label 0] set labinst [lindex $label 2] if {$labinst == {}} { set labname ${labtext} } else { set labname ${labinst}/${labtext} } lappend labellist $labname } # Backslash substitute brackets prior to using lsearch, or this won't work # on such labels. Hopefully this won't confuse things. . . set newrlist [string map {\[ < \] >} $rlist] set newlabellist [string map {\[ < \] >} $labellist] # Compare labellist to rlist---they are supposed to be the same! foreach entry $newlabellist { if {[lsearch $newrlist $entry] < 0} { if {"$infolevel" == "verbose"} { puts stderr "ERROR: Net entry $entry in layout is not in the netlist!" } incr errors } } foreach entry $newrlist { if {[lsearch $newlabellist $entry] < 0} { if {"$infolevel" == "verbose"} { puts stderr "ERROR: Net entry $entry in netlist is not in the layout!" } incr errors } } if {$errors == 0 && "$infolevel" == "verbose"} {puts stdout "VERIFIED"} } # If column is "routed" and we're not verified, move to "unrouted". # If column is "unrouted" and we're verified, move to "routed". if {"$column" == "routed"} { if {$errors > 0} { .mazehelper.routed.contents delete $cidx $cidx .mazehelper.unrouted.contents insert end $rlist } else { .mazehelper.routed.contents selection clear $cidx incr startidx } } else { if {$errors == 0} { .mazehelper.unrouted.contents delete $cidx $cidx .mazehelper.routed.contents insert end $rlist } else { .mazehelper.unrouted.contents selection clear $cidx incr startidx } } # Get the selection list again set sellist [.mazehelper.${column}.contents curselection] } magic::resumeall .mazehelper.${column}.contents selection set $startidx } #----------------------------------------------------------------- # Reset the maze helper, deleting all routes. #----------------------------------------------------------------- proc resetmazehelper {} { .mazehelper.routed.contents delete 0 end .mazehelper.unrouted.contents delete 0 end } #----------------------------------------------------------------- # Add the "mazehelper" function to the Magic Options #----------------------------------------------------------------- proc addmazehelper {optmenu} { global Opts $optmenu add check -label "Maze Router" -variable Opts(mazeroute) -command \ {if {$Opts(mazeroute) == 0} {destroy .mazehelper} else {genmazehelper}} } #-----------------------------------------------------------------