//routenet.bcpl

// Analyzes nets and prepares them for permutation

// last modified by E. McCreight, May 11, 1979 6:33 PM

get "route.defs"

let RouteEarlyNets(net) be if net>>net.wireEarly then RouteNet(net)

and RouteNetIfExternal(net) be
[
let pin = net>>net.pinList
while @pin ne mark do
[
let icinst = pin-offset icinst.pin↑1/16
let pinNo = 1
while icinst>>icinst.type ne typeIcinst do
[
icinst = icinst-1
pinNo = pinNo+1
]
if Icclass(icinst)>>icclass.isConnector then
[
RouteNet(net)
return
]
pin = @pin
]
]

and RouteNet(net) be
[
static [ currentNet; crucialNet = -1 ]

currentNet = net
if net eq crucialNet then CallSwat("Routing crucial net..")

if net>>net.isSame % net>>net.hasBeenRouted then return //net already done
if net>>net.isTraceWired then return // not suitable for routing

WeAre(doingRouting)

net>>net.hasBeenRouted = true

let xv = vec 100
let yv = vec 100
let permv = vec 100
let pointv = vec 100
let tpermv = vec 100
let isoutput = vec 100
let btnl = nil
let tbtnl = nil

let lp = net>>net.pinList
let nnodes = 0
let epcount = 0
let outcount = 0
let eclPins = false
let eclOutputs = false
let hasTWPin = false
let outnum = 0
let nterm = 0
let netString = FindNameesString(net)

let clusterBaseVec = vec 30
clusterBaseVec!0 = 29
ComputeClusters(clusterBaseVec, net)
let singleCluster = clusterBaseVec!0 eq 1

until (@lp) eq mark do
[
//set up argument vectors for the router
if nnodes ge 100 then
[
Warning("*nNet $S has too many nodes to route.",
netString)
return
]
nnodes = nnodes+1

pointv!nnodes = lp
isoutput!nnodes = false

let icinst = lp-offset icinst.pin↑1/16
let pin = 1
while icinst>>icinst.type ne typeIcinst do
[
icinst = icinst-1
pin = pin+1
]

GetPinCoord(icinst, pin, lv xv!nnodes, lv yv!nnodes)

let icclass = Icclass(icinst)
let pinAttributes = (icclass>>icclass.PinAttributes)(icinst, pin)

if pinAttributes<<pinattributes.isTerminator then nterm = nterm+1
if icclass>>icclass.isTraceWired then hasTWPin = true
if pinAttributes<<pinattributes.isEcl then
[
eclPins = true
if pinAttributes<<pinattributes.isOutput then eclOutputs = true
]

if pinAttributes<<pinattributes.isOutput then
[
if epcount eq 0 then outnum = nnodes
isoutput!nnodes = true
outcount = outcount+1
]
if icclass>>icclass.isConnector then
[
epcount = epcount+1
outnum = 1
if singleCluster then
MakeFirst(nnodes, xv, yv, pointv, isoutput)
]
lp = @lp
]

//do a little checking on the validity of the net

unless hasTWPin do
[
let incount = nnodes-outcount-epcount
if (incount eq 0)&(epcount eq 0) then
[
Warning( "*nNet $S consists of outputs exclusively.",netString)
]
if (outcount eq 0)&(epcount eq 0) then
[
Warning( "*nNet $S consists of inputs exclusively.",netString)
]
if (outcount eq 0)&(incount eq 0) then
[
Warning( "*nNet $S consists of edge pins exclusively.",netString)
]

if epcount gr 1 then
[
Warning( "*nNet *"$S*" has more than one off-board connector pin",
netString)
]
]

if nnodes le 0 then
[
unless net>>net.isTraceWired % net eq dontCareNet do
Serious("*nEmpty net: *"$S*"", netString)
return
]

if nnodes eq 1 then
[
Warning("*nSingle Node Net: *"$S*"", netString)
return
]

if nnodes eq 2 then return // already routed

// First route the net as it is

Route(nnodes,xv,yv,permv,epcount ne 0,StandardMetric,
(singleCluster? empty, clusterBaseVec))
btnl = bestTotalNetLength

//for ECL nets, if outcount = 1 and epcount = 0,
//we route the net two ways, one with the output forced to the
//end of the net, once with it unconstrained. If the constrained
//length is less then 1.2x the unconstrained length+2", the constrained
//form of the net is used. This puts the output at the end of the net.

if (outcount eq 1)&(epcount eq 0)& eclPins & singleCluster then
[
// force the single output to the end of the net.

MakeFirst(outnum, xv, yv, pointv, isoutput)
outnum = 1

Route(nnodes,xv,yv,tpermv,true,StandardMetric)

// if this routing is at most 20%+2" worse than the
// unconstrained routing, use it.

if bestTotalNetLength le (btnl+btnl/5+80) then
[
btnl = bestTotalNetLength
permv = tpermv
]
]

//Reorder the list based on the permutation

net>>net.pinList = pointv!(permv!1) //list head
for j = 1 to nnodes-1 do @(pointv!(permv!j)) = pointv!(permv!(j+1))
@(pointv!(permv!nnodes)) = lp //last node
]


and MakeFirst(i, v0, v1, v2, v3, v4, v5; numargs na) be
[
for j=0 to na-2 do
[
let v = (lv v0)!j
let t = v!1
v!1 = v!i
v!i = t
]
]

and ComputeLengths(net) be
[ // Compute the total net length and shortest arc length, or area for Multiwire
let shortestarc = infinity
let netlength = 0
let minx,miny = infinity,infinity
let maxx, maxy = 0,0
let pin = net>>net.pinList
if @pin ne mark then
[
let x1,y1 = nil,nil
GetPinCoord(0, pin, lv x1, lv y1)
if x1 ls minx then minx = x1
if x1 gr maxx then maxx = x1
if y1 ls miny then miny = y1
if y1 gr maxy then maxy = y1

pin = @pin
while @pin ne mark do
[
let x2,y2 = nil,nil
GetPinCoord(0, pin, lv x2, lv y2)
if x2 ls minx then minx = x2
if x2 gr maxx then maxx = x2
if y2 ls miny then miny = y2
if y2 gr maxy then maxy = y2

let arclength = StandardMetric(x1, y1, x2, y2)
netlength = netlength+arclength
if arclength ls shortestarc then shortestarc = arclength
pin = @pin
x1 = x2; y1 = y2
]
]
net>>net.shortestarc = shortestarc eq infinity? 0, // single-node net
(doMultiWire?
(((maxx-minx)/4)*((maxy-miny)/4)), // compute area of enclosing rectangle
shortestarc // shortest arc for Rosemary
)
net>>net.netlength = netlength
]