//routeterm.bcpl

// Ecl termination module

// last modified by E. McCreight, March 28, 1980 5:23 PM

get "route.defs"

static
[
CriticalNetLength = 160 // <4" => 1 terminator always suffices
CriticalDriverStubLen = 80
// >2" from driver to end => 2 terminators
CriticalStubLength = 80 // >2" => terminator must go at end
currentTermType
currentTermClass

installedTermPinCount
installedTermPins
nextInstalledTerm
]

manifest
[
atBegin = 1
atEnd = 2
]

let AddEclTerminators(net) be
[
unless net>>net.hasBeenRouted do return
let where = WhereToAddEclTerms(net)
if (where & atBegin) ne 0 then
AddBeginTerminator(net, StdEclTerminator())
if (where & atEnd) ne 0 then
[
ReverseNet(net)
AddBeginTerminator(net, StdEclTerminator())
ReverseNet(net)
]
net>>net.dontTerminate = true
]

and StdEclTerminator() = valof
[
static [ stdEclTerminator = 0 ]

if stdEclTerminator eq 0 then
stdEclTerminator = MustFindNamee("Term100/8/Term100", typeIctype)

resultis stdEclTerminator
]

and ReverseNet(net) be
[
let pins = vec 200
let npins = 0
let pin = net>>net.pinList
while @pin ne mark do
[
npins = npins+1
pins!npins = pin
pin = @pin
]
pins!0 = pin
pins!(npins+1) = lv (net>>net.pinList)
for i=0 to npins do @(pins!(i+1)) = pins!i
]

and WhereToAddEclTerms(net) = valof
[
// This module is getting entirely out of hand. Let me see if
// I can describe what’s going on. The major choice
// is how many terminating resistors to add to a net,
// and where to put them.
// The following set of productions represent the
// current policy

//
* If the net has already had terminators added or if
//
there was a ! associated with the net,
//
add no terminators.
//
* If the net is not to be re-worked, add no terminators.
//
* If the net has only one node, add no terminators.
//
* If there is no Ecl in the net, add no terminators.
//
* If there is any recognizable termination in the net,
//
add no terminators.
//
* If there is an off-board connection,
//
terminate at the end farthest from
//
any off-board connection.
//
* If the maximum distance from any Ecl output to the
//
net end closest to an Ecl output is less than
//
CriticalDriverStubLen then terminate at the end
//
farthest from an Ecl output.
//
* If the total net length is less that CriticalNetLength then
//
terminate at the and farthest from an Ecl output.
//
* Otherwise terminate at both ends.

// Termination may be placed as far as CriticalTermStubLen from
// the nearest net end, so long as total wire length is minimized.

if net>>net.dontTerminate % net>>net.isSame then resultis empty
let netHasEcl = false
let eclOutNearBegin,connNearBegin = infinity,infinity
let eclOutNearEnd,connNearEnd = nil,nil
let distance = 0
let pinSeq = 0
let oldx, oldy = nil,nil
let pin = net>>net.pinList
while @pin ne mark do
[
pinSeq = pinSeq+1
let pinNo = 1
let icinst = pin-offset icinst.pin↑1/16
while icinst>>icinst.type ne typeIcinst do
[
icinst = icinst-1
pinNo = pinNo+1
]
let x,y = nil,nil
GetPinCoord(icinst, pinNo, lv x, lv y)
if pinSeq gr 1 then
distance = distance+StandardMetric(x, y, oldx, oldy)

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

if pinAttributes<<pinattributes.isTerminator then resultis empty

if icclass>>icclass.isConnector then
[
connNearEnd = distance
if connNearBegin eq infinity then
connNearBegin = distance
]

if pinAttributes<<pinattributes.isEcl then
[
netHasEcl = true
if pinAttributes<<pinattributes.isOutput then
[
eclOutNearEnd = distance
if eclOutNearBegin eq infinity then
eclOutNearBegin = distance
]
]

oldx = x; oldy = y
pin = @pin
]

unless netHasEcl & pinSeq gr 1 do resultis empty

if connNearBegin ne infinity then resultis
(connNearEnd gr distance-connNearBegin)? atBegin,atEnd

if eclOutNearBegin eq infinity then
[
unless net>>net.isTraceWired do
Warning("*nNet $S seems to be an Ecl net with no drivers, no termination added", FindNameesString(net))
resultis empty
]

if distance ls CriticalNetLength %
eclOutNearEnd ls CriticalDriverStubLen %
distance-eclOutNearBegin ls CriticalDriverStubLen then resultis
(eclOutNearEnd gr distance-eclOutNearBegin)? atBegin,atEnd

resultis atBegin+atEnd
]


