DIRECTORY CD, CDBasics, CDCells, CDOps, CDProperties, CDRects, CDSymbolicObjects, Convert, Core, CoreGeometry, CoreOps, ExtendCells, HashTable, PWPins, PWRoute, Rope, Route, RouteUtil, RTBasic, SC, SCChanUtil, SCInstUtil, SCNetUtil, SCPrivate, SCRoutePinsUtil, SCRowUtil, SCUtil, Sinix, SinixOps; SCRouteImpl: CEDAR PROGRAM IMPORTS CDBasics, CDCells, CDOps, CDProperties, CDRects, CDSymbolicObjects, Convert, CoreGeometry, CoreOps, ExtendCells, HashTable, PWPins, PWRoute, Rope, RouteUtil, RTBasic, SCChanUtil, SCInstUtil, SCNetUtil, SCRoutePinsUtil, SCRowUtil, SCUtil, Sinix, SinixOps EXPORTS SCPrivate SHARES SC = BEGIN DetailRoute: PUBLIC PROC [handle: SC.Handle, viaTable: HashTable.Table] RETURNS [result: SC.Result] = { lgRows: SCPrivate.LgRows _ NARROW[handle.layoutData, SCPrivate.LayoutData].lgRows; SCInstUtil.AllOffsets[handle]; [lgRows.maxRowWidth, lgRows.numMaxRows] _ SCRowUtil.FindMaxRow[handle]; PrepareLogicRoute[handle]; result _ NEW[SC.ResultRec _ [handle: handle, object: RouteRows[handle, viaTable]]]; SCInstUtil.AsgnChanPos[handle]; [] _ SCUtil.WriteResults["End detailed routing\n", handle, 0]}; CreatePinsForChannel: PROC [handle: SC.Handle, rowChan: SCPrivate.RowChan] = { AllPins: SCInstUtil.EachPinProc = { IF netPin.net #NIL THEN { IF side = SCInstUtil.PosOf[instance, netPin.pin].sideOn THEN { alwaysUse: BOOLEAN _ (chan = 1 OR chan = rowChans.count) AND netPin.net.externNet = externalNet AND SCNetUtil.ExitOnSide[handle, netPin.net, side]; rowOffset: SC.Number _ lgRow.rowOrg.p - lgRows.horzRowOrg; rect: CD.Rect _ SCInstUtil.RotateRect[instance, netPin.pin.rect]; position: CD.Position _ SCUtil.PQToXY[handle, [p: rowOffset + instance.offset + rect.x1, q: rect.y1]]; SCRoutePinsUtil.EnterPin[rect, position, netPin, RTBasic.OtherSide[side], lgRow.shell, alwaysUse]}}}; ExternalPins: SCInstUtil.EachPinProc = { IF netPin.net #NIL AND netPin.net.externNet = externalNet THEN { IF side = SCInstUtil.PosOf[instance, netPin.pin].sideOn AND SCNetUtil.ExitOnSide[handle, netPin.net, side] THEN { rowOffset: SC.Number _ lgRow.rowOrg.p - lgRows.horzRowOrg; rect: CD.Rect _ SCInstUtil.RotateRect[instance, netPin.pin.rect]; position: CD.Position _ SCUtil.PQToXY[handle, [p: rowOffset + instance.offset + rect.x1, q: rect.y1]]; SCRoutePinsUtil.EnterPin[rect, position, netPin, RTBasic.OtherSide[side], lgRow.shell, TRUE]}}}; InternalPinsInstance: SCRowUtil.EachInstProc = { [] _ SCInstUtil.EnumeratePinsOnInst[instance, AllPins]}; ExternalPinsInstance: SCRowUtil.EachInstProc = { [] _ SCInstUtil.EnumeratePinsOnInst[instance, ExternalPins]}; lgRows: SCPrivate.LgRows _ NARROW[handle.layoutData, SCPrivate.LayoutData].lgRows; rowChans: SCPrivate.RowChans _ NARROW[handle.layoutData, SCPrivate.LayoutData].rowChans; chan: SCPrivate.MaxChanSr _ rowChan.chanNum; side: SC.Side; lgRow: SCPrivate.LgRow; SELECT TRUE FROM chan = 1 AND lgRows.count = 1 => { side _ bottom; lgRow _ lgRows.rows[1]; [] _ SCRowUtil.EnumerateAllInstsOnRow[handle, lgRow.rowNum, InternalPinsInstance]}; chan = 1 => { side _ bottom; lgRow _ lgRows.rows[1]; [] _ SCRowUtil.EnumerateAllInstsOnRow[handle, lgRow.rowNum, ExternalPinsInstance]}; chan = lgRows.count + 1 => { side _ top; lgRow _ lgRows.rows[lgRows.count]; [] _ SCRowUtil.EnumerateAllInstsOnRow[handle, lgRow.rowNum, ExternalPinsInstance]}; ENDCASE => { side _ top; lgRow _ lgRows.rows[chan - 1]; [] _ SCRowUtil.EnumerateAllInstsOnRow[handle, lgRow.rowNum, InternalPinsInstance]; side _ bottom; lgRow _ lgRows.rows[chan]; [] _ SCRowUtil.EnumerateAllInstsOnRow[handle, lgRow.rowNum, InternalPinsInstance]}}; CreateExitsForChannel: PROC [handle: SC.Handle, rowChan: SCPrivate.RowChan] = { ExitPins: SCChanUtil.EachExitProc = { SCRoutePinsUtil.EnterExit[exit, lrSide, cell]}; EachSide: SCRowUtil.EachSideProc ~ { IF side = left OR side = right THEN { cell _ CDCells.CreateEmptyCell[]; [] _ SCChanUtil.EnumerateExits[handle, rowChan, side, ExitPins]; rowChan.exitCells[side] _ cell}}; cell: CD.Object; [] _ SCRowUtil.EnumerateSides[handle, EachSide]}; FinishExitsForChannel: PROC [handle: SC.Handle, rowChan: SCPrivate.RowChan] = { EachSide: SCRowUtil.EachSideProc ~ { IF side = left OR side = right THEN { rect: CD.Rect; cell: CD.Object _ rowChan.exitCells[side]; SELECT side FROM -- pw needs a better way of finding sides left => rect _ [-trunkWidth, -trunkWidth, trunkWidth, 2*trunkWidth]; right => rect _ [0, -trunkWidth, 2*trunkWidth, 2*trunkWidth]; ENDCASE; CDCells.SetInterestRect[design: NIL, cell: cell, r: rect]; -- set interestRect of cell RTBasic.RepositionCell[cell]}}; trunkWidth: SC.Number _ handle.rules.rowRules.trunkWidth; [] _ SCRowUtil.EnumerateSides[handle, EachSide]}; PrepareLogicRoute: PROC [handle: SC.Handle] = { ForEachRow: SCRowUtil.EachRowProc = { ExtendPin: ExtendCells.ExtendSegmentProc = { obj: CD.Object _ CDCells.CreateEmptyCell[]; connection: CD.Object _ CDRects.CreateRect[[extension, max-min], layer]; connectionInst: CD.Instance _ RouteUtil.Include[obj, connection]; name: Rope.ROPE _ CoreOps.GetShortWireName[wire]; CDProperties.PutProp[connectionInst, $SignalName, name]; CDProperties.PutProp[connectionInst, $InstanceName, name]; RTBasic.RepositionCell[obj]; RETURN[obj]}; leftObject, rightObject: CD.Object _ NIL; maxRowWidth: CD.Number _ layoutData.lgRows.maxRowWidth; rowLength: SC.Number _ lgRow.size.p; rowOffset: SC.Number _ lgRow.rowOrg.p - layoutData.lgRows.horzRowOrg; IF rowOffset > 0THEN { -- needs filler on left to extend power leftWirePins: LIST OF ExtendCells.WirePin _ BuildWirePins[handle: handle, cellType: lgRow.lgsOnRow[1].object.cellType, objSide: left]; leftObject _ ExtendCells.ExtendObject[wirePins: leftWirePins, size: [rowOffset, lgRow.size.q], side: left, extendProc: ExtendPin]; RTBasic.SetCDCellName[leftObject, Rope.Cat[handle.name, "Left", Convert.RopeFromInt[lgRow.rowNum]]]}; IF rowOffset + rowLength < maxRowWidth THEN { -- needs filler on right to extend power rightWirePins: LIST OF ExtendCells.WirePin _ BuildWirePins[handle: handle, cellType: lgRow.lgsOnRow[lgRow.nLgsOnRow].object.cellType, objSide: right]; rightObject _ ExtendCells.ExtendObject[wirePins: rightWirePins, size: [maxRowWidth - (rowOffset + rowLength), lgRow.size.q], side: right, extendProc: ExtendPin]; RTBasic.SetCDCellName[rightObject, Rope.Cat[handle.name, "Right", Convert.RopeFromInt[lgRow.rowNum]]]}; lgRow.shell _ CDCells.CreateEmptyCell[]; lgRow.cdOb _ ConstructRow[handle, leftObject, lgRow, rightObject]}; ForEachChannel: SCChanUtil.EachRowChanProc = { useThisChan: BOOLEAN _ (1 < rowChan.chanNum AND rowChan.chanNum < rowChans.count) OR (rowChan.chanNum = 1 AND rowChans.count = 2); SCRoutePinsUtil.InitGetChanPins[handle]; IF useThisChan THEN CreateExitsForChannel[handle: handle, rowChan: rowChan]; CreatePinsForChannel[handle: handle, rowChan: rowChan]; SCRoutePinsUtil.EnterNetDat[handle, chan, MakeNetPin, MakeExit, NIL]; SCRoutePinsUtil.TermGetChanPins[handle]; IF useThisChan THEN FinishExitsForChannel[handle: handle, rowChan: rowChan]}; MakeExit: SCRoutePinsUtil.ExitProc = { pinOb: CD.Object _ CDSymbolicObjects.CreatePin[size: [trunkWidth, exit.net.trunkWidth]]; pinInst: CD.Instance _ RouteUtil.Include[cell: cell, ob: pinOb]; CDSymbolicObjects.SetName[pinInst, exit.net.name]; CDSymbolicObjects.SetLayer[pinInst, exit.layer]}; MakeNetPin: SCRoutePinsUtil.PinProc = { pinOb: CD.Object _ CDSymbolicObjects.CreatePin[CDBasics.SizeOfRect[rect]]; pinInst: CD.Instance _ RouteUtil.Include[cell: cell, ob: pinOb, position: position]; CDSymbolicObjects.SetName[pinInst, netPin.net.name]; CDSymbolicObjects.SetLayer[pinInst, netPin.pin.layer]}; FinishRow: SCRowUtil.EachRowProc = { rect: CD.Rect _ [0, 0, layoutData.lgRows.maxRowWidth, lgRow.size.q]; CDCells.SetInterestRect[design: NIL, cell: lgRow.shell, r: rect]; -- set interestRect of cell RTBasic.RepositionCell[lgRow.shell]}; layoutData: SCPrivate.LayoutData _ NARROW[handle.layoutData]; rowChans: SCPrivate.RowChans _ layoutData.rowChans; [] _ SCRowUtil.EnumerateRows[handle: handle, eachRow: ForEachRow]; [] _ SCChanUtil.EnumerateRowChans[handle: handle, eachRowChan: ForEachChannel]; [] _ SCRowUtil.EnumerateRows[handle: handle, eachRow: FinishRow]}; ConstructRow: PROC [handle: SC.Handle, leftObject: CD.Object, lgRow: SCPrivate.LgRow, rightObject: CD.Object] RETURNS [row: CD.Object _ CDCells.CreateEmptyCell[]] ~ { ForEachInstance: SCRowUtil.EachInstProc = { object: CD.Object _ instance.object.cdOb; instOrientation: CD.Orientation _ SCInstUtil.CDOrien[instance]; instPosition: CD.Position _ CDOps.FitObjectI[ob: object, location: [offset, 0], orientation: instOrientation].off; cdInst: CD.Instance _ RouteUtil.Include[cell: row, ob: object, position: instPosition, orientation: instOrientation]; CDProperties.PutInstanceProp[cdInst, $InstanceName, instance.name]; CDProperties.PutProp[cdInst, $StopEnumerateDeepPins, $StopEnumerateDeepPins]; offset _ offset + RTBasic.IRSize[object].x}; offset: SC.Number _ 0; IF leftObject # NIL THEN { [] _ RouteUtil.Include[cell: row, ob: leftObject, position: [offset, 0]]; offset _ offset + RTBasic.IRSize[leftObject].x}; [] _ SCRowUtil.EnumerateAllInstsOnRow[handle, lgRow.rowNum, ForEachInstance]; IF rightObject # NIL THEN { [] _ RouteUtil.Include[cell: row, ob: rightObject, position: [offset, 0]]; offset _ offset + RTBasic.IRSize[rightObject].x}; RTBasic.SetCDCellName[row, Rope.Cat[handle.name, "IntRow", Convert.RopeFromInt[lgRow.rowNum]]]; RTBasic.RepositionCell[row]}; RouteRows: PROC [handle: SC.Handle, viaTable: HashTable.Table] RETURNS [obj: CD.Object _ CDCells.CreateEmptyCell[]] ~ { IncludeRow: PROC [rowNum: NAT] ~ { RowSidePublics: CoreGeometry.EachWirePinProc ~ { pin: SCPrivate.PublicPin _ MakeSidePin[wire, min, max, side, layer]; IF pin # NIL THEN lgRow.publics _ CONS[pin, lgRow.publics]}; lgRow: SCPrivate.LgRow _ lgRows.rows[rowNum]; row: CD.Object _ lgRow.cdOb; rowInst: CD.Instance _ RouteUtil.Include[cell: obj, ob: row, position: [0, offset]]; [] _ CoreGeometry.EnumerateNonOverlappingSides[mode.decoration, lgRow.lgsOnRow[1].object.cellType, RowSidePublics]; [] _ CoreGeometry.EnumerateNonOverlappingSides[mode.decoration, lgRow.lgsOnRow[1].object.cellType, RowSidePublics]; CDProperties.PutInstanceProp[rowInst, $InstanceName, Rope.Cat["Row", Convert.RopeFromInt[rowNum]]]; IF rowNum = 1 THEN AddTBPublics[handle, lgRow, bottom, 0]; offset _ offset + lgRow.size.q; IF rowNum = lgRows.count THEN AddTBPublics[handle, lgRow, top, offset]}; RouteChannel: PROC [bottomRow, topRow: CD.Object, rowChan: SCPrivate.RowChan] ~ { ChanPublics: CoreGeometry.EachWirePinProc ~ { pin: SCPrivate.PublicPin _ MakeSidePin[wire, min, max, side, layer]; IF pin # NIL THEN rowChan.publics _ CONS[pin, rowChan.publics]}; result: Route.RoutingResult _ PWRoute.DoRoute[bottomRow, topRow, rowChan.exitCells[left], rowChan.exitCells[right], routerParams, FALSE, channel]; channel: CD.Object _ PWRoute.GetRouting[result, NIL, routerParams]; chanInst: CD.Instance _ RouteUtil.Include[cell: obj, ob: channel, position: [0, offset]]; cellType: Core.CellType _ NARROW [Sinix.Extract[channel, mode].result]; [] _ CoreGeometry.EnumerateNonOverlappingSides[mode.decoration, cellType, ChanPublics]; CDProperties.PutInstanceProp[chanInst, $InstanceName, Rope.Cat[handle.name, "Chan", Convert.RopeFromInt[rowChan.chanNum]]]; offset _ offset + RTBasic.IRSize[channel].y; rowChan.routing _ result; rowChan.chanWidth _ RTBasic.IRSize[channel].y}; MakeSidePin: PROC [wire: Core.Wire, min, max: INT, side: CoreGeometry.Side, layer: CD.Layer] RETURNS [pin: SCPrivate.PublicPin _ NIL] ~ { IF side = left OR side = right THEN { name: Rope.ROPE _ CoreOps.GetShortWireName[wire]; net: SCPrivate.Net _ SCUtil.FindNet[handle, name]; trunkWidth: SC.Number _ handle.rules.rowRules.trunkWidth; position: CD.Position _ [IF side = left THEN 0 ELSE lgRows.maxRowWidth - trunkWidth, offset + min]; rect: CD.Object _ CDRects.CreateRect[[trunkWidth, max-min], layer]; trans: CD.Transformation _ [position, original]; RETURN[NEW[SCPrivate.PublicPinRec _ [net.wire, rect, trans]]]}}; EachChannel: SCChanUtil.EachRowChanProc ~ { IF chan # 1 THEN IncludeRow[chan-1]; IF chan = 1 AND rowChans.count = 2 THEN { TransferExits: ExtendCells.ExtendSegmentProc = { net: SCPrivate.Net _ SCUtil.FindNetByWire[handle, wire]; xfer: BOOLEAN _ net.externNet = externalNet AND SCNetUtil.ExitOnSide[handle, net, bottom] AND side = bottom; IF xfer THEN { pinInst: CD.Instance; pinSize: CD.Position _ [max - min, handle.rules.rowRules.branchWidth]; object: CD.Object _ CDCells.CreateEmptyCell[]; pinInst _ RouteUtil.Include[cell: object, ob: CDSymbolicObjects.CreatePin[pinSize], position: [0, extension - handle.rules.rowRules.branchWidth]]; CDSymbolicObjects.SetName[pinInst, net.name]; CDSymbolicObjects.SetLayer[pinInst, layer]; RTBasic.RepositionCell[object]; RETURN[object]}}; topRow: CD.Object _ lgRows.rows[chan].shell; wirePins: LIST OF ExtendCells.WirePin _ BuildWirePinsFromObj[handle: handle, template: topRow, objSide: bottom]; bottomRow: CD.Object _ ExtendCells.ExtendObject[wirePins: wirePins, size: RTBasic.IRSize[topRow], side: bottom, extendProc: TransferExits]; RouteChannel[bottomRow, topRow, rowChan]}; IF chan # rowChans.count AND chan # 1 THEN { bottomRow: CD.Object _ lgRows.rows[chan-1].shell; topRow: CD.Object _ lgRows.rows[chan].shell; RouteChannel[bottomRow, topRow, rowChan]}}; layoutData: SCPrivate.LayoutData _ NARROW[handle.layoutData]; rowChans: SCPrivate.RowChans _ layoutData.rowChans; lgRows: SCPrivate.LgRows _ layoutData.lgRows; mode: Sinix.Mode _ SinixOps.GetExtractMode[handle.rules.technology]; table: HashTable.Table _ IF viaTable # NIL THEN viaTable ELSE HashTable.Create[equal: EqualProc, hash: HashProc]; routerParams: PWRoute.RouterParams _ NEW[PWRoute.RouterParamsRec _ [trunkLayer: handle.rules.horizLayer, branchLayer: handle.rules.vertLayer, technologyKey: NARROW[handle.rules.technology, CD.Technology].key, signalBreakAtExit: FALSE, signalSinglePinNets: TRUE, viaTable: table]]; offset: SC.Number _ 0; parms: SCPrivate.Parms _ NARROW[handle.parms]; parms.viaTable _ table; [] _ SCChanUtil.EnumerateRowChans[handle, EachChannel]; RTBasic.SetCDCellName[obj, handle.name]; RTBasic.RepositionCell[obj]}; BuildWirePins: PROC [handle: SC.Handle, cellType: Core.CellType, objSide: CoreGeometry.Side] RETURNS [wirePins: LIST OF ExtendCells.WirePin _ NIL] ~ { EachWirePin: CoreGeometry.EachWirePinProc = { IF side=objSide THEN wirePins _ CONS [[wire, min, max, layer], wirePins]}; mode: Sinix.Mode = SinixOps.GetExtractMode[handle.rules.technology]; [] _ CoreGeometry.EnumerateWireSides[mode.decoration, cellType, EachWirePin]}; BuildWirePinsFromObj: PROC [handle: SC.Handle, template: CD.Object, objSide: PWPins.Side] RETURNS [wirePins: LIST OF ExtendCells.WirePin _ NIL] ~ { EachPin: PWPins.InstanceEnumerator = { IF objSide=PWPins.GetSide[template, inst] THEN { layer: CD.Layer _ CDSymbolicObjects.GetLayer[inst]; net: SCPrivate.Net _ SCUtil.FindNet[handle, CDSymbolicObjects.GetName[inst]]; rect: CD.Rect _ CDSymbolicObjects.Denotes[inst]; min: CD.Number _ IF objSide = top OR objSide = bottom THEN rect.x1 ELSE rect.y1; max: CD.Number _ IF objSide = top OR objSide = bottom THEN rect.x2 ELSE rect.y2; wirePins _ CONS [[net.wire, min, max, layer], wirePins]}}; [] _ PWPins.EnumerateEdgePins[template, EachPin]}; AddTBPublics: PROC[handle: SC.Handle, lgRow: SCPrivate.LgRow, objSide: PWPins.Side, yCord: CD.Number] ~ { EachPin: PWPins.InstanceEnumerator = { IF objSide=PWPins.GetSide[lgRow.shell, inst] THEN { layer: CD.Layer _ CDSymbolicObjects.GetLayer[inst]; net: SCPrivate.Net _ SCUtil.FindNet[handle, CDSymbolicObjects.GetName[inst]]; trunkWidth: SC.Number _ handle.rules.rowRules.trunkWidth; denote: CD.Rect _ CDSymbolicObjects.Denotes[inst]; public: CD.Object _ CDRects.CreateRect[[denote.x2-denote.x1, trunkWidth], layer]; position: CD.Position _ [denote.x1, IF objSide = bottom THEN yCord ELSE yCord - trunkWidth]; lgRow.publics _ CONS[NEW[SCPrivate.PublicPinRec _ [net.wire, public, [position, original]]], lgRow.publics]}}; [] _ PWPins.EnumerateEdgePins[lgRow.shell, EachPin]}; EqualProc: PROC [k1, k2: HashTable.Key] RETURNS [eq: BOOL] = { p1: Route.Position _ NARROW[k1, REF Route.Position]^; p2: Route.Position _ NARROW[k2, REF Route.Position]^; eq _ p1.x = p2.x AND p1.y = p2.y}; HashProc: PROC [k: HashTable.Key] RETURNS [hash: CARDINAL] = { size: Route.Position _ NARROW[k, REF Route.Position]^; hash _ size.x + size.y}; END. ΔSCRouteImpl.mesa: Implements of SC.DetailedRoute Copyright c 1986, 1987 by Xerox Corporation. All rights reserved. Frank Bowers June 3, 1986 10:58:48 am PDT Last Edited by: Bryan Preas February 17, 1987 6:30:59 pm PST NOTE: Both PWPins and CoreGeometry Pins are used. This will change as soon as PWRoute removes dependence on PWPins [instance: SCPrivate.Instance, pin: NAT, netPin: SCPrivate.PinNet] [instance: SCPrivate.Instance, pin: NAT, netPin: SCPrivate.PinNet] only one row, must route one exterior channel. Choose the bottom one bottom exterior channel, transfer the exterior pins top exterior channel, transfer the exterior pins the interior channels PROC [exitNum: SCPrivate.MaxExitsSr, lrSide: SCPrivate.LRSide, rowChan: SCPrivate.RowChan, exit: SCPrivate.Exit] RETURNS [quit: BOOL _ FALSE] [side: SC.Side, bpRow: SCPrivate.BpRow] RETURNS [quit: BOOL _ FALSE] [side: SC.Side, bpRow: SCPrivate.BpRow] RETURNS [quit: BOOL _ FALSE] [row: SCPrivate.MaxRowSr, lgRow: SCPrivate.LgRow] RETURNS [quit: BOOL _ FALSE] PROC [wire: Wire, min, max: INT, layer: Layer, side: Side, extension: INT] RETURNS [Object _ NIL]; PROC [chan: SCPrivate.MaxChanSr, rowChan: SCPrivate.RowChan] RETURNS [quit: BOOL _ FALSE] PROC [exit: SCPrivate.Exit, cell: CD.Object, trunkWidth: SC.Number, side: SCPrivate.LRSide]; PROC [rect: SC.Rect, position: CD.Position, netPin: SCPrivate.PinNet, cell: CD.Object]; [row: SCPrivate.MaxRowSr, lgRow: SCPrivate.LgRow] RETURNS [quit: BOOL _ FALSE] [pos: NAT, instance: SCPrivate.Instance] RETURNS [quit: BOOL _ FALSE] route the channels and include rows and channels into the layout include the specified row in the object being constructed PROC [wire: Wire, min, max: INT, side: Side, layer: CD.Layer] RETURNS [quit: BOOL _ FALSE]; add decoration for public; wire is an unbound wire route the specified channeland include in the object being constructed PROC [wire: Wire, min, max: INT, side: Side, layer: CD.Layer] RETURNS [quit: BOOL _ FALSE]; add decoration for public; wire is an unbound wire make a public for decoration [chan: SCPrivate.MaxChanSr, rowChan: SCPrivate.RowChan] RETURNS [quit: BOOL _ FALSE] route each channel do the bottom channel if it is the only one PROC [wire: Wire, min, max: INT, layer: Layer, side: Side, extension: INT] RETURNS [Object _ NIL]; do the interior channels extract a list of wirePins from side of template extract a list of wirePins from side of template TEMPORARY add pins on side of template to public decoration TEMPORARY Κͺ˜codešœ!Οkœ™1Kšœ Οmœ7™BKšœ&™)Kšœ9™Kš œ œ œœ$œ0˜“Kšœ œ-˜:Kšœœ9˜AKšœ œZ˜fKšœe˜e——K˜—šŸ œ˜(Kš $‘ ™BK™šœ œœ%œ˜Cšœ6œ0œ˜qKšœ œ-˜:Kšœœ9˜AKšœ œZ˜fKšœWœ˜`——K˜—šŸœ˜0Kšœ8˜8K˜—šŸœ˜0Kšœ=˜=K˜—Kšœœ1˜RKšœœ3˜XKšœ,˜,Kšœœ˜Kšœ˜šœœ˜šœ œ˜"KšœD™DKšœ&˜&KšœS˜S—šœ ˜ Kšœ3™3Kšœ&˜&KšœS˜S—šœ˜Kšœ0™0Kšœ.˜.KšœS˜S—šœ˜ Kšœ™Kšœ*˜*KšœR˜RKšœ)˜)KšœT˜T——K˜—šŸœœ œ(˜OK˜šŸœ˜%Kšœmœœœ™Kšœ/˜/K˜—šŸœ˜$Kš œœœœœ™Dšœ œœ˜%Kšœ!˜!Kšœ@˜@Kšœ!˜!—K˜—Kšœœ˜Kšœ1˜1K˜—šŸœœ œ(˜OK˜šŸœ˜$Kš œœœœœ™Dšœ œœ˜%Kšœœ˜Kšœœ"˜*šœœΟc)˜:KšœD˜DKšœ=˜=Kšœ˜—Kšœ œ’˜VKšœ˜—K˜—Kšœ œ+˜9Kšœ1˜1K˜—šŸœœ œ ˜/K˜šŸ œ˜%Kšœ2œœœ™NK˜šŸ œ#˜,Kš œœ'œœ œ™bKšœœ$˜+Kšœ œ:˜HKšœœ/˜AKšœ œ"˜1Kšœ8˜8Kšœ:˜:Kšœ˜šœ˜ K˜——Kšœœ œ˜)Kšœ œ(˜7Kšœ œ˜$Kšœ œ8˜EK˜šœœ’'˜>Kšœœœq˜†Kšœ‚˜‚Kšœe˜e—K˜šœ%œ’(˜WKšœœœ€˜–Kšœ‘˜‘Kšœg˜gK˜—Kšœ(˜(KšœC˜CK˜—šŸœ ˜.Kšœ9œœœ™YKš œ œœ#œœ˜‚Kšœ(˜(Kšœ œ9˜LKšœ7˜7Kšœ@œ˜EKšœ(˜(Kšœ œ:˜M—K˜šŸœ˜&Kšœœœ!™\K˜KšœœO˜XKšœ œ5˜@Kšœ2˜2Kšœ1˜1K˜—šŸ œ˜'Kšœœœ+œ ™WK™KšœœA˜JKšœ œI˜TKšœ4˜4Kšœ7˜7K˜—šŸ œ˜$šœ2œœœ™NK˜—Kšœœ<˜DKšœ œ’˜]Kšœ%˜%K˜—Kšœ#œ˜=Kšœ3˜3KšœB˜BKšœO˜OKšœB˜BK˜—šŸ œœ œœ.œ œœ(˜¦K˜šŸœ˜+Kš œœ œœœ™EKšœœ˜)Kšœœ,˜?Kšœœb˜rKšœœk˜uKšœC˜CKšœM˜MKšœ,˜,K˜—Kšœœ ˜šœœœ˜KšœI˜IKšœ0˜0K˜—šœM˜MK˜—šœœœ˜KšœJ˜JKšœ1˜1K˜—Kšœ_˜_Kšœ˜—K˜š Ÿ œœ œ$œœ(˜wKšœ@™@K˜šŸ œœ œ˜"Kšœ9™9K˜šŸœ"˜0Kš œœœœœœ™[Kšœ2™2JšœD˜DKšœœœœ˜œ œœœ˜–Kšœ0™0J˜šŸ œ"˜-Jšœœ œ&˜JJ˜—JšœD˜DJšœN˜NK˜—šŸœœ œœœ œœœ˜“Kšœ0™0Kšœ ™ J˜šŸœ Οbœ˜&šœ(œ˜0Jšœœ*˜3JšœM˜MKšœœ(˜0Kš œœ œœœ œ ˜PKš œœ œœœ œ ˜PJšœ œ+˜:—J˜—Jšœ2˜2K˜—šŸ œœ œ>œ ˜iKšœ1™1Kšœ ™ K˜šŸœ £œ˜&šœ+œ˜3Jšœœ*˜3JšœM˜MKšœ œ+˜9Kšœœ(˜2KšœœG˜Qšœ œ ˜Kšœ œœœ˜D—KšœœœV˜n—J˜—Jšœ5˜5K˜—šŸ œœœœ˜>Kšœœœ˜5Kšœœœ˜5Kšœœ˜"K˜—šŸœœœœ˜>Kšœœœ˜6Kšœ˜K˜—šœ˜K˜K˜K˜—K˜—…—@2X