DIRECTORY Atom USING [GetPName], CD, CDBasics, CDCells, CDCommandOps, CDDirectory, CDExtras, CDImports, CDInstances USING [InstRectO], CDIO, CDMenus, CDOps USING [DrawDesign], CDOrient, CDPolygons, CDProperties, CDSequencer USING [Command, ImplementCommand], CDVArrow USING [RemoveArrow, ShowArrow], CStitching, FS USING [StreamOpen, Error], IO USING [Close, CR, int, PutChar, PutF, PutFR, PutRope, PutText, refAny, rope, STREAM, time], List USING [Append, Nconc], Rope, TerminalIO; BrandyCIFter: CEDAR PROGRAM IMPORTS Atom, CD, CDInstances, CDBasics, CDCells, CDCommandOps, CDDirectory, CDExtras, CDImports, CDIO, CDMenus, CDOps, CDOrient, CDProperties, CDSequencer, CDVArrow, CStitching, FS, IO, List, Rope, TerminalIO = BEGIN CIFUnits: TYPE = CD.Number; CIFPos: TYPE = CD.Position -- in CIFUnits -- ; CIFRect: TYPE = CD.Rect -- in CIFUnits -- ; Rational: TYPE = RECORD [num, denom: INT _ 1]; CIFDest: TYPE = REF CIFDestRec _ NIL; CIFDestRec: TYPE = RECORD [ cifDest: Rope.ROPE, deltaRadius: INT -- nanometers, + means cif is bigger ]; CIFDirective: TYPE = REF CIFDirectiveRec _ NIL; CIFDirectiveRec: TYPE = RECORD [ cdSource: CD.Layer, cifDest: Rope.ROPE, deltaRadius: INT -- nanometers, + means cif is bigger ]; MaskStateRef: TYPE = REF MaskState; MaskState: TYPE = RECORD [ areas, unbloated: CStitching.Tesselation _ NIL, partClip: CIFRect _ [0,0,0,0] ]; cellIDKey: REF ATOM = NEW[ATOM _ $CellIDNo]; --not accessible debugging: BOOL _ FALSE; -- used for printing out debugging messages design: CD.Design ; topLayerOb: CD.Object _ NEW[CD.ObjectRep]; stopFlag: REF BOOL _ NEW[BOOL]; clearIDsRec: CD.DrawRef; cifMeasureR: CD.DrawRef; cifDrawRec: CD.DrawRef; --for CIFOrArea contextFilter: REF CD.ContextFilter _ NEW[CD.ContextFilter_ALL[FALSE]]; --used in cifDrawRec infinity: CD.Number _ LAST[CD.Number]; --used to initialize sizes and positions cellCount: INTEGER _ 0; -- used to assign consecutive CIF ID numbers cifLayerAnnounced: BOOL _ FALSE; lambda: INT _ 0; cifPerLambda: INT _ 100; -- CIF units per lambda nmPerCIF: INT = 10; -- nanometers per CIF unit, given 0.01 microns per CIF unit cifScaling: Rational; cifFile: IO.STREAM; -- file where output will be stored comment, base, name, lastCellName: Rope.ROPE; mainRect: CD.Rect _ [x1: infinity, y1: infinity, x2: -infinity, y2: -infinity]; cifDirectives: LIST OF REF ANY -- CIFDirective -- _ NIL; currentDirective: CIFDirective; partWidth: CIFUnits _ 800000; -- window for flat CIF partHeight: CIFUnits _ 800000; FreeStorage: PROC [] = BEGIN cifMeasureR _ clearIDsRec _ cifDrawRec _ NIL; --help the garbage collector design _ NIL; cifFile _ NIL; END; SimpleRect: PROC [ob: CD.Object] RETURNS [yes: BOOL] = INLINE BEGIN yes _ ob.class.wireTyped AND ob.class.objectType=$Rect END; specialProperties: CDProperties.PropRef _ CDProperties.InitPropRef[]; SetUpSpecialClasses: PROC [] = BEGIN x: REF _ CDProperties.GetProp[$CDxCIFRegistrations, $CDxCIFTechnology]; IF design.technology.key#x AND design.technology#x THEN { IF x=NIL THEN TerminalIO.WriteRope["CIF commandfile not set up\n"] ELSE TerminalIO.WriteRope["CIF commandfile set up for different technology\n"]; ERROR ABORTED }; WITH CDProperties.GetProp[$CDxCIFRegistrations, $CDxCIFSpecials] SELECT FROM pRef: CDProperties.PropRef => specialProperties _ pRef; ENDCASE => specialProperties _ CDProperties.InitPropRef[]; END; IsSpecialCase: PROC [ob: CD.Object] RETURNS [yes: BOOL_FALSE] = BEGIN yes _ CDProperties.GetListProp[specialProperties^, ob.class.objectType]#NIL END; HandleSpecialCase: PROC [ob: CD.Object] = BEGIN WITH CDProperties.GetListProp[specialProperties^, ob.class.objectType] SELECT FROM rp: REF PROC [ob: CD.Object] RETURNS [Rope.ROPE] => { r: Rope.ROPE _ rp^[ob]; IO.PutRope[cifFile, r]; }; r: Rope.ROPE => { IO.PutRope[cifFile, r]; }; rt: REF TEXT => { IO.PutText[cifFile, rt]; }; ENDCASE => { TerminalIO.WriteRope["object class handled specialy is inconsistent\n"]; ERROR }; END; ClearCellIDs: PROC [inst: CD.Instance, pos: CD.Position, orient: CD.Orientation, pr: REF CD.DrawInformation] -- CD.DrawProc -- = BEGIN CDProperties.PutObjectProp[onto: inst.ob, prop: cellIDKey, val: NIL]; IF inst.ob.class.inDirectory THEN inst.ob.class.drawMe[inst: inst, pos: inst.location, orient: inst.orientation, pr: clearIDsRec]; END; SymHeader: PROC [obj: CD.Object]= BEGIN cellID: REF INT _ NEW[INT _ (cellCount _ cellCount + 1)]; cifFile.PutF["DS %d %d %d;\n", IO.int[cellCount], IO.int[cifScaling.num], IO.int[cifScaling.denom]]; CDProperties.PutObjectProp[obj, cellIDKey, cellID]; END; UnfinishedSymHeader: PROC [obj: CD.Object]= BEGIN cellID: REF INT _ NEW[INT _ (cellCount _ cellCount + 1)]; cifFile.PutF["DS %d ", IO.int[cellCount]]; CDProperties.PutObjectProp[obj, cellIDKey, cellID]; END; SymTrailer: PROC = BEGIN cifFile.PutRope["DF;\n"]; END; CallChildren: CD.DrawProc = BEGIN IF ~SimpleRect[inst.ob] THEN { cifPos: CIFPos _ CDPosToCIF[CDOrient.MapPoint[ pointInCell: [0, 0], cellSize: inst.ob.size, cellInstOrient: orient, cellInstPos: pos ]]; CIFSymbolCall[inst.ob, orient, cifPos]; }; END; DrawRectChilds: CD.DrawProc = BEGIN IF SimpleRect[inst.ob] THEN { inst.ob.class.drawMe[inst: inst, pos: pos, orient: orient, pr: pr]; }; END; GenerateCompositeOb: PROC [obj: CD.Object] = BEGIN cf: REF CD.ContextFilter _ NEW[CD.ContextFilter_ALL[FALSE]]; pr: CD.DrawRef _ CD.CreateDrawRef[[ stopFlag: stopFlag, design: design ]]; dummyInst: CD.Instance = NEW[CD.InstanceRep]; dummyInst^.ob _ obj; SymHeader[obj]; CIFDrawCellName[CDDirectory.Name[obj]]; pr.contextFilter _ NIL; pr.drawChild _ CallChildren; pr.drawRect _ IgnoreRect; obj.class.drawMe[dummyInst, [0, 0], CD.original, pr]; pr.drawChild _ DrawRectChilds; pr.contextFilter _ cf; pr.drawRect _ CIFOrArea; FOR directives: LIST OF REF ANY _ cifDirectives, directives.rest WHILE directives#NIL DO currentDirective _ NARROW[directives.first]; cifLayerAnnounced _ FALSE; cf[currentDirective.cdSource] _ TRUE; obj.class.drawMe[dummyInst, [0, 0], CD.original, pr]; cf[currentDirective.cdSource] _ FALSE; ENDLOOP; SymTrailer[]; END; GenerateGeometricOb: PROC [obj: CD.Object] = BEGIN dummyInst: CD.Instance = NEW[CD.InstanceRep]; dummyInst^.ob _ obj; IF IsSpecialCase[obj] THEN { UnfinishedSymHeader[obj]; HandleSpecialCase[obj] } ELSE { SymHeader[obj]; FOR directives: LIST OF REF ANY _ cifDirectives, directives.rest WHILE directives#NIL DO currentDirective _ NARROW[directives.first]; cifLayerAnnounced _ FALSE; cifDrawRec.contextFilter _ contextFilter; contextFilter[currentDirective.cdSource] _ TRUE; obj.class.drawMe[inst: dummyInst, pos: [0, 0], orient: 0, pr: cifDrawRec]; contextFilter[currentDirective.cdSource] _ FALSE; ENDLOOP; }; SymTrailer[]; END; MustCompensate: PROC [layer: CD.Layer] RETURNS [BOOL] = BEGIN FOR dList: LIST OF REF ANY _ cifDirectives, dList.rest WHILE dList#NIL DO cdir: CIFDirective _ NARROW[dList.first]; IF cdir.cdSource=layer AND cdir.deltaRadius#0 THEN RETURN [TRUE]; ENDLOOP; RETURN [FALSE]; END; GeneratePolygon: PROC [obj: CD.Object] = BEGIN CifPolygonOut: PROC [points: LIST OF CD.Position] = BEGIN IF NOT cifLayerAnnounced THEN { cifFile.PutF["L %g;\n", IO.rope[currentDirective.cifDest]]; cifLayerAnnounced _ TRUE; }; cifFile.PutRope["P"]; FOR p: LIST OF CD.Position _ points, p.rest WHILE p#NIL DO cifPos: CD.Position = CDPosToCIF[p.first]; cifFile.PutRope[" "]; CIFOutPoint[cifPos.x, cifPos.y]; ENDLOOP; cifFile.PutRope[";\n"]; END; polygon: CDPolygons.PolygonPtr = NARROW[obj.specificRef]; bad: BOOL _ FALSE; SymHeader[obj]; FOR directives: LIST OF REF ANY _ cifDirectives, directives.rest WHILE directives#NIL DO currentDirective _ NARROW[directives.first]; cifLayerAnnounced _ FALSE; IF currentDirective.cdSource=obj.layer THEN { IF currentDirective.deltaRadius#0 THEN bad _ TRUE; CifPolygonOut[polygon.points]; } ENDLOOP; IF bad THEN { TerminalIO.WriteRope["**polygon shrink or bloat failed\n"]; cifFile.PutRope["( polygon has incorrect sizing );\n"]; }; SymTrailer[]; END; CIFDefineObject: PROC [obj: CD.Object] = BEGIN CheckChildren: CDDirectory.EnumerateObjectsProc = BEGIN CIFDefineObject[me] END; IF debugging THEN TerminalIO.WriteRope["Entering CIFDefineObject\n"]; IF CDProperties.GetObjectProp[obj, cellIDKey]#NIL THEN RETURN; IF obj.class.inDirectory THEN { CDDirectory.EnumerateChildObjects[obj, CheckChildren, NIL]; }; WITH obj.specificRef SELECT FROM refPtr: CDImports.ImportPtr => { IF refPtr.boundInstance=NIL THEN ERROR; CIFDefineObject[refPtr.boundInstance.ob]; SymHeader[obj]; CIFDrawCellName[Rope.Cat[refPtr.designName, ".", refPtr.objectName]]; CIFSymbolCall[refPtr.boundInstance.ob, CD.original, [0, 0]]; SymTrailer[]; }; cellptr: CD.CellPtr => { SymHeader[obj]; CIFDrawCellName[cellptr.name]; FOR instList: CD.InstanceList _ cellptr.contents, instList.rest WHILE instList#NIL DO IF ~SimpleRect[instList.first.ob] THEN { cifPos: CIFPos _ CDPosToCIF[CDOrient.MapPoint[ pointInCell: [0, 0], cellSize: instList.first.ob.size, cellInstOrient: instList.first.orientation, cellInstPos: instList.first.location ]]; CIFSymbolCall[instList.first.ob, instList.first.orientation, cifPos]; }; ENDLOOP; FOR directives: LIST OF REF ANY _ cifDirectives, directives.rest WHILE directives#NIL DO currentDirective _ NARROW[directives.first]; cifLayerAnnounced _ FALSE; FOR instList: CD.InstanceList _ cellptr.contents, instList.rest WHILE instList#NIL DO IF SimpleRect[instList.first.ob] THEN { instList.first.ob.class.drawMe[inst: instList.first, pos: instList.first.location, orient: instList.first.orientation, pr: cifDrawRec]; }; ENDLOOP; ENDLOOP; FOR instList: CD.InstanceList _ cellptr.contents, instList.rest WHILE instList#NIL DO value: REF = CDProperties.GetInstanceProp[instList.first, $SignalName]; IF value#NIL THEN IF SimpleRect[instList.first.ob] THEN CIFLabelTerminalIO[instList.first, NARROW[value], instList.first.ob.layer]; ENDLOOP; SymTrailer[]; }; -- of cell ENDCASE => -- crazy or transistors or geometry or rects IF obj.class.inDirectory THEN GenerateCompositeOb[obj] ELSE IF ~SimpleRect[obj] THEN { IF ISTYPE[obj.specificRef, CDPolygons.PolygonPtr] AND ~MustCompensate[obj.layer] THEN GeneratePolygon[obj] ELSE GenerateGeometricOb[obj]; }; IF debugging THEN TerminalIO.WriteRope["Leaving CIFDefineObject\n"]; END; CIFSymbolCall: PROC[ob: CD.Object, orient: CD.Orientation_CDOrient.original, pos: CD.Position_ [0,0]] = BEGIN WITH CDProperties.GetObjectProp[ob, cellIDKey] SELECT FROM iDNo: REF INT => { cifFile.PutF["C %d", IO.int[iDNo^]]; IF orient#CD.original THEN { xrotation: CD.Orientation _ CDOrient.ConcentrateOnRotate90[orient]; IF xrotation#CD.original THEN { cifFile.PutRope[" R "]; cifFile.PutRope[SELECT xrotation FROM CDOrient.rotate90 => "0,1" , CDOrient.rotate180=> "-1,0", CDOrient.rotate270=> "0,-1", ENDCASE=> "1,0" -- never use this one ]; }; IF CDOrient.IncludesMirrorX[orient] THEN cifFile.PutRope[" M X "]; }; IF pos#[0, 0] THEN { cifFile.PutRope[" T "]; CIFOutPoint[pos.x, pos.y]; }; cifFile.PutRope[";\n"]; }; ENDCASE => ERROR; END; CIFMeasureArea: PROC [r: CD.Rect, l: CD.Layer, pr: CD.DrawRef] = BEGIN r _ CDBasics.NormalizeRect[r]; IF CDBasics.NonEmpty[r] THEN mainRect _ CDBasics.Surround[mainRect, r]; END; IgnoreRect: PROC [r: CD.Rect, l: CD.Layer, pr: CD.DrawRef] = BEGIN END; CIFOrArea: PROC [r: CD.Rect, l: CD.Layer, pr: CD.DrawRef] = BEGIN IF l=currentDirective.cdSource AND CDBasics.NonEmpty[r] THEN { rd: CIFRect _ CDBasics.Extend[CDRectToCIF[CDBasics.NormalizeRect[r]], currentDirective.deltaRadius/nmPerCIF]; IF CDBasics.NonEmpty[rd] THEN CIFRectOut[rd]; } END; CIFRectOut: PROC [r: CIFRect] = BEGIN IF NOT CDBasics.NonEmpty[r] THEN RETURN; IF NOT cifLayerAnnounced THEN { cifFile.PutF["L %g;\n", IO.rope[currentDirective.cifDest]]; cifLayerAnnounced _ TRUE; }; cifFile.PutF["B %d %d %d %d;\n",-- BOX command, in prescaled CIF units IO.int[PrescaleCIF[[r.x2-r.x1, 1]]] -- length -- , IO.int[PrescaleCIF[[r.y2-r.y1, 1]]] -- width -- , IO.int[PrescaleCIF[[r.x1+r.x2, 2]]] -- center x-coord -- , IO.int[PrescaleCIF[[r.y1+r.y2, 2]]] -- center y-coord -- ]; END; CIFOutPoint: PROC[x, y: CIFUnits] = BEGIN cifFile.PutF["%d,%d", IO.int[PrescaleCIF[[x,1]]], IO.int[PrescaleCIF[[y,1]]]] END; CIFLabelTerminalIO: PROC [ap: CD.Instance, s: Rope.ROPE, lev: CD.Layer] = BEGIN pt: CD.Position _ CDBasics.Center[CDRectToCIF[CDInstances.InstRectO[ap]]]; cifFile.PutF["94 %s ", IO.rope[s]]; CIFOutPoint[pt.x, pt.y]; cifFile.PutChar[' ]; FOR directives: LIST OF REF ANY _ cifDirectives, directives.rest WHILE directives#NIL DO d: CIFDirective = NARROW[directives.first]; IF d.cdSource = lev THEN {cifFile.PutRope[d.cifDest]; EXIT}; ENDLOOP; cifFile.PutRope[";\n"]; END; CIFDrawCellName: PROC[s: Rope.ROPE] = BEGIN lastCellName _ s; IF ~Rope.IsEmpty[s] THEN cifFile.PutF["9 %s;\n", IO.rope[s]]; END; RopeNeeded: SIGNAL [ ref: REF REF ] = CODE; ToRope: PROC [ ref: REF ] RETURNS [ rope: Rope.ROPE ] = BEGIN WITH ref SELECT FROM r: Rope.ROPE => rope _ r; rt: REF TEXT => rope _ Rope.FromRefText[rt]; a: ATOM => rope _ Atom.GetPName[a]; ri: REF INT => rope _ IO.PutFR[format: "%d", v1: IO.int[ri^]]; ENDCASE => IF ref=NIL THEN rope _ NIL ELSE { refRef: REF REF = NEW[REF _ ref]; SIGNAL RopeNeeded[refRef]; rope _ ToRope[refRef^ ! RopeNeeded => GOTO NoHelp]; EXITS NoHelp => ERROR; }; END; CantRepresentExactly: ERROR = CODE; SetCIFScaling: PROC = BEGIN cifScaling _ ReduceTerms[[num: cifPerLambda, denom: 2*lambda]]; -- the "2" allows for box centers FOR directives: LIST OF REF ANY _ cifDirectives, directives.rest WHILE directives#NIL DO d: CIFDirective = NARROW[directives.first]; cif: CIFUnits; IF ABS[d.deltaRadius] MOD nmPerCIF # 0 THEN ERROR CantRepresentExactly; cif _ ABS[d.deltaRadius]/nmPerCIF; cifScaling _ ReduceTerms[[ num: cifScaling.num, denom: (cifScaling.num*cifScaling.denom)/GCD[cif*cifScaling.denom, cifScaling.num]]]; ENDLOOP; END; CollectDirectives: PROC = BEGIN GetDirectives: PROC [p: REF, lev: CD.Layer] RETURNS [l: LIST OF REF ANY _ NIL] = BEGIN IF p#NIL THEN { WITH p SELECT FROM d: CIFDest => l _ LIST[NEW[CIFDirectiveRec _ [lev, d.cifDest, d.deltaRadius]]]; list: LIST OF REF ANY => l _ List.Append[GetDirectives[list.first, lev], GetDirectives[list.rest, lev]]; ENDCASE => l _ LIST[NEW[CIFDirectiveRec _ [lev, ToRope[p], 0]]]; }; END; cifDirectives _ NIL; FOR layers: LIST OF CD.Layer _ design.technology.usedLayers, layers.rest WHILE layers#NIL DO cifDirectives _ List.Append[GetDirectives[CDProperties.GetLayerProp[layers.first, $CDxCIFRegistrations], layers.first ], cifDirectives]; ENDLOOP; END; GetCifPerLambda: PROC [] = BEGIN WITH CDProperties.GetProp[$CDxCIFRegistrations, $CDxCIFUnitsPerLambda] SELECT FROM ri: REF INT => cifPerLambda _ ri^; ENDCASE => cifPerLambda _ 0; TerminalIO.WriteF["using %g cif units per lambda; (1 CIF unit = 0.01 microns)\n", IO.int[cifPerLambda]] ; IF cifPerLambda<2 THEN { TerminalIO.WriteRope["cif units per lambda not reasonable\n"]; ERROR ABORTED }; END; WriteCIF: PROC[ comm: CDSequencer.Command] = BEGIN ENABLE BEGIN ABORTED => {TerminalIO.WriteRope[" CIF generation aborted\n"]; GOTO Exit}; RopeNeeded => { explanation: Rope.ROPE = IO.PutFR[format: "Please enter a string corresponding to the value %g: ", v1: IO.refAny[ref^]]; ref^ _ TerminalIO.RequestRope[explanation]; RESUME; }; UNWIND => NULL; END; --enable mainOb: CD.Object; cifKey: Rope.ROPE; design _ comm.design; lambda _ design.technology.lambda; SetUpSpecialClasses[]; cifKey _ ToRope[CDProperties.GetProp[$CDxCIFRegistrations, $CDxCIFName]]; IF Rope.IsEmpty[cifKey] THEN { TerminalIO.WriteRope["The CIF layers for this technology are not yet defined\n"]; RETURN }; TerminalIO.WriteRopes["Use CIF layer definitions [", cifKey, "]\n"]; mainOb _ CDExtras.CreateDummyObject[design]; cellCount _ 0; stopFlag^ _ FALSE; cifMeasureR _ CD.CreateDrawRef[[ interestClip: CDBasics.universe, drawRect: CIFMeasureArea, stopFlag: stopFlag, design: design ]]; clearIDsRec _ CD.CreateDrawRef[[ interestClip: CDBasics.universe, drawChild: ClearCellIDs, stopFlag: stopFlag, design: design ]]; cifDrawRec _ CD.CreateDrawRef[[ interestClip: CDBasics.universe, drawRect: CIFOrArea, stopFlag: stopFlag, design: design ]]; CDOps.DrawDesign[design, cifMeasureR]; GetCifPerLambda[]; TerminalIO.WriteRopes["Input name of CIF file (", CDIO.GetWorkingDirectory[design], ") "]; base _ TerminalIO.RequestRope[">> "]; IF Rope.IsEmpty[base] THEN base _ design.name; IF Rope.IsEmpty[base] THEN base _ "foo"; name _ CDIO.MakeName[base: base, ext: ".cif", wDir: CDIO.GetWorkingDirectory[design]]; cifFile _ FS.StreamOpen[name, create ! FS.Error => IF error.group#bug THEN { TerminalIO.WriteRope[error.explanation]; GOTO FileIOOpenFailed; } ]; cifFile.PutF["( %s );\n", IO.rope[design.name]]; cifFile.PutF["( generated %g by Xerox PARC ChipNDale and BrandyCIFter);\n", IO.time[]]; cifFile.PutF["( with lambda = %d CIF units );\n", IO.int[cifPerLambda]]; cifFile.PutF["( origin = [x: 0, y: 0], size = [x: %d, y: %d] CIF units );\n", IO.int[CDCIFUp[mainRect.x2-mainRect.x1]], IO.int[CDCIFUp[mainRect.y2-mainRect.y1]]]; cifFile.PutF["( CIF KEY: %s );\n", IO.rope[cifKey]]; TerminalIO.WriteRope["Comment line: \n"]; comment _ TerminalIO.RequestRope["(parens must balance, CR for no comment)\n>> "]; WHILE NOT Rope.IsEmpty[comment] DO cifFile.PutF["( %s );\n", IO.rope[comment]]; comment _ NIL; TerminalIO.WriteRope["Another comment line:\n"]; comment_TerminalIO.RequestRope[ "(parens must balance, CR for no further comments)\n>> "]; ENDLOOP; IO.PutChar[cifFile, IO.CR]; CollectDirectives[]; SetCIFScaling[]; ClearCellIDs[design.actual.first.dummyCell, [0,0], 0, NIL]; CIFDefineObject[mainOb]; SymHeader[topLayerOb]; CIFSymbolCall[mainOb]; FOR directives: LIST OF REF ANY _ cifDirectives, directives.rest WHILE directives#NIL DO currentDirective _ NARROW[directives.first]; IF currentDirective.deltaRadius<0 THEN { cifLayerAnnounced _ FALSE; MakeFlatCIF[rectProc: CIFRectOut]; }; ENDLOOP; SymTrailer[]; CIFSymbolCall[topLayerOb, CDOrient.original, [x: -cifPerLambda*mainRect.x1/lambda, y: -cifPerLambda*mainRect.y2/lambda]]; cifFile.PutRope[";\nEnd ... \n"]; cifFile.Close[]; TerminalIO.WriteF["\nCIF file on %g\n", IO.rope[name]]; FreeStorage[] EXITS Exit => FreeStorage[]; FileIOOpenFailed => { TerminalIO.WriteRope["Open Failed\n"]; FreeStorage[] }; END; -- of WriteCIF MakeFlatCIF: PROC [rectProc: PROC[rect: CIFRect]] = BEGIN AnalyzeTesselation: PROC [ms: MaskStateRef] = BEGIN active: CD.Rect = ms.areas.BBox[]; IF CDBasics.NonEmpty[active] THEN { IF currentDirective.deltaRadius#0 THEN { t: CStitching.Tesselation = ms.unbloated; ms.unbloated _ ms.areas; ms.areas _ t; CStitching.ChangeRect[plane: ms.areas, rect: CDBasics.universe, new: NIL]; IF currentDirective.deltaRadius>0 THEN CStitching.EnumerateArea[plane: ms.unbloated, rect: CDBasics.universe, eachTile: BloatTile, data: ms] ELSE { CStitching.ChangeRect[plane: ms.areas, rect: active, new: $covered]; CStitching.EnumerateArea[plane: ms.unbloated, rect: CDBasics.universe, eachTile: BloatTile, data: ms, skip: $covered]; }; }; CStitching.EnumerateArea[plane: ms.areas, rect: CDBasics.universe, eachTile: OutputTile, data: ms]; TerminalIO.WriteRope["."]; }; END; OutputTile: PROCEDURE [tile: CStitching.Tile, data: REF ANY] = -- CStitching.PerTileProc BEGIN -- only called on tiles with non-NIL values ms: MaskStateRef = NARROW[data]; cr: CIFRect = CDBasics.Intersection[tile.Area, ms.partClip]; rectProc[cr]; END; designClip: CD.Rect; cifClip: CD.Rect; nRows, nCols: NAT; ms: MaskStateRef = NEW[MaskState _ [ areas: CStitching.NewTesselation[], unbloated: CStitching.NewTesselation[] ]]; dr: CD.DrawRef _ CD.CreateDrawRef[[ drawRect: NoteBoundingBox, devicePrivate: NEW[CD.Rect _ CDBasics.empty], interestClip: CDBasics.universe, stopFlag: stopFlag, design: design ]]; CDOps.DrawDesign[design, dr]; -- measure design designClip _ NARROW[dr.devicePrivate, REF CD.Rect]^; cifClip _ Bloat[CDRectToCIF[designClip], ABS[currentDirective.deltaRadius]/nmPerCIF]; nRows _ Ceiling[num: cifClip.y2-cifClip.y1, denom: partHeight]; nCols _ Ceiling[num: cifClip.x2-cifClip.x1, denom: partWidth]; TerminalIO.WriteF[".. in %d rows and %d columns..", IO.int[nRows], IO.int[nCols]]; dr.drawRect _ NoteRectangle; dr.devicePrivate _ ms; TerminalIO.WriteF["%s -> %s", IO.rope[ToRope[CD.LayerKey[currentDirective.cdSource]]], IO.rope[currentDirective.cifDest]]; FOR col: NAT IN [0..nCols) DO FOR row: NAT IN [0..nRows) DO ms.areas.ChangeRect[rect: CDBasics.universe, new: NIL]; ms.partClip _ [ -- in cif space x1: cifClip.x1+col*partWidth, y1: cifClip.y1+row*partHeight, x2: cifClip.x1+col*partWidth+MIN[partWidth, cifClip.x2-cifClip.x1-partWidth*col], y2: cifClip.y1+row*partHeight+MIN[partHeight, cifClip.y2-cifClip.y1-partHeight*row] ]; dr.interestClip _ CIFRectToCD[CDBasics.Extend[ms.partClip, 1+ABS[currentDirective.deltaRadius]/nmPerCIF]]; CDVArrow.ShowArrow[design: design, pos: [x: (dr.interestClip.x1+dr.interestClip.x2)/2, y: (dr.interestClip.y1+dr.interestClip.y2)/2]]; -- keep user happy CDOps.DrawDesign[design, dr]; -- build tesselation of the relevant design rectangle AnalyzeTesselation[ms]; -- sends the current part to s ENDLOOP; ENDLOOP; TerminalIO.WriteRope["+"]; CDVArrow.RemoveArrow[design: design]; dr _ NIL; END; NoteBoundingBox: PROC [ r: CD.Rect, l: CD.Layer, pr: CD.DrawRef ] = BEGIN bb: REF CD.Rect = NARROW[pr.devicePrivate]; bb^ _ CDBasics.Surround[bb^, r]; END; NoteRectangle: PROC [r: CD.Rect, l: CD.Layer, pr: CD.DrawRef] = BEGIN ms: MaskStateRef = NARROW[pr.devicePrivate]; IF l=currentDirective.cdSource AND CDBasics.NonEmpty[r] THEN ms.areas.ChangeRect[rect: CDRectToCIF[r], new: $covered]; END; BloatTile: PROCEDURE [tile: CStitching.Tile, data: REF ANY] = -- CStitching.PerTileProc -- BEGIN ms: MaskStateRef = NARROW[data]; cr: CD.Rect = CDBasics.Intersection[CDBasics.universe, Bloat[tile.Area, ABS[currentDirective.deltaRadius/nmPerCIF]]]; IF CDBasics.NonEmpty[cr] THEN ms.areas.ChangeRect[rect: cr, new: tile.value]; END; Bloat: PROC [r: CD.Rect, delta: CD.Number] RETURNS [br: CD.Rect] = INLINE BEGIN br _ [x1: MAX[FIRST[CD.Number]+delta, r.x1]-delta, y1: MAX[FIRST[CD.Number]+delta, r.y1]-delta, x2: MIN[LAST[CD.Number]-delta, r.x2]+delta, y2: MIN[LAST[CD.Number]-delta, r.y2]+delta] END; CDRectToCIF: PROC [ cdr: CD.Rect ] RETURNS [ cifr: CIFRect ] = BEGIN cifr _ CDBasics.NormalizeRect[[ x1: cifPerLambda*cdr.x1/lambda, y1: cifPerLambda*cdr.y1/lambda, x2: cifPerLambda*cdr.x2/lambda, y2: cifPerLambda*cdr.y2/lambda ]] END; CDPosToCIF: PROC [ cdp: CD.Position ] RETURNS [ cifp: CIFPos ] = BEGIN cifp _ [x: cifPerLambda*cdp.x/lambda, y: cifPerLambda*cdp.y/lambda] END; CDCIFUp: PROC [ n: CD.Number ] RETURNS [ cif: CIFUnits ] = BEGIN cif _ (cifPerLambda*n+lambda-1)/lambda END; CIFRectToCD: PROC [ cifr: CIFRect ] RETURNS [ cdr: CD.Rect ] = BEGIN cdr _ CDBasics.NormalizeRect[[ x1: lambda*cifr.x1/cifPerLambda, y1: lambda*cifr.y1/cifPerLambda, x2: (lambda*cifr.x2+cifPerLambda-1)/cifPerLambda, y2: (lambda*cifr.y2+cifPerLambda-1)/cifPerLambda ]] END; PrescaleCIF: PROC [ cif: Rational ] RETURNS [ INT ] = BEGIN n: INT = cif.num*cifScaling.denom; d: INT = cif.denom*cifScaling.num; IF ABS[n] MOD ABS[d] # 0 THEN ERROR CantRepresentExactly; RETURN[n/d]; END; Ceiling: PROC [num, denom: CD.Number] RETURNS [ c: INT ] = BEGIN c _ (num+denom-1)/denom END; ReduceTerms: PROC [ r: Rational ] RETURNS [ Rational ] = BEGIN gcd: INT = GCD[r.num, r.denom]; RETURN[[num: r.num/gcd, denom: r.denom/gcd]]; END; GCD: PROC [ m, n: INT ] RETURNS [ INT ] = BEGIN r: INT; SELECT m FROM <0 => m _ -m; ENDCASE => NULL; SELECT n FROM <0 => n _ -n; >0 => NULL; ENDCASE => RETURN[m]; r _ m MOD n; WHILE r>0 DO m _ n; n _ r; r _ m MOD n; ENDLOOP; RETURN[n]; END; WriteCIFComm: PROC [comm: CDSequencer.Command] = BEGIN TerminalIO.WriteRope["Hierarchical CIF output\n"]; TerminalIO.WriteRope["Version of December 6, 1985 11:17:55 am PST\n"]; TerminalIO.WriteRope["better read back the cif file and look at it\n"]; IF CDCells.IsPushedIn[comm.design] THEN { TerminalIO.WriteRope["**Design is pushed in\n"]; RETURN }; IF CDImports.HasUnloadedImports[comm.design] THEN { TerminalIO.WriteRope["**Design has non bound imports\n"]; RETURN }; [] _ CDCommandOps.CallWithResource[WriteCIF, comm, $CIF, stopFlag]; END; RegisterRegistration: PROC [] = TRUSTED BEGIN new: LIST OF REF ANY _ LIST[$CDxCIFName, $CDxCIFTechnology, $CDxCIFSpecials, $CDxCIFUnitsPerLambda]; list: LIST OF REF ANY _ WITH CDProperties.GetAtomProp[$RegistrationRoot, $CDxCIFRegistrations] SELECT FROM lora: LIST OF REF ANY => lora, loa: LIST OF ATOM => LOOPHOLE[loa], a: ATOM=> LIST[a], ENDCASE => NIL; list _ List.Nconc[list, new]; CDProperties.PutAtomProp[onto: $RegistrationRoot, prop: $CDxCIFRegistrations, val: list]; END; RegisterRegistration[]; CDSequencer.ImplementCommand[key: $WriteCif, proc: WriteCIFComm, queue: doQueue]; CDMenus.CreateEntry[menu: $ProgramMenu, entry: "Hierarchical CIF", key: $WriteCif]; END. ψBrandyCIFter.mesa A module to output CIF files from ChipNDale Copyright c 1984, 1985 by Xerox Corporation. All rights reserved. Last Edited by: Kimr, October 15, 1984 5:15:13 pm PDT Last Edited by: Jacobi, October 30, 1984 12:10:38 pm PST Last Edited by McCreight, October 17, 1984 7:02:18 pm PDT Last Edited by Jacobi, March 25, 1986 5:22:24 pm PST --TYPES --Global Variables --layer assumed to be unchanged until the next layer is announced cifPerCD: INT; -- cifPerLambda/l, since l is defined in CD units per lambda --..output numbers*cifScaling = CIFUnits --..CIFUnits/cifScaling = output numbers --set to design size in CIFMeasureArea, used to switch between ChipNDale and CIF coordinate systems --Procedures --%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --used to set all CellIDNo's to NIL at the start --start outputting a new cell and assign its cellID --start outputting a new cell and assign its cellID --but don't output the scaling --finish off cell description --logically local to GenerateCompositeOb --PROC [inst: Instance, pos: Position, orient: Orientation, pr: REF DrawInformation] --logically local to GenerateCompositeOb --PROC [inst: Instance, pos: Position, orient: Orientation, pr: REF DrawInformation] --output composite objects --the children must be already defined --match the type of drawMe parameter --call the children --draw the rectangles --IF currentDirective.deltaRadius>=0 THEN redundant but easier to understand --output CIF for structures only identified as geometries --match the type of drawMe parameter --IF currentDirective.deltaRadius>=0 THEN redundant but easier to understand --output CIF for structures for polygons --points are in design coords, not yet CIF coords --Print out CIF pertaining to object starting with the furthest object from the top; After leaving procedure, object will have been assigned a CIF ID # in its properties field --non-NIL value indicates that object has already been assigned an ID # and is therefore already defined --We should also make a test here to decide whether any hierarchical geometry results from this object, and assign a special value to this object's attribute $CellIDNo if not. --For each member of the instance list, determine whether obj is a cell. If so, determine transformations --for each layer, pick out the rectangles belonging to that layer --and output them --IF currentDirective.deltaRadius>=0 THEN removed for simplicity --generate all signal names --pos in CIFUnits --indicate which cell is called using its ID number --OUTPUT PROCEDURES -- CD.DrawRectProc -- Sets mainRect to be a large as is necessary to contain design. --.. adds the condition that cif/cifScaling is guaranteed to be an integer, so cif*cifScaling.denom MOD cifScaling.num = 0 --assign world variable from sequencer --initialize main object, layer names, resolution, cellCount, and draw records --Search world data structure: normalize coordinates, --set mainRect to design size, mark used layers --get file name cifPerCD _ cifPerLambda/lambda; need not be integer.. --output header --ask for comment lines --define hybrid top layer object --Flat CIF code --the tesselation is in cif coordinates --Be careful not to exceed the limits of a CD.Number, even temporarily --rounds to rect beeing bigger Κ#˜codešœ?™?Kšœ Οmœ7™BKšœ5™5Kšœ8™8K™9Kšœ4™4—K˜šΟk ˜ Kšœžœ ˜Kšžœ˜Kšœ ˜ Kšœ˜Kšœ ˜ Kšœ ˜ Kšœ ˜ Kšœ ˜ Kšœ žœ ˜Kšžœ˜Kšœ˜Kšœžœ˜Kšœ ˜ Kšœ ˜ Kšœ ˜ Kšœ žœ˜.Kšœ žœ˜(Kšœ ˜ Kšžœžœ˜Kšžœžœ žœ=žœ˜^Kšœžœ˜Kšœ˜Kšœ ˜ —Kšœ˜šΟb œžœž˜Kš žœžœEžœ\žœžœ˜Σ—Kšž˜K˜KšŸ™™Kšœ žœžœ˜Kšœžœžœ Οcœ˜.Kšœ žœžœ œ˜+Kšœ žœžœžœ˜.Kšœ žœžœžœ˜%šœ žœžœ˜Kšœžœ˜Kšœ žœ $˜5Kšœ˜—Kšœžœžœžœ˜/šœžœžœ˜ Kšœ žœ˜Kšœžœ˜Kšœ žœ $˜5K˜—Kšœžœžœ ˜#šœ žœžœ˜Kšœ+žœ˜0Kšœ˜Kšœ˜K˜——K™KšŸ™˜Kš œ žœžœžœžœ ˜=K˜Kšœ žœžœ +˜EK˜Kšœžœ ˜Kšœ žœ žœžœ ˜*Kš œ žœžœžœžœ˜Kšœ žœ ˜Kšœ žœ ˜šœ žœ  ˜(Kšœžœžœžœžœžœžœ ˜\K˜—K˜Kšœ žœ žœžœ  (˜PKšœ˜Kšœ žœ ,˜FK˜šœžœžœ˜ KšœA™A—K˜Kšœžœ˜Kšœžœ  ˜1Kšœ žœ ;˜OKšœ žœ Πcg ‘ "™LK˜šœ˜Kšœ(™(Kšœ(™(—K˜Kšœ žœžœ $˜8Kšœ(žœ˜-K˜šœO˜OKšœc™c—K™Kš œžœžœžœž œžœ˜9Kšœ˜K˜Kšœ ˜4Kšœ˜K˜—Kšœ˜KšΟn ™ ˜š’ œžœ˜Kšž˜Kšœ)žœ ˜JKšœ žœ˜ Kšœ žœ˜Kšžœ˜—K˜š ’ œžœžœ žœžœ˜6Kšž ˜ Kšœžœ˜6Kšžœ˜——˜Kšœ ™ K˜KšœE˜EK˜š’œžœ˜Kšž˜KšœžœA˜Gšžœžœžœ˜9Kšžœžœžœ5˜BKšžœK˜OKšžœž˜ K˜—šžœ=žœž˜LKšœ7˜7Kšžœ3˜:—Kšžœ˜—K˜š ’ œžœžœ žœžœžœ˜?Kšž˜KšœHž˜KKšžœ˜—K˜š’œžœžœ ˜)Kšž˜šžœCžœž˜Rš œžœžœžœ žœžœ˜5Kšœžœ ˜Kšžœ˜K˜—šœžœ˜Kšžœ˜K˜—šœžœžœ˜Kšžœ˜K˜—šžœ˜ KšœH˜HKšž˜K˜——Kšžœ˜—K™K™Kšœ ™ K˜š’ œžœžœžœžœžœžœ œ˜€Kšœ0™0Kšž˜Kšœ@žœ˜Ešžœž˜!Kšœa˜a—Kšžœ˜—K˜š’ œžœžœ ˜!Kšœ3™3Kšž˜Kš œžœžœžœžœ ˜9šœ˜Kšžœžœžœ˜E—Kšœ3˜3Kšžœ˜K˜—š’œžœžœ ˜+Kšœ3™3Kšœ™Kšž˜Kš œžœžœžœžœ ˜9šœ˜Kšžœ˜—Kšœ3˜3Kšžœ˜K˜—š’ œžœ˜Kšœ™Kšž˜Kšœ˜Kšžœ˜K˜—Kšœ(™(šŸ œžœ ˜KšœT™TKšž˜šžœžœ˜šœ.˜.Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜—Kšœ'˜'Kšœ˜—Kšžœ˜—K˜Kšœ(™(šŸœžœ ˜KšœT™TKšž˜šžœžœ˜KšœC˜CKšœ˜—Kšžœ˜—K˜š’œžœžœ ˜,Kšœ™Kšœ&™&Kšž˜Kš œžœžœžœžœžœžœ˜<šœžœ žœ˜#Kšœ˜Kšœ˜Kšœ˜—šœ žœ žœžœ˜.Kšœ$™$—Kšœ˜Kšœ˜Kšœ'˜'Kšœ™Kšœžœ˜Kšœ˜Kšœ˜Kšœ5˜5Kšœ™Kšœ˜Kšœ˜Kšœ˜šžœ žœžœžœžœ"žœ žœž˜XKšœžœ˜,šœžœ˜Kšžœ!žœ"™L—Kšœ žœ˜%Kšœ5˜5Kšœ žœ˜&Kšžœ˜—Kšœ ˜ Kšžœ˜—K˜š’œžœžœ ˜,Kšœ9™9Kšž˜šœ žœ žœžœ˜.Kšœ$™$—Kšœ˜šžœžœ˜Kšœ˜Kšœ˜K˜—šžœ˜Kšœ˜šžœ žœžœžœžœ"žœ žœž˜XKšœžœ˜,šœžœ˜KšœL™L—Kšœ)˜)Kšœ+žœ˜0KšœJ˜JKšœ+žœ˜1Kšžœ˜—K˜—Kšœ ˜ Kšžœ˜—K˜š ’œžœ žœžœžœ˜7Kšž˜šžœžœžœžœžœžœžœž˜IKšœžœ˜)Kš žœžœžœžœžœ˜AKšžœ˜—Kšžœžœ˜Kšžœ˜—K˜š’œžœžœ ˜(Kšœ(™(Kšž˜K˜š ’ œžœ žœžœžœ ˜3Kšœ1™1Kšž˜šžœžœž˜Kšœžœ!˜;Kšœžœ˜Kšžœ˜—Kšœ˜š žœžœžœžœžœžœž˜:Kšœžœ ˜*Kšœ˜Kšœ ˜ Kšžœ˜—Kšœ˜Kšžœ˜—K˜Kšœ!žœ˜9Kšœžœžœ˜Kšœ˜šžœ žœžœžœžœ"žœ žœž˜XKšœžœ˜,Kšœžœ˜šžœ%ž˜-Kšžœ žœž˜2Kšœ˜K˜—Kšžœ˜—šžœžœ˜ Kšœ;˜;Kšœ7˜7K˜—Kšœ ˜ Kšžœ˜—K˜š’œžœžœ ˜(Kšœ°™°Kšž˜K˜šŸ œ%˜2Kšž˜Kšœ˜Kšžœ˜K˜—Kšžœ žœ4˜EKšœ˜šžœ,žœžœžœ˜AKšœh™h—Kšœ˜Kšœ―™―K˜šžœžœ˜Kšœ6žœ˜;K˜—šžœžœž˜ šœ ˜ Kšžœžœžœžœ˜'Kšœ)˜)Kšœ˜KšœE˜EKšœ'žœ˜Kšœm˜mKšžœžœ˜-Kšœ˜—Kšžœ˜—Kšœ˜š’ œžœ˜Kšž˜Kšžœžœžœžœ˜(šžœžœž˜Kšœžœ!˜;Kšœžœ˜Kšžœ˜—šœ  &˜FKšžœ"  œ˜2Kšžœ"  œ˜1Kšžœ" œ˜:Kšžœ" œ˜;—Kšžœ˜K˜—š’ œžœ˜#Kšž˜Kšœžœžœ˜MKšžœ˜K˜—š ’œžœžœžœžœ ˜IKšž˜KšœJ˜JKšœžœ ˜#Kšœ˜Kšœ˜šžœ žœžœžœžœ"žœ žœž˜XKšœžœ˜+Kšžœžœžœ˜šžœ˜ Kšžœžœžœž˜šžœ˜Kš œžœžœžœžœ˜!Kšžœ˜Kšœ&žœ ˜3Kšžœ žœ˜Kšœ˜———Kšžœ˜K˜—K˜Kšœžœžœ˜#K˜š’ œžœ˜Kšž˜Kšœ@ !˜ašžœ žœžœžœžœ"žœ žœž˜XKšœžœ˜+Kšœ˜šžœžœžœž˜+Kšžœ˜—Kšœžœ˜"šœ˜Kšœ˜Kšœ)žœ)˜UKšœz™z—Kšžœ˜—Kšžœ˜K˜K˜—š’œžœ˜Kšž˜K˜š’ œžœžœžœžœžœžœžœžœžœ˜PKšž˜šžœžœžœ˜šžœžœž˜Kšœžœžœ5˜PKš œžœžœžœžœS˜hKšžœžœžœ)˜@—Kšœ˜—Kšžœ˜K˜—Kšœžœ˜š žœ žœžœžœ3žœžœž˜\Kšœˆ˜ˆKšžœ˜—Kšžœ˜K˜—š’œžœ˜Kšž˜šžœCžœž˜RKšœžœžœ˜"Kšžœ˜—Kšœi˜išžœžœ˜Kšœ>˜>Kšžœž˜ K˜—Kšžœ˜—Kšœ˜š’œžœ˜,šžœ˜Kšžœž˜ Kšžœ9žœ˜Kšœ˜KšœžœžœLžœ˜xKšœ+˜+Kšžœ˜Kšœ˜—Kšžœžœ˜Kšžœ ˜ —Kšœ˜Kšœ&™&Kšœ˜Kšœ žœ˜Kšœ˜Kšœ"˜"Kšœ˜KšœI˜Išžœžœ˜KšœQ˜QKšž˜K˜—šœD˜DK˜—KšœO™OK™K˜Kšœ,˜,Kšœ˜Kšœ žœ˜Kšœ˜šœžœ˜ Kšœ ˜ Kšœ˜Kšœ˜Kšœ˜Kšœ˜—K˜šœžœ˜ Kšœ ˜ Kšœ˜Kšœ˜Kšœ˜Kšœ˜—Kšœ˜šœžœ˜ Kšœ ˜ Kšœ˜Kšœ˜Kšœ˜Kšœ˜—Kšœ˜Kšœ6™6Kšœ1™1K™Kšœ)˜)Kšœ˜K™Kšœ™Kšœ2žœ$˜ZKšœ%˜%Kšžœžœ˜.Kšžœžœ˜(Kšœžœ)žœ˜Všœ žœ˜(šžœ ˜ šžœžœ˜Kšœ)˜)Kšžœ˜Kšœ˜—Kšœ˜Kšœ˜——Kšœ5™5K˜Kšœ™Kšœžœ˜0KšœLžœ ˜WKšœ3žœ˜IKšœNžœ(žœ(˜’Kšœ#žœ˜4K˜Kšœ™Kšœ)˜)KšœR˜Ršžœžœž˜"Kšœ,˜,Kšœ žœ˜Kšœ0˜0šœ˜Kšœ:˜:—Kšžœ˜—šžœžœžœ˜Kšœ˜K˜—Kšœ˜Kšœ˜Kšœ6žœ˜;Kšœ˜K˜Kšœ ™ Kšœ˜Kšœ˜šžœ žœžœžœžœ"žœ žœž˜XKšœžœ˜,šžœ žœ˜(Kšœžœ˜Kšœ"˜"Kšœ˜—Kšžœ˜—K˜ K˜Kšœy˜yKšœ#˜#Kšœ˜Kšœ(žœ ˜7Kšœ ˜ šž˜Kšœ˜šœ˜Kšœ'˜'Kšœ ˜ Kšœ˜——Kšžœ ˜K˜—K˜—šΡbls™K˜—™š’ œžœ žœ˜3Kšž˜K˜š’œžœ˜-Kšž˜Kšœ'™'Kšœžœ˜"šžœžœ˜#šžœ žœ˜(Kšœ)˜)Kšœ˜Kšœ ˜ KšœEžœ˜Jšžœ ž˜&Kšœe˜e—šžœ˜KšœD˜DKšœv˜vKšœ˜—Kšœ˜—Kšœc˜cKšœ˜Kšœ˜—Kšžœ˜K˜—š ’ œž œžœžœžœ ˜YKšžœ +˜1Kšœžœ˜ Kšœ<˜K˜Kšœ4žœ žœ ˜RK™Kšœ˜K˜K˜Kšœžœ žœ(žœ!˜zšžœžœžœ ž˜šžœžœžœ ž˜Kšœ2žœ˜7šœ ˜Kšœ˜Kšœ˜Kšœžœ1˜QKšœžœ2˜SKšœ˜—Kšœ=žœ*˜jšœ"˜"Kšœd ˜v—Kšœ 5˜SKšœ ˜6Kšžœ˜—Kšžœ˜—Kšœ˜Kšœ%˜%Kšœžœ˜ Kšžœ˜K˜—š ’œžœžœ žœ žœ ˜CKšž˜Kšœžœžœžœ˜+Kšœ ˜ Kšžœ˜K˜—š ’ œžœžœ žœ žœ ˜?Kšž˜Kšœžœ˜,šžœžœž˜Kšž˜šœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜—Kšžœ˜K˜—š’ œžœžœ žœ˜@Kšž˜KšœC˜CKšžœ˜K˜—š’œžœžœ žœ˜:Kšž˜Kšœ&˜&Kšžœ˜K˜—š’ œžœžœžœ ˜>Kšœ™šž˜šœ˜Kšœ ˜ Kšœ ˜ Kšœ1˜1Kšœ0˜0Kšœ˜——Kšžœ˜—K˜š’ œžœžœžœ˜5Kšž˜Kšœžœ˜"Kšœžœ˜"Kš žœžœžœžœžœžœ˜9Kšžœ˜ šžœ˜K˜——š ’œžœžœ žœžœ˜:Kšž˜Kšœ˜Kšžœ˜K˜—š’ œžœžœ˜8Kšž˜Kšœžœžœ˜Kšžœ'˜-Kšžœ˜K˜—š Πbkœžœ žœžœžœ˜)Kšž˜Kšœžœ˜šžœž˜ Kšœ ˜ Kšžœžœ˜—šžœž˜ Kšœ ˜ Kšœžœ˜ Kšžœžœ˜—Kšœžœ˜ Kšžœžœžœžœ˜0Kšžœ˜ Kšžœ˜——K˜K˜š£ œžœ˜0Kšžœ˜Kšœ3˜3KšœG˜GKšœH˜Hšžœ!žœ˜)Kšœ1˜1Kšž˜K˜—šžœ+žœ˜3Kšœ:˜:Kšž˜K˜—KšœC˜CKšžœ˜K˜—K˜š’œžœ˜Kšžœž˜ š œžœžœžœžœ˜KšžœI˜M—šœžœžœžœžœžœCžœž˜jKš œžœžœžœžœ ˜Kš œžœžœžœžœ˜#Kšœžœžœ˜Kšžœžœ˜—Kšœ˜KšœY˜YKšžœ˜—K˜K˜Kšœ˜KšœQ˜QKšœS˜SKšžœ˜—…—ad‘Ϋ