and AddBeginTerminator(net, termType) be
[
let first = net>>net.pinList
let xf, yf = nil,nil
GetPinCoord(0, net>>net.pinList, lv xf, lv yf)
let xt, yt = nil, nil
let termPin = FindBestTerm(termType, xf, yf, lv xt, lv yt)
if termPin eq 0 then
[
Serious("*nNo more terminators available.")
return
]

//there are two choices for the position of the terminator
//we just found. (a) at the end of the net (endPos), and (b) between
//nodes Between1 and Between2. We choose such that the net
//is minimally lengthened, except that we do not permit an unterminated
//stub longer than CriticalStubLength.

let goesBeforeFirst = false

let second = @first
let xs, ys = nil,nil
GetPinCoord(0, second, lv xs, lv ys)

let dTermToFirst = StandardMetric(xt, yt, xf, yf)
let termPredecessor = (dTermToFirst gr CriticalStubLength %
dTermToFirst+StandardMetric(xf, yf, xs, ys) le
StandardMetric(xf, yf, xt, yt)+StandardMetric(xt, yt, xs, ys))?
lv (net>>net.pinList), first

@termPin = @termPredecessor
@termPredecessor = termPin
]


and LocateAvailTerms(icclass) be
[
static [ locateSpergeVec ]

unless icclass>>icclass.nPotentialPins ge 0 do return // not really terminator

// the data in icclass.permutation is interpreted as follows:

//
if ule icclass>>icclass.nPotentialPins then it is an index
//
to be sent to icclass.LocatePotentialPin

//
if ugr icclass.nPotentialPins then it is a pointer to a pin
//
in an icinst.

currentTermClass = icclass
installedTermPinCount = 0
MapNamees(typeIcinst, CountInstalledTerms)
installedTermPins = Allocate(SilZone, installedTermPinCount+1)
nextInstalledTerm = 1
MapNamees(typeIcinst, FindInstalledTerminators)

let npp = icclass>>icclass.nPotentialPins
let perm = Allocate(SilZone, npp+installedTermPinCount+1)
locateSpergeVec = Allocate(SilZone, npp+installedTermPinCount+1)

for i=1 to npp+installedTermPinCount do
[
let x,y = nil,nil
FindTermPin(icclass, i, installedTermPins, lv x, lv y)
perm>>permutation.element↑i = i
locateSpergeVec!i = Sperge(x, y)
]
perm>>permutation.nelements = npp+installedTermPinCount

let CompareSperges(i, j) = locateSpergeVec!i-locateSpergeVec!j
Sort(perm, CompareSperges)

icclass>>icclass.permutation = perm
let spergevec = Allocate(SilZone, perm>>permutation.nelements+1)
spergevec!0 = perm>>permutation.nelements
for i=1 to spergevec!0 do
[
spergevec!i = locateSpergeVec!(perm!i)
if perm!i gr npp then perm!i = installedTermPins!(perm!i-npp)
]
icclass>>icclass.spergevec = spergevec
Free(SilZone, locateSpergeVec)
Free(SilZone, installedTermPins)
]

and FreeTermStorage(icclass) be
[
unless icclass>>icclass.nPotentialPins ge 0 do return

if icclass>>icclass.permutation ne 0 then Free(SilZone, icclass>>icclass.permutation)
icclass>>icclass.permutation = 0
if icclass>>icclass.spergevec ne 0 then Free(SilZone, icclass>>icclass.spergevec)
icclass>>icclass.spergevec = 0
]

