DIRECTORY PLAGen, CD, CDIO, CDRects, CDRoutingObjects, CDSimpleRules, CDViewer, CMosB, Core, CoreClasses, CoreCreate, CoreDirectory, CoreGeometry, CoreOps, IO, PLAOps, PWCore, TilingClass, Real, REFBit, Rope, RopeList, Sinix, Sisyph, TerminalIO, ViewerOps, WireIconExtras; PLAGenImpl: CEDAR PROGRAM IMPORTS CD, CDIO, CDRects, CDRoutingObjects, CDSimpleRules, CDViewer, CMosB, CoreClasses, CoreCreate, CoreDirectory, CoreGeometry, CoreOps, IO, PLAOps, PWCore, TilingClass, Real, REFBit, Rope, RopeList, Sinix, Sisyph, TerminalIO, ViewerOps, WireIconExtras EXPORTS PLAGen = BEGIN ROPE: TYPE = Rope.ROPE; ROPES: TYPE = LIST OF Rope.ROPE; CellType: TYPE = Core.CellType; Wire: TYPE = Core.Wire; Wires: TYPE = Core.Wires; DriverTileType: TYPE = {left, in, between, extra, out2, out1, rightHalf}; BodyTileKey: TYPE = RECORD[rt: RowType, tv: TileVariety, tt: TileType, sz: Size _ norm]; RowType: TYPE = {header, footer, conn, dataUp, dataDn, blank, inCntc}; TileVariety: TYPE = {glue, and, or}; TileType: TYPE = {leftSide, between, rightSide, left, right, nc, extra}; Size: TYPE = {norm, big}; minOutsRp: ROPE _ "PLAGenOuts"; rowsPerExtraRp: ROPE _ "PLAGenRowsPerExtra"; vddNm: ROPE _ "Vdd"; gndNm: ROPE _ "Gnd"; inNm: ROPE _ "in"; in0Nm: ROPE _ "in0"; in1Nm: ROPE _ "in1"; outNm: ROPE _ "out"; out0Nm: ROPE _ "out0"; out1Nm: ROPE _ "out1"; logicInNm: ROPE _ "logicIn"; logicOutNm: ROPE _ "logicOut"; rowsPerDr: INT _ 3; -- not easily changed outsPerExtra: INT _ 10; -- can be changed layRules: ATOM _ $cmosB; Signal: SIGNAL = CODE; ExpToPlaCell: PUBLIC PROC[expr: REF, type: ATOM _ NIL] RETURNS[cell: CellType] = { pla: PLAOps.PLA _ PLAOps.ExpressionToPLA[expr]; IF type=NIL THEN type _ $buffered; SELECT type FROM $basic => cell _ PLABodyBasic[pla]; $bufferedBot => cell _ PLABuffered[pla]; $basicRt => cell _ PLABodyBasic[pla, TRUE]; ENDCASE => ERROR}; PLABuffered: PROC[pla: PLAOps.PLA] RETURNS[cell: CellType] = { logic: CellType _ PLABodyBasic[pla]; bufs: CellType _ BufferedPLADrivers[pla]; vdd: Wire _ CoreOps.CreateWires[0, vddNm]; gnd: Wire _ CoreOps.CreateWires[0, gndNm]; logicIn: Wire _ WireIconExtras.RefWire[pla.data, logicInNm, TRUE]; logicOut: Wire _ WireIconExtras.RefWire[pla.out, logicOutNm, FALSE]; in: Wire _ WireIconExtras.RefWire[pla.data, inNm, FALSE]; out: Wire _ WireIconExtras.RefWire[pla.out, outNm, FALSE]; logicI: CoreCreate.CellInstance _ CoreCreate.Instance[logic, [inNm, logicIn], [outNm, logicOut], [vddNm, vdd], [gndNm, gnd] ]; bufsI: CoreCreate.CellInstance _ CoreCreate.Instance[bufs, [inNm, in], [outNm, out], [logicInNm, logicIn], [logicOutNm, logicOut], [vddNm, vdd], [gndNm, gnd] ]; StripSubWireNames[logicIn]; StripSubWireNames[logicOut]; AddSubWireNamePrefix[to: out, prefix: "Nxt"]; cell _ CoreCreate.Cell[ public: CoreCreate.WireList[LIST[in, out, vdd, gnd]], onlyInternal: CoreCreate.WireList[LIST[logicIn, logicOut]], instances: LIST[bufsI, logicI], name: pla.name ]; PWCore.SetAbutY[cell]; RETURN[cell]}; PLAInSB: PUBLIC PROC[cx: PLAGen.Context, tttFile: ROPE] RETURNS[cell: CellType] = { pla: PLAOps.PLA _ PLAOps.ReadPLAFile [tttFile.Cat[".ttt"], TerminalIO.TOS[], TRUE]; rec: CoreClasses.RecordCellType _ NEW[CoreClasses.RecordCellTypeRec[0]]; in: Wire _ WireIconExtras.RefWire[pla.data, "in", TRUE]; out: Wire _ WireIconExtras.RefWire[pla.out, "out", FALSE]; public: Wire _ CoreOps.CreateWire[LIST[in]]; internal: Wire _ CoreOps.CreateWire[LIST[in]]; flatIn: Wire _ CoreOps.CreateWire[Flatten[in]]; flatInOrd: Wire _ CoreOps.CreateWire[Flatten[ReorderWire[in, GetSideNames[cx, right]]]]; insAtTop: BOOL _ GetSideNames[cx, top] #NIL; insAtBot: BOOL _ GetSideNames[cx, bottom] #NIL AND ~insAtTop; ins: NAT _ flatIn.size/2; nofOrCols: NAT _ NofOrCols[MAX[CoreOps.WireBits[out], GetCxParam[cx, minOutsRp]]]; leftSize: INT _ 44 * CMosB.lambda; betweenSize: INT _ 16 * CMosB.lambda; rightSize: INT _ 16 * CMosB.lambda; blankSize: INT _ 8 * CMosB.lambda; biasX: INT _ 48 * CMosB.lambda; biasY: INT _ 12 * CMosB.lambda; colAnd: INT _ 16 * CMosB.lambda; colOr: INT _ 8 * CMosB.lambda; halfColSize: INT _ 8 * CMosB.lambda; rowSize: INT _ 24 * CMosB.lambda; halfRowSize: INT _ 16 * CMosB.lambda; obj: CD.Object; contObj: CD.Object _ CDSimpleRules.Contact[layRules, CMosB.met, CMosB.met2]; contW: INT _ CD.InterestSize[contObj].x; metW: INT _ CDSimpleRules.MinWidth[layRules, CMosB.met]; met2W: INT _ CDSimpleRules.MinWidth[layRules, CMosB.met2]; iSizeX: NAT _ leftSize +ins*colAnd +betweenSize +nofOrCols*colOr +rightSize; iSizeY: NAT _ blankSize + ins*rowSize + blankSize; nodes: LIST OF CDRoutingObjects.Node; cell _ CoreClasses.CreateRecordCell[public, internal, NIL, tttFile.Cat["InSB"]]; FOR indexX: INT IN [0..ins) DO FOR polarity: INT IN [0..2) DO node: CDRoutingObjects.Node; pub: Wire _ flatIn[2*indexX + polarity]; name: ROPE _ CoreOps.GetFullWireName[public, pub]; indexY: INT _ WireIndex[flatInOrd, pub]/2; refX: INT _ biasX + indexX * colAnd + polarity * halfColSize; refY: INT _ biasY + indexY * rowSize + polarity * halfRowSize; verYSize: INT _ SELECT TRUE FROM insAtTop AND insAtBot => iSizeY, insAtTop => iSizeY-refY, insAtBot => refY, ENDCASE => ERROR; horSize: CD.Position _ [iSizeX-refX, metW]; verSize: CD.Position _ [met2W, verYSize]; horPos: CD.Position _ [refX, refY-metW/2]; verPos: CD.Position _ [refX-met2W/2, (IF insAtBot THEN 0 ELSE refY)]; contPos: CD.Position _ [refX-contW/2, refY-contW/2]; horObj: CD.Object _ CDRects.CreateRect[horSize, CMosB.met]; verObj: CD.Object _ CDRects.CreateRect[verSize, CMosB.met2]; pos: LIST OF CDRoutingObjects.PlacedObject _ NIL; IF name=NIL THEN ERROR; pos _ CONS[[ horObj, horPos], pos]; --pins _ CONS[[horObj, [horPos]], pins]; pos _ CONS[[ verObj, verPos], pos]; --pins _ CONS[[verObj, [verPos]], pins]; pos _ CONS[[ contObj, contPos], pos]; node _ CDRoutingObjects.CreateNode [pos, LIST[[$SignalName, name], [$InstanceName, name]]]; nodes _ CONS[node, nodes]; ENDLOOP; ENDLOOP; obj _ CDRoutingObjects.CreateRoutingObject[nodes, [x1: 0, y1: 0, x2: iSizeX, y2: iSizeY]]; PWCore.SetLayout[cell, $Value, $PWCoreValue, obj]}; PLAOutDrHeader: PUBLIC PROC[tttFile: ROPE] RETURNS[cell: CellType] = { pla: PLAOps.PLA _ PLAOps.ReadPLAFile[tttFile.Cat[".ttt"], TerminalIO.TOS[], TRUE]; out: Wire _ WireIconExtras.RefWire[pla.out, "out", FALSE]; outSize: INT _ CoreOps.WireBits[out]; minSize: NAT _ (rowsPerDr-1)*outSize; outFill: INT _ MAX[pla.termList.length, minSize] - minSize; header: CellType _ Get[orig, "PLAGen.DrHeader.sch"]; blank: CellType _ Get[orig, "PLAGen.DrBlank.sch"]; name: ROPE _ "DrHeader"; insts: CoreCreate.CellInstances _ LIST[CoreCreate.Instance[header]]; IF outFill=0 THEN RETURN[header]; name _ IO.PutFR["%gWith%gBlanks", IO.rope[name], IO.int[outFill]]; FOR i: INT IN [1..outFill] DO insts _ CONS[CoreCreate.Instance[blank], insts]; ENDLOOP; cell _ CoreCreate.Cell[ public: CoreOps.CopyWire[header.public], instances: insts, name: name]; PWCore.SetAbutY[cell]}; PLABody: PUBLIC PROC[cx: PLAGen.Context, tttFile: ROPE] RETURNS[cell: CellType] = { pla: PLAOps.PLA _ PLAOps.ReadPLAFile[tttFile, TerminalIO.TOS[], TRUE]; minOuts: INT _ GetCxParam[cx, minOutsRp]; rowsPerExtra: INT _ GetCxParam[cx, rowsPerExtraRp, 1000]; rightNames: ROPES _ GetSideNames[cx, right]; cell _ PLABodyBasic[pla, rightNames#NIL, rightNames, minOuts, rowsPerExtra]}; PLABodyBasic: PROC[ pla: PLAOps.PLA, horOuts: BOOL _ FALSE, rtNms: ROPES _ NIL, minOuts: INT _ 0, rowsPerExtra: INT _ 10] RETURNS[cell: CellType] = { vdd: Wire _ CoreOps.CreateWires[0, vddNm]; gnd: Wire _ CoreOps.CreateWires[0, gndNm]; in: Wire _ WireIconExtras.RefWire[pla.data, "in", TRUE]; out: Wire _ WireIconExtras.RefWire[pla.out, "out", FALSE]; flatIn: Wire _ CoreOps.CreateWire[Flatten[in]]; flatOut: Wire _ CoreOps.CreateWire[Flatten[out]]; flatOutOrd: Wire _ CoreOps.CreateWire[Flatten[ReorderWire[out, rtNms]]]; public: Wire _ NIL; baseRows: NAT _ (IF horOuts THEN flatOut.size+MAX[flatOut.size*(rowsPerDr-1), pla.termList.length] ELSE pla.termList.length); nofRows: NAT _ 2 + baseRows + (baseRows-1)/rowsPerExtra; nofOrCols: NAT _ NofOrCols[MAX[flatOut.size, minOuts] ]; nofAndCols: NAT _ flatIn.size/2; row: INT _ 0; up: BOOL _ FALSE; cRow: INT _ 0; fillLt: INT _ 0; ta: TilingClass.TileArray _ NEW[TilingClass.TileArrayRec[nofRows]]; terms: LIST OF PLAOps.Term _ NIL; data: PLAOps.Term _ NIL; GetRow: PROC[type: RowType] RETURNS[rowRef: TilingClass.TileRow ] = { AndBit: PROC[index: INT] RETURNS[TileType] = { RETURN[SELECT type FROM dataUp, dataDn => SELECT PLAOps.GetInQrt [data, REFBit.Desc[pla.data].bitForm[index].firstBit] FROM zero => left, one => right, ENDCASE => nc, ENDCASE => nc]}; OrBit: PROC[index: INT] RETURNS[TileType] = { IF (index+1) MOD (outsPerExtra+1) = 0 THEN RETURN[extra]; index _ index - index/(outsPerExtra+1); IF index >= flatOut.size THEN RETURN[nc]; SELECT type FROM conn => IF flatOutOrd[cRow]=flatOut[index] THEN RETURN[left]; dataUp, dataDn => IF PLAOps.GetOutQrt [data, REFBit.Desc[pla.out].bitForm[index].firstBit]=one THEN RETURN[left]; ENDCASE; RETURN[nc]}; TileIt: PROC[ cell: CellType, pubType: {in, out, none} _ none, index: INT _ 0, header: BOOL_FALSE ] = { pas: LIST OF CoreCreate.PA _ NIL; SELECT pubType FROM in => pas _ LIST[[in0Nm, flatIn[2*index+0]], [in1Nm, flatIn[2*index+1]] ]; out => pas _ LIST[[outNm, flatOut[index]]]; ENDCASE; IF header THEN pas _ CONS[[vddNm, vdd], CONS[[gndNm, gnd], pas]]; rowRef[col] _ NEW[TilingClass.TileRec _ [cell, pas]]; col _ col+1}; col: INT _ 0; rowRef _ NEW[TilingClass.TileRowRec[1 + nofAndCols + 1 + nofOrCols + 1]]; SELECT type FROM conn => { up _ FALSE}; dataUp, dataDn => { IF terms = NIL THEN data _ NIL ELSE {data _ terms.first; terms _ terms.rest}; type _ IF data=NIL THEN blank ELSE IF (up_~up) THEN dataUp ELSE dataDn}; ENDCASE; IF (type=dataUp OR type=dataDn) AND data#NIL AND TermLoad[pla, data].load > hotTermThreshold THEN TileIt[ BodyTile[[type, glue, leftSide, big ]], none, 0] ELSE TileIt[ BodyTile[[type, glue, leftSide ]], none, 0]; FOR i: INT IN [0..nofAndCols) DO IF row=0 AND AndBit[i]#extra THEN {TileIt[BodyTile[[type, and, AndBit[i]]], in, i]} ELSE {TileIt[BodyTile[[type, and, AndBit[i]]]]}; ENDLOOP; TileIt[ BodyTile[[type, glue, between]]]; FOR i: INT IN [0..nofOrCols) DO outI: INT _ i-i/(outsPerExtra+1); IF row=0 AND OrBit[i]#extra AND outI < flatOut.size THEN {TileIt[ BodyTile[[type, or, OrBit[i]]], out, outI]} ELSE {TileIt[ BodyTile[[type, or, OrBit[i]]]]} ENDLOOP; IF type#conn THEN {TileIt[BodyTile[[type,glue,rightSide]], none, 0, (type=header OR type=footer)]} ELSE {TileIt[BodyTile[[type,glue,rightSide]] ]} }; hotTermThreshold: INT _ HotTermThreshold[pla]; TerminalIO.PutRope[Estimates[pla, 2, TRUE, TRUE]]; IF horOuts THEN terms _ GetHorOutsTermList[pla, flatOut, flatOutOrd] ELSE FOR term: PLAOps.Term _ pla.termList.end, term.last WHILE term#NIL DO terms _ CONS[PLAOps.CopyTerm[term], terms] ENDLOOP; FOR row IN [0..ta.size) DO extraRow: PROC RETURNS[BOOL] = {RETURN[(row-1+1) MOD (rowsPerExtra+1) = 0]}; SELECT TRUE FROM row=0 => {ta[row]_ GetRow[footer]; fillLt _ rowsPerDr-1}; row+1=nofRows => {ta[row]_ GetRow[header]}; fillLt#0 => {ta[row]_ GetRow[dataUp]; fillLt _ fillLt -1}; extraRow[] => {ta[row]_ GetRow[inCntc]}; cRow=flatOut.size => {ta[row]_ GetRow[dataUp]}; ~horOuts => {ta[row]_ GetRow[dataUp]}; --fillLt not used unless horOuts ENDCASE => {ta[row]_ GetRow[conn]; fillLt_rowsPerDr-1; cRow_cRow+1}; ENDLOOP; IF terms # NIL THEN ERROR; AddSubWireNamePrefix[to: out, prefix: "Nxt"]; cell _ TilingClass.CreateTiling[ public: CoreOps.CreateWire[LIST[in, out, vdd, gnd]], tileArray: ta, neighborX: TilingClass.LayoutNeighborX, neighborY: TilingClass.LayoutNeighborY, name: pla.name, props: NIL ]}; BufferedPLADrivers: PROC[pla: PLAOps.PLA] RETURNS[cell: CellType]={ vdd: Wire _ CoreOps.CreateWires[0, vddNm]; gnd: Wire _ CoreOps.CreateWires[0, gndNm]; lIn: Wire _ WireIconExtras.RefWire[pla.data, logicInNm, TRUE]; lOut: Wire _ WireIconExtras.RefWire[pla.out, logicOutNm, FALSE]; dIn: Wire _ WireIconExtras.RefWire[pla.data, inNm, FALSE]; dOut: Wire _ WireIconExtras.RefWire[pla.out, outNm, FALSE]; flatLIn: Wire _ CoreOps.CreateWire[Flatten[lIn]]; flatLOut: Wire _ CoreOps.CreateWire[Flatten[lOut]]; flatDIn: Wire _ CoreOps.CreateWire[Flatten[dIn]]; flatDOut: Wire _ CoreOps.CreateWire[Flatten[dOut]]; nofOrCols: NAT _ NofOrCols[flatDOut.size]; nofOrColExs: NAT _ nofOrCols/(outsPerExtra+1); nofOrColOut: NAT _ nofOrCols - nofOrColExs; nofAndCols: NAT _ flatDIn.size; ta: TilingClass.TileArray _ NEW[TilingClass.TileArrayRec[1]]; orIndex: INT _ 0; extras: INT _ 0; col: INT _ 0; TileIt: PROC[ cell: CellType, pubType: {in, out2, out1, filler} _ filler, index: INT _ 0] = { pas: LIST OF CoreCreate.PA _ LIST[[vddNm, vdd], [gndNm, gnd]]; SELECT pubType FROM in => pas _ CONS[[inNm, flatDIn[index]], CONS[[out0Nm, flatLIn[2*index+0]], CONS[[out1Nm, flatLIn[2*index+1]], pas]]]; out1 => pas _ CONS[[in0Nm, flatLOut[index]], CONS[[out0Nm, flatDOut[index]], pas]]; out2 => pas _ CONS[[in0Nm, flatLOut[index]], CONS[[out0Nm, flatDOut[index]], CONS[[in1Nm, flatLOut[index+1]], CONS[[out1Nm, flatDOut[index+1]], pas]]]]; ENDCASE; ta[0][col] _ NEW[TilingClass.TileRec _ [cell, pas]]; col _ col+1}; ta[0] _ NEW[TilingClass.TileRowRec [1 + nofAndCols + 1 + nofOrColExs + nofOrColOut/2 + 3]]; TileIt[ DriverTile[left], filler]; FOR i: INT IN [0..nofAndCols) DO TileIt[DriverTile[in], in, i] ENDLOOP; TileIt[ DriverTile[between], filler]; WHILE orIndex < nofOrCols DO IF ((orIndex+1) MOD (outsPerExtra+1)) = 0 THEN {TileIt[ DriverTile[extra], filler]; orIndex _ orIndex+1; extras _ extras+1; LOOP} ELSE {IF orIndex+1=nofOrCols THEN {TileIt[ DriverTile[out1], out1, orIndex-extras]; orIndex _ orIndex+1} ELSE {TileIt[ DriverTile[out2], out2, orIndex-extras]; orIndex _ orIndex+2}}; ENDLOOP; TileIt[ DriverTile[rightHalf], filler]; TileIt[ DriverTile[rightHalf], filler]; IF ((nofOrCols-extras) MOD 2)=0 THEN TileIt[ DriverTile[rightHalf], filler]; StripSubWireNames[lIn]; -- to prevent confusion with driver in subWire names StripSubWireNames[lOut]; -- to prevent confusion with driver out subWire names cell _ TilingClass.CreateTiling[ public: CoreOps.CreateWire[LIST[dIn, dOut, lIn, lOut, vdd, gnd]], tileArray: ta, neighborX: TilingClass.LayoutNeighborX, neighborY: TilingClass.LayoutNeighborY, name: pla.name, props: NIL ]}; GetCxParam: PROC[cx: Sisyph.Context, param: ROPE, default: INT_0] RETURNS[nofOrCols: INT] = { RETURN[IF (cx=NIL OR NOT Sisyph.FetchInt[cx, param].found) THEN default ELSE Sisyph.FetchInt[cx, param].value]}; NofOrCols: PROC[nofOuts: INT] RETURNS[nofOrCols: INT] = {RETURN[nofOuts+(nofOuts-1)/outsPerExtra]}; StripSubWireNames: PROC[w: Wire] = { IF w.size=0 THEN RETURN; FOR i: INT IN [0..w.size) DO []_CoreOps.SetShortWireName[w[i], NIL]; StripSubWireNames[w[i]]; ENDLOOP}; AddSubWireNamePrefix: PROC[to: Wire, prefix: ROPE] = { FOR i: INT IN [0..to.size) DO name1: ROPE _ CoreOps.GetShortWireName[to[i]]; []_CoreOps.SetShortWireName[to[i], Rope.Cat[prefix, name1]] ENDLOOP;}; AddSubWireNamePrefixIfDuplicate: PROC[ref, to: Wire, prefix: ROPE] = { FOR i: INT IN [0..to.size) DO name1: ROPE _ CoreOps.GetShortWireName[to[i]]; FOR j: INT IN [0..ref.size) DO IF CoreOps.GetShortWireName[ref[j]].Equal[name1] THEN EXIT; REPEAT FINISHED => LOOP ENDLOOP; []_CoreOps.SetShortWireName[to[i], Rope.Cat[prefix, name1]] ENDLOOP;}; GetHorOutsTermList: PROC[pla: PLAOps.PLA, flatOut, flatOutOrd: Wire] RETURNS[terms: LIST OF PLAOps.Term _ NIL] = { SeqTermLists: TYPE = REF SeqTermListsRec; SeqTermListsRec: TYPE = RECORD[SEQUENCE size: CARDINAL OF LIST OF PLAOps.Term]; TwoTermRecs: TYPE = RECORD[t1, t2: PLAOps.TermRec]; MoveFromTermsToOuts: PROC[idx: INT] = { terms _ CONS[NIL, terms]; IF terms=NIL THEN RETURN; FOR list: LIST OF PLAOps.Term _ terms, list.rest WHILE list.rest#NIL DO term: PLAOps.Term _ list.rest.first; IF PLAOps.GetOutQrt[term, idx]#one THEN {list.rest _ list.rest.rest; terms _ terms.rest; outs[idx] _ CONS[term, outs[idx]]; RETURN}; ENDLOOP; FOR list: LIST OF PLAOps.Term _ terms, list.rest WHILE list.rest#NIL DO term: PLAOps.Term _ list.rest.first; FOR i: INT IN [0..outs.size) DO IF PLAOps.GetOutQrt[term, i]#one THEN FOR sub: LIST OF PLAOps.Term _ outs[i], sub.rest WHILE sub#NIL DO IF PLAOps.GetOutQrt[sub.first, idx]#one THEN { [term^, sub.first^] _ TwoTermRecs[sub.first^, term^]; list.rest _ list.rest.rest; terms _ terms.rest; outs[idx] _ CONS[term, outs[idx]]; RETURN} ENDLOOP; ENDLOOP; REPEAT FINISHED => ERROR ENDLOOP}; outForm: PLAOps.Format _ REFBit.Desc[pla.out].bitForm; end: PLAOps.Term; outs: SeqTermLists _ NEW[SeqTermListsRec[pla.termList.outBits]]; FOR output: INT IN [pla.termList.length..flatOutOrd.size*2) DO terms _ CONS[PLAOps.NewTerm[pla.termList.inBits, pla.termList.outBits], terms] ENDLOOP; FOR term: PLAOps.Term _ pla.termList.end, term.last WHILE term#NIL DO terms _ CONS[PLAOps.CopyTerm[term], terms] ENDLOOP; FOR output: INT IN [0..flatOutOrd.size) DO outs[output] _ NIL ENDLOOP; FOR output: INT IN [0..flatOutOrd.size) DO FOR index: INT IN [0..flatOut.size) DO IF flatOutOrd[output] # flatOut[index] THEN LOOP; MoveFromTermsToOuts[ outForm[index].firstBit ]; MoveFromTermsToOuts[ outForm[index].firstBit ]; EXIT REPEAT FINISHED => ERROR ENDLOOP; ENDLOOP; FOR output: INT DECREASING IN [0..flatOutOrd.size) DO FOR index: INT IN [0..flatOut.size) DO IF flatOutOrd[output] # flatOut[index] THEN LOOP; FOR sub: LIST OF PLAOps.Term _ outs[outForm[index].firstBit], sub.rest WHILE sub#NIL DO terms _ CONS[sub.first, terms] ENDLOOP; outs[outForm[index].firstBit] _ NIL; ENDLOOP ENDLOOP; terms _ ReverseTerms[terms]; -- make last first UNTIL end#NIL DO end _ terms.first; terms _ terms.rest ENDLOOP; -- remove end term terms _ ReverseTerms[terms]; -- make first first terms _ CONS[end, terms]; -- add end term to beginning FOR list: LIST OF PLAOps.Term _ terms, list.rest WHILE list#NIL DO FOR i: CARDINAL IN [0..list.first.out.wdSize) DO IF list.first.out[i].d#0 THEN EXIT; REPEAT FINISHED => list.first _ NIL ENDLOOP ENDLOOP}; ReverseTerms: PROC[terms: LIST OF PLAOps.Term] RETURNS[new: LIST OF PLAOps.Term] = {FOR terms _ terms, terms.rest WHILE terms#NIL DO new_CONS[terms.first, new] ENDLOOP}; WireIndex: PROC[big, little: Wire] RETURNS[index: INT _ -1] = { FOR index: INT IN [0..big.size) DO IF big[index]=little THEN RETURN[index] ENDLOOP; RETURN[-1]}; ReorderWire: PROC[wire: Wire, names: ROPES, extras: BOOL _ FALSE] RETURNS[Wire] = { wires: Wires _ NIL; nms: ROPES _ CONS[NIL, names]; IF names=NIL THEN RETURN[wire]; IF extras THEN FOR nms _ nms, nms.rest WHILE nms.rest#NIL DO index: INT _ CoreOps.GetWireIndex[wire, nms.rest.first]; IF index=-1 THEN nms.rest _ nms.rest.rest REPEAT FINISHED => names _ nms.rest ENDLOOP; FOR names _ names, names.rest WHILE names#NIL DO index: INT _ CoreOps.GetWireIndex[wire, names.first]; IF index=-1 THEN { TerminalIO.PutF ["The icon wire name \"%g\" is not an element of the PLA interface.\n", IO.rope[names.first]]; ERROR}; wires _ CONS[wire[index], wires] ENDLOOP; RETURN[ CoreOps.CreateWire[ CoreOps.Reverse[wires]]]}; Flatten: PROC[wire: Wire] RETURNS[wires: Wires _ NIL] = { temp: Wires; IF wire.size=0 THEN RETURN[LIST[wire]]; FOR ii: INT IN [0..wire.size) DO FOR temp _ Flatten[wire[ii]], temp.rest WHILE temp#NIL DO wires _ CONS[temp.first, wires] ENDLOOP ENDLOOP; temp _ wires; wires _ NIL; FOR temp _ temp, temp.rest WHILE temp#NIL DO wires _ CONS[temp.first, wires] ENDLOOP}; GetSideNames: PROC[cx: PLAGen.Context, side: CoreGeometry.Side] RETURNS[names: ROPES] = { obj: CD.Object _ Sisyph.GetCDObj[cx]; cell: CellType _ NARROW[Sinix.ExtractCell[obj, Sisyph.mode, NIL, cx].result]; eachSortedPin: CoreGeometry.EachSortedPinProc = {names _ CONS[CoreOps.GetShortWireName[wire], names]}; []_CoreGeometry.EnumerateSortedSides [Sisyph.mode.decoration, cell, side, eachSortedPin]; names _ RopeList.Reverse[names]}; Get: PROC [orientation: {orig, flipY}, name: ROPE] RETURNS [cellType: CellType] ~ { libraryNm: IO.ROPE = "PLAGen"; library: CoreDirectory.Library _ CoreDirectory.FetchLibrary[libraryNm]; IF library=NIL THEN library _ CoreDirectory.RegisterLibrary[CoreDirectory.CreateLibrary[], libraryNm]; cellType _ CoreDirectory.Fetch[library, name]; IF cellType=NIL THEN { designName: IO.ROPE _ name.Substr[0, name.Find["."]]; cellName: IO.ROPE _ name.Substr[name.Index[0, "."]+1]; design: CD.Design _ CDViewer.FindDesign[designName]; cx: PLAGen.Context; IF design=NIL THEN { design _ CDIO.ReadDesign[designName]; IF design=NIL THEN ERROR; ViewerOps.CloseViewer[CDViewer.CreateViewer[design, FALSE]]}; -- caches the design cx _ Sisyph.Create[design]; cellType _ Sisyph.ExtractSchematicByName[name: cellName, cx: cx]; []_CoreDirectory.Insert[library, name, cellType]}; IF orientation=flipY THEN { flipName: IO.ROPE _ Rope.Cat["FlipY.", name]; flipCT: CellType _ CoreDirectory.Fetch[library, flipName]; IF flipCT = NIL THEN { flipCT _ PWCore.RotateCellType[cellType, $FlipY]; []_CoreDirectory.Insert[library, flipName, flipCT]}; cellType _ flipCT} }; DriverTile: PROC[key: DriverTileType] RETURNS[cell: CellType] = {SELECT key FROM left => cell _ Get[orig, "PLAGen.BPlaDrLeftSide.sch" ]; in => cell _ Get[orig, "PLAGen.BPlaDrDualIn.sch" ]; between => cell _ Get[orig, "PLAGen.BPlaDrBetween.sch" ]; extra => cell _ Get[orig, "PLAGen.BPlaDrOrEx.sch" ]; out2 => cell _ Get[orig, "PLAGen.BPlaDrDualOutInv.sch" ]; out1 => cell _ Get[orig, "PLAGen.BPlaDrSingOutInv.sch" ]; rightHalf => cell _ Get[orig, "PLAGen.BPlaDrRightSideHalf.sch" ]; ENDCASE}; BodyTile: PROC[key: BodyTileKey] RETURNS[cell: CellType] = {SELECT key FROM [header, glue, leftSide ] => cell _ Get[orig, "PLAGen.HPlaHLeftSide.sch" ]; [header, glue, between ] => cell _ Get[orig, "PLAGen.HPlaHBetween.sch" ]; [header, glue, rightSide ] => cell _ Get[orig, "PLAGen.HPlaHRightSide.sch" ]; [header, and, nc ] => cell _ Get[orig, "PLAGen.HPlaHAnd.sch" ]; [header, or, nc ] => cell _ Get[orig, "PLAGen.HPlaHOr.sch" ]; [header, or, extra ] => cell _ Get[orig, "PLAGen.HPlaHOrEx.sch" ]; [footer, glue, leftSide ] => cell _ Get[flipY, "PLAGen.HPlaHLeftSide.sch" ]; [footer, glue, between ] => cell _ Get[flipY, "PLAGen.HPlaHBetween.sch" ]; [footer, glue, rightSide ] => cell _ Get[flipY, "PLAGen.HPlaHRightSide.sch" ]; [footer, and, nc ] => cell _ Get[flipY, "PLAGen.HPlaHAnd.sch" ]; [footer, or, nc ] => cell _ Get[flipY, "PLAGen.BPlaHOr.sch" ]; [footer, or, extra ] => cell _ Get[flipY, "PLAGen.HPlaHOrEx.sch" ]; [blank, glue, leftSide ] => cell _ Get[orig, "PLAGen.HPlaBLeftSide.sch" ]; [blank, glue, between ] => cell _ Get[orig, "PLAGen.HPlaBBetween.sch" ]; [blank, glue, rightSide ] => cell _ Get[orig, "PLAGen.HPlaBRightSide.sch" ]; [blank, and, nc ] => cell _ Get[orig, "PLAGen.HPlaBAnd.sch" ]; [blank, or, nc ] => cell _ Get[orig, "PLAGen.BOr.sch" ]; [blank, or, extra ] => cell _ Get[orig, "PLAGen.HPlaBOrEx.sch" ]; [conn, glue, leftSide ] => cell _ Get[orig, "PLAGen.HPlaBLeftSide.sch" ]; [conn, glue, between ] => cell _ Get[orig, "PLAGen.HPlaBBetween.sch" ]; [conn, glue, rightSide ] => cell _ Get[orig, "PLAGen.HPlaCRightSide.sch" ]; [conn, and, nc ] => cell _ Get[orig, "PLAGen.HPlaBAnd.sch" ]; [conn, or, left ] => cell _ Get[orig, "PLAGen.COr.sch" ]; [conn, or, nc ] => cell _ Get[orig, "PLAGen.COrNC.sch" ]; [conn, or, extra ] => cell _ Get[orig, "PLAGen.HPlaCOrEx.sch" ]; [dataUp, glue, leftSide, big ] => cell _ Get[orig, "PLAGen.HPlaDLeftSideHot.sch" ]; [dataUp, glue, leftSide ] => cell _ Get[orig, "PLAGen.HPlaDLeftSide.sch" ]; [dataUp, glue, between ] => cell _ Get[orig, "PLAGen.HPlaDBetween.sch" ]; [dataUp, glue, rightSide ] => cell _ Get[orig, "PLAGen.HPlaDRightSide.sch" ]; [dataUp, and, left ] => cell _ Get[orig, "PLAGen.DAndLt.sch" ]; [dataUp, and, right ] => cell _ Get[orig, "PLAGen.DAndRt.sch" ]; [dataUp, and, nc ] => cell _ Get[orig, "PLAGen.DAnd.sch" ]; [dataUp, or, left ] => cell _ Get[orig, "PLAGen.DOr.sch" ]; [dataUp, or, nc ] => cell _ Get[orig, "PLAGen.DOrNC.sch" ]; [dataUp, or, extra ] => cell _ Get[orig, "PLAGen.HPlaDOrEx.sch" ]; [dataDn, glue, leftSide, big ] => cell _ Get[orig, "PLAGen.HPlaDLeftSideHot.sch" ]; [dataDn, glue, leftSide ] => cell _ Get[orig, "PLAGen.HPlaDLeftSide.sch" ]; [dataDn, glue, between ] => cell _ Get[flipY, "PLAGen.HPlaDBetween.sch" ]; [dataDn, glue, rightSide ] => cell _ Get[flipY, "PLAGen.HPlaDRightSide.sch" ]; [dataDn, and, left ] => cell _ Get[orig, "PLAGen.DAndLt.sch" ]; [dataDn, and, right ] => cell _ Get[orig, "PLAGen.DAndRt.sch" ]; [dataDn, and, nc ] => cell _ Get[orig, "PLAGen.DAnd.sch" ]; [dataDn, or, left ] => cell _ Get[flipY, "PLAGen.DOr.sch" ]; [dataDn, or, nc ] => cell _ Get[flipY, "PLAGen.DOrNC.sch" ]; [dataDn, or, extra ] => cell _ Get[flipY, "PLAGen.HPlaDOrEx.sch" ]; [inCntc, glue, leftSide ] => cell _ Get[orig, "PLAGen.HPlaInCntcLeftSide.sch" ]; [inCntc, glue, between ] => cell _ Get[orig, "PLAGen.HPlaInCntcBetween.sch" ]; [inCntc, glue, rightSide ] => cell _ Get[orig, "PLAGen.HPlaInCntcRightSide.sch" ]; [inCntc, and, nc ] => cell _ Get[orig, "PLAGen.HPlaInCntcAnd.sch" ]; [inCntc, or, nc ] => cell _ Get[orig, "PLAGen.HPlaInCntcOr.sch" ]; [inCntc, or, extra ] => cell _ Get[orig, "PLAGen.HPlaInCntcOrEx.sch" ]; ENDCASE}; nSqRes: REAL = 18.0; pSqRes: REAL = nSqRes*2.5; InDrRes: PROC[drSize: INT] RETURNS[REAL] = {RETURN[nSqRes * .5 / drSize]}; TermPURes: PROC[hot: BOOL] RETURNS[REAL] = {RETURN[IF hot THEN pSqRes*.75 ELSE pSqRes]}; OutPURes: PROC RETURNS[REAL] = {RETURN[pSqRes*.75]}; InCap: PROC[ldsPerIn, nofTerms: INT] RETURNS[REAL] = {RETURN[.03 + .0100 * ldsPerIn + .0015 * nofTerms]}; TermCap: PROC[ldsPerTerm, nofIns, nofOuts: INT] RETURNS[REAL] = {RETURN[.0020 * nofIns + .0100 * ldsPerTerm + .0012 * nofOuts]}; OutCap: PROC[ldsPerOut, nofTerms, drSize: INT] RETURNS[REAL] = {RETURN[.0010 * nofTerms + .0100 * ldsPerOut + .0100 * drSize*3.5]}; Estimates: PUBLIC PROC[ pla: PLAOps.PLA, drSize: INT _ 0, useHotTermPU: BOOL _ TRUE, showOutLoads: BOOL _ TRUE] RETURNS[rope: IO.ROPE] = { nofIns: INT _ REFBit.Desc[pla.data].bitForm.size; nofOuts: INT _ REFBit.Desc[pla.out].bitForm.size; nofTerms: INT _ pla.termList.length; ldsPerIn: INT _ MaxLdsPerIn[pla]; ldsPerTerm: INT _ MaxLdsPerTerm[pla].maxLoad; insPerTerm: INT _ MaxLdsPerTerm[pla].maxIns; outsPerTerm: INT _ MaxLdsPerTerm[pla].maxOuts; ldsPerOut: INT _ MaxLdsPerOut[pla]; hotTermT: INT _ IF useHotTermPU THEN HotTermThreshold[pla] ELSE ldsPerTerm; hotTerms: INT _ NofHotTerms[pla, hotTermT]; inCap: REAL _ InCap [ldsPerIn, nofTerms]; termCap: REAL _ TermCap [ldsPerTerm, nofIns, nofOuts]; outCap: REAL _ OutCap [ldsPerOut, nofTerms, drSize]; inRest: REAL _ InDrRes[drSize]; tUpRes: REAL _ TermPURes[useHotTermPU]; oUpRes: REAL _ OutPURes[]; tDnRes: REAL _ (tUpRes * nSqRes/2) / (tUpRes - nSqRes/2); oDnRes: REAL _ (oUpRes * nSqRes/2) / (oUpRes - nSqRes/2); inTime: REAL _ .7 * inCap * inRest; termUpTime: REAL _ .7 * termCap * tUpRes; termDnTime: REAL _ .7 * termCap * tDnRes; outUpTime: REAL _ .7 * outCap * oUpRes; outDnTime: REAL _ .7 * outCap * oDnRes; onTime: REAL _ inTime+termUpTime+outDnTime; offTime: REAL _ inTime+termDnTime+outUpTime; ros: IO.STREAM _ IO.ROS[]; ros.PutF["\nTiming and power estimates for PLA: %g\n", IO.rope[pla.name]]; ros.PutF[" BufUnits: %3g\n", IO.int[drSize]]; ros.PutF[" HotThld: %3g\n", IO.int[hotTermT]]; ros.PutF[" HotTerms: %3g\n", IO.int[hotTerms]]; ros.PutF[" Ins Terms Outs\n"]; ros.PutF[" Count: %3g %3g %3g\n", IO.int[nofIns], IO.int[nofTerms], IO.int[nofOuts] ]; ros.PutF[" Max Ld: %3g %3g %3g MxTIns: %3g MxTOuts: %3g\n", IO.int[ldsPerIn], IO.int[ldsPerTerm], IO.int[ldsPerOut], IO.int[insPerTerm], IO.int[outsPerTerm] ]; ros.PutF[" Max Cap: %6.2f %6.2f %6.2f pF\n", IO.real[inCap], IO.real[termCap], IO.real[outCap]]; ros.PutF[" Time Up: %6.2f %6.2f %6.2f nS\n", IO.real[inTime], IO.real[termUpTime], IO.real[outUpTime]]; ros.PutF[" Time Dn: %6.2f %6.2f %6.2f nS\n", IO.real[inTime], IO.real[termDnTime], IO.real[outDnTime]]; ros.PutF[" Times: Av:%6.2f On:%6.2f Off:%6.2f nS\n", IO.real[(onTime+offTime)/2], IO.real[onTime], IO.real[offTime] ]; ros.PutF[" Power: %6.2f mW\n\n", IO.real[25.0*(hotTerms/TermPURes[TRUE]+(nofTerms-hotTerms)/TermPURes[FALSE])]]; IF showOutLoads THEN { bitFormat: PLAOps.Format _ REFBit.Desc[pla.out].bitForm; ros.PutRope[" Out Lds:\n"]; FOR out: INT IN [0..bitFormat.size) DO ros.PutF[" %3g: %3g\n", IO.int[out], IO.int[OutLds[pla, out]]] ENDLOOP}; rope _ IO.RopeFromROS[ros]}; HotTermThreshold: PUBLIC PROC[pla: PLAOps.PLA] RETURNS[threshold: INT _ LAST[INT]] = { nofIns: INT _ REFBit.Desc[pla.data].bitForm.size; nofOuts: INT _ REFBit.Desc[pla.out].bitForm.size; max: INT _ MaxLdsPerTerm[pla].maxLoad; termCap: REAL _ TermCap [max, nofIns, nofOuts]; biasCap: REAL _ TermCap [0, nofIns, nofOuts]; factCap: REAL _ (termCap-biasCap)/max; -- sb .01 rRatio: REAL _ TermPURes[TRUE]/TermPURes[FALSE]; -- sb .75 nReal: REAL _ rRatio*max - (1 - rRatio)*biasCap/factCap; threshold _ Real.Round[nReal]}; TermLoad: PUBLIC PROC[pla: PLAOps.PLA, term: PLAOps.Term] RETURNS[load, ins, outs: INT _ 0] = { inForm: PLAOps.Format _ REFBit.Desc[pla.data].bitForm; outForm: PLAOps.Format _ REFBit.Desc[pla.out].bitForm; FOR in: INT IN [0..inForm.size) DO IF PLAOps.GetInQrt[term, inForm[in].firstBit]#dontcare THEN ins _ ins+1 ENDLOOP; FOR out: INT IN [0..outForm.size) DO IF PLAOps.GetOutQrt[term, outForm[out].firstBit]=one THEN outs _ outs+1 ENDLOOP; load _ ins+outs}; MaxLdsPerIn: PROC[pla: PLAOps.PLA] RETURNS[max: INT _ 0] = { IntSeq: TYPE = RECORD[SEQUENCE size: NAT OF INT]; inForm: PLAOps.Format _ REFBit.Desc[pla.data].bitForm; ones: REF IntSeq _ NEW[IntSeq[inForm.size]]; zeros: REF IntSeq _ NEW[IntSeq[inForm.size]]; FOR in: INT IN [0..inForm.size) DO ones[in] _ zeros[in] _ 0 ENDLOOP; FOR term: PLAOps.Term _ pla.termList.begin, term.next WHILE term#NIL DO FOR in: INT IN [0..inForm.size) DO SELECT PLAOps.GetInQrt[term, inForm[in].firstBit] FROM one => ones[in] _ ones[in] +1; zero => zeros[in] _ zeros[in] +1; ENDCASE ENDLOOP ENDLOOP; FOR in: INT IN [0..inForm.size) DO max _ MAX[ones[in], zeros[in], max] ENDLOOP}; MaxLdsPerTerm: PROC[pla: PLAOps.PLA] RETURNS[maxLoad, maxIns, maxOuts: INT _ 0] = { load, in, out: INT _ 0; FOR term: PLAOps.Term _ pla.termList.begin, term.next WHILE term#NIL DO [load, in, out] _ TermLoad[pla, term]; maxLoad _ MAX[maxLoad, load]; maxIns _ MAX[maxIns, in]; maxOuts _ MAX[maxOuts, out] ENDLOOP}; NofHotTerms: PROC[pla: PLAOps.PLA, threshold: INT] RETURNS[cnt: INT _ 0] = { FOR term: PLAOps.Term _ pla.termList.begin, term.next WHILE term#NIL DO IF TermLoad[pla, term].load > threshold THEN cnt _ cnt+1 ENDLOOP}; MaxLdsPerOut: PROC[pla: PLAOps.PLA] RETURNS[max: INT _ 0] = { bitFormat: PLAOps.Format _ REFBit.Desc[pla.out].bitForm; FOR out: INT IN [0..bitFormat.size) DO max _ MAX[max, OutLds[pla, out]] ENDLOOP}; OutLds: PROC[pla: PLAOps.PLA, out: INT] RETURNS[cnt: INT _ 0] = { bitFormat: PLAOps.Format _ REFBit.Desc[pla.out].bitForm; FOR term: PLAOps.Term _ pla.termList.begin, term.next WHILE term#NIL DO IF PLAOps.GetOutQrt[term, bitFormat[out].firstBit]=one THEN cnt _ cnt+1 ENDLOOP}; END. îPLAGenImpl.mesa Don Curry December 31, 1987 10:29:13 am PST Types, constants and globals Exported Procedures $latchedBot => cell _ XXX[pla]; $clockedRt => cell _ XXX[pla]; $latchedRt => cell _ XXX[pla]; AddSubWireNamePrefixIfDuplicate[ref: in, to: out, prefix: "Nxt"]; pins: LIST OF CoreGeometry.Instance _ NIL; CoreGeometry.AddPins[PWCore.extractMode.decoration, pub, pins]; PWCore.PutObject[PWCore.extractMode.decoration, cell, obj]}; []_PWCore.Layout[header]; []_PWCore.Layout[blank]; Domestic Procedures The type of PLA is implied by where the ouputs are specified in the icon. output names on right => output connections positioned every 3 rows `out' or output names on bottom => output connections at bottom `in' or input names may be on top or bottom or both. MainLine GetRow Code MainLine PLAOut Code AddSubWireNamePrefixIfDuplicate[ref: in, to: out, prefix: "Nxt"]; MainLine Code AddSubWireNamePrefixIfClash: PROC[to, ck: Wire, prefix: ROPE] = { FOR i: INT IN [0..to.size) DO CkClash: PROC [w: Wire] = {clash _ clash OR CoreOps.GetShortWireName[w].Equal[name]}; clash: BOOL _ FALSE; name: ROPE _ CoreOps.GetShortWireName[to[i]]; IF name.Length[] > 0 THEN [] _ CoreOps.VisitRootAtomics[ck, CkClash]; IF clash THEN []_CoreOps.SetShortWireName[to[i], Rope.Cat[prefix, name]]; IF to[i].size>0 THEN AddSubWireNamePrefixIfClash[to[i], ck, prefix]; ENDLOOP}; Assumes 3 rows per driver MoveOutsBackToTerms Tiles Performance estimation drSize=1 => pE=10 and nE=4, assume in driver size = out driver size Ê#A˜šœ™Jšœ+™+—J˜šÏk œ˜ Jšœœˆœr˜†—J˜šÏn œœœ˜Jšœœœ|œq˜ÿJšœ ˜Jš˜—headšÏl™Jšœœœ˜Jš œœœœœ˜"Jšœ œ˜!Jšœ œ ˜Jšœ œ˜Jšœœ5˜IJšœ œœ>˜XJšœ œ9˜GJšœœ˜%Jšœ œ:˜JJšœ œ˜Jšœ œ˜ Jšœœ˜,Jšœ œ ˜Jšœ œ ˜Jšœ œ˜Jšœ œ ˜Jšœ œ ˜Jšœ œ ˜Jšœ œ ˜Jšœ œ ˜Jšœ œ ˜Jšœ œ˜Jšœ œÏc˜*Jšœœ ˜*Jšœ œ˜Jšžœœœ˜—šŸ™šž œœœœœœœ˜RJšœœ ˜1Jšœœœ˜"šœ˜Jšœ%˜%Jšœ(˜(Jšœœ™Jšœ&œ˜,Jšœœ™Jšœœ™Jšœœ˜—J˜—šž œœ œœ˜>Jšœ&˜&Jšœ+˜+Jšœ4˜4Jšœ4˜4Jšœ?œ˜EJšœAœ˜HJšœ9œ˜@Jšœ9œ˜@šœ>˜>Jšœ˜Jšœ˜J˜J˜—šœ<˜Jšœœ˜Jšœ œ œž œ˜RJšœ œ˜#Jšœ œ˜%Jšœ œ˜$Jšœ œ˜#Jšœ œ˜!Jšœ œ˜!Jšœ œ˜!Jšœ œ˜!Jšœ œ˜%Jšœ œ˜"Jšœ œ˜%Jšœœ˜Jšœ œA˜MJšœœœ˜)Jšœœ/˜:Jšœœ0˜;Jšœ œA˜MJ•StartOfExpansionT -- [x1: D2Basic.Number, y1: D2Basic.Number, x2: D2Basic.Number, y2: D2Basic.Number]šœ œ'˜3Jšœ œœ˜'Jšœ6œ˜Pšœ œœ ˜šœ œœ˜Jšœ˜Jšœ+˜+Jšœœ(˜3Jšœœ!˜,Jšœœ6˜@Jšœœ7˜Ašœ œœœ˜ Jšœ œ˜ Jšœ˜Jšœ˜Jšœ œ˜—Jšœ œ!˜,Jšœ œ˜)Jšœœ#˜-Jš œœœ œœ˜EJšœ œ)˜4Jšœœ1˜;Jšœœ2˜Jšœ œ˜&Jšœ œ˜&Jšœ œœ)˜œ˜EJšœ9œ˜@Jšœ:œ˜AJšœ3˜3Jšœ5˜5Jšœ3˜3Jšœ5˜5Jšœ œ˜+Jšœ œ˜/Jšœ œ˜,Jšœ œ˜ Jšœœ˜AJšœ œ˜Jšœ œ˜Jšœœ˜šžœœ˜ Jšœ˜Jšœ+˜+Jšœœ ˜Jš œœœ œœ˜?šœ ˜šœ ˜ Jšœ˜Jšœ˜"Jšœ&˜*—šœ˜Jšœ˜Jšœ"˜&—šœ˜Jšœ˜Jšœ˜Jšœ˜ Jšœ&˜*—Jšœ˜—Jšœ œ$˜4Jšœ ˜ —šœœ˜"Jšœ8˜8—Jšœ"˜"Jš œœœœœ˜GJšœ%˜%šœ˜šœœ˜)JšœNœ˜Wšœœ˜JšœG˜KJšœI˜M——Jšœ˜—Jšœ'˜'Jšœ'˜'šœœ˜Jšœ(˜,—Jšœ 4˜LJšœ 5˜NJšœ ™ šœ ˜ Jšœœ"˜CJšœ˜Jšœ(˜(Jšœ(˜(Jšœ˜Jšœ œ˜—J˜—šž œœœ œ˜AJšœ œ˜š œœœœœ"˜:Jšœ œ$˜5—J˜—š ž œœ œœ œ˜8Jšœœ$˜+J˜—šžœœ ˜$Jšœ œœ˜šœœœ ˜Jšœ"œœ˜J—J˜—šžœœœ˜6šœœœ˜Jšœœ#˜.Jšœ<œ˜F—J˜—šžœœœ˜Fšœœœ˜Jšœœ#˜.šœœœ˜Jšœ/œœ˜;Jšœœœœ˜ —Jšœ<œ˜F—J˜—šžœœœ™Ašœœœ™šžœœ ™Jšœœ*™;—Jšœœœ™Jšœœ#™-Jšœœ,™EJšœœ<™IJšœœ1œ™N—J™—šžœœ œ˜Dšœœœœ˜-Jšœ™—Jšœœœ˜*Jšœœœœœœœœ˜OJšœœœ˜4šžœœœ˜'Jšœœœ ˜Jšœœœœ˜š œœœ œ œ˜GJšœ$˜$šœ!˜'Jšœ=œœ˜\—Jšœ˜—š œœœ œ œ˜GJšœ$˜$šœœœ˜ šœ˜%š œœœ!œœ˜Ašœ&œ˜.Jšœ5˜5Jšœ/˜/Jšœ œœ˜*—Jšœ˜——Jšœ˜—Jšœœœœ˜"——Jšœ7˜7Jšœ˜Jšœœ(˜Bšœ œœ*˜>JšœœCœ˜W—šœ1œœ˜EJšœœœ˜3—Jš œ œœœœœ˜Fšœ œœ˜*šœœœ˜&Jšœ%œœ˜1Jšœ/˜/Jšœ/˜/Jš œœœœœ˜&—Jšœ˜—Jšœ™š œ œ œœ˜5šœœœ˜&Jšœ%œœ˜1šœœœ6˜FJš œœœ œœ˜8—Jšœ œœœ˜5——Jšœ ˜0Jš œœœ'œ ˜RJšœ ˜1Jšœœ ˜6š œœœ œœ˜Bšœœœ˜0Jšœœœ˜#Jš œœœœœ˜5——J˜—šž œœœœœœœ˜RJš œœœœœœœ˜VJ˜—šž œœœœ ˜?šœœœ˜"Jšœœœœ˜0Jšœ˜ —J˜—š ž œœœ œœœ ˜SJšœœ˜Jšœœœœ ˜Jšœœœœ˜š œœœœ œ˜˜EJ˜—šž œœœ˜Jšœœ˜Jšœ œ˜Jšœœ˜šœœœ˜Jšœœœ˜—JšœC™CJšœ œ&˜2Jšœ œ%˜2Jšœ œ˜$Jšœ œ˜"Jšœ œ˜-Jšœ œ˜,Jšœ œ˜.Jšœ œ˜#Jš œ œœœœ ˜KJšœ œ˜,Jšœœ ˜,Jšœ œ)˜7Jšœ œ)˜6Jšœ œ˜ Jšœ œ˜(Jšœ œ˜Jšœ œ-˜:Jšœ œ-˜:Jšœ œ˜&Jšœ œ˜*Jšœ œ˜*Jšœ œ˜(Jšœ œ˜(Jšœ œ ˜-Jšœ œ ˜.Jš œœœœœ˜JšÏf8Ðfk£˜KJš£¤£˜.Jš£¤£˜0Jš£¤£˜0Jš£6˜6š£5˜5Jšœ ˜Jšœ˜Jšœ˜—š£S˜SJšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜—š£8˜8Jšœ ˜Jšœ˜Jšœ˜—š£8˜8Jšœ˜Jšœ˜Jšœ˜—š£8˜8Jšœ˜Jšœ˜Jšœ˜—š£8˜8Jšœ˜Jšœ˜Jšœ˜—š£$˜$Jšœœ œ˜O—šœ˜Jšœ8˜8Jš£˜šœœœ˜&Jš £¤£œ£¤£œ£˜DJšœ˜ ——Jšœœ˜J˜—šžœ œ œ˜.Jšœ œœœ˜'Jšœœ&˜1Jšœ œ%˜1Jšœœ˜'Jšœ œ"˜/Jšœ œ!˜.Jšœ œ(˜5Jšœœ œ œ  ˜:Jšœœ-˜9Jšœ!˜!J˜—šžœ œ œ˜Jš œœœœœœœ˜1Jšœ6˜6Jšœœ œ˜.Jšœœ œ˜/Jš œœœœœ˜Dšœ3œœ˜Gšœœœ˜"šœ,˜6Jšœ˜Jšœ!˜!Jšœœœ˜———Jš œœœœœœ˜PJ˜—šž œœ œ˜%Jšœœ ˜.Jšœœ˜šœ3œœ˜GJšœ&˜&Jšœ œ˜Jšœ œ˜Jšœ œœ˜%—J˜—š ž œœ œ œœœ ˜Mšœ3œœ˜GJšœ&œ œ˜B—J˜—š ž œœ œœœ ˜>Jšœ8˜8Jš œœœœœœ˜QJ˜—š žœœ œœœœ ˜AJšœ8˜8šœ3œœ˜Gšœ4˜6Jšœ œ˜———J˜—Jšœ˜—…—|,¥[