and FindBestTerm(termType, x, y, ptx, pty) = valof
[
let termClass = Icclass(termType)
unless termClass>>icclass.nPotentialPins ge 0 do CallSwat()

let perm = termClass>>icclass.permutation
let spergevec = termClass>>icclass.spergevec
currentTermType = termType
let FBFindSperge(i) = (Icclass(currentTermType)>>icclass.spergevec)!i

while perm>>permutation.nelements gr 0 do
[
let nearestIndex = FindNearest(x, y, perm>>permutation.nelements,
FBFindSperge)
let icinst,pin = nil,nil
let icinst = empty
let pinNo = nil
let pinName = vec 20
pinNo = perm!nearestIndex
FindTermPin(termClass, perm!nearestIndex, empty, ptx, pty, lv icinst, lv pinNo, pinName)

// Disqualify this pin from future searches
let nelements = perm>>permutation.nelements
MoveBlock(lv perm>>permutation.element↑nearestIndex,
lv perm>>permutation.element↑(nearestIndex+1),
nelements-nearestIndex)
MoveBlock(lv spergevec!nearestIndex,
lv spergevec!(nearestIndex+1),
nelements-nearestIndex)
perm>>permutation.nelements = nelements-1
spergevec!0 = nelements-1

let result = InstallThisTerminator(icinst, pinNo, pinName)
if result ne 0 then resultis result
]
]

and InstallThisTerminator(icinst, pinNo, pinName) = valof
[
let NewAutoTerm(icinst) = NewICInst(icinst, currentTermType)
if icinst eq empty then
[
let boardLoc = vec 20
let modifier = 0
pinNo = 0
ParsePin(pinName, boardLoc, lv pinNo, lv modifier)
icinst = TryFindingNamee(boardLoc, typeIcinst)
if icinst eq empty then
[
if TryInserting(boardLoc, currentTermType) ne 0 then resultis 0
icinst = DefineNamee(boardLoc, typeIcinst, NewAutoTerm,
Npins(currentTermType)-1)
]
]
unless icinst>>icinst.ictype eq currentTermType do resultis 0
unless icinst>>icinst.pin↑pinNo eq 0 do resultis 0
resultis lv icinst>>icinst.pin↑pinNo
]

and FindTermPin(icclass, i, installedTermPins, px, py, pIcinst, pPinNo, pinName; numargs na) be
[
let pn = vec 20
let useless = nil
DefaultArgs(lv na, -5, lv na, lv useless, pn)

test Usc(i, icclass>>icclass.nPotentialPins) le 0
ifso
[
@pIcinst = empty
(icclass>>icclass.LocatePotentialPin)(0, i, px, py, 0, pinName)
]
ifnot
[
if installedTermPins ne empty then
i=installedTermPins!(i-icclass>>icclass.nPotentialPins)
@pPinNo = i
FindIcinst(pIcinst, pPinNo, px, py)
]
]

and CountInstalledTerms(icinst) be
[
let icclass = Icclass(icinst)
if icclass ne currentTermClass then return
for i=1 to Npins(icinst) do
[
if icinst>>icinst.pin↑i eq empty &
(icclass>>icclass.PinAttributes)(icinst, i)<<pinattributes.isTerminator
ne 0
then
installedTermPinCount = installedTermPinCount+1
]
]

and FindInstalledTerminators(icinst) be
[
let icclass = Icclass(icinst)
if icclass ne currentTermClass then return
for i=1 to Npins(icinst) do
[
if icinst>>icinst.pin↑i eq empty &
(icclass>>icclass.PinAttributes)(icinst, i)<<pinattributes.isTerminator
ne 0
then
[
installedTermPins!nextInstalledTerm = lv icinst>>icinst.pin↑i
nextInstalledTerm = nextInstalledTerm+1
]
]